Prepare new maemo release
[maemo-rb.git] / apps / plugins / rockpaint.c
blob37f9ee9429e37d3210f089bb2ec3f81157309a86
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 #elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
236 #define ROCKPAINT_QUIT BUTTON_POWER
237 #define ROCKPAINT_DRAW BUTTON_SELECT
238 #define ROCKPAINT_MENU BUTTON_VOL_DOWN
239 #define ROCKPAINT_TOOLBAR BUTTON_PREV
240 #define ROCKPAINT_TOOLBAR2 BUTTON_NEXT
241 #define ROCKPAINT_UP BUTTON_UP
242 #define ROCKPAINT_DOWN BUTTON_DOWN
243 #define ROCKPAINT_LEFT BUTTON_LEFT
244 #define ROCKPAINT_RIGHT BUTTON_RIGHT
246 #elif CONFIG_KEYPAD == SAMSUNG_YPR0_PAD
247 #define ROCKPAINT_QUIT BUTTON_BACK
248 #define ROCKPAINT_DRAW BUTTON_SELECT
249 #define ROCKPAINT_MENU BUTTON_MENU
250 #define ROCKPAINT_TOOLBAR BUTTON_USER
251 #define ROCKPAINT_TOOLBAR2 ( BUTTON_USER | BUTTON_REPEAT )
252 #define ROCKPAINT_UP BUTTON_UP
253 #define ROCKPAINT_DOWN BUTTON_DOWN
254 #define ROCKPAINT_LEFT BUTTON_LEFT
255 #define ROCKPAINT_RIGHT BUTTON_RIGHT
257 #elif (CONFIG_KEYPAD == HM60X_PAD)
258 #define ROCKPAINT_QUIT BUTTON_POWER
259 #define ROCKPAINT_DRAW BUTTON_SELECT
260 #define ROCKPAINT_MENU (BUTTON_POWER | BUTTON_SELECT)
261 #define ROCKPAINT_TOOLBAR (BUTTON_POWER | BUTTON_UP)
262 #define ROCKPAINT_TOOLBAR2 (BUTTON_POWER | BUTTON_LEFT)
263 #define ROCKPAINT_UP BUTTON_UP
264 #define ROCKPAINT_DOWN BUTTON_DOWN
265 #define ROCKPAINT_LEFT BUTTON_LEFT
266 #define ROCKPAINT_RIGHT BUTTON_RIGHT
268 #elif (CONFIG_KEYPAD == HM801_PAD)
269 #define ROCKPAINT_QUIT BUTTON_POWER
270 #define ROCKPAINT_DRAW BUTTON_SELECT
271 #define ROCKPAINT_MENU BUTTON_PLAY
272 #define ROCKPAINT_TOOLBAR BUTTON_PREV
273 #define ROCKPAINT_TOOLBAR2 BUTTON_NEXT
274 #define ROCKPAINT_UP BUTTON_UP
275 #define ROCKPAINT_DOWN BUTTON_DOWN
276 #define ROCKPAINT_LEFT BUTTON_LEFT
277 #define ROCKPAINT_RIGHT BUTTON_RIGHT
279 #else
280 #error "Please define keys for this keypad"
281 #endif
283 #ifdef HAVE_TOUCHSCREEN
284 #ifndef ROCKPAINT_QUIT
285 #define ROCKPAINT_QUIT BUTTON_TOPLEFT
286 #endif
287 #ifndef ROCKPAINT_DRAW
288 #define ROCKPAINT_DRAW BUTTON_CENTER
289 #endif
290 #ifndef ROCKPAINT_MENU
291 #define ROCKPAINT_MENU BUTTON_TOPRIGHT
292 #endif
293 #ifndef ROCKPAINT_TOOLBAR
294 #define ROCKPAINT_TOOLBAR BUTTON_BOTTOMLEFT
295 #endif
296 #ifndef ROCKPAINT_TOOLBAR2
297 #define ROCKPAINT_TOOLBAR2 BUTTON_BOTTOMRIGHT
298 #endif
299 #ifndef ROCKPAINT_UP
300 #define ROCKPAINT_UP BUTTON_TOPMIDDLE
301 #endif
302 #ifndef ROCKPAINT_DOWN
303 #define ROCKPAINT_DOWN BUTTON_BOTTOMMIDDLE
304 #endif
305 #ifndef ROCKPAINT_LEFT
306 #define ROCKPAINT_LEFT BUTTON_MIDLEFT
307 #endif
308 #ifndef ROCKPAINT_RIGHT
309 #define ROCKPAINT_RIGHT BUTTON_MIDRIGHT
310 #endif
311 #endif
313 /***********************************************************************
314 * Palette Default Colors
315 ***********************************************************************/
316 #define COLOR_BLACK LCD_RGBPACK(0,0,0)
317 #define COLOR_WHITE LCD_RGBPACK(255,255,255)
318 #define COLOR_DARKGRAY LCD_RGBPACK(128,128,128)
319 #define COLOR_LIGHTGRAY LCD_RGBPACK(192,192,192)
320 #define COLOR_RED LCD_RGBPACK(128,0,0)
321 #define COLOR_LIGHTRED LCD_RGBPACK(255,0,0)
322 #define COLOR_DARKYELLOW LCD_RGBPACK(128,128,0)
323 #define COLOR_YELLOW LCD_RGBPACK(255,255,0)
324 #define COLOR_GREEN LCD_RGBPACK(0,128,0)
325 #define COLOR_LIGHTGREN LCD_RGBPACK(0,255,0)
326 #define COLOR_CYAN LCD_RGBPACK(0,128,128)
327 #define COLOR_LIGHTCYAN LCD_RGBPACK(0,255,255)
328 #define COLOR_BLUE LCD_RGBPACK(0,0,128)
329 #define COLOR_LIGHTBLUE LCD_RGBPACK(0,0,255)
330 #define COLOR_PURPLE LCD_RGBPACK(128,0,128)
331 #define COLOR_PINK LCD_RGBPACK(255,0,255)
332 #define COLOR_BROWN LCD_RGBPACK(128,64,0)
333 #define COLOR_LIGHTBROWN LCD_RGBPACK(255,128,64)
335 /***********************************************************************
336 * Program Colors
337 ***********************************************************************/
338 #define ROCKPAINT_PALETTE LCD_RGBPACK(0,64,128)
339 #define ROCKPAINT_SELECTED LCD_RGBPACK(128,192,255)
341 #define ROWS LCD_HEIGHT
342 #define COLS LCD_WIDTH
345 * Toolbar positioning stuff ... don't read this unless you really need to
347 * TB Toolbar
348 * SP Separator
349 * SC Selected Color
350 * PL Palette
351 * TL Tools
354 /* Separator sizes */
355 #define TB_SP_MARGIN 3
356 #define TB_SP_WIDTH (2+2*TB_SP_MARGIN)
358 /* Selected color sizes */
359 #define TB_SC_SIZE 12
361 /* Palette sizes */
362 #define TB_PL_COLOR_SIZE 7
363 #define TB_PL_COLOR_SPACING 2
364 #define TB_PL_WIDTH ( 9 * TB_PL_COLOR_SIZE + 8 * TB_PL_COLOR_SPACING )
365 #define TB_PL_HEIGHT ( TB_PL_COLOR_SIZE * 2 + TB_PL_COLOR_SPACING )
367 /* Tools sizes */
368 #define TB_TL_SIZE 8
369 #define TB_TL_SPACING 2
370 #define TB_TL_WIDTH ( 7 * ( TB_TL_SIZE + TB_TL_SPACING ) - TB_TL_SPACING )
371 #define TB_TL_HEIGHT ( 2 * TB_TL_SIZE + TB_TL_SPACING )
373 /* Menu button size ... gruik */
374 #define TB_MENU_MIN_WIDTH 30
376 /* Selected colors position */
377 #define TB_SC_FG_TOP 2
378 #define TB_SC_FG_LEFT 2
379 #define TB_SC_BG_TOP (TB_SC_FG_TOP+TB_PL_COLOR_SIZE*2+TB_PL_COLOR_SPACING-TB_SC_SIZE)
380 #define TB_SC_BG_LEFT (TB_SC_FG_LEFT+TB_PL_COLOR_SIZE*2+TB_PL_COLOR_SPACING-TB_SC_SIZE)
382 /* Palette position */
383 #define TB_PL_TOP TB_SC_FG_TOP
384 #define TB_PL_LEFT (TB_SC_BG_LEFT + TB_SC_SIZE + TB_PL_COLOR_SPACING)
386 /* Tools position */
387 #define TB_TL_TOP TB_SC_FG_TOP
388 #define TB_TL_LEFT ( TB_PL_LEFT + TB_PL_WIDTH-1 + TB_SP_WIDTH )
390 #if TB_TL_LEFT + TB_TL_WIDTH + TB_MENU_MIN_WIDTH >= LCD_WIDTH
391 #undef TB_TL_TOP
392 #undef TB_TL_LEFT
393 #define TB_TL_TOP ( TB_PL_TOP + TB_PL_HEIGHT + 4 )
394 #define TB_TL_LEFT TB_SC_FG_LEFT
395 #endif
397 /* Menu button position */
398 #define TB_MENU_TOP ( TB_TL_TOP + (TB_TL_HEIGHT-8)/2 )
399 #define TB_MENU_LEFT ( TB_TL_LEFT + TB_TL_WIDTH-1 + TB_SP_WIDTH )
401 #define TB_HEIGHT ( TB_TL_TOP + TB_TL_HEIGHT + 1 )
404 static void draw_pixel(int x,int y);
405 static void draw_line( int x1, int y1, int x2, int y2 );
406 static void draw_rect( int x1, int y1, int x2, int y2 );
407 static void draw_rect_full( int x1, int y1, int x2, int y2 );
408 static void draw_toolbars(bool update);
409 static void inv_cursor(bool update);
410 static void restore_screen(void);
411 static void clear_drawing(void);
412 static void reset_tool(void);
413 static void goto_menu(void);
414 static int load_bitmap( const char *filename );
415 static int save_bitmap( char *filename );
417 /***********************************************************************
418 * Global variables
419 ***********************************************************************/
421 static int drawcolor=0; /* Current color (in palette) */
422 static int bgdrawcolor=9; /* Current background color (in palette) */
423 static int img_height = ROWS;
424 static int img_width = COLS;
425 bool isbg = false; /* gruik ugly hack alert */
427 static int preview=false; /* Is preview mode on ? */
429 /* TODO: clean this up */
430 static int x=0, y=0; /* cursor position */
431 static int prev_x=-1, prev_y=-1; /* previous saved cursor position */
432 static int prev_x2=-1, prev_y2=-1;
433 static int prev_x3=-1, prev_y3=-1;
436 static int bsize=1; /* brush size */
437 static int bspeed=1; /* brush speed */
439 enum Tools { Brush = 0, /* Regular brush */
440 Fill = 1, /* Fill a shape with current color */
441 SelectRectangle = 2,
442 ColorPicker = 3, /* Pick a color */
443 Line = 4, /* Draw a line between two points */
444 Unused = 5, /* THIS IS UNUSED ... */
445 Curve = 6,
446 Text = 7,
447 Rectangle = 8, /* Draw a rectangle */
448 RectangleFull = 9,
449 Oval = 10, /* Draw an oval */
450 OvalFull = 11,
451 LinearGradient = 12,
452 RadialGradient = 13
455 enum States { State0 = 0, /* initial state */
456 State1,
457 State2,
458 State3,
461 enum Tools tool = Brush;
462 enum States state = State0;
464 static bool quit=false;
465 static int gridsize=0;
467 static fb_data rp_colors[18] =
469 COLOR_BLACK, COLOR_DARKGRAY, COLOR_RED, COLOR_DARKYELLOW,
470 COLOR_GREEN, COLOR_CYAN, COLOR_BLUE, COLOR_PURPLE, COLOR_BROWN,
471 COLOR_WHITE, COLOR_LIGHTGRAY, COLOR_LIGHTRED, COLOR_YELLOW,
472 COLOR_LIGHTGREN, COLOR_LIGHTCYAN, COLOR_LIGHTBLUE, COLOR_PINK,
473 COLOR_LIGHTBROWN
476 static fb_data save_buffer[ ROWS*COLS ];
478 struct tool_func {
479 void (*state_func)(void);
480 void (*preview_func)(void);
483 struct incdec_ctx {
484 int max;
485 int step[2];
486 bool wrap;
488 struct incdec_ctx incdec_x = { COLS, { 1, 4}, true };
489 struct incdec_ctx incdec_y = { ROWS, { 1, 4}, true };
491 /* Maximum string size allowed for the text tool */
492 #define MAX_TEXT 256
494 union buf
496 /* Used by fill and gradient algorithms */
497 struct
499 short x;
500 short y;
501 } coord[ ROWS*COLS ];
503 /* Used by bezier curve algorithms */
504 struct
506 short x1, y1;
507 short x2, y2;
508 short x3, y3;
509 short x4, y4;
510 short depth;
511 } bezier[ (ROWS*COLS)/5 ]; /* We have 4.5 times more data per struct
512 * than coord ... so we divide to take
513 * less memory. */
515 /* Used to cut/copy/paste data */
516 fb_data clipboard[ ROWS*COLS ];
518 /* Used for text mode */
519 struct
521 char text[MAX_TEXT];
522 char font[MAX_PATH];
523 bool initialized;
524 size_t cache_used;
525 /* fonts from cache_first to cache_last are stored. */
526 int cache_first;
527 int cache_last;
528 /* save these so that cache can be re-used next time. */
529 int fvi;
530 int si;
531 } text;
534 static union buf *buffer;
535 static bool audio_buf = false;
537 /* Current filename */
538 static char filename[MAX_PATH];
540 static bool incdec_value(int *pval, struct incdec_ctx *ctx, bool inc, bool bigstep)
542 bool of = true;
543 int step = ctx->step[bigstep?1:0];
544 step = inc?step: -step;
545 *pval += step;
546 if (ctx->wrap)
548 if (*pval < 0) *pval += ctx->max;
549 else if (*pval >= ctx->max) *pval -= ctx->max;
550 else of = false;
552 else
554 if (*pval < 0) *pval = 0;
555 else if (*pval > ctx->max) *pval = ctx->max;
556 else of = false;
558 return of;
561 /***********************************************************************
562 * Offscreen buffer/Text/Fonts handling
564 * Parts of code taken from firmware/drivers/lcd-16bit.c
565 ***********************************************************************/
566 static void buffer_mono_bitmap_part(
567 fb_data *buf, int buf_width, int buf_height,
568 const unsigned char *src, int src_x, int src_y,
569 int stride, int x, int y, int width, int height )
570 /* this function only draws the foreground part of the bitmap */
572 const unsigned char *src_end;
573 fb_data *dst, *dst_end;
574 unsigned fgcolor = rb->lcd_get_foreground();
576 /* nothing to draw? */
577 if( ( width <= 0 ) || ( height <= 0 ) || ( x >= buf_width )
578 || ( y >= buf_height ) || ( x + width <= 0 ) || ( y + height <= 0 ) )
579 return;
581 /* clipping */
582 if( x < 0 )
584 width += x;
585 src_x -= x;
586 x = 0;
588 if( y < 0 )
590 height += y;
591 src_y -= y;
592 y = 0;
594 if( x + width > buf_width )
595 width = buf_width - x;
596 if( y + height > buf_height )
597 height = buf_height - y;
599 src += stride * (src_y >> 3) + src_x; /* move starting point */
600 src_y &= 7;
601 src_end = src + width;
603 dst = buf + y*buf_width + x;
607 const unsigned char *src_col = src++;
608 unsigned data = *src_col >> src_y;
609 fb_data *dst_col = dst++;
610 int numbits = 8 - src_y;
612 dst_end = dst_col + height * buf_width;
615 if( data & 0x01 )
616 *dst_col = fgcolor; /* FIXME ? */
618 dst_col += buf_width;
620 data >>= 1;
621 if( --numbits == 0 )
623 src_col += stride;
624 data = *src_col;
625 numbits = 8;
627 } while( dst_col < dst_end );
628 } while( src < src_end );
631 /* draw alpha bitmap for anti-alias font */
632 #define ALPHA_COLOR_FONT_DEPTH 2
633 #define ALPHA_COLOR_LOOKUP_SHIFT (1 << ALPHA_COLOR_FONT_DEPTH)
634 #define ALPHA_COLOR_LOOKUP_SIZE ((1 << ALPHA_COLOR_LOOKUP_SHIFT) - 1)
635 #define ALPHA_COLOR_PIXEL_PER_BYTE (8 >> ALPHA_COLOR_FONT_DEPTH)
636 #define ALPHA_COLOR_PIXEL_PER_WORD (32 >> ALPHA_COLOR_FONT_DEPTH)
637 #ifdef CPU_ARM
638 #define BLEND_INIT do {} while (0)
639 #define BLEND_START(acc, color, alpha) \
640 asm volatile("mul %0, %1, %2" : "=&r" (acc) : "r" (color), "r" (alpha))
641 #define BLEND_CONT(acc, color, alpha) \
642 asm volatile("mla %0, %1, %2, %0" : "+&r" (acc) : "r" (color), "r" (alpha))
643 #define BLEND_OUT(acc) do {} while (0)
644 #elif defined(CPU_COLDFIRE)
645 #define ALPHA_BITMAP_READ_WORDS
646 #define BLEND_INIT coldfire_set_macsr(EMAC_UNSIGNED)
647 #define BLEND_START(acc, color, alpha) \
648 asm volatile("mac.l %0, %1, %%acc0" :: "%d" (color), "d" (alpha))
649 #define BLEND_CONT BLEND_START
650 #define BLEND_OUT(acc) asm volatile("movclr.l %%acc0, %0" : "=d" (acc))
651 #else
652 #define BLEND_INIT do {} while (0)
653 #define BLEND_START(acc, color, alpha) ((acc) = (color) * (alpha))
654 #define BLEND_CONT(acc, color, alpha) ((acc) += (color) * (alpha))
655 #define BLEND_OUT(acc) do {} while (0)
656 #endif
658 /* Blend the given two colors */
659 static inline unsigned blend_two_colors(unsigned c1, unsigned c2, unsigned a)
661 a += a >> (ALPHA_COLOR_LOOKUP_SHIFT - 1);
662 #if (LCD_PIXELFORMAT == RGB565SWAPPED)
663 c1 = swap16(c1);
664 c2 = swap16(c2);
665 #endif
666 unsigned c1l = (c1 | (c1 << 16)) & 0x07e0f81f;
667 unsigned c2l = (c2 | (c2 << 16)) & 0x07e0f81f;
668 unsigned p;
669 BLEND_START(p, c1l, a);
670 BLEND_CONT(p, c2l, ALPHA_COLOR_LOOKUP_SIZE + 1 - a);
671 BLEND_OUT(p);
672 p = (p >> ALPHA_COLOR_LOOKUP_SHIFT) & 0x07e0f81f;
673 p |= (p >> 16);
674 #if (LCD_PIXELFORMAT == RGB565SWAPPED)
675 return swap16(p);
676 #else
677 return p;
678 #endif
681 static void buffer_alpha_bitmap_part(
682 fb_data *buf, int buf_width, int buf_height,
683 const unsigned char *src, int src_x, int src_y,
684 int stride, int x, int y, int width, int height )
686 fb_data *dst;
687 unsigned fg_pattern = rb->lcd_get_foreground();
689 /* nothing to draw? */
690 if ((width <= 0) || (height <= 0) || (x >= buf_width) ||
691 (y >= buf_height) || (x + width <= 0) || (y + height <= 0))
692 return;
694 /* initialize blending */
695 BLEND_INIT;
697 /* clipping */
698 if (x < 0)
700 width += x;
701 src_x -= x;
702 x = 0;
704 if (y < 0)
706 height += y;
707 src_y -= y;
708 y = 0;
710 if (x + width > buf_width)
711 width = buf_width - x;
712 if (y + height > buf_height)
713 height = buf_height - y;
715 dst = buf + y*buf_width + x;
717 int col, row = height;
718 unsigned data, pixels;
719 unsigned skip_end = (stride - width);
720 unsigned skip_start = src_y * stride + src_x;
722 #ifdef ALPHA_BITMAP_READ_WORDS
723 uint32_t *src_w = (uint32_t *)((uintptr_t)src & ~3);
724 skip_start += ALPHA_COLOR_PIXEL_PER_BYTE * ((uintptr_t)src & 3);
725 src_w += skip_start / ALPHA_COLOR_PIXEL_PER_WORD;
726 data = letoh32(*src_w++);
727 #else
728 src += skip_start / ALPHA_COLOR_PIXEL_PER_BYTE;
729 data = *src;
730 #endif
731 pixels = skip_start % ALPHA_COLOR_PIXEL_PER_WORD;
732 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
733 #ifdef ALPHA_BITMAP_READ_WORDS
734 pixels = 8 - pixels;
735 #endif
739 col = width;
740 #ifdef ALPHA_BITMAP_READ_WORDS
741 #define UPDATE_SRC_ALPHA do { \
742 if (--pixels) \
743 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
744 else \
746 data = letoh32(*src_w++); \
747 pixels = ALPHA_COLOR_PIXEL_PER_WORD; \
749 } while (0)
750 #elif ALPHA_COLOR_PIXEL_PER_BYTE == 2
751 #define UPDATE_SRC_ALPHA do { \
752 if (pixels ^= 1) \
753 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
754 else \
755 data = *(++src); \
756 } while (0)
757 #else
758 #define UPDATE_SRC_ALPHA do { \
759 if (pixels = (++pixels % ALPHA_COLOR_PIXEL_PER_BYTE)) \
760 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
761 else \
762 data = *(++src); \
763 } while (0)
764 #endif
768 *dst=blend_two_colors(*dst, fg_pattern,
769 data & ALPHA_COLOR_LOOKUP_SIZE );
770 dst++;
771 UPDATE_SRC_ALPHA;
773 while (--col);
774 #ifdef ALPHA_BITMAP_READ_WORDS
775 if (skip_end < pixels)
777 pixels -= skip_end;
778 data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
779 } else {
780 pixels = skip_end - pixels;
781 src_w += pixels / ALPHA_COLOR_PIXEL_PER_WORD;
782 pixels %= ALPHA_COLOR_PIXEL_PER_WORD;
783 data = letoh32(*src_w++);
784 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
785 pixels = 8 - pixels;
787 #else
788 if (skip_end)
790 pixels += skip_end;
791 if (pixels >= ALPHA_COLOR_PIXEL_PER_BYTE)
793 src += pixels / ALPHA_COLOR_PIXEL_PER_BYTE;
794 pixels %= ALPHA_COLOR_PIXEL_PER_BYTE;
795 data = *src;
796 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
797 } else
798 data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
800 #endif
801 dst += LCD_WIDTH - width;
802 } while (--row);
805 static void buffer_putsxyofs( fb_data *buf, int buf_width, int buf_height,
806 int x, int y, int ofs, const unsigned char *str )
808 unsigned short ch;
809 unsigned short *ucs;
811 struct font *pf = rb->font_get( FONT_UI );
812 if( !pf ) pf = rb->font_get( FONT_SYSFIXED );
814 ucs = rb->bidi_l2v( str, 1 );
816 while( (ch = *ucs++) != 0 && x < buf_width )
818 int width;
819 const unsigned char *bits;
821 /* get proportional width and glyph bits */
822 width = rb->font_get_width( pf, ch );
824 if( ofs > width )
826 ofs -= width;
827 continue;
830 bits = rb->font_get_bits( pf, ch );
832 if (pf->depth)
833 buffer_alpha_bitmap_part( buf, buf_width, buf_height, bits, ofs, 0,
834 width, x, y, width - ofs, pf->height);
835 else
836 buffer_mono_bitmap_part( buf, buf_width, buf_height, bits, ofs, 0,
837 width, x, y, width - ofs, pf->height);
839 x += width - ofs;
840 ofs = 0;
844 /***********************************************************************
845 * Menu handling
846 ***********************************************************************/
847 enum {
848 /* Main menu */
849 MAIN_MENU_RESUME,
850 MAIN_MENU_NEW, MAIN_MENU_LOAD, MAIN_MENU_SAVE,
851 MAIN_MENU_SET_WIDTH, MAIN_MENU_SET_HEIGHT,
852 MAIN_MENU_BRUSH_SIZE, MAIN_MENU_BRUSH_SPEED, MAIN_MENU_COLOR,
853 MAIN_MENU_GRID_SIZE,
854 MAIN_MENU_PLAYBACK_CONTROL,
855 MAIN_MENU_EXIT,
857 enum {
858 /* Select action menu */
859 SELECT_MENU_CUT, SELECT_MENU_COPY,
860 SELECT_MENU_INVERT, SELECT_MENU_HFLIP, SELECT_MENU_VFLIP,
861 SELECT_MENU_ROTATE90, SELECT_MENU_ROTATE180, SELECT_MENU_ROTATE270,
862 SELECT_MENU_CANCEL,
864 enum {
865 /* Text menu */
866 TEXT_MENU_TEXT, TEXT_MENU_FONT,
867 TEXT_MENU_PREVIEW, TEXT_MENU_APPLY, TEXT_MENU_CANCEL,
870 MENUITEM_STRINGLIST(main_menu, "RockPaint", NULL,
871 "Resume", "New", "Load", "Save",
872 "Set Width", "Set Height",
873 "Brush Size", "Brush Speed",
874 "Choose Color", "Grid Size",
875 "Playback Control", "Exit");
876 MENUITEM_STRINGLIST(select_menu, "Select...", NULL,
877 "Cut", "Copy",
878 "Invert", "Horizontal Flip", "Vertical Flip",
879 "Rotate 90°", "Rotate 180°", "Rotate 270°",
880 "Cancel");
881 MENUITEM_STRINGLIST(text_menu, "Text", NULL,
882 "Set Text", "Change Font",
883 "Preview", "Apply", "Cancel");
884 static const int times_list[] = { 1, 2, 4, 8 };
885 static const int gridsize_list[] = { 0, 5, 10, 20 };
886 static const struct opt_items times_options[] = {
887 { "1x", -1 }, { "2x", -1 }, { "4x", -1 }, { "8x", -1 }
889 static const struct opt_items gridsize_options[] = {
890 { "No grid", -1 }, { "5px", -1 }, { "10px", -1 }, { "20px", -1 }
893 static int draw_window( int height, int width,
894 int *top, int *left,
895 const char *title )
897 int fh;
898 rb->lcd_getstringsize( title, NULL, &fh );
899 fh++;
901 const int _top = ( LCD_HEIGHT - height ) / 2;
902 const int _left = ( LCD_WIDTH - width ) / 2;
903 if( top ) *top = _top;
904 if( left ) *left = _left;
905 rb->lcd_set_background(COLOR_BLUE);
906 rb->lcd_set_foreground(COLOR_LIGHTGRAY);
907 rb->lcd_fillrect( _left, _top, width, height );
908 rb->lcd_set_foreground(COLOR_BLUE);
909 rb->lcd_fillrect( _left, _top, width, fh+4 );
910 rb->lcd_set_foreground(COLOR_WHITE);
911 rb->lcd_putsxy( _left+2, _top+2, title );
912 rb->lcd_set_foreground(COLOR_BLACK);
913 rb->lcd_drawrect( _left, _top, width, height );
914 return _top+fh+4;
917 /***********************************************************************
918 * File browser
919 ***********************************************************************/
921 char bbuf[MAX_PATH]; /* used by file and font browsers */
922 char bbuf_s[MAX_PATH]; /* used by file and font browsers */
923 struct tree_context *tree = NULL;
925 static bool check_extention(const char *filename, const char *ext)
927 const char *p = rb->strrchr( filename, '.' );
928 return ( p != NULL && !rb->strcasecmp( p, ext ) );
931 /* only displayes directories and .bmp files */
932 static bool callback_show_item(char *name, int attr, struct tree_context *tc)
934 (void) tc;
935 if( ( attr & ATTR_DIRECTORY ) ||
936 ( !(attr & ATTR_DIRECTORY) && check_extention( name, ".bmp" ) ) )
938 return true;
940 return false;
943 static bool browse( char *dst, int dst_size, const char *start )
945 struct browse_context browse;
947 rb->browse_context_init(&browse, SHOW_ALL,
948 BROWSE_SELECTONLY|BROWSE_NO_CONTEXT_MENU,
949 NULL, NOICON, start, NULL);
951 browse.callback_show_item = callback_show_item;
952 browse.buf = dst;
953 browse.bufsize = dst_size;
955 rb->rockbox_browse(&browse);
957 return (browse.flags & BROWSE_SELECTED);
960 /***********************************************************************
961 * Font browser
963 * FIXME: This still needs some work ... it currently only works fine
964 * on the simulators, disk spins too much on real targets -> rendered
965 * font buffer needed.
966 ***********************************************************************/
968 * cache font preview handling assumes:
969 * - fvi doesn't decrease by more than 1.
970 * In other words, cache_first-1 must be cached before cache_first-2 is cached.
971 * - there is enough space to store all preview currently displayed.
973 static bool browse_fonts( char *dst, int dst_size )
975 #define LINE_SPACE 2
976 #define PREVIEW_SIZE(x) ((x)->size)
977 #define PREVIEW_NEXT(x) (struct font_preview *)((char*)(x) + PREVIEW_SIZE(x))
979 struct tree_context backup;
980 struct entry *dc, *e;
981 int dirfilter = SHOW_FONT;
983 struct font_preview {
984 unsigned short width;
985 unsigned short height;
986 size_t size; /* to avoid calculating size each time. */
987 fb_data preview[0];
988 } *font_preview = NULL;
990 int top = 0;
992 int fvi = 0; /* first visible item */
993 int lvi = 0; /* last visible item */
994 int si = 0; /* selected item */
995 int li = 0; /* last item */
996 int nvih = 0; /* next visible item height */
997 int i;
998 bool need_redraw = true; /* Do we need to redraw ? */
999 bool reset_font = false;
1000 bool ret = false;
1002 int cp = 0; /* current position */
1003 int sp = 0; /* selected position */
1004 int fh, fw; /* font height, width */
1006 unsigned char *cache = (unsigned char *) buffer + sizeof(buffer->text);
1007 size_t cache_size = sizeof(*buffer) - sizeof(buffer->text);
1008 size_t cache_used = 0;
1009 int cache_first = 0, cache_last = -1;
1010 char *a;
1012 rb->snprintf( bbuf_s, MAX_PATH, FONT_DIR "/%s.fnt",
1013 rb->global_settings->font_file );
1015 tree = rb->tree_get_context();
1016 backup = *tree;
1017 dc = rb->tree_get_entries(tree);
1018 a = backup.currdir+rb->strlen(backup.currdir)-1;
1019 if( *a != '/' )
1021 *++a = '/';
1023 rb->strcpy( a+1, dc[tree->selected_item].name );
1024 tree->dirfilter = &dirfilter;
1025 tree->browse = NULL;
1026 rb->strcpy( bbuf, FONT_DIR "/" );
1027 rb->set_current_file( bbuf );
1029 if( buffer->text.initialized )
1031 cache_used = buffer->text.cache_used;
1032 cache_first = buffer->text.cache_first;
1033 cache_last = buffer->text.cache_last;
1034 fvi = buffer->text.fvi;
1035 si = buffer->text.si;
1037 buffer->text.initialized = true;
1039 while( 1 )
1041 if( !need_redraw )
1043 /* we don't need to redraw ... but we need to unselect
1044 * the previously selected item */
1045 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1046 rb->lcd_fillrect( 10, sp, LCD_WIDTH-10, font_preview->height );
1047 rb->lcd_set_drawmode(DRMODE_SOLID);
1050 if( need_redraw )
1052 need_redraw = false;
1054 rb->lcd_set_foreground(COLOR_BLACK);
1055 rb->lcd_set_background(COLOR_LIGHTGRAY);
1056 rb->lcd_clear_display();
1058 rb->font_getstringsize( "Fonts", NULL, &fh, FONT_UI );
1059 rb->lcd_putsxy( 2, 2, "Fonts" );
1060 top = fh + 4 + LINE_SPACE;
1062 font_preview = (struct font_preview *) cache;
1063 /* get first font preview to be displayed. */
1064 for( i = cache_first; i < cache_last && i < fvi; i++ )
1066 font_preview = PREVIEW_NEXT(font_preview);
1068 for( ; fvi < lvi && nvih > 0; fvi++ )
1070 nvih -= font_preview->height + LINE_SPACE;
1071 font_preview = PREVIEW_NEXT(font_preview);
1073 nvih = 0;
1074 i = fvi;
1076 cp = top;
1077 while( cp <= LCD_HEIGHT+LINE_SPACE && i < tree->filesindir )
1079 e = &dc[i];
1080 if( i < cache_first || i > cache_last )
1082 size_t siz;
1083 reset_font = true;
1084 rb->snprintf( bbuf, MAX_PATH, FONT_DIR "/%s", e->name );
1085 rb->font_getstringsize( e->name, &fw, &fh, FONT_UI );
1086 if( fw > LCD_WIDTH ) fw = LCD_WIDTH;
1087 siz = (sizeof(struct font_preview) + fw*fh*FB_DATA_SZ+3) & ~3;
1088 if( i < cache_first )
1090 /* insert font preview to the top. */
1091 cache_used = 0;
1092 for( ; cache_first <= cache_last; cache_first++ )
1094 font_preview = (struct font_preview *) (cache + cache_used);
1095 size_t size = PREVIEW_SIZE(font_preview);
1096 if( cache_used + size >= cache_size - siz )
1097 break;
1098 cache_used += size;
1100 cache_last = cache_first-1;
1101 cache_first = i;
1102 rb->memmove( cache+siz, cache, cache_used );
1103 font_preview = (struct font_preview *) cache;
1105 else /* i > cache_last */
1107 /* add font preview to the bottom. */
1108 font_preview = (struct font_preview *) cache;
1109 while( cache_used >= cache_size - siz )
1111 cache_used -= PREVIEW_SIZE(font_preview);
1112 font_preview = PREVIEW_NEXT(font_preview);
1113 cache_first++;
1115 cache_last = i;
1116 rb->memmove( cache, font_preview, cache_used );
1117 font_preview = (struct font_preview *) (cache + cache_used);
1119 cache_used += siz;
1120 /* create preview cache. */
1121 font_preview->width = fw;
1122 font_preview->height = fh;
1123 font_preview->size = siz;
1124 /* clear with background. */
1125 for( siz = fw*fh; siz > 0; )
1127 font_preview->preview[--siz] = COLOR_LIGHTGRAY;
1129 buffer_putsxyofs( font_preview->preview,
1130 fw, fh, 0, 0, 0, e->name );
1132 else
1134 fw = font_preview->width;
1135 fh = font_preview->height;
1137 if( cp + fh >= LCD_HEIGHT )
1139 nvih = fh;
1140 break;
1142 rb->lcd_bitmap( font_preview->preview, 10, cp, fw, fh );
1143 cp += fh + LINE_SPACE;
1144 i++;
1145 font_preview = PREVIEW_NEXT(font_preview);
1147 lvi = i-1;
1148 li = tree->filesindir-1;
1149 if( reset_font )
1151 // fixme rb->font_load(NULL, bbuf_s );
1152 reset_font = false;
1154 if( lvi-fvi+1 < tree->filesindir )
1156 rb->gui_scrollbar_draw( rb->screens[SCREEN_MAIN], 0, top,
1157 9, LCD_HEIGHT-top,
1158 tree->filesindir, fvi, lvi+1, VERTICAL );
1162 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1163 sp = top;
1164 font_preview = (struct font_preview *) cache;
1165 for( i = cache_first; i < si; i++ )
1167 if( i >= fvi )
1168 sp += font_preview->height + LINE_SPACE;
1169 font_preview = PREVIEW_NEXT(font_preview);
1171 rb->lcd_fillrect( 10, sp, LCD_WIDTH-10, font_preview->height );
1172 rb->lcd_set_drawmode(DRMODE_SOLID);
1174 rb->lcd_update();
1176 switch( rb->button_get(true) )
1178 case ROCKPAINT_UP:
1179 case ROCKPAINT_UP|BUTTON_REPEAT:
1180 if( si > 0 )
1182 si--;
1183 if( si < fvi )
1185 fvi = si;
1186 nvih = 0;
1187 need_redraw = true;
1190 break;
1192 case ROCKPAINT_DOWN:
1193 case ROCKPAINT_DOWN|BUTTON_REPEAT:
1194 if( si < li )
1196 si++;
1197 if( si > lvi )
1199 need_redraw = true;
1202 break;
1204 case ROCKPAINT_RIGHT:
1205 case ROCKPAINT_DRAW:
1206 ret = true;
1207 rb->snprintf( dst, dst_size, FONT_DIR "/%s", dc[si].name );
1208 /* fall through */
1209 case ROCKPAINT_LEFT:
1210 case ROCKPAINT_QUIT:
1211 buffer->text.cache_used = cache_used;
1212 buffer->text.cache_first = cache_first;
1213 buffer->text.cache_last = cache_last;
1214 buffer->text.fvi = fvi;
1215 buffer->text.si = si;
1216 *tree = backup;
1217 rb->set_current_file( backup.currdir );
1218 return ret;
1221 #undef LINE_SPACE
1222 #undef PREVIEW_SIZE
1223 #undef PREVIEW_NEXT
1226 /***********************************************************************
1227 * HSVRGB Color chooser
1228 ***********************************************************************/
1229 static unsigned int color_chooser( unsigned int color )
1231 int red = RGB_UNPACK_RED( color );
1232 int green = RGB_UNPACK_GREEN( color );
1233 int blue = RGB_UNPACK_BLUE( color );
1234 int hue, saturation, value;
1235 int r, g, b; /* temp variables */
1236 int i, top, left;
1237 int button;
1238 int *pval;
1239 static struct incdec_ctx ctxs[] = {
1240 { 3600, { 10, 100}, true }, /* hue */
1241 { 0xff, { 1, 8}, false }, /* the others */
1244 enum BaseColor { Hue = 0, Saturation = 1, Value = 2,
1245 Red = 3, Green = 4, Blue = 5 };
1246 enum BaseColor current = Red;
1247 bool has_changed;
1249 restore_screen();
1251 rgb2hsv( red, green, blue, &hue, &saturation, &value );
1253 while( 1 )
1255 has_changed = false;
1256 color = LCD_RGBPACK( red, green, blue );
1258 #define HEIGHT ( 100 )
1259 #define WIDTH ( 150 )
1261 top = draw_window( HEIGHT, WIDTH, NULL, &left, "Color chooser" );
1262 top -= 15;
1264 for( i=0; i<100; i++ )
1266 hsv2rgb( i*36, saturation, value, &r, &g, &b );
1267 rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
1268 rb->lcd_vline( left+15+i, top+20, top+27 );
1269 hsv2rgb( hue, i*255/100, value, &r, &g, &b );
1270 rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
1271 rb->lcd_vline( left+15+i, top+30, top+37 );
1272 hsv2rgb( hue, saturation, i*255/100, &r, &g, &b );
1273 rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
1274 rb->lcd_vline( left+15+i, top+40, top+47 );
1275 rb->lcd_set_foreground( LCD_RGBPACK( i*255/100, green, blue ) );
1276 rb->lcd_vline( left+15+i, top+50, top+57 );
1277 rb->lcd_set_foreground( LCD_RGBPACK( red, i*255/100, blue ) );
1278 rb->lcd_vline( left+15+i, top+60, top+67 );
1279 rb->lcd_set_foreground( LCD_RGBPACK( red, green, i*255/100 ) );
1280 rb->lcd_vline( left+15+i, top+70, top+77 );
1283 rb->lcd_set_foreground(COLOR_BLACK);
1284 #define POSITION( a, i ) \
1285 rb->lcd_drawpixel( left+14+i, top + 19 + a ); \
1286 rb->lcd_drawpixel( left+16+i, top + 19 + a ); \
1287 rb->lcd_drawpixel( left+14+i, top + 28 + a ); \
1288 rb->lcd_drawpixel( left+16+i, top + 28 + a );
1289 POSITION( 0, hue/36 );
1290 POSITION( 10, saturation*99/255 );
1291 POSITION( 20, value*99/255 );
1292 POSITION( 30, red*99/255 );
1293 POSITION( 40, green*99/255 );
1294 POSITION( 50, blue*99/255 );
1295 #undef POSITION
1296 rb->lcd_set_background(COLOR_LIGHTGRAY);
1297 rb->lcd_setfont( FONT_SYSFIXED );
1298 rb->lcd_putsxyf( left + 117, top + 20, "%d", hue/10 );
1299 rb->lcd_putsxyf( left + 117, top + 30, "%d.%d",
1300 saturation/255, ((saturation*100)/255)%100 );
1301 rb->lcd_putsxyf( left + 117, top + 40, "%d.%d",
1302 value/255, ((value*100)/255)%100 );
1303 rb->lcd_putsxyf( left + 117, top + 50, "%d", red );
1304 rb->lcd_putsxyf( left + 117, top + 60, "%d", green );
1305 rb->lcd_putsxyf( left + 117, top + 70, "%d", blue );
1306 rb->lcd_setfont( FONT_UI );
1308 #define CURSOR( l ) \
1309 rb->lcd_bitmap_transparent_part( rockpaint_hsvrgb, 1, 1, 16, left+l+1, top+20, 6, 58 ); \
1310 rb->lcd_bitmap_transparent_part( rockpaint_hsvrgb, 8, 10*current, 16, left+l, top+19+10*current, 8, 10 );
1311 CURSOR( 5 );
1312 #undef CURSOR
1314 rb->lcd_set_foreground( color );
1315 rb->lcd_fillrect( left+15, top+85, 100, 8 );
1317 rb->lcd_update();
1319 switch( button = rb->button_get(true) )
1321 case ROCKPAINT_UP:
1322 current = ( current + 5 )%6;
1323 break;
1325 case ROCKPAINT_DOWN:
1326 current = ( current + 1 )%6;
1327 break;
1329 case ROCKPAINT_LEFT:
1330 case ROCKPAINT_LEFT|BUTTON_REPEAT:
1331 case ROCKPAINT_RIGHT:
1332 case ROCKPAINT_RIGHT|BUTTON_REPEAT:
1333 has_changed = true;
1334 switch( current )
1336 case Hue:
1337 pval = &hue;
1338 break;
1339 case Saturation:
1340 pval = &saturation;
1341 break;
1342 case Value:
1343 pval = &value;
1344 break;
1345 case Red:
1346 pval = &red;
1347 break;
1348 case Green:
1349 pval = &green;
1350 break;
1351 case Blue:
1352 pval = &blue;
1353 break;
1354 default:
1355 pval = NULL;
1356 break;
1358 if (pval)
1360 incdec_value(pval, &ctxs[(current != Hue? 1: 0)],
1361 (button&ROCKPAINT_RIGHT), (button&BUTTON_REPEAT));
1363 break;
1365 case ROCKPAINT_DRAW:
1366 return color;
1368 if( has_changed )
1370 switch( current )
1372 case Hue:
1373 case Saturation:
1374 case Value:
1375 hsv2rgb( hue, saturation, value, &red, &green, &blue );
1376 break;
1378 case Red:
1379 case Green:
1380 case Blue:
1381 rgb2hsv( red, green, blue, &hue, &saturation, &value );
1382 break;
1385 #undef HEIGHT
1386 #undef WIDTH
1390 /***********************************************************************
1391 * Misc routines
1392 ***********************************************************************/
1393 static void init_buffer(void)
1395 int i;
1396 fb_data color = rp_colors[ bgdrawcolor ];
1397 for( i = 0; i < ROWS*COLS; i++ )
1399 save_buffer[i] = color;
1403 static void draw_pixel(int x,int y)
1405 if( !preview )
1407 if( x < 0 || x >= COLS || y < 0 || y >= ROWS ) return;
1408 if( isbg )
1410 save_buffer[ x+y*COLS ] = rp_colors[bgdrawcolor];
1412 else
1414 save_buffer[ x+y*COLS ] = rp_colors[drawcolor];
1417 rb->lcd_drawpixel(x,y);
1420 static void color_picker( int x, int y )
1422 if( preview )
1424 rb->lcd_set_foreground( save_buffer[ x+y*COLS ] );
1425 #define PSIZE 12
1426 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1427 if( x >= COLS - PSIZE ) x -= PSIZE + 2;
1428 if( y >= ROWS - PSIZE ) y -= PSIZE + 2;
1429 rb->lcd_drawrect( x + 2, y + 2, PSIZE - 2, PSIZE - 2 );
1430 rb->lcd_set_drawmode(DRMODE_SOLID);
1431 rb->lcd_fillrect( x + 3, y + 3, PSIZE - 4, PSIZE - 4 );
1432 #undef PSIZE
1433 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1435 else
1437 rp_colors[ drawcolor ] = save_buffer[ x+y*COLS ];
1441 static void draw_select_rectangle( int x1, int y1, int x2, int y2 )
1442 /* This is a preview mode only function */
1444 int i,a;
1445 if( x1 > x2 )
1447 i = x1;
1448 x1 = x2;
1449 x2 = i;
1451 if( y1 > y2 )
1453 i = y1;
1454 y1 = y2;
1455 y2 = i;
1457 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1458 i = 0;
1459 for( a = x1; a < x2; a++, i++ )
1460 if( i%2 )
1461 rb->lcd_drawpixel( a, y1 );
1462 for( a = y1; a < y2; a++, i++ )
1463 if( i%2 )
1464 rb->lcd_drawpixel( x2, a );
1465 if( y2 != y1 )
1466 for( a = x2; a > x1; a--, i++ )
1467 if( i%2 )
1468 rb->lcd_drawpixel( a, y2 );
1469 if( x2 != x1 )
1470 for( a = y2; a > y1; a--, i++ )
1471 if( i%2 )
1472 rb->lcd_drawpixel( x1, a );
1473 rb->lcd_set_drawmode(DRMODE_SOLID);
1476 static void copy_to_clipboard( void )
1478 /* This needs to be optimised ... but i'm lazy ATM */
1479 rb->memcpy( buffer->clipboard, save_buffer, COLS*ROWS*sizeof( fb_data ) );
1482 /* no preview mode handling atm ... do we need it ? (one if) */
1483 static void draw_invert( int x1, int y1, int x2, int y2 )
1485 int i;
1486 if( x1 > x2 )
1488 i = x1;
1489 x1 = x2;
1490 x2 = i;
1492 if( y1 > y2 )
1494 i = y1;
1495 y1 = y2;
1496 y2 = i;
1499 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1500 rb->lcd_fillrect( x1, y1, x2-x1+1, y2-y1+1 );
1501 rb->lcd_set_drawmode(DRMODE_SOLID);
1503 for( ; y1<=y2; y1++ )
1505 for( i = x1; i<=x2; i++ )
1507 save_buffer[ y1*COLS + i ] = ~save_buffer[ y1*COLS + i ];
1510 /*if( update )*/ rb->lcd_update();
1513 static void draw_hflip( int x1, int y1, int x2, int y2 )
1515 int i;
1516 if( x1 > x2 )
1518 i = x1;
1519 x1 = x2;
1520 x2 = i;
1522 if( y1 > y2 )
1524 i = y1;
1525 y1 = y2;
1526 y2 = i;
1529 copy_to_clipboard();
1531 for( i = 0; i <= y2 - y1; i++ )
1533 rb->memcpy( save_buffer+(y1+i)*COLS+x1,
1534 buffer->clipboard+(y2-i)*COLS+x1,
1535 (x2-x1+1)*sizeof( fb_data ) );
1537 restore_screen();
1538 rb->lcd_update();
1541 static void draw_vflip( int x1, int y1, int x2, int y2 )
1543 int i;
1544 if( x1 > x2 )
1546 i = x1;
1547 x1 = x2;
1548 x2 = i;
1550 if( y1 > y2 )
1552 i = y1;
1553 y1 = y2;
1554 y2 = i;
1557 copy_to_clipboard();
1559 for( ; y1 <= y2; y1++ )
1561 for( i = 0; i <= x2 - x1; i++ )
1563 save_buffer[y1*COLS+x1+i] = buffer->clipboard[y1*COLS+x2-i];
1566 restore_screen();
1567 rb->lcd_update();
1570 /* direction: -1 = left, 1 = right */
1571 static void draw_rot_90_deg( int x1, int y1, int x2, int y2, int direction )
1573 int i, j;
1574 if( x1 > x2 )
1576 i = x1;
1577 x1 = x2;
1578 x2 = i;
1580 if( y1 > y2 )
1582 i = y1;
1583 y1 = y2;
1584 y2 = i;
1587 copy_to_clipboard();
1589 fb_data color = rp_colors[ bgdrawcolor ];
1590 const int width = x2 - x1, height = y2 - y1;
1591 const int sub_half = width/2-height/2, add_half = (width+height)/2;
1592 if( width > height )
1594 for( i = 0; i <= height; i++ )
1596 for( j = 0; j < sub_half; j++ )
1597 save_buffer[(y1+i)*COLS+x1+j] = color;
1598 for( j = add_half+1; j <= width; j++ )
1599 save_buffer[(y1+i)*COLS+x1+j] = color;
1602 else if( width < height )
1604 for( j = 0; j <= width; j++ )
1606 for( i = 0; i < -sub_half; i++ )
1607 save_buffer[(y1+i)*COLS+x1+j] = color;
1608 for( i = add_half+1; i <= height; i++ )
1609 save_buffer[(y1+i)*COLS+x1+j] = color;
1612 int x3 = x1 + sub_half, y3 = y1 - sub_half;
1613 int is = x3<0?-x3:0, ie = COLS-x3-1, js = y3<0?-y3:0, je = ROWS-y3-1;
1614 if( ie > height ) ie = height;
1615 if( je > width ) je = width;
1616 for( i = is; i <= ie; i++ )
1618 for( j = js; j <= je; j++ )
1620 int x, y;
1621 if(direction > 0)
1623 x = x1+j;
1624 y = y1+height-i;
1626 else
1628 x = x1+width-j;
1629 y = y1+i;
1631 save_buffer[(y3+j)*COLS+x3+i] = buffer->clipboard[y*COLS+x];
1634 restore_screen();
1635 rb->lcd_update();
1638 static void draw_paste_rectangle( int src_x1, int src_y1, int src_x2,
1639 int src_y2, int x1, int y1, int cut )
1641 int i, width, height;
1642 if( cut )
1644 i = drawcolor;
1645 drawcolor = bgdrawcolor;
1646 draw_rect_full( src_x1, src_y1, src_x2, src_y2 );
1647 drawcolor = i;
1649 if( src_x1 > src_x2 )
1651 i = src_x1;
1652 src_x1 = src_x2;
1653 src_x2 = i;
1655 if( src_y1 > src_y2 )
1657 i = src_y1;
1658 src_y1 = src_y2;
1659 src_y2 = i;
1661 width = src_x2 - src_x1 + 1;
1662 height = src_y2 - src_y1 + 1;
1663 /* clipping */
1664 if( x1 + width > COLS )
1665 width = COLS - x1;
1666 if( y1 + height > ROWS )
1667 height = ROWS - y1;
1669 rb->lcd_bitmap_part( buffer->clipboard, src_x1, src_y1, COLS,
1670 x1, y1, width, height );
1671 if( !preview )
1673 for( i = 0; i < height; i++ )
1675 rb->memcpy( save_buffer+(y1+i)*COLS+x1,
1676 buffer->clipboard+(src_y1+i)*COLS+src_x1,
1677 width*sizeof( fb_data ) );
1682 static void show_grid( bool update )
1684 int i;
1685 if( gridsize > 0 )
1687 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1688 for( i = gridsize; i < img_width; i+= gridsize )
1690 rb->lcd_vline( i, 0, img_height-1 );
1692 for( i = gridsize; i < img_height; i+= gridsize )
1694 rb->lcd_hline( 0, img_width-1, i );
1696 rb->lcd_set_drawmode(DRMODE_SOLID);
1697 if( update ) rb->lcd_update();
1701 static void draw_text( int x, int y )
1703 int selected = 0;
1704 int current_font_id = rb->global_status->font_id[SCREEN_MAIN];
1705 buffer->text.text[0] = '\0';
1706 buffer->text.font[0] = '\0';
1707 while( 1 )
1709 switch( rb->do_menu( &text_menu, &selected, NULL, NULL ) )
1711 case TEXT_MENU_TEXT:
1712 rb->lcd_set_foreground(COLOR_BLACK);
1713 rb->kbd_input( buffer->text.text, MAX_TEXT );
1714 break;
1716 case TEXT_MENU_FONT:
1717 if (current_font_id != rb->global_status->font_id[SCREEN_MAIN])
1718 rb->font_unload(current_font_id);
1719 if(browse_fonts( buffer->text.font, MAX_PATH ) )
1721 current_font_id = rb->font_load(buffer->text.font );
1722 rb->lcd_setfont(current_font_id);
1724 break;
1726 case TEXT_MENU_PREVIEW:
1727 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1728 while( 1 )
1730 int button;
1731 restore_screen();
1732 rb->lcd_putsxy( x, y, buffer->text.text );
1733 rb->lcd_update();
1734 switch( button = rb->button_get( true ) )
1736 case ROCKPAINT_LEFT:
1737 case ROCKPAINT_LEFT | BUTTON_REPEAT:
1738 case ROCKPAINT_RIGHT:
1739 case ROCKPAINT_RIGHT | BUTTON_REPEAT:
1740 incdec_value(&x, &incdec_x,
1741 (button&ROCKPAINT_RIGHT), (button&BUTTON_REPEAT));
1742 break;
1744 case ROCKPAINT_UP:
1745 case ROCKPAINT_UP | BUTTON_REPEAT:
1746 case ROCKPAINT_DOWN:
1747 case ROCKPAINT_DOWN | BUTTON_REPEAT:
1748 incdec_value(&y, &incdec_y,
1749 (button&ROCKPAINT_DOWN), (button&BUTTON_REPEAT));
1750 break;
1752 case ROCKPAINT_DRAW:
1753 break;
1754 default:
1755 if(rb->default_event_handler(button)
1756 == SYS_USB_CONNECTED)
1757 button = ROCKPAINT_DRAW;
1758 break;
1760 if( button == ROCKPAINT_DRAW ) break;
1762 break;
1764 case TEXT_MENU_APPLY:
1765 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1766 buffer_putsxyofs( save_buffer, COLS, ROWS, x, y, 0,
1767 buffer->text.text );
1768 case TEXT_MENU_CANCEL:
1769 default:
1770 restore_screen();
1771 if( buffer->text.font[0] )
1773 rb->snprintf( buffer->text.font, MAX_PATH,
1774 FONT_DIR "/%s.fnt",
1775 rb->global_settings->font_file );
1776 if (current_font_id != rb->global_status->font_id[SCREEN_MAIN])
1777 rb->font_unload(current_font_id);
1778 rb->lcd_setfont(FONT_UI);
1780 return;
1785 static void draw_brush( int x, int y )
1787 int i,j;
1788 for( i=-bsize/2+(bsize+1)%2; i<=bsize/2; i++ )
1790 for( j=-bsize/2+(bsize+1)%2; j<=bsize/2; j++ )
1792 draw_pixel( x+i, y+j );
1797 /* This is an implementation of Bresenham's line algorithm.
1798 * See http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm.
1800 static void draw_line( int x1, int y1, int x2, int y2 )
1802 int x = x1;
1803 int y = y1;
1804 int deltax = x2 - x1;
1805 int deltay = y2 - y1;
1806 int i;
1808 int xerr = abs(deltax);
1809 int yerr = abs(deltay);
1810 int xstep = deltax > 0 ? 1 : -1;
1811 int ystep = deltay > 0 ? 1 : -1;
1812 int err;
1814 if (yerr > xerr)
1816 /* more vertical */
1817 err = yerr;
1818 xerr <<= 1;
1819 yerr <<= 1;
1821 /* to leave off the last pixel of the line, leave off the "+ 1" */
1822 for (i = err + 1; i; --i)
1824 draw_pixel(x, y);
1825 y += ystep;
1826 err -= xerr;
1827 if (err < 0) {
1828 x += xstep;
1829 err += yerr;
1833 else
1835 /* more horizontal */
1836 err = xerr;
1837 xerr <<= 1;
1838 yerr <<= 1;
1840 for (i = err + 1; i; --i)
1842 draw_pixel(x, y);
1843 x += xstep;
1844 err -= yerr;
1845 if (err < 0) {
1846 y += ystep;
1847 err += xerr;
1853 static void draw_curve( int x1, int y1, int x2, int y2,
1854 int xa, int ya, int xb, int yb )
1856 int i = 0;
1857 short xl1, yl1;
1858 short xl2, yl2;
1859 short xl3, yl3;
1860 short xl4, yl4;
1861 short xr1, yr1;
1862 short xr2, yr2;
1863 short xr3, yr3;
1864 short xr4, yr4;
1865 short depth;
1866 short xh, yh;
1868 if( x1 == x2 && y1 == y2 )
1870 draw_pixel( x1, y1 );
1871 return;
1874 // if( preview )
1876 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1877 if( xa == -1 || ya == -1 )
1879 rb->lcd_drawline( x1, y1, xb, yb );
1880 rb->lcd_drawline( x2, y2, xb, yb );
1882 else
1884 rb->lcd_drawline( x1, y1, xa, ya );
1885 rb->lcd_drawline( x2, y2, xb, yb );
1887 rb->lcd_set_drawmode(DRMODE_SOLID);
1890 if( xa == -1 || ya == -1 )
1891 /* We only have 3 of the points
1892 * This will currently only be used in preview mode */
1894 #define PUSH( a1, b1, a2, b2, a3, b3, d ) \
1895 buffer->bezier[i].x1 = a1; \
1896 buffer->bezier[i].y1 = b1; \
1897 buffer->bezier[i].x2 = a2; \
1898 buffer->bezier[i].y2 = b2; \
1899 buffer->bezier[i].x3 = a3; \
1900 buffer->bezier[i].y3 = b3; \
1901 buffer->bezier[i].depth = d; \
1902 i++;
1903 #define POP( a1, b1, a2, b2, a3, b3, d ) \
1904 i--; \
1905 a1 = buffer->bezier[i].x1; \
1906 b1 = buffer->bezier[i].y1; \
1907 a2 = buffer->bezier[i].x2; \
1908 b2 = buffer->bezier[i].y2; \
1909 a3 = buffer->bezier[i].x3; \
1910 b3 = buffer->bezier[i].y3; \
1911 d = buffer->bezier[i].depth;
1913 PUSH( x1<<4, y1<<4, xb<<4, yb<<4, x2<<4, y2<<4, 0 );
1914 while( i )
1916 /* de Casteljau's algorithm (see wikipedia) */
1917 POP( xl1, yl1, xb, yb, xr3, yr3, depth );
1918 if( depth < 10 ) /* check that the stack's 'i' doesn't overflow */
1920 xl2 = ( xl1 + xb )>>1;
1921 yl2 = ( yl1 + yb )>>1;
1922 xr2 = ( xb + xr3 )>>1;
1923 yr2 = ( yb + yr3 )>>1;
1924 xr1 = ( xl2 + xr2 )>>1;
1925 yr1 = ( yl2 + yr2 )>>1;
1926 xl3 = xr1;
1927 yl3 = yr1;
1928 PUSH( xl1, yl1, xl2, yl2, xl3, yl3, depth+1 );
1929 PUSH( xr1, yr1, xr2, yr2, xr3, yr3, depth+1 );
1931 else
1933 draw_line( ((xl1>>3)+1)>>1, ((yl1>>3)+1)>>1,
1934 ((xr3>>3)+1)>>1, ((yr3>>3)+1)>>1 );
1937 #undef PUSH
1938 #undef POP
1940 else /* We have the 4 points */
1942 #define PUSH( a1, b1, a2, b2, a3, b3, a4, b4, d ) \
1943 buffer->bezier[i].x1 = a1; \
1944 buffer->bezier[i].y1 = b1; \
1945 buffer->bezier[i].x2 = a2; \
1946 buffer->bezier[i].y2 = b2; \
1947 buffer->bezier[i].x3 = a3; \
1948 buffer->bezier[i].y3 = b3; \
1949 buffer->bezier[i].x4 = a4; \
1950 buffer->bezier[i].y4 = b4; \
1951 buffer->bezier[i].depth = d; \
1952 i++;
1953 #define POP( a1, b1, a2, b2, a3, b3, a4, b4, d ) \
1954 i--; \
1955 a1 = buffer->bezier[i].x1; \
1956 b1 = buffer->bezier[i].y1; \
1957 a2 = buffer->bezier[i].x2; \
1958 b2 = buffer->bezier[i].y2; \
1959 a3 = buffer->bezier[i].x3; \
1960 b3 = buffer->bezier[i].y3; \
1961 a4 = buffer->bezier[i].x4; \
1962 b4 = buffer->bezier[i].y4; \
1963 d = buffer->bezier[i].depth;
1965 PUSH( x1<<4, y1<<4, xa<<4, ya<<4, xb<<4, yb<<4, x2<<4, y2<<4, 0 );
1966 while( i )
1968 /* de Casteljau's algorithm (see wikipedia) */
1969 POP( xl1, yl1, xa, ya, xb, yb, xr4, yr4, depth );
1970 if( depth < 10 ) /* check that the stack's 'i' doesn't overflow */
1972 xl2 = ( xl1 + xa )>>1;
1973 yl2 = ( yl1 + ya )>>1;
1974 xh = ( xa + xb )>>1;
1975 yh = ( ya + yb )>>1;
1976 xr3 = ( xb + xr4 )>>1;
1977 yr3 = ( yb + yr4 )>>1;
1978 xl3 = ( xl2 + xh )>>1;
1979 yl3 = ( yl2 + yh )>>1;
1980 xr2 = ( xr3 + xh )>>1;
1981 yr2 = ( yr3 + yh )>>1;
1982 xl4 = ( xl3 + xr2 )>>1;
1983 yl4 = ( yl3 + yr2 )>>1;
1984 xr1 = xl4;
1985 yr1 = yl4;
1986 PUSH( xl1, yl1, xl2, yl2, xl3, yl3, xl4, yl4, depth+1 );
1987 PUSH( xr1, yr1, xr2, yr2, xr3, yr3, xr4, yr4, depth+1 );
1989 else
1991 draw_line( ((xl1>>3)+1)>>1, ((yl1>>3)+1)>>1,
1992 ((xr4>>3)+1)>>1, ((yr4>>3)+1)>>1 );
1995 #undef PUSH
1996 #undef POP
2000 static void draw_rect( int x1, int y1, int x2, int y2 )
2002 draw_line( x1, y1, x1, y2 );
2003 draw_line( x1, y1, x2, y1 );
2004 draw_line( x1, y2, x2, y2 );
2005 draw_line( x2, y1, x2, y2 );
2008 static void togglebg( void )
2010 if( isbg )
2012 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2014 else
2016 rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
2018 isbg = !isbg;
2021 static void draw_rect_full( int x1, int y1, int x2, int y2 )
2023 /* GRUIK */
2024 int x;
2025 togglebg();
2026 if( x1 > x2 )
2028 x = x1;
2029 x1 = x2;
2030 x2 = x;
2032 x = x1;
2033 do {
2034 draw_line( x, y1, x, y2 );
2035 } while( ++x <= x2 );
2036 togglebg();
2037 draw_rect( x1, y1, x2, y2 );
2040 static void draw_oval( int x1, int y1, int x2, int y2, bool full )
2042 /* TODO: simplify :) */
2043 int cx = (x1+x2)>>1;
2044 int cy = (y1+y2)>>1;
2046 int rx = (x1-x2)>>1;
2047 int ry = (y1-y2)>>1;
2048 if( rx < 0 ) rx *= -1;
2049 if( ry < 0 ) ry *= -1;
2051 if( rx == 0 || ry == 0 )
2053 draw_line( x1, y1, x2, y2 );
2054 return;
2057 int x,y;
2058 int dst, old_dst;
2060 for( x = 0; x < rx; x++ )
2062 y = 0;
2063 dst = -0xfff;
2064 do {
2065 old_dst = dst;
2066 dst = ry * ry * x * x + rx * rx * y * y - rx * rx * ry * ry;
2067 y++;
2068 } while( dst < 0 );
2069 if( -old_dst < dst ) y--;
2070 if( full )
2072 draw_line( cx+x, cy, cx+x, cy+y );
2073 draw_line( cx+x, cy, cx+x, cy-y );
2074 draw_line( cx-x, cy, cx-x, cy+y );
2075 draw_line( cx-x, cy, cx-x, cy-y );
2077 else
2079 draw_pixel( cx+x, cy+y );
2080 draw_pixel( cx+x, cy-y );
2081 draw_pixel( cx-x, cy+y );
2082 draw_pixel( cx-x, cy-y );
2085 for( y = 0; y < ry; y++ )
2087 x = 0;
2088 dst = -0xfff;
2089 do {
2090 old_dst = dst;
2091 dst = ry * ry * x * x + rx * rx * y * y - rx * rx * ry * ry;
2092 x++;
2093 } while( dst < 0 );
2094 if( -old_dst < dst ) x--;
2095 if( full )
2097 draw_line( cx+x, cy, cx+x, cy+y );
2098 draw_line( cx+x, cy, cx+x, cy-y );
2099 draw_line( cx-x, cy, cx-x, cy+y );
2100 draw_line( cx-x, cy, cx-x, cy-y );
2102 else
2104 draw_pixel( cx+x, cy+y );
2105 draw_pixel( cx+x, cy-y );
2106 draw_pixel( cx-x, cy+y );
2107 draw_pixel( cx-x, cy-y );
2112 static void draw_oval_empty( int x1, int y1, int x2, int y2 )
2114 draw_oval( x1, y1, x2, y2, false );
2117 static void draw_oval_full( int x1, int y1, int x2, int y2 )
2119 togglebg();
2120 draw_oval( x1, y1, x2, y2, true );
2121 togglebg();
2122 draw_oval( x1, y1, x2, y2, false );
2125 static void draw_fill( int x0, int y0 )
2127 #define PUSH( a, b ) \
2128 draw_pixel( (int)a, (int)b ); \
2129 buffer->coord[i].x = a; \
2130 buffer->coord[i].y = b; \
2131 i++;
2132 #define POP( a, b ) \
2133 i--; \
2134 a = buffer->coord[i].x; \
2135 b = buffer->coord[i].y;
2137 unsigned int i=0;
2138 short x = x0;
2139 short y = y0;
2140 unsigned int prev_color = save_buffer[ x0+y0*COLS ];
2142 if( preview )
2143 return;
2144 if( prev_color == rp_colors[ drawcolor ] ) return;
2146 PUSH( x, y );
2148 while( i != 0 )
2150 POP( x, y );
2151 if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
2153 PUSH( x-1, y );
2155 if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
2157 PUSH( x+1, y );
2159 if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
2161 PUSH( x, y-1 );
2163 if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
2165 PUSH( x, y+1 );
2168 #undef PUSH
2169 #undef POP
2173 /* For preview purposes only */
2174 /* use same algorithm as draw_line() to draw line. */
2175 static void line_gradient( int x1, int y1, int x2, int y2 )
2177 int h1, s1, v1, h2, s2, v2, r, g, b;
2178 int xerr = x2 - x1, yerr = y2 - y1, xstep, ystep;
2179 int i, delta, err;
2180 fb_data color1, color2;
2182 if( xerr == 0 && yerr == 0 )
2184 draw_pixel( x1, y1 );
2185 return;
2188 xstep = xerr > 0 ? 1 : -1;
2189 ystep = yerr > 0 ? 1 : -1;
2190 xerr = abs(xerr) << 1;
2191 yerr = abs(yerr) << 1;
2193 color1 = rp_colors[ bgdrawcolor ];
2194 color2 = rp_colors[ drawcolor ];
2196 r = RGB_UNPACK_RED( color1 );
2197 g = RGB_UNPACK_GREEN( color1 );
2198 b = RGB_UNPACK_BLUE( color1 );
2199 rgb2hsv( r, g, b, &h1, &s1, &v1 );
2201 r = RGB_UNPACK_RED( color2 );
2202 g = RGB_UNPACK_GREEN( color2 );
2203 b = RGB_UNPACK_BLUE( color2 );
2204 rgb2hsv( r, g, b, &h2, &s2, &v2 );
2206 if( xerr > yerr )
2208 err = xerr>>1;
2209 delta = err+1;
2210 /* to leave off the last pixel of the line, leave off the "+ 1" */
2211 for (i = delta; i; --i)
2213 hsv2rgb( h2+((h1-h2)*i)/delta,
2214 s2+((s1-s2)*i)/delta,
2215 v2+((v1-v2)*i)/delta,
2216 &r, &g, &b );
2217 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2218 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2219 draw_pixel(x1, y1);
2220 x1 += xstep;
2221 err -= yerr;
2222 if (err < 0) {
2223 y1 += ystep;
2224 err += xerr;
2228 else /* yerr >= xerr */
2230 err = yerr>>1;
2231 delta = err+1;
2232 for (i = delta; i; --i)
2234 hsv2rgb( h2+((h1-h2)*i)/delta,
2235 s2+((s1-s2)*i)/delta,
2236 v2+((v1-v2)*i)/delta,
2237 &r, &g, &b );
2238 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2239 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2240 draw_pixel(x1, y1);
2241 y1 += ystep;
2242 err -= xerr;
2243 if (err < 0) {
2244 x1 += xstep;
2245 err += yerr;
2249 rp_colors[ drawcolor ] = color2;
2252 /* macros used by linear_gradient() and radial_gradient(). */
2253 #define PUSH( _x, _y ) \
2254 save_buffer[(_x)+(_y)*COLS] = mark_color; \
2255 buffer->coord[i].x = (short)(_x); \
2256 buffer->coord[i].y = (short)(_y); \
2257 i++;
2258 #define POP( _x, _y ) \
2259 i--; \
2260 _x = (int)buffer->coord[i].x; \
2261 _y = (int)buffer->coord[i].y;
2262 #define PUSH2( _x, _y ) \
2263 j--; \
2264 buffer->coord[j].x = (short)(_x); \
2265 buffer->coord[j].y = (short)(_y);
2266 #define POP2( _x, _y ) \
2267 _x = (int)buffer->coord[j].x; \
2268 _y = (int)buffer->coord[j].y; \
2269 j++;
2271 static void linear_gradient( int x1, int y1, int x2, int y2 )
2273 int r1 = RGB_UNPACK_RED( rp_colors[ bgdrawcolor ] );
2274 int g1 = RGB_UNPACK_GREEN( rp_colors[ bgdrawcolor ] );
2275 int b1 = RGB_UNPACK_BLUE( rp_colors[ bgdrawcolor ] );
2276 int r2 = RGB_UNPACK_RED( rp_colors[ drawcolor ] );
2277 int g2 = RGB_UNPACK_GREEN( rp_colors[ drawcolor ] );
2278 int b2 = RGB_UNPACK_BLUE( rp_colors[ drawcolor ] );
2279 fb_data color = rp_colors[ drawcolor ];
2281 int h1, s1, v1, h2, s2, v2, r, g, b;
2283 /* radius^2 */
2284 int radius2 = ( x1 - x2 ) * ( x1 - x2 ) + ( y1 - y2 ) * ( y1 - y2 );
2285 int dist2, i=0, j=COLS*ROWS;
2287 /* We only propagate the gradient to neighboring pixels with the same
2288 * color as ( x1, y1 ) */
2289 fb_data prev_color = save_buffer[ x1+y1*COLS ];
2290 /* to mark pixel that the pixel is already in LIFO. */
2291 fb_data mark_color = ~prev_color;
2293 int x = x1;
2294 int y = y1;
2296 if( radius2 == 0 ) return;
2297 if( preview )
2299 line_gradient( x1, y1, x2, y2 );
2300 return;
2302 if( rp_colors[ drawcolor ] == rp_colors[ bgdrawcolor ] )
2304 draw_fill( x1, y1 );
2305 return;
2308 rgb2hsv( r1, g1, b1, &h1, &s1, &v1 );
2309 rgb2hsv( r2, g2, b2, &h2, &s2, &v2 );
2311 PUSH( x, y );
2313 while( i > 0 )
2315 POP( x, y );
2317 dist2 = ( x2 - x1 ) * ( x - x1 ) + ( y2 - y1 ) * ( y - y1 );
2318 if( dist2 <= 0 )
2320 rp_colors[ drawcolor ] = rp_colors[ bgdrawcolor ];
2322 else if( dist2 < radius2 )
2324 hsv2rgb( h1+((h2-h1)*dist2)/radius2,
2325 s1+((s2-s1)*dist2)/radius2,
2326 v1+((v2-v1)*dist2)/radius2,
2327 &r, &g, &b );
2328 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2330 else
2332 rp_colors[ drawcolor ] = color;
2334 if( rp_colors[ drawcolor ] == prev_color )
2336 /* "mark" that pixel was checked. correct color later. */
2337 PUSH2( x, y );
2338 rp_colors[ drawcolor ] = mark_color;
2340 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2341 draw_pixel( x, y );
2343 if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
2345 PUSH( x-1, y );
2347 if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
2349 PUSH( x+1, y );
2351 if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
2353 PUSH( x, y-1 );
2355 if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
2357 PUSH( x, y+1 );
2360 while (j < COLS*ROWS)
2362 /* correct color. */
2363 POP2( x, y );
2364 rp_colors[ drawcolor ] = prev_color;
2365 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2366 draw_pixel( x, y );
2368 rp_colors[ drawcolor ] = color;
2371 static void radial_gradient( int x1, int y1, int x2, int y2 )
2373 int r1 = RGB_UNPACK_RED( rp_colors[ bgdrawcolor ] );
2374 int g1 = RGB_UNPACK_GREEN( rp_colors[ bgdrawcolor ] );
2375 int b1 = RGB_UNPACK_BLUE( rp_colors[ bgdrawcolor ] );
2376 int r2 = RGB_UNPACK_RED( rp_colors[ drawcolor ] );
2377 int g2 = RGB_UNPACK_GREEN( rp_colors[ drawcolor ] );
2378 int b2 = RGB_UNPACK_BLUE( rp_colors[ drawcolor ] );
2379 fb_data color = rp_colors[ drawcolor ];
2381 int h1, s1, v1, h2, s2, v2, r, g, b;
2383 /* radius^2 */
2384 int radius2 = ( x1 - x2 ) * ( x1 - x2 ) + ( y1 - y2 ) * ( y1 - y2 );
2385 int dist2, i=0, j=COLS*ROWS;
2387 /* We only propagate the gradient to neighboring pixels with the same
2388 * color as ( x1, y1 ) */
2389 fb_data prev_color = save_buffer[ x1+y1*COLS ];
2390 /* to mark pixel that the pixel is already in LIFO. */
2391 fb_data mark_color = ~prev_color;
2393 int x = x1;
2394 int y = y1;
2396 if( radius2 == 0 ) return;
2397 if( preview )
2399 line_gradient( x1, y1, x2, y2 );
2400 return;
2402 if( rp_colors[ drawcolor ] == rp_colors[ bgdrawcolor ] )
2404 draw_fill( x1, y1 );
2405 return;
2408 rgb2hsv( r1, g1, b1, &h1, &s1, &v1 );
2409 rgb2hsv( r2, g2, b2, &h2, &s2, &v2 );
2411 PUSH( x, y );
2413 while( i > 0 )
2415 POP( x, y );
2417 dist2 = ( x - x1 ) * ( x - x1 ) + ( y - y1 ) * ( y - y1 );
2418 if( dist2 < radius2 )
2420 hsv2rgb( h1+((h2-h1)*dist2)/radius2,
2421 s1+((s2-s1)*dist2)/radius2,
2422 v1+((v2-v1)*dist2)/radius2,
2423 &r, &g, &b );
2424 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2426 else
2428 rp_colors[ drawcolor ] = color;
2430 if( rp_colors[ drawcolor ] == prev_color )
2432 /* "mark" that pixel was checked. correct color later. */
2433 PUSH2( x, y );
2434 rp_colors[ drawcolor ] = mark_color;
2436 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2437 draw_pixel( x, y );
2439 if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
2441 PUSH( x-1, y );
2443 if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
2445 PUSH( x+1, y );
2447 if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
2449 PUSH( x, y-1 );
2451 if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
2453 PUSH( x, y+1 );
2456 while (j < COLS*ROWS)
2458 /* correct color. */
2459 POP2( x, y );
2460 rp_colors[ drawcolor ] = prev_color;
2461 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2462 draw_pixel( x, y );
2464 rp_colors[ drawcolor ] = color;
2467 #undef PUSH
2468 #undef POP
2469 #undef PUSH2
2470 #undef POP2
2472 static void draw_toolbars(bool update)
2474 int i;
2475 #define TOP (LCD_HEIGHT-TB_HEIGHT)
2476 rb->lcd_set_background( COLOR_LIGHTGRAY );
2477 rb->lcd_set_foreground( COLOR_LIGHTGRAY );
2478 rb->lcd_fillrect( 0, TOP, LCD_WIDTH, TB_HEIGHT );
2479 rb->lcd_set_foreground( COLOR_BLACK );
2480 rb->lcd_drawrect( 0, TOP, LCD_WIDTH, TB_HEIGHT );
2482 rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
2483 rb->lcd_fillrect( TB_SC_BG_LEFT, TOP+TB_SC_BG_TOP,
2484 TB_SC_SIZE, TB_SC_SIZE );
2485 rb->lcd_set_foreground(ROCKPAINT_PALETTE);
2486 rb->lcd_drawrect( TB_SC_BG_LEFT, TOP+TB_SC_BG_TOP,
2487 TB_SC_SIZE, TB_SC_SIZE );
2488 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2489 rb->lcd_fillrect( TB_SC_FG_LEFT, TOP+TB_SC_FG_TOP,
2490 TB_SC_SIZE, TB_SC_SIZE );
2491 rb->lcd_set_foreground(ROCKPAINT_PALETTE);
2492 rb->lcd_drawrect( TB_SC_FG_LEFT, TOP+TB_SC_FG_TOP,
2493 TB_SC_SIZE, TB_SC_SIZE );
2495 for( i=0; i<18; i++ )
2497 rb->lcd_set_foreground( rp_colors[i] );
2498 rb->lcd_fillrect(
2499 TB_PL_LEFT+(i%9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING ),
2500 TOP+TB_PL_TOP+(i/9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING),
2501 TB_PL_COLOR_SIZE, TB_PL_COLOR_SIZE );
2502 rb->lcd_set_foreground( ROCKPAINT_PALETTE );
2503 rb->lcd_drawrect(
2504 TB_PL_LEFT+(i%9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING ),
2505 TOP+TB_PL_TOP+(i/9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING),
2506 TB_PL_COLOR_SIZE, TB_PL_COLOR_SIZE );
2509 #define SEPARATOR( x, y ) \
2510 rb->lcd_set_foreground( COLOR_WHITE ); \
2511 rb->lcd_vline( x, TOP+y, TOP+y+TB_PL_HEIGHT-1 ); \
2512 rb->lcd_set_foreground( COLOR_DARKGRAY ); \
2513 rb->lcd_vline( x+1, TOP+y, TOP+y+TB_PL_HEIGHT-1 );
2514 SEPARATOR( TB_PL_LEFT + TB_PL_WIDTH - 1 + TB_SP_MARGIN, TB_PL_TOP );
2516 rb->lcd_bitmap_transparent( rockpaint, TB_TL_LEFT, TOP+TB_TL_TOP,
2517 TB_TL_WIDTH, TB_TL_HEIGHT );
2518 rb->lcd_set_foreground(ROCKPAINT_PALETTE);
2519 rb->lcd_drawrect( TB_TL_LEFT+(TB_TL_SIZE+TB_TL_SPACING)*(tool/2),
2520 TOP+TB_TL_TOP+(TB_TL_SIZE+TB_TL_SPACING)*(tool%2),
2521 TB_TL_SIZE, TB_TL_SIZE );
2523 SEPARATOR( TB_TL_LEFT + TB_TL_WIDTH - 1 + TB_SP_MARGIN, TB_TL_TOP );
2525 rb->lcd_setfont( FONT_SYSFIXED );
2526 rb->lcd_putsxy( TB_MENU_LEFT, TOP+TB_MENU_TOP, "Menu" );
2527 rb->lcd_setfont( FONT_UI );
2528 #undef TOP
2530 if( update ) rb->lcd_update();
2533 static void toolbar( void )
2535 int button, i, j;
2536 restore_screen();
2537 draw_toolbars( false );
2538 y = LCD_HEIGHT-TB_HEIGHT/2;
2539 inv_cursor( true );
2540 while( 1 )
2542 switch( button = rb->button_get( true ) )
2544 case ROCKPAINT_DRAW:
2545 #define TOP ( LCD_HEIGHT - TB_HEIGHT )
2546 if( y >= TOP + TB_SC_FG_TOP
2547 && y < TOP + TB_SC_FG_TOP + TB_SC_SIZE
2548 && x >= TB_SC_FG_LEFT
2549 && x < TB_SC_FG_LEFT + TB_SC_SIZE )
2551 /* click on the foreground color */
2552 rp_colors[drawcolor] = color_chooser( rp_colors[drawcolor] );
2554 else if( y >= TOP + TB_SC_BG_TOP
2555 && y < TOP + TB_SC_BG_TOP + TB_SC_SIZE
2556 && x >= TB_SC_BG_LEFT
2557 && x < TB_SC_BG_LEFT + TB_SC_SIZE )
2559 /* click on the background color */
2560 i = drawcolor;
2561 drawcolor = bgdrawcolor;
2562 bgdrawcolor = i;
2564 else if( y >= TOP + TB_PL_TOP
2565 && y < TOP + TB_PL_TOP + TB_PL_HEIGHT
2566 && x >= TB_PL_LEFT
2567 && x < TB_PL_LEFT + TB_PL_WIDTH )
2569 /* click on the palette */
2570 i = (x - TB_PL_LEFT)%(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2571 j = (y - (TOP+TB_PL_TOP) )%(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2572 if( i >= TB_PL_COLOR_SIZE || j >= TB_PL_COLOR_SIZE )
2573 break;
2574 i = ( x - TB_PL_LEFT )/(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2575 j = ( y - (TOP+TB_PL_TOP) )/(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2576 drawcolor = j*(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING)+i;
2578 else if( y >= TOP+TB_TL_TOP
2579 && y < TOP + TB_TL_TOP + TB_TL_HEIGHT
2580 && x >= TB_TL_LEFT
2581 && x <= TB_TL_LEFT + TB_TL_WIDTH )
2583 /* click on the tools */
2584 i = (x - TB_TL_LEFT ) % (TB_TL_SIZE+TB_TL_SPACING);
2585 j = (y - (TOP+TB_TL_TOP) ) %(TB_TL_SIZE+TB_TL_SPACING);
2586 if( i >= TB_TL_SIZE || j >= TB_TL_SIZE ) break;
2587 i = ( x - TB_TL_LEFT )/(TB_TL_SIZE+TB_TL_SPACING);
2588 j = ( y - (TOP+TB_TL_TOP) )/(TB_TL_SIZE+TB_TL_SPACING);
2589 tool = i*2+j;
2590 reset_tool();
2591 if( tool == Text )
2593 buffer->text.initialized = false;
2596 else if( x >= TB_MENU_LEFT && y >= TOP+TB_MENU_TOP-2)
2598 /* menu button */
2599 goto_menu();
2601 #undef TOP
2602 restore_screen();
2603 draw_toolbars( false );
2604 inv_cursor( true );
2605 break;
2607 case ROCKPAINT_LEFT:
2608 case ROCKPAINT_LEFT | BUTTON_REPEAT:
2609 case ROCKPAINT_RIGHT:
2610 case ROCKPAINT_RIGHT | BUTTON_REPEAT:
2611 inv_cursor(false);
2612 incdec_value(&x, &incdec_x,
2613 (button&ROCKPAINT_RIGHT), (button&BUTTON_REPEAT));
2614 inv_cursor(true);
2615 break;
2617 case ROCKPAINT_UP:
2618 case ROCKPAINT_UP | BUTTON_REPEAT:
2619 case ROCKPAINT_DOWN:
2620 case ROCKPAINT_DOWN | BUTTON_REPEAT:
2621 inv_cursor(false);
2622 if (incdec_value(&y, &incdec_y,
2623 (button&ROCKPAINT_DOWN), (button&BUTTON_REPEAT))
2624 || y < LCD_HEIGHT-TB_HEIGHT)
2626 /* went out of region. exit toolbar. */
2627 return;
2629 inv_cursor(true);
2630 break;
2632 case ROCKPAINT_TOOLBAR:
2633 case ROCKPAINT_TOOLBAR2:
2634 return;
2636 if( quit ) return;
2640 static void inv_cursor(bool update)
2642 rb->lcd_set_foreground(COLOR_BLACK);
2643 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
2644 /* cross painting */
2645 rb->lcd_hline(x-4,x+4,y);
2646 rb->lcd_vline(x,y-4,y+4);
2647 rb->lcd_set_foreground(rp_colors[drawcolor]);
2648 rb->lcd_set_drawmode(DRMODE_SOLID);
2650 if( update ) rb->lcd_update();
2653 static void restore_screen(void)
2655 rb->lcd_bitmap( save_buffer, 0, 0, COLS, ROWS );
2656 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
2657 rb->lcd_vline( img_width, 0, ROWS );
2658 rb->lcd_hline( 0, COLS, img_height );
2659 rb->lcd_drawpixel( img_width, img_height );
2660 rb->lcd_set_drawmode(DRMODE_SOLID);
2663 static void clear_drawing(void)
2665 init_buffer();
2666 img_height = ROWS;
2667 img_width = COLS;
2668 rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
2669 rb->lcd_fillrect( 0, 0, COLS, ROWS );
2670 rb->lcd_update();
2673 static void goto_menu(void)
2675 int multi;
2676 int selected = 0;
2678 while( 1 )
2680 switch( rb->do_menu( &main_menu, &selected, NULL, false ) )
2682 case MAIN_MENU_NEW:
2683 clear_drawing();
2684 return;
2686 case MAIN_MENU_LOAD:
2687 if( browse( filename, MAX_PATH, "/" ) )
2689 if( load_bitmap( filename ) <= 0 )
2691 rb->splashf( 1*HZ, "Error while loading %s",
2692 filename );
2693 clear_drawing();
2695 else
2697 rb->splashf( 1*HZ, "Image loaded (%s)", filename );
2698 restore_screen();
2699 inv_cursor(true);
2700 return;
2703 break;
2705 case MAIN_MENU_SAVE:
2706 rb->lcd_set_foreground(COLOR_BLACK);
2707 if (!filename[0])
2708 rb->strcpy(filename,"/");
2709 if( !rb->kbd_input( filename, MAX_PATH ) )
2711 if( !check_extention( filename, ".bmp" ) )
2712 rb->strcat(filename, ".bmp");
2713 save_bitmap( filename );
2714 rb->splashf( 1*HZ, "File saved (%s)", filename );
2716 break;
2718 case MAIN_MENU_SET_WIDTH:
2719 rb->set_int( "Set Width", "px", UNIT_INT, &img_width,
2720 NULL, 1, 1, COLS, NULL );
2721 break;
2722 case MAIN_MENU_SET_HEIGHT:
2723 rb->set_int( "Set Height", "px", UNIT_INT, &img_height,
2724 NULL, 1, 1, ROWS, NULL );
2725 break;
2726 case MAIN_MENU_BRUSH_SIZE:
2727 for(multi = 0; multi<4; multi++)
2728 if(bsize == times_list[multi]) break;
2729 rb->set_option( "Brush Size", &multi, INT, times_options, 4, NULL );
2730 if( multi >= 0 )
2731 bsize = times_list[multi];
2732 break;
2734 case MAIN_MENU_BRUSH_SPEED:
2735 for(multi = 0; multi<3; multi++)
2736 if(bspeed == times_list[multi]) break;
2737 rb->set_option( "Brush Speed", &multi, INT, times_options, 3, NULL );
2738 if( multi >= 0 ) {
2739 bspeed = times_list[multi];
2740 incdec_x.step[0] = bspeed;
2741 incdec_x.step[1] = bspeed * 4;
2742 incdec_y.step[0] = bspeed;
2743 incdec_y.step[1] = bspeed * 4;
2745 break;
2747 case MAIN_MENU_COLOR:
2748 rp_colors[drawcolor] = color_chooser( rp_colors[drawcolor] );
2749 break;
2751 case MAIN_MENU_GRID_SIZE:
2752 for(multi = 0; multi<4; multi++)
2753 if(gridsize == gridsize_list[multi]) break;
2754 rb->set_option( "Grid Size", &multi, INT, gridsize_options, 4, NULL );
2755 if( multi >= 0 )
2756 gridsize = gridsize_list[multi];
2757 break;
2759 case MAIN_MENU_PLAYBACK_CONTROL:
2760 if (!audio_buf)
2761 playback_control( NULL );
2762 else
2763 rb->splash(HZ, "Cannot restart playback");
2764 break;
2766 case MAIN_MENU_EXIT:
2767 restore_screen();
2768 quit=true;
2769 return;
2771 case MAIN_MENU_RESUME:
2772 default:
2773 restore_screen();
2774 return;
2775 }/* end switch */
2776 }/* end while */
2779 static void reset_tool( void )
2781 prev_x = -1;
2782 prev_y = -1;
2783 prev_x2 = -1;
2784 prev_y2 = -1;
2785 prev_x3 = -1;
2786 prev_y3 = -1;
2787 /* reset state */
2788 state = State0;
2789 /* always preview color picker */
2790 preview = (tool == ColorPicker);
2793 /* brush tool */
2794 static void state_func_brush(void)
2796 if( state == State0 )
2798 state = State1;
2800 else
2802 state = State0;
2806 /* fill tool */
2807 static void state_func_fill(void)
2809 draw_fill( x, y );
2810 restore_screen();
2813 /* select rectangle tool */
2814 static void state_func_select(void)
2816 int mode;
2817 if( state == State0 )
2819 prev_x = x;
2820 prev_y = y;
2821 preview = true;
2822 state = State1;
2824 else if( state == State1 )
2826 mode = rb->do_menu( &select_menu, NULL, NULL, false );
2827 switch( mode )
2829 case SELECT_MENU_CUT:
2830 case SELECT_MENU_COPY:
2831 prev_x2 = x;
2832 prev_y2 = y;
2833 if( prev_x < x ) x = prev_x;
2834 if( prev_y < y ) y = prev_y;
2835 prev_x3 = abs(prev_x2 - prev_x);
2836 prev_y3 = abs(prev_y2 - prev_y);
2837 copy_to_clipboard();
2838 state = (mode == SELECT_MENU_CUT? State2: State3);
2839 break;
2841 case SELECT_MENU_INVERT:
2842 draw_invert( prev_x, prev_y, x, y );
2843 reset_tool();
2844 break;
2846 case SELECT_MENU_HFLIP:
2847 draw_hflip( prev_x, prev_y, x, y );
2848 reset_tool();
2849 break;
2851 case SELECT_MENU_VFLIP:
2852 draw_vflip( prev_x, prev_y, x, y );
2853 reset_tool();
2854 break;
2856 case SELECT_MENU_ROTATE90:
2857 draw_rot_90_deg( prev_x, prev_y, x, y, 1 );
2858 reset_tool();
2859 break;
2861 case SELECT_MENU_ROTATE180:
2862 draw_hflip( prev_x, prev_y, x, y );
2863 draw_vflip( prev_x, prev_y, x, y );
2864 reset_tool();
2865 break;
2867 case SELECT_MENU_ROTATE270:
2868 draw_rot_90_deg( prev_x, prev_y, x, y, -1 );
2869 reset_tool();
2870 break;
2872 case SELECT_MENU_CANCEL:
2873 reset_tool();
2874 break;
2876 default:
2877 break;
2879 restore_screen();
2881 else
2883 preview = false;
2884 draw_paste_rectangle( prev_x, prev_y, prev_x2, prev_y2,
2885 x, y, state == State2 );
2886 reset_tool();
2887 restore_screen();
2891 static void preview_select(void)
2893 if( state == State1 )
2895 /* we are defining the selection */
2896 draw_select_rectangle( prev_x, prev_y, x, y );
2898 else
2900 /* we are pasting the selected data */
2901 draw_paste_rectangle( prev_x, prev_y, prev_x2, prev_y2,
2902 x, y, state == State2 );
2903 draw_select_rectangle( x, y, x+prev_x3, y+prev_y3 );
2907 /* color picker tool */
2908 static void state_func_picker(void)
2910 preview = false;
2911 color_picker( x, y );
2912 reset_tool();
2915 static void preview_picker(void)
2917 color_picker( x, y );
2920 /* curve tool */
2921 static void state_func_curve(void)
2923 if( state == State0 )
2925 prev_x = x;
2926 prev_y = y;
2927 preview = true;
2928 state = State1;
2930 else if( state == State1 )
2932 prev_x2 = x;
2933 prev_y2 = y;
2934 state = State2;
2936 else if( state == State2 )
2938 prev_x3 = x;
2939 prev_y3 = y;
2940 state = State3;
2942 else
2944 preview = false;
2945 draw_curve( prev_x, prev_y, prev_x2, prev_y2,
2946 prev_x3, prev_y3, x, y );
2947 reset_tool();
2948 restore_screen();
2952 static void preview_curve(void)
2954 if( state == State1 )
2956 draw_line( prev_x, prev_y, x, y );
2958 else
2960 draw_curve( prev_x, prev_y, prev_x2, prev_y2,
2961 prev_x3, prev_y3, x, y );
2965 /* text tool */
2966 static void state_func_text(void)
2968 draw_text( x, y );
2971 /* tools which take 2 point */
2972 static void preview_2point(void);
2973 static void state_func_2point(void)
2975 if( state == State0 )
2977 prev_x = x;
2978 prev_y = y;
2979 state = State1;
2980 preview = true;
2982 else
2984 preview = false;
2985 preview_2point();
2986 reset_tool();
2987 restore_screen();
2991 static void preview_2point(void)
2993 if( state == State1 )
2995 switch( tool )
2997 case Line:
2998 draw_line( prev_x, prev_y, x, y );
2999 break;
3000 case Rectangle:
3001 draw_rect( prev_x, prev_y, x, y );
3002 break;
3003 case RectangleFull:
3004 draw_rect_full( prev_x, prev_y, x, y );
3005 break;
3006 case Oval:
3007 draw_oval_empty( prev_x, prev_y, x, y );
3008 break;
3009 case OvalFull:
3010 draw_oval_full( prev_x, prev_y, x, y );
3011 break;
3012 case LinearGradient:
3013 linear_gradient( prev_x, prev_y, x, y );
3014 break;
3015 case RadialGradient:
3016 radial_gradient( prev_x, prev_y, x, y );
3017 break;
3018 default:
3019 break;
3021 if( !preview )
3023 reset_tool();
3024 restore_screen();
3029 static const struct tool_func tools[14] = {
3030 [Brush] = { state_func_brush, NULL },
3031 [Fill] = { state_func_fill, NULL },
3032 [SelectRectangle] = { state_func_select, preview_select },
3033 [ColorPicker] = { state_func_picker, preview_picker },
3034 [Line] = { state_func_2point, preview_2point },
3035 [Unused] = { NULL, NULL },
3036 [Curve] = { state_func_curve, preview_curve },
3037 [Text] = { state_func_text, NULL },
3038 [Rectangle] = { state_func_2point, preview_2point },
3039 [RectangleFull] = { state_func_2point, preview_2point },
3040 [Oval] = { state_func_2point, preview_2point },
3041 [OvalFull] = { state_func_2point, preview_2point },
3042 [LinearGradient] = { state_func_2point, preview_2point },
3043 [RadialGradient] = { state_func_2point, preview_2point },
3046 static bool rockpaint_loop( void )
3048 int button = 0, i, j;
3049 bool bigstep;
3051 x = 10;
3052 toolbar();
3053 x = 0; y = 0;
3054 restore_screen();
3055 inv_cursor(true);
3057 while (!quit) {
3058 button = rb->button_get(true);
3059 bigstep = (button & BUTTON_REPEAT) && !(tool == Brush && state == State1);
3061 switch(button)
3063 case ROCKPAINT_QUIT:
3064 if (state != State0)
3066 reset_tool();
3067 restore_screen();
3068 inv_cursor(true);
3070 else
3072 rb->lcd_set_drawmode(DRMODE_SOLID);
3073 return PLUGIN_OK;
3075 break;
3077 case ROCKPAINT_MENU:
3078 goto_menu();
3079 restore_screen();
3080 inv_cursor(true);
3081 break;
3083 case ROCKPAINT_DRAW:
3084 if( tools[tool].state_func )
3086 inv_cursor(false);
3087 tools[tool].state_func();
3088 inv_cursor(true);
3090 break;
3092 case ROCKPAINT_DRAW|BUTTON_REPEAT:
3093 if( tool == Curve && state != State0 )
3095 /* 3 point bezier curve */
3096 preview = false;
3097 draw_curve( prev_x, prev_y, prev_x2, prev_y2,
3098 -1, -1, x, y );
3099 reset_tool();
3100 restore_screen();
3101 inv_cursor( true );
3103 break;
3105 case ROCKPAINT_TOOLBAR:
3106 case ROCKPAINT_TOOLBAR2:
3107 i = x; j = y;
3108 x = (button == ROCKPAINT_TOOLBAR2) ? 110: 10;
3109 toolbar();
3110 x = i; y = j;
3111 restore_screen();
3112 inv_cursor(true);
3113 break;
3115 case ROCKPAINT_LEFT:
3116 case ROCKPAINT_LEFT | BUTTON_REPEAT:
3117 case ROCKPAINT_RIGHT:
3118 case ROCKPAINT_RIGHT | BUTTON_REPEAT:
3119 inv_cursor(false);
3120 incdec_value(&x, &incdec_x,
3121 (button&ROCKPAINT_RIGHT), bigstep);
3122 inv_cursor(true);
3123 break;
3125 case ROCKPAINT_UP:
3126 case ROCKPAINT_UP | BUTTON_REPEAT:
3127 case ROCKPAINT_DOWN:
3128 case ROCKPAINT_DOWN | BUTTON_REPEAT:
3129 inv_cursor(false);
3130 if (incdec_value(&y, &incdec_y,
3131 (button&ROCKPAINT_DOWN), bigstep)
3132 && (button&ROCKPAINT_DOWN))
3134 toolbar();
3135 restore_screen();
3137 inv_cursor(true);
3138 break;
3140 default:
3141 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
3142 return PLUGIN_USB_CONNECTED;
3143 break;
3145 if( tool == Brush && state == State1 )
3147 inv_cursor(false);
3148 draw_brush( x, y );
3149 inv_cursor(true);
3151 if( preview && tools[tool].preview_func )
3153 restore_screen();
3154 tools[tool].preview_func();
3155 inv_cursor( true );
3157 if( gridsize > 0 )
3159 show_grid( true );
3160 show_grid( false );
3164 return PLUGIN_OK;
3167 static int load_bitmap( const char *file )
3169 struct bitmap bm;
3170 bool ret;
3171 int i, j;
3172 fb_data color = rp_colors[ bgdrawcolor ];
3174 bm.data = (char*)save_buffer;
3175 ret = rb->read_bmp_file( file, &bm, ROWS*COLS*sizeof( fb_data ),
3176 FORMAT_NATIVE, NULL );
3178 if((bm.width > COLS ) || ( bm.height > ROWS ))
3179 return -1;
3181 img_width = bm.width;
3182 img_height = bm.height;
3183 for( i = bm.height-1; i >= 0; i-- )
3185 rb->memmove( save_buffer+i*COLS, save_buffer+i*bm.width,
3186 sizeof( fb_data )*bm.width );
3187 for( j = bm.width; j < COLS; j++ )
3188 save_buffer[j+i*COLS] = color;
3190 for( i = bm.height*COLS; i < ROWS*COLS; i++ )
3191 save_buffer[i] = color;
3193 return ret;
3196 static int save_bitmap( char *file )
3198 struct bitmap bm;
3199 int i;
3200 for(i = 0; i < img_height; i++)
3202 rb->memcpy( buffer->clipboard+i*img_width, save_buffer+i*COLS,
3203 sizeof( fb_data )*img_width );
3205 bm.data = (char*)buffer->clipboard;
3206 bm.height = img_height;
3207 bm.width = img_width;
3208 bm.format = FORMAT_NATIVE;
3209 return save_bmp_file( file, &bm );
3212 enum plugin_status plugin_start(const void* parameter)
3214 size_t buffer_size;
3215 unsigned char *temp;
3216 temp = rb->plugin_get_buffer(&buffer_size);
3217 if (buffer_size < sizeof(*buffer) + 3)
3219 /* steal from audiobuffer if plugin buffer is too small */
3220 temp = rb->plugin_get_audio_buffer(&buffer_size);
3221 if (buffer_size < sizeof(*buffer) + 3)
3223 rb->splash(HZ, "Not enough memory");
3224 return PLUGIN_ERROR;
3226 audio_buf = true;
3228 buffer = (union buf*) (((uintptr_t)temp + 3) & ~3);
3230 rb->lcd_set_foreground(COLOR_WHITE);
3231 rb->lcd_set_backdrop(NULL);
3232 rb->lcd_fillrect(0,0,LCD_WIDTH,LCD_HEIGHT);
3233 rb->splash( HZ/2, "Rock Paint");
3235 rb->lcd_clear_display();
3237 filename[0] = '\0';
3239 if( parameter )
3241 if( load_bitmap( parameter ) <= 0 )
3243 rb->splash( 1*HZ, "File Open Error");
3244 clear_drawing();
3246 else
3248 rb->splashf( 1*HZ, "Image loaded (%s)", (char *)parameter );
3249 restore_screen();
3250 rb->strcpy( filename, parameter );
3253 else
3255 clear_drawing();
3257 inv_cursor(true);
3259 return rockpaint_loop();