Explicitely say 'minutes' when speaking the battery time, fixes FS#11932.
[kugel-rb.git] / apps / plugins / rockpaint.c
blob0acbe41aaf05b9b0688bc71574b5da16c74e38a5
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 #else
214 #error "Please define keys for this keypad"
215 #endif
217 #ifdef HAVE_TOUCHSCREEN
218 #ifndef ROCKPAINT_QUIT
219 #define ROCKPAINT_QUIT BUTTON_TOPLEFT
220 #endif
221 #ifndef ROCKPAINT_DRAW
222 #define ROCKPAINT_DRAW BUTTON_CENTER
223 #endif
224 #ifndef ROCKPAINT_MENU
225 #define ROCKPAINT_MENU BUTTON_TOPRIGHT
226 #endif
227 #ifndef ROCKPAINT_TOOLBAR
228 #define ROCKPAINT_TOOLBAR BUTTON_BOTTOMLEFT
229 #endif
230 #ifndef ROCKPAINT_TOOLBAR2
231 #define ROCKPAINT_TOOLBAR2 BUTTON_BOTTOMRIGHT
232 #endif
233 #ifndef ROCKPAINT_UP
234 #define ROCKPAINT_UP BUTTON_TOPMIDDLE
235 #endif
236 #ifndef ROCKPAINT_DOWN
237 #define ROCKPAINT_DOWN BUTTON_BOTTOMMIDDLE
238 #endif
239 #ifndef ROCKPAINT_LEFT
240 #define ROCKPAINT_LEFT BUTTON_MIDLEFT
241 #endif
242 #ifndef ROCKPAINT_RIGHT
243 #define ROCKPAINT_RIGHT BUTTON_MIDRIGHT
244 #endif
245 #endif
247 /***********************************************************************
248 * Palette Default Colors
249 ***********************************************************************/
250 #define COLOR_BLACK LCD_RGBPACK(0,0,0)
251 #define COLOR_WHITE LCD_RGBPACK(255,255,255)
252 #define COLOR_DARKGRAY LCD_RGBPACK(128,128,128)
253 #define COLOR_LIGHTGRAY LCD_RGBPACK(192,192,192)
254 #define COLOR_RED LCD_RGBPACK(128,0,0)
255 #define COLOR_LIGHTRED LCD_RGBPACK(255,0,0)
256 #define COLOR_DARKYELLOW LCD_RGBPACK(128,128,0)
257 #define COLOR_YELLOW LCD_RGBPACK(255,255,0)
258 #define COLOR_GREEN LCD_RGBPACK(0,128,0)
259 #define COLOR_LIGHTGREN LCD_RGBPACK(0,255,0)
260 #define COLOR_CYAN LCD_RGBPACK(0,128,128)
261 #define COLOR_LIGHTCYAN LCD_RGBPACK(0,255,255)
262 #define COLOR_BLUE LCD_RGBPACK(0,0,128)
263 #define COLOR_LIGHTBLUE LCD_RGBPACK(0,0,255)
264 #define COLOR_PURPLE LCD_RGBPACK(128,0,128)
265 #define COLOR_PINK LCD_RGBPACK(255,0,255)
266 #define COLOR_BROWN LCD_RGBPACK(128,64,0)
267 #define COLOR_LIGHTBROWN LCD_RGBPACK(255,128,64)
269 /***********************************************************************
270 * Program Colors
271 ***********************************************************************/
272 #define ROCKPAINT_PALETTE LCD_RGBPACK(0,64,128)
273 #define ROCKPAINT_SELECTED LCD_RGBPACK(128,192,255)
275 #define ROWS LCD_HEIGHT
276 #define COLS LCD_WIDTH
279 * Toolbar positioning stuff ... don't read this unless you really need to
281 * TB Toolbar
282 * SP Separator
283 * SC Selected Color
284 * PL Palette
285 * TL Tools
288 /* Separator sizes */
289 #define TB_SP_MARGIN 3
290 #define TB_SP_WIDTH (2+2*TB_SP_MARGIN)
292 /* Selected color sizes */
293 #define TB_SC_SIZE 12
295 /* Palette sizes */
296 #define TB_PL_COLOR_SIZE 7
297 #define TB_PL_COLOR_SPACING 2
298 #define TB_PL_WIDTH ( 9 * TB_PL_COLOR_SIZE + 8 * TB_PL_COLOR_SPACING )
299 #define TB_PL_HEIGHT ( TB_PL_COLOR_SIZE * 2 + TB_PL_COLOR_SPACING )
301 /* Tools sizes */
302 #define TB_TL_SIZE 8
303 #define TB_TL_SPACING 2
304 #define TB_TL_WIDTH ( 7 * ( TB_TL_SIZE + TB_TL_SPACING ) - TB_TL_SPACING )
305 #define TB_TL_HEIGHT ( 2 * TB_TL_SIZE + TB_TL_SPACING )
307 /* Menu button size ... gruik */
308 #define TB_MENU_MIN_WIDTH 30
310 /* Selected colors position */
311 #define TB_SC_FG_TOP 2
312 #define TB_SC_FG_LEFT 2
313 #define TB_SC_BG_TOP (TB_SC_FG_TOP+TB_PL_COLOR_SIZE*2+TB_PL_COLOR_SPACING-TB_SC_SIZE)
314 #define TB_SC_BG_LEFT (TB_SC_FG_LEFT+TB_PL_COLOR_SIZE*2+TB_PL_COLOR_SPACING-TB_SC_SIZE)
316 /* Palette position */
317 #define TB_PL_TOP TB_SC_FG_TOP
318 #define TB_PL_LEFT (TB_SC_BG_LEFT + TB_SC_SIZE + TB_PL_COLOR_SPACING)
320 /* Tools position */
321 #define TB_TL_TOP TB_SC_FG_TOP
322 #define TB_TL_LEFT ( TB_PL_LEFT + TB_PL_WIDTH-1 + TB_SP_WIDTH )
324 #if TB_TL_LEFT + TB_TL_WIDTH + TB_MENU_MIN_WIDTH >= LCD_WIDTH
325 #undef TB_TL_TOP
326 #undef TB_TL_LEFT
327 #define TB_TL_TOP ( TB_PL_TOP + TB_PL_HEIGHT + 4 )
328 #define TB_TL_LEFT TB_SC_FG_LEFT
329 #endif
331 /* Menu button position */
332 #define TB_MENU_TOP ( TB_TL_TOP + (TB_TL_HEIGHT-8)/2 )
333 #define TB_MENU_LEFT ( TB_TL_LEFT + TB_TL_WIDTH-1 + TB_SP_WIDTH )
335 #define TB_HEIGHT ( TB_TL_TOP + TB_TL_HEIGHT + 1 )
338 static void draw_pixel(int x,int y);
339 static void draw_line( int x1, int y1, int x2, int y2 );
340 static void draw_rect( int x1, int y1, int x2, int y2 );
341 static void draw_rect_full( int x1, int y1, int x2, int y2 );
342 static void draw_toolbars(bool update);
343 static void inv_cursor(bool update);
344 static void restore_screen(void);
345 static void clear_drawing(void);
346 static void reset_tool(void);
347 static void goto_menu(void);
348 static int load_bitmap( const char *filename );
349 static int save_bitmap( char *filename );
351 /***********************************************************************
352 * Global variables
353 ***********************************************************************/
355 static int drawcolor=0; /* Current color (in palette) */
356 static int bgdrawcolor=9; /* Current background color (in palette) */
357 static int img_height = ROWS;
358 static int img_width = COLS;
359 bool isbg = false; /* gruik ugly hack alert */
361 static int preview=false; /* Is preview mode on ? */
363 /* TODO: clean this up */
364 static int x=0, y=0; /* cursor position */
365 static int prev_x=-1, prev_y=-1; /* previous saved cursor position */
366 static int prev_x2=-1, prev_y2=-1;
367 static int prev_x3=-1, prev_y3=-1;
370 static int bsize=1; /* brush size */
371 static int bspeed=1; /* brush speed */
373 enum Tools { Brush = 0, /* Regular brush */
374 Fill = 1, /* Fill a shape with current color */
375 SelectRectangle = 2,
376 ColorPicker = 3, /* Pick a color */
377 Line = 4, /* Draw a line between two points */
378 Unused = 5, /* THIS IS UNUSED ... */
379 Curve = 6,
380 Text = 7,
381 Rectangle = 8, /* Draw a rectangle */
382 RectangleFull = 9,
383 Oval = 10, /* Draw an oval */
384 OvalFull = 11,
385 LinearGradient = 12,
386 RadialGradient = 13
389 enum States { State0 = 0, /* initial state */
390 State1,
391 State2,
392 State3,
395 enum Tools tool = Brush;
396 enum States state = State0;
398 static bool quit=false;
399 static int gridsize=0;
401 static fb_data rp_colors[18] =
403 COLOR_BLACK, COLOR_DARKGRAY, COLOR_RED, COLOR_DARKYELLOW,
404 COLOR_GREEN, COLOR_CYAN, COLOR_BLUE, COLOR_PURPLE, COLOR_BROWN,
405 COLOR_WHITE, COLOR_LIGHTGRAY, COLOR_LIGHTRED, COLOR_YELLOW,
406 COLOR_LIGHTGREN, COLOR_LIGHTCYAN, COLOR_LIGHTBLUE, COLOR_PINK,
407 COLOR_LIGHTBROWN
410 static fb_data save_buffer[ ROWS*COLS ];
412 struct tool_func {
413 void (*state_func)(void);
414 void (*preview_func)(void);
417 struct incdec_ctx {
418 int max;
419 int step[2];
420 bool wrap;
422 struct incdec_ctx incdec_x = { COLS, { 1, 4}, true };
423 struct incdec_ctx incdec_y = { ROWS, { 1, 4}, true };
425 /* Maximum string size allowed for the text tool */
426 #define MAX_TEXT 256
428 union buf
430 /* Used by fill and gradient algorithms */
431 struct
433 short x;
434 short y;
435 } coord[ ROWS*COLS ];
437 /* Used by bezier curve algorithms */
438 struct
440 short x1, y1;
441 short x2, y2;
442 short x3, y3;
443 short x4, y4;
444 short depth;
445 } bezier[ (ROWS*COLS)/5 ]; /* We have 4.5 times more data per struct
446 * than coord ... so we divide to take
447 * less memory. */
449 /* Used to cut/copy/paste data */
450 fb_data clipboard[ ROWS*COLS ];
452 /* Used for text mode */
453 struct
455 char text[MAX_TEXT];
456 char font[MAX_PATH];
457 bool initialized;
458 size_t cache_used;
459 /* fonts from cache_first to cache_last are stored. */
460 int cache_first;
461 int cache_last;
462 /* save these so that cache can be re-used next time. */
463 int fvi;
464 int si;
465 } text;
468 static union buf *buffer;
469 static bool audio_buf = false;
471 /* Current filename */
472 static char filename[MAX_PATH];
474 static bool incdec_value(int *pval, struct incdec_ctx *ctx, bool inc, bool bigstep)
476 bool of = true;
477 int step = ctx->step[bigstep?1:0];
478 step = inc?step: -step;
479 *pval += step;
480 if (ctx->wrap)
482 if (*pval < 0) *pval += ctx->max;
483 else if (*pval >= ctx->max) *pval -= ctx->max;
484 else of = false;
486 else
488 if (*pval < 0) *pval = 0;
489 else if (*pval > ctx->max) *pval = ctx->max;
490 else of = false;
492 return of;
495 /***********************************************************************
496 * Offscreen buffer/Text/Fonts handling
498 * Parts of code taken from firmware/drivers/lcd-16bit.c
499 ***********************************************************************/
500 static void buffer_mono_bitmap_part(
501 fb_data *buf, int buf_width, int buf_height,
502 const unsigned char *src, int src_x, int src_y,
503 int stride, int x, int y, int width, int height )
504 /* this function only draws the foreground part of the bitmap */
506 const unsigned char *src_end;
507 fb_data *dst, *dst_end;
508 unsigned fgcolor = rb->lcd_get_foreground();
510 /* nothing to draw? */
511 if( ( width <= 0 ) || ( height <= 0 ) || ( x >= buf_width )
512 || ( y >= buf_height ) || ( x + width <= 0 ) || ( y + height <= 0 ) )
513 return;
515 /* clipping */
516 if( x < 0 )
518 width += x;
519 src_x -= x;
520 x = 0;
522 if( y < 0 )
524 height += y;
525 src_y -= y;
526 y = 0;
528 if( x + width > buf_width )
529 width = buf_width - x;
530 if( y + height > buf_height )
531 height = buf_height - y;
533 src += stride * (src_y >> 3) + src_x; /* move starting point */
534 src_y &= 7;
535 src_end = src + width;
537 dst = buf + y*buf_width + x;
541 const unsigned char *src_col = src++;
542 unsigned data = *src_col >> src_y;
543 fb_data *dst_col = dst++;
544 int numbits = 8 - src_y;
546 dst_end = dst_col + height * buf_width;
549 if( data & 0x01 )
550 *dst_col = fgcolor; /* FIXME ? */
552 dst_col += buf_width;
554 data >>= 1;
555 if( --numbits == 0 )
557 src_col += stride;
558 data = *src_col;
559 numbits = 8;
561 } while( dst_col < dst_end );
562 } while( src < src_end );
565 static void buffer_putsxyofs( fb_data *buf, int buf_width, int buf_height,
566 int x, int y, int ofs, const unsigned char *str )
568 unsigned short ch;
569 unsigned short *ucs;
571 struct font *pf = rb->font_get( FONT_UI );
572 if( !pf ) pf = rb->font_get( FONT_SYSFIXED );
574 ucs = rb->bidi_l2v( str, 1 );
576 while( (ch = *ucs++) != 0 && x < buf_width )
578 int width;
579 const unsigned char *bits;
581 /* get proportional width and glyph bits */
582 width = rb->font_get_width( pf, ch );
584 if( ofs > width )
586 ofs -= width;
587 continue;
590 bits = rb->font_get_bits( pf, ch );
592 buffer_mono_bitmap_part( buf, buf_width, buf_height, bits, ofs, 0,
593 width, x, y, width - ofs, pf->height);
595 x += width - ofs;
596 ofs = 0;
600 /***********************************************************************
601 * Menu handling
602 ***********************************************************************/
603 enum {
604 /* Main menu */
605 MAIN_MENU_RESUME,
606 MAIN_MENU_NEW, MAIN_MENU_LOAD, MAIN_MENU_SAVE,
607 MAIN_MENU_SET_WIDTH, MAIN_MENU_SET_HEIGHT,
608 MAIN_MENU_BRUSH_SIZE, MAIN_MENU_BRUSH_SPEED, MAIN_MENU_COLOR,
609 MAIN_MENU_GRID_SIZE,
610 MAIN_MENU_PLAYBACK_CONTROL,
611 MAIN_MENU_EXIT,
613 enum {
614 /* Select action menu */
615 SELECT_MENU_CUT, SELECT_MENU_COPY,
616 SELECT_MENU_INVERT, SELECT_MENU_HFLIP, SELECT_MENU_VFLIP,
617 SELECT_MENU_ROTATE90, SELECT_MENU_ROTATE180, SELECT_MENU_ROTATE270,
618 SELECT_MENU_CANCEL,
620 enum {
621 /* Text menu */
622 TEXT_MENU_TEXT, TEXT_MENU_FONT,
623 TEXT_MENU_PREVIEW, TEXT_MENU_APPLY, TEXT_MENU_CANCEL,
626 MENUITEM_STRINGLIST(main_menu, "RockPaint", NULL,
627 "Resume", "New", "Load", "Save",
628 "Set Width", "Set Height",
629 "Brush Size", "Brush Speed",
630 "Choose Color", "Grid Size",
631 "Playback Control", "Exit");
632 MENUITEM_STRINGLIST(select_menu, "Select...", NULL,
633 "Cut", "Copy",
634 "Invert", "Horizontal Flip", "Vertical Flip",
635 "Rotate 90°", "Rotate 180°", "Rotate 270°",
636 "Cancel");
637 MENUITEM_STRINGLIST(text_menu, "Text", NULL,
638 "Set Text", "Change Font",
639 "Preview", "Apply", "Cancel");
640 static const int times_list[] = { 1, 2, 4, 8 };
641 static const int gridsize_list[] = { 0, 5, 10, 20 };
642 static const struct opt_items times_options[] = {
643 { "1x", -1 }, { "2x", -1 }, { "4x", -1 }, { "8x", -1 }
645 static const struct opt_items gridsize_options[] = {
646 { "No grid", -1 }, { "5px", -1 }, { "10px", -1 }, { "20px", -1 }
649 static int draw_window( int height, int width,
650 int *top, int *left,
651 const char *title )
653 int fh;
654 rb->lcd_getstringsize( title, NULL, &fh );
655 fh++;
657 const int _top = ( LCD_HEIGHT - height ) / 2;
658 const int _left = ( LCD_WIDTH - width ) / 2;
659 if( top ) *top = _top;
660 if( left ) *left = _left;
661 rb->lcd_set_background(COLOR_BLUE);
662 rb->lcd_set_foreground(COLOR_LIGHTGRAY);
663 rb->lcd_fillrect( _left, _top, width, height );
664 rb->lcd_set_foreground(COLOR_BLUE);
665 rb->lcd_fillrect( _left, _top, width, fh+4 );
666 rb->lcd_set_foreground(COLOR_WHITE);
667 rb->lcd_putsxy( _left+2, _top+2, title );
668 rb->lcd_set_foreground(COLOR_BLACK);
669 rb->lcd_drawrect( _left, _top, width, height );
670 return _top+fh+4;
673 /***********************************************************************
674 * File browser
675 ***********************************************************************/
677 char bbuf[MAX_PATH]; /* used by file and font browsers */
678 char bbuf_s[MAX_PATH]; /* used by file and font browsers */
679 struct tree_context *tree = NULL;
681 static bool check_extention(const char *filename, const char *ext)
683 const char *p = rb->strrchr( filename, '.' );
684 return ( p != NULL && !rb->strcasecmp( p, ext ) );
687 /* only displayes directories and .bmp files */
688 static bool callback_show_item(char *name, int attr, struct tree_context *tc)
690 (void) tc;
691 if( ( attr & ATTR_DIRECTORY ) ||
692 ( !(attr & ATTR_DIRECTORY) && check_extention( name, ".bmp" ) ) )
694 return true;
696 return false;
699 static bool browse( char *dst, int dst_size, const char *start )
701 struct browse_context browse;
703 rb->browse_context_init(&browse, SHOW_ALL,
704 BROWSE_SELECTONLY|BROWSE_NO_CONTEXT_MENU,
705 NULL, NOICON, start, NULL);
707 browse.callback_show_item = callback_show_item;
708 browse.buf = dst;
709 browse.bufsize = dst_size;
711 rb->rockbox_browse(&browse);
713 return (browse.flags & BROWSE_SELECTED);
716 /***********************************************************************
717 * Font browser
719 * FIXME: This still needs some work ... it currently only works fine
720 * on the simulators, disk spins too much on real targets -> rendered
721 * font buffer needed.
722 ***********************************************************************/
724 * cache font preview handling assumes:
725 * - fvi doesn't decrease by more than 1.
726 * In other words, cache_first-1 must be cached before cache_first-2 is cached.
727 * - there is enough space to store all preview currently displayed.
729 static bool browse_fonts( char *dst, int dst_size )
731 #define LINE_SPACE 2
732 #define PREVIEW_SIZE(x) ((x)->size)
733 #define PREVIEW_NEXT(x) (struct font_preview *)((char*)(x) + PREVIEW_SIZE(x))
735 struct tree_context backup;
736 struct entry *dc, *e;
737 int dirfilter = SHOW_FONT;
739 struct font_preview {
740 unsigned short width;
741 unsigned short height;
742 size_t size; /* to avoid calculating size each time. */
743 fb_data preview[0];
744 } *font_preview = NULL;
746 int top = 0;
748 int fvi = 0; /* first visible item */
749 int lvi = 0; /* last visible item */
750 int si = 0; /* selected item */
751 int li = 0; /* last item */
752 int nvih = 0; /* next visible item height */
753 int i;
754 bool need_redraw = true; /* Do we need to redraw ? */
755 bool reset_font = false;
756 bool ret = false;
758 int cp = 0; /* current position */
759 int sp = 0; /* selected position */
760 int fh, fw; /* font height, width */
762 unsigned char *cache = (unsigned char *) buffer + sizeof(buffer->text);
763 size_t cache_size = sizeof(*buffer) - sizeof(buffer->text);
764 size_t cache_used = 0;
765 int cache_first = 0, cache_last = -1;
766 char *a;
768 rb->snprintf( bbuf_s, MAX_PATH, FONT_DIR "/%s.fnt",
769 rb->global_settings->font_file );
771 tree = rb->tree_get_context();
772 backup = *tree;
773 dc = tree->dircache;
774 a = backup.currdir+rb->strlen(backup.currdir)-1;
775 if( *a != '/' )
777 *++a = '/';
779 rb->strcpy( a+1, dc[tree->selected_item].name );
780 tree->dirfilter = &dirfilter;
781 tree->browse = NULL;
782 rb->strcpy( bbuf, FONT_DIR "/" );
783 rb->set_current_file( bbuf );
785 if( buffer->text.initialized )
787 cache_used = buffer->text.cache_used;
788 cache_first = buffer->text.cache_first;
789 cache_last = buffer->text.cache_last;
790 fvi = buffer->text.fvi;
791 si = buffer->text.si;
793 buffer->text.initialized = true;
795 while( 1 )
797 if( !need_redraw )
799 /* we don't need to redraw ... but we need to unselect
800 * the previously selected item */
801 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
802 rb->lcd_fillrect( 10, sp, LCD_WIDTH-10, font_preview->height );
803 rb->lcd_set_drawmode(DRMODE_SOLID);
806 if( need_redraw )
808 need_redraw = false;
810 rb->lcd_set_foreground(COLOR_BLACK);
811 rb->lcd_set_background(COLOR_LIGHTGRAY);
812 rb->lcd_clear_display();
814 rb->font_getstringsize( "Fonts", NULL, &fh, FONT_UI );
815 rb->lcd_putsxy( 2, 2, "Fonts" );
816 top = fh + 4 + LINE_SPACE;
818 font_preview = (struct font_preview *) cache;
819 /* get first font preview to be displayed. */
820 for( i = cache_first; i < cache_last && i < fvi; i++ )
822 font_preview = PREVIEW_NEXT(font_preview);
824 for( ; fvi < lvi && nvih > 0; fvi++ )
826 nvih -= font_preview->height + LINE_SPACE;
827 font_preview = PREVIEW_NEXT(font_preview);
829 nvih = 0;
830 i = fvi;
832 cp = top;
833 while( cp <= LCD_HEIGHT+LINE_SPACE && i < tree->filesindir )
835 e = &dc[i];
836 if( i < cache_first || i > cache_last )
838 size_t siz;
839 reset_font = true;
840 rb->snprintf( bbuf, MAX_PATH, FONT_DIR "/%s", e->name );
841 rb->font_load(NULL, bbuf );
842 rb->font_getstringsize( e->name, &fw, &fh, FONT_UI );
843 if( fw > LCD_WIDTH ) fw = LCD_WIDTH;
844 siz = (sizeof(struct font_preview) + fw*fh*FB_DATA_SZ+3) & ~3;
845 if( i < cache_first )
847 /* insert font preview to the top. */
848 cache_used = 0;
849 for( ; cache_first <= cache_last; cache_first++ )
851 font_preview = (struct font_preview *) (cache + cache_used);
852 size_t size = PREVIEW_SIZE(font_preview);
853 if( cache_used + size >= cache_size - siz )
854 break;
855 cache_used += size;
857 cache_last = cache_first-1;
858 cache_first = i;
859 rb->memmove( cache+siz, cache, cache_used );
860 font_preview = (struct font_preview *) cache;
862 else /* i > cache_last */
864 /* add font preview to the bottom. */
865 font_preview = (struct font_preview *) cache;
866 while( cache_used >= cache_size - siz )
868 cache_used -= PREVIEW_SIZE(font_preview);
869 font_preview = PREVIEW_NEXT(font_preview);
870 cache_first++;
872 cache_last = i;
873 rb->memmove( cache, font_preview, cache_used );
874 font_preview = (struct font_preview *) (cache + cache_used);
876 cache_used += siz;
877 /* create preview cache. */
878 font_preview->width = fw;
879 font_preview->height = fh;
880 font_preview->size = siz;
881 /* clear with background. */
882 for( siz = fw*fh; siz > 0; )
884 font_preview->preview[--siz] = COLOR_LIGHTGRAY;
886 buffer_putsxyofs( font_preview->preview,
887 fw, fh, 0, 0, 0, e->name );
889 else
891 fw = font_preview->width;
892 fh = font_preview->height;
894 if( cp + fh >= LCD_HEIGHT )
896 nvih = fh;
897 break;
899 rb->lcd_bitmap( font_preview->preview, 10, cp, fw, fh );
900 cp += fh + LINE_SPACE;
901 i++;
902 font_preview = PREVIEW_NEXT(font_preview);
904 lvi = i-1;
905 li = tree->filesindir-1;
906 if( reset_font )
908 rb->font_load(NULL, bbuf_s );
909 reset_font = false;
911 if( lvi-fvi+1 < tree->filesindir )
913 rb->gui_scrollbar_draw( rb->screens[SCREEN_MAIN], 0, top,
914 9, LCD_HEIGHT-top,
915 tree->filesindir, fvi, lvi+1, VERTICAL );
919 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
920 sp = top;
921 font_preview = (struct font_preview *) cache;
922 for( i = cache_first; i < si; i++ )
924 if( i >= fvi )
925 sp += font_preview->height + LINE_SPACE;
926 font_preview = PREVIEW_NEXT(font_preview);
928 rb->lcd_fillrect( 10, sp, LCD_WIDTH-10, font_preview->height );
929 rb->lcd_set_drawmode(DRMODE_SOLID);
931 rb->lcd_update();
933 switch( rb->button_get(true) )
935 case ROCKPAINT_UP:
936 case ROCKPAINT_UP|BUTTON_REPEAT:
937 if( si > 0 )
939 si--;
940 if( si < fvi )
942 fvi = si;
943 nvih = 0;
944 need_redraw = true;
947 break;
949 case ROCKPAINT_DOWN:
950 case ROCKPAINT_DOWN|BUTTON_REPEAT:
951 if( si < li )
953 si++;
954 if( si > lvi )
956 need_redraw = true;
959 break;
961 case ROCKPAINT_RIGHT:
962 case ROCKPAINT_DRAW:
963 ret = true;
964 rb->snprintf( dst, dst_size, FONT_DIR "/%s", dc[si].name );
965 /* fall through */
966 case ROCKPAINT_LEFT:
967 case ROCKPAINT_QUIT:
968 buffer->text.cache_used = cache_used;
969 buffer->text.cache_first = cache_first;
970 buffer->text.cache_last = cache_last;
971 buffer->text.fvi = fvi;
972 buffer->text.si = si;
973 *tree = backup;
974 rb->set_current_file( backup.currdir );
975 return ret;
978 #undef LINE_SPACE
979 #undef PREVIEW_SIZE
980 #undef PREVIEW_NEXT
983 /***********************************************************************
984 * HSVRGB Color chooser
985 ***********************************************************************/
986 static unsigned int color_chooser( unsigned int color )
988 int red = RGB_UNPACK_RED( color );
989 int green = RGB_UNPACK_GREEN( color );
990 int blue = RGB_UNPACK_BLUE( color );
991 int hue, saturation, value;
992 int r, g, b; /* temp variables */
993 int i, top, left;
994 int button;
995 int *pval;
996 static struct incdec_ctx ctxs[] = {
997 { 3600, { 10, 100}, true }, /* hue */
998 { 0xff, { 1, 8}, false }, /* the others */
1001 enum BaseColor { Hue = 0, Saturation = 1, Value = 2,
1002 Red = 3, Green = 4, Blue = 5 };
1003 enum BaseColor current = Red;
1004 bool has_changed;
1006 restore_screen();
1008 rgb2hsv( red, green, blue, &hue, &saturation, &value );
1010 while( 1 )
1012 has_changed = false;
1013 color = LCD_RGBPACK( red, green, blue );
1015 #define HEIGHT ( 100 )
1016 #define WIDTH ( 150 )
1018 top = draw_window( HEIGHT, WIDTH, NULL, &left, "Color chooser" );
1019 top -= 15;
1021 for( i=0; i<100; i++ )
1023 hsv2rgb( i*36, saturation, value, &r, &g, &b );
1024 rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
1025 rb->lcd_vline( left+15+i, top+20, top+27 );
1026 hsv2rgb( hue, i*255/100, value, &r, &g, &b );
1027 rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
1028 rb->lcd_vline( left+15+i, top+30, top+37 );
1029 hsv2rgb( hue, saturation, i*255/100, &r, &g, &b );
1030 rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
1031 rb->lcd_vline( left+15+i, top+40, top+47 );
1032 rb->lcd_set_foreground( LCD_RGBPACK( i*255/100, green, blue ) );
1033 rb->lcd_vline( left+15+i, top+50, top+57 );
1034 rb->lcd_set_foreground( LCD_RGBPACK( red, i*255/100, blue ) );
1035 rb->lcd_vline( left+15+i, top+60, top+67 );
1036 rb->lcd_set_foreground( LCD_RGBPACK( red, green, i*255/100 ) );
1037 rb->lcd_vline( left+15+i, top+70, top+77 );
1040 rb->lcd_set_foreground(COLOR_BLACK);
1041 #define POSITION( a, i ) \
1042 rb->lcd_drawpixel( left+14+i, top + 19 + a ); \
1043 rb->lcd_drawpixel( left+16+i, top + 19 + a ); \
1044 rb->lcd_drawpixel( left+14+i, top + 28 + a ); \
1045 rb->lcd_drawpixel( left+16+i, top + 28 + a );
1046 POSITION( 0, hue/36 );
1047 POSITION( 10, saturation*99/255 );
1048 POSITION( 20, value*99/255 );
1049 POSITION( 30, red*99/255 );
1050 POSITION( 40, green*99/255 );
1051 POSITION( 50, blue*99/255 );
1052 #undef POSITION
1053 rb->lcd_set_background(COLOR_LIGHTGRAY);
1054 rb->lcd_setfont( FONT_SYSFIXED );
1055 rb->lcd_putsxyf( left + 117, top + 20, "%d", hue/10 );
1056 rb->lcd_putsxyf( left + 117, top + 30, "%d.%d",
1057 saturation/255, ((saturation*100)/255)%100 );
1058 rb->lcd_putsxyf( left + 117, top + 40, "%d.%d",
1059 value/255, ((value*100)/255)%100 );
1060 rb->lcd_putsxyf( left + 117, top + 50, "%d", red );
1061 rb->lcd_putsxyf( left + 117, top + 60, "%d", green );
1062 rb->lcd_putsxyf( left + 117, top + 70, "%d", blue );
1063 rb->lcd_setfont( FONT_UI );
1065 #define CURSOR( l ) \
1066 rb->lcd_bitmap_transparent_part( rockpaint_hsvrgb, 1, 1, 16, left+l+1, top+20, 6, 58 ); \
1067 rb->lcd_bitmap_transparent_part( rockpaint_hsvrgb, 8, 10*current, 16, left+l, top+19+10*current, 8, 10 );
1068 CURSOR( 5 );
1069 #undef CURSOR
1071 rb->lcd_set_foreground( color );
1072 rb->lcd_fillrect( left+15, top+85, 100, 8 );
1074 rb->lcd_update();
1076 switch( button = rb->button_get(true) )
1078 case ROCKPAINT_UP:
1079 current = ( current + 5 )%6;
1080 break;
1082 case ROCKPAINT_DOWN:
1083 current = ( current + 1 )%6;
1084 break;
1086 case ROCKPAINT_LEFT:
1087 case ROCKPAINT_LEFT|BUTTON_REPEAT:
1088 case ROCKPAINT_RIGHT:
1089 case ROCKPAINT_RIGHT|BUTTON_REPEAT:
1090 has_changed = true;
1091 switch( current )
1093 case Hue:
1094 pval = &hue;
1095 break;
1096 case Saturation:
1097 pval = &saturation;
1098 break;
1099 case Value:
1100 pval = &value;
1101 break;
1102 case Red:
1103 pval = &red;
1104 break;
1105 case Green:
1106 pval = &green;
1107 break;
1108 case Blue:
1109 pval = &blue;
1110 break;
1111 default:
1112 pval = NULL;
1113 break;
1115 if (pval)
1117 incdec_value(pval, &ctxs[(current != Hue? 1: 0)],
1118 (button&ROCKPAINT_RIGHT), (button&BUTTON_REPEAT));
1120 break;
1122 case ROCKPAINT_DRAW:
1123 return color;
1125 if( has_changed )
1127 switch( current )
1129 case Hue:
1130 case Saturation:
1131 case Value:
1132 hsv2rgb( hue, saturation, value, &red, &green, &blue );
1133 break;
1135 case Red:
1136 case Green:
1137 case Blue:
1138 rgb2hsv( red, green, blue, &hue, &saturation, &value );
1139 break;
1142 #undef HEIGHT
1143 #undef WIDTH
1147 /***********************************************************************
1148 * Misc routines
1149 ***********************************************************************/
1150 static void init_buffer(void)
1152 int i;
1153 fb_data color = rp_colors[ bgdrawcolor ];
1154 for( i = 0; i < ROWS*COLS; i++ )
1156 save_buffer[i] = color;
1160 static void draw_pixel(int x,int y)
1162 if( !preview )
1164 if( x < 0 || x >= COLS || y < 0 || y >= ROWS ) return;
1165 if( isbg )
1167 save_buffer[ x+y*COLS ] = rp_colors[bgdrawcolor];
1169 else
1171 save_buffer[ x+y*COLS ] = rp_colors[drawcolor];
1174 rb->lcd_drawpixel(x,y);
1177 static void color_picker( int x, int y )
1179 if( preview )
1181 rb->lcd_set_foreground( save_buffer[ x+y*COLS ] );
1182 #define PSIZE 12
1183 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1184 if( x >= COLS - PSIZE ) x -= PSIZE + 2;
1185 if( y >= ROWS - PSIZE ) y -= PSIZE + 2;
1186 rb->lcd_drawrect( x + 2, y + 2, PSIZE - 2, PSIZE - 2 );
1187 rb->lcd_set_drawmode(DRMODE_SOLID);
1188 rb->lcd_fillrect( x + 3, y + 3, PSIZE - 4, PSIZE - 4 );
1189 #undef PSIZE
1190 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1192 else
1194 rp_colors[ drawcolor ] = save_buffer[ x+y*COLS ];
1198 static void draw_select_rectangle( int x1, int y1, int x2, int y2 )
1199 /* This is a preview mode only function */
1201 int i,a;
1202 if( x1 > x2 )
1204 i = x1;
1205 x1 = x2;
1206 x2 = i;
1208 if( y1 > y2 )
1210 i = y1;
1211 y1 = y2;
1212 y2 = i;
1214 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1215 i = 0;
1216 for( a = x1; a < x2; a++, i++ )
1217 if( i%2 )
1218 rb->lcd_drawpixel( a, y1 );
1219 for( a = y1; a < y2; a++, i++ )
1220 if( i%2 )
1221 rb->lcd_drawpixel( x2, a );
1222 if( y2 != y1 )
1223 for( a = x2; a > x1; a--, i++ )
1224 if( i%2 )
1225 rb->lcd_drawpixel( a, y2 );
1226 if( x2 != x1 )
1227 for( a = y2; a > y1; a--, i++ )
1228 if( i%2 )
1229 rb->lcd_drawpixel( x1, a );
1230 rb->lcd_set_drawmode(DRMODE_SOLID);
1233 static void copy_to_clipboard( void )
1235 /* This needs to be optimised ... but i'm lazy ATM */
1236 rb->memcpy( buffer->clipboard, save_buffer, COLS*ROWS*sizeof( fb_data ) );
1239 /* no preview mode handling atm ... do we need it ? (one if) */
1240 static void draw_invert( int x1, int y1, int x2, int y2 )
1242 int i;
1243 if( x1 > x2 )
1245 i = x1;
1246 x1 = x2;
1247 x2 = i;
1249 if( y1 > y2 )
1251 i = y1;
1252 y1 = y2;
1253 y2 = i;
1256 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1257 rb->lcd_fillrect( x1, y1, x2-x1+1, y2-y1+1 );
1258 rb->lcd_set_drawmode(DRMODE_SOLID);
1260 for( ; y1<=y2; y1++ )
1262 for( i = x1; i<=x2; i++ )
1264 save_buffer[ y1*COLS + i ] = ~save_buffer[ y1*COLS + i ];
1267 /*if( update )*/ rb->lcd_update();
1270 static void draw_hflip( int x1, int y1, int x2, int y2 )
1272 int i;
1273 if( x1 > x2 )
1275 i = x1;
1276 x1 = x2;
1277 x2 = i;
1279 if( y1 > y2 )
1281 i = y1;
1282 y1 = y2;
1283 y2 = i;
1286 copy_to_clipboard();
1288 for( i = 0; i <= y2 - y1; i++ )
1290 rb->memcpy( save_buffer+(y1+i)*COLS+x1,
1291 buffer->clipboard+(y2-i)*COLS+x1,
1292 (x2-x1+1)*sizeof( fb_data ) );
1294 restore_screen();
1295 rb->lcd_update();
1298 static void draw_vflip( int x1, int y1, int x2, int y2 )
1300 int i;
1301 if( x1 > x2 )
1303 i = x1;
1304 x1 = x2;
1305 x2 = i;
1307 if( y1 > y2 )
1309 i = y1;
1310 y1 = y2;
1311 y2 = i;
1314 copy_to_clipboard();
1316 for( ; y1 <= y2; y1++ )
1318 for( i = 0; i <= x2 - x1; i++ )
1320 save_buffer[y1*COLS+x1+i] = buffer->clipboard[y1*COLS+x2-i];
1323 restore_screen();
1324 rb->lcd_update();
1327 /* direction: -1 = left, 1 = right */
1328 static void draw_rot_90_deg( int x1, int y1, int x2, int y2, int direction )
1330 int i, j;
1331 if( x1 > x2 )
1333 i = x1;
1334 x1 = x2;
1335 x2 = i;
1337 if( y1 > y2 )
1339 i = y1;
1340 y1 = y2;
1341 y2 = i;
1344 copy_to_clipboard();
1346 fb_data color = rp_colors[ bgdrawcolor ];
1347 const int width = x2 - x1, height = y2 - y1;
1348 const int sub_half = width/2-height/2, add_half = (width+height)/2;
1349 if( width > height )
1351 for( i = 0; i <= height; i++ )
1353 for( j = 0; j < sub_half; j++ )
1354 save_buffer[(y1+i)*COLS+x1+j] = color;
1355 for( j = add_half+1; j <= width; j++ )
1356 save_buffer[(y1+i)*COLS+x1+j] = color;
1359 else if( width < height )
1361 for( j = 0; j <= width; j++ )
1363 for( i = 0; i < -sub_half; i++ )
1364 save_buffer[(y1+i)*COLS+x1+j] = color;
1365 for( i = add_half+1; i <= height; i++ )
1366 save_buffer[(y1+i)*COLS+x1+j] = color;
1369 int x3 = x1 + sub_half, y3 = y1 - sub_half;
1370 int is = x3<0?-x3:0, ie = COLS-x3-1, js = y3<0?-y3:0, je = ROWS-y3-1;
1371 if( ie > height ) ie = height;
1372 if( je > width ) je = width;
1373 for( i = is; i <= ie; i++ )
1375 for( j = js; j <= je; j++ )
1377 int x, y;
1378 if(direction > 0)
1380 x = x1+j;
1381 y = y1+height-i;
1383 else
1385 x = x1+width-j;
1386 y = y1+i;
1388 save_buffer[(y3+j)*COLS+x3+i] = buffer->clipboard[y*COLS+x];
1391 restore_screen();
1392 rb->lcd_update();
1395 static void draw_paste_rectangle( int src_x1, int src_y1, int src_x2,
1396 int src_y2, int x1, int y1, int cut )
1398 int i, width, height;
1399 if( cut )
1401 i = drawcolor;
1402 drawcolor = bgdrawcolor;
1403 draw_rect_full( src_x1, src_y1, src_x2, src_y2 );
1404 drawcolor = i;
1406 if( src_x1 > src_x2 )
1408 i = src_x1;
1409 src_x1 = src_x2;
1410 src_x2 = i;
1412 if( src_y1 > src_y2 )
1414 i = src_y1;
1415 src_y1 = src_y2;
1416 src_y2 = i;
1418 width = src_x2 - src_x1 + 1;
1419 height = src_y2 - src_y1 + 1;
1420 /* clipping */
1421 if( x1 + width > COLS )
1422 width = COLS - x1;
1423 if( y1 + height > ROWS )
1424 height = ROWS - y1;
1426 rb->lcd_bitmap_part( buffer->clipboard, src_x1, src_y1, COLS,
1427 x1, y1, width, height );
1428 if( !preview )
1430 for( i = 0; i < height; i++ )
1432 rb->memcpy( save_buffer+(y1+i)*COLS+x1,
1433 buffer->clipboard+(src_y1+i)*COLS+src_x1,
1434 width*sizeof( fb_data ) );
1439 static void show_grid( bool update )
1441 int i;
1442 if( gridsize > 0 )
1444 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1445 for( i = gridsize; i < img_width; i+= gridsize )
1447 rb->lcd_vline( i, 0, img_height-1 );
1449 for( i = gridsize; i < img_height; i+= gridsize )
1451 rb->lcd_hline( 0, img_width-1, i );
1453 rb->lcd_set_drawmode(DRMODE_SOLID);
1454 if( update ) rb->lcd_update();
1458 static void draw_text( int x, int y )
1460 int selected = 0;
1461 buffer->text.text[0] = '\0';
1462 buffer->text.font[0] = '\0';
1463 while( 1 )
1465 switch( rb->do_menu( &text_menu, &selected, NULL, NULL ) )
1467 case TEXT_MENU_TEXT:
1468 rb->lcd_set_foreground(COLOR_BLACK);
1469 rb->kbd_input( buffer->text.text, MAX_TEXT );
1470 break;
1472 case TEXT_MENU_FONT:
1473 if( browse_fonts( buffer->text.font, MAX_PATH ) )
1475 rb->font_load(NULL, buffer->text.font );
1477 break;
1479 case TEXT_MENU_PREVIEW:
1480 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1481 while( 1 )
1483 int button;
1484 restore_screen();
1485 rb->lcd_putsxy( x, y, buffer->text.text );
1486 rb->lcd_update();
1487 switch( button = rb->button_get( true ) )
1489 case ROCKPAINT_LEFT:
1490 case ROCKPAINT_LEFT | BUTTON_REPEAT:
1491 case ROCKPAINT_RIGHT:
1492 case ROCKPAINT_RIGHT | BUTTON_REPEAT:
1493 incdec_value(&x, &incdec_x,
1494 (button&ROCKPAINT_RIGHT), (button&BUTTON_REPEAT));
1495 break;
1497 case ROCKPAINT_UP:
1498 case ROCKPAINT_UP | BUTTON_REPEAT:
1499 case ROCKPAINT_DOWN:
1500 case ROCKPAINT_DOWN | BUTTON_REPEAT:
1501 incdec_value(&y, &incdec_y,
1502 (button&ROCKPAINT_DOWN), (button&BUTTON_REPEAT));
1503 break;
1505 case ROCKPAINT_DRAW:
1506 break;
1507 default:
1508 if(rb->default_event_handler(button)
1509 == SYS_USB_CONNECTED)
1510 button = ROCKPAINT_DRAW;
1511 break;
1513 if( button == ROCKPAINT_DRAW ) break;
1515 break;
1517 case TEXT_MENU_APPLY:
1518 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1519 buffer_putsxyofs( save_buffer, COLS, ROWS, x, y, 0,
1520 buffer->text.text );
1521 case TEXT_MENU_CANCEL:
1522 default:
1523 restore_screen();
1524 if( buffer->text.font[0] )
1526 rb->snprintf( buffer->text.font, MAX_PATH,
1527 FONT_DIR "/%s.fnt",
1528 rb->global_settings->font_file );
1529 rb->font_load(NULL, buffer->text.font );
1531 return;
1536 static void draw_brush( int x, int y )
1538 int i,j;
1539 for( i=-bsize/2+(bsize+1)%2; i<=bsize/2; i++ )
1541 for( j=-bsize/2+(bsize+1)%2; j<=bsize/2; j++ )
1543 draw_pixel( x+i, y+j );
1548 /* This is an implementation of Bresenham's line algorithm.
1549 * See http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm.
1551 static void draw_line( int x1, int y1, int x2, int y2 )
1553 int x = x1;
1554 int y = y1;
1555 int deltax = x2 - x1;
1556 int deltay = y2 - y1;
1557 int i;
1559 int xerr = abs(deltax);
1560 int yerr = abs(deltay);
1561 int xstep = deltax > 0 ? 1 : -1;
1562 int ystep = deltay > 0 ? 1 : -1;
1563 int err;
1565 if (yerr > xerr)
1567 /* more vertical */
1568 err = yerr;
1569 xerr <<= 1;
1570 yerr <<= 1;
1572 /* to leave off the last pixel of the line, leave off the "+ 1" */
1573 for (i = err + 1; i; --i)
1575 draw_pixel(x, y);
1576 y += ystep;
1577 err -= xerr;
1578 if (err < 0) {
1579 x += xstep;
1580 err += yerr;
1584 else
1586 /* more horizontal */
1587 err = xerr;
1588 xerr <<= 1;
1589 yerr <<= 1;
1591 for (i = err + 1; i; --i)
1593 draw_pixel(x, y);
1594 x += xstep;
1595 err -= yerr;
1596 if (err < 0) {
1597 y += ystep;
1598 err += xerr;
1604 static void draw_curve( int x1, int y1, int x2, int y2,
1605 int xa, int ya, int xb, int yb )
1607 int i = 0;
1608 short xl1, yl1;
1609 short xl2, yl2;
1610 short xl3, yl3;
1611 short xl4, yl4;
1612 short xr1, yr1;
1613 short xr2, yr2;
1614 short xr3, yr3;
1615 short xr4, yr4;
1616 short depth;
1617 short xh, yh;
1619 if( x1 == x2 && y1 == y2 )
1621 draw_pixel( x1, y1 );
1622 return;
1625 // if( preview )
1627 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1628 if( xa == -1 || ya == -1 )
1630 rb->lcd_drawline( x1, y1, xb, yb );
1631 rb->lcd_drawline( x2, y2, xb, yb );
1633 else
1635 rb->lcd_drawline( x1, y1, xa, ya );
1636 rb->lcd_drawline( x2, y2, xb, yb );
1638 rb->lcd_set_drawmode(DRMODE_SOLID);
1641 if( xa == -1 || ya == -1 )
1642 /* We only have 3 of the points
1643 * This will currently only be used in preview mode */
1645 #define PUSH( a1, b1, a2, b2, a3, b3, d ) \
1646 buffer->bezier[i].x1 = a1; \
1647 buffer->bezier[i].y1 = b1; \
1648 buffer->bezier[i].x2 = a2; \
1649 buffer->bezier[i].y2 = b2; \
1650 buffer->bezier[i].x3 = a3; \
1651 buffer->bezier[i].y3 = b3; \
1652 buffer->bezier[i].depth = d; \
1653 i++;
1654 #define POP( a1, b1, a2, b2, a3, b3, d ) \
1655 i--; \
1656 a1 = buffer->bezier[i].x1; \
1657 b1 = buffer->bezier[i].y1; \
1658 a2 = buffer->bezier[i].x2; \
1659 b2 = buffer->bezier[i].y2; \
1660 a3 = buffer->bezier[i].x3; \
1661 b3 = buffer->bezier[i].y3; \
1662 d = buffer->bezier[i].depth;
1664 PUSH( x1<<4, y1<<4, xb<<4, yb<<4, x2<<4, y2<<4, 0 );
1665 while( i )
1667 /* de Casteljau's algorithm (see wikipedia) */
1668 POP( xl1, yl1, xb, yb, xr3, yr3, depth );
1669 if( depth < 10 ) /* check that the stack's 'i' doesn't overflow */
1671 xl2 = ( xl1 + xb )>>1;
1672 yl2 = ( yl1 + yb )>>1;
1673 xr2 = ( xb + xr3 )>>1;
1674 yr2 = ( yb + yr3 )>>1;
1675 xr1 = ( xl2 + xr2 )>>1;
1676 yr1 = ( yl2 + yr2 )>>1;
1677 xl3 = xr1;
1678 yl3 = yr1;
1679 PUSH( xl1, yl1, xl2, yl2, xl3, yl3, depth+1 );
1680 PUSH( xr1, yr1, xr2, yr2, xr3, yr3, depth+1 );
1682 else
1684 draw_line( ((xl1>>3)+1)>>1, ((yl1>>3)+1)>>1,
1685 ((xr3>>3)+1)>>1, ((yr3>>3)+1)>>1 );
1688 #undef PUSH
1689 #undef POP
1691 else /* We have the 4 points */
1693 #define PUSH( a1, b1, a2, b2, a3, b3, a4, b4, d ) \
1694 buffer->bezier[i].x1 = a1; \
1695 buffer->bezier[i].y1 = b1; \
1696 buffer->bezier[i].x2 = a2; \
1697 buffer->bezier[i].y2 = b2; \
1698 buffer->bezier[i].x3 = a3; \
1699 buffer->bezier[i].y3 = b3; \
1700 buffer->bezier[i].x4 = a4; \
1701 buffer->bezier[i].y4 = b4; \
1702 buffer->bezier[i].depth = d; \
1703 i++;
1704 #define POP( a1, b1, a2, b2, a3, b3, a4, b4, d ) \
1705 i--; \
1706 a1 = buffer->bezier[i].x1; \
1707 b1 = buffer->bezier[i].y1; \
1708 a2 = buffer->bezier[i].x2; \
1709 b2 = buffer->bezier[i].y2; \
1710 a3 = buffer->bezier[i].x3; \
1711 b3 = buffer->bezier[i].y3; \
1712 a4 = buffer->bezier[i].x4; \
1713 b4 = buffer->bezier[i].y4; \
1714 d = buffer->bezier[i].depth;
1716 PUSH( x1<<4, y1<<4, xa<<4, ya<<4, xb<<4, yb<<4, x2<<4, y2<<4, 0 );
1717 while( i )
1719 /* de Casteljau's algorithm (see wikipedia) */
1720 POP( xl1, yl1, xa, ya, xb, yb, xr4, yr4, depth );
1721 if( depth < 10 ) /* check that the stack's 'i' doesn't overflow */
1723 xl2 = ( xl1 + xa )>>1;
1724 yl2 = ( yl1 + ya )>>1;
1725 xh = ( xa + xb )>>1;
1726 yh = ( ya + yb )>>1;
1727 xr3 = ( xb + xr4 )>>1;
1728 yr3 = ( yb + yr4 )>>1;
1729 xl3 = ( xl2 + xh )>>1;
1730 yl3 = ( yl2 + yh )>>1;
1731 xr2 = ( xr3 + xh )>>1;
1732 yr2 = ( yr3 + yh )>>1;
1733 xl4 = ( xl3 + xr2 )>>1;
1734 yl4 = ( yl3 + yr2 )>>1;
1735 xr1 = xl4;
1736 yr1 = yl4;
1737 PUSH( xl1, yl1, xl2, yl2, xl3, yl3, xl4, yl4, depth+1 );
1738 PUSH( xr1, yr1, xr2, yr2, xr3, yr3, xr4, yr4, depth+1 );
1740 else
1742 draw_line( ((xl1>>3)+1)>>1, ((yl1>>3)+1)>>1,
1743 ((xr4>>3)+1)>>1, ((yr4>>3)+1)>>1 );
1746 #undef PUSH
1747 #undef POP
1751 static void draw_rect( int x1, int y1, int x2, int y2 )
1753 draw_line( x1, y1, x1, y2 );
1754 draw_line( x1, y1, x2, y1 );
1755 draw_line( x1, y2, x2, y2 );
1756 draw_line( x2, y1, x2, y2 );
1759 static void togglebg( void )
1761 if( isbg )
1763 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1765 else
1767 rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
1769 isbg = !isbg;
1772 static void draw_rect_full( int x1, int y1, int x2, int y2 )
1774 /* GRUIK */
1775 int x;
1776 togglebg();
1777 if( x1 > x2 )
1779 x = x1;
1780 x1 = x2;
1781 x2 = x;
1783 x = x1;
1784 do {
1785 draw_line( x, y1, x, y2 );
1786 } while( ++x <= x2 );
1787 togglebg();
1788 draw_rect( x1, y1, x2, y2 );
1791 static void draw_oval( int x1, int y1, int x2, int y2, bool full )
1793 /* TODO: simplify :) */
1794 int cx = (x1+x2)>>1;
1795 int cy = (y1+y2)>>1;
1797 int rx = (x1-x2)>>1;
1798 int ry = (y1-y2)>>1;
1799 if( rx < 0 ) rx *= -1;
1800 if( ry < 0 ) ry *= -1;
1802 if( rx == 0 || ry == 0 )
1804 draw_line( x1, y1, x2, y2 );
1805 return;
1808 int x,y;
1809 int dst, old_dst;
1811 for( x = 0; x < rx; x++ )
1813 y = 0;
1814 dst = -0xfff;
1815 do {
1816 old_dst = dst;
1817 dst = ry * ry * x * x + rx * rx * y * y - rx * rx * ry * ry;
1818 y++;
1819 } while( dst < 0 );
1820 if( -old_dst < dst ) y--;
1821 if( full )
1823 draw_line( cx+x, cy, cx+x, cy+y );
1824 draw_line( cx+x, cy, cx+x, cy-y );
1825 draw_line( cx-x, cy, cx-x, cy+y );
1826 draw_line( cx-x, cy, cx-x, cy-y );
1828 else
1830 draw_pixel( cx+x, cy+y );
1831 draw_pixel( cx+x, cy-y );
1832 draw_pixel( cx-x, cy+y );
1833 draw_pixel( cx-x, cy-y );
1836 for( y = 0; y < ry; y++ )
1838 x = 0;
1839 dst = -0xfff;
1840 do {
1841 old_dst = dst;
1842 dst = ry * ry * x * x + rx * rx * y * y - rx * rx * ry * ry;
1843 x++;
1844 } while( dst < 0 );
1845 if( -old_dst < dst ) x--;
1846 if( full )
1848 draw_line( cx+x, cy, cx+x, cy+y );
1849 draw_line( cx+x, cy, cx+x, cy-y );
1850 draw_line( cx-x, cy, cx-x, cy+y );
1851 draw_line( cx-x, cy, cx-x, cy-y );
1853 else
1855 draw_pixel( cx+x, cy+y );
1856 draw_pixel( cx+x, cy-y );
1857 draw_pixel( cx-x, cy+y );
1858 draw_pixel( cx-x, cy-y );
1863 static void draw_oval_empty( int x1, int y1, int x2, int y2 )
1865 draw_oval( x1, y1, x2, y2, false );
1868 static void draw_oval_full( int x1, int y1, int x2, int y2 )
1870 togglebg();
1871 draw_oval( x1, y1, x2, y2, true );
1872 togglebg();
1873 draw_oval( x1, y1, x2, y2, false );
1876 static void draw_fill( int x0, int y0 )
1878 #define PUSH( a, b ) \
1879 draw_pixel( (int)a, (int)b ); \
1880 buffer->coord[i].x = a; \
1881 buffer->coord[i].y = b; \
1882 i++;
1883 #define POP( a, b ) \
1884 i--; \
1885 a = buffer->coord[i].x; \
1886 b = buffer->coord[i].y;
1888 unsigned int i=0;
1889 short x = x0;
1890 short y = y0;
1891 unsigned int prev_color = save_buffer[ x0+y0*COLS ];
1893 if( preview )
1894 return;
1895 if( prev_color == rp_colors[ drawcolor ] ) return;
1897 PUSH( x, y );
1899 while( i != 0 )
1901 POP( x, y );
1902 if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
1904 PUSH( x-1, y );
1906 if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
1908 PUSH( x+1, y );
1910 if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
1912 PUSH( x, y-1 );
1914 if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
1916 PUSH( x, y+1 );
1919 #undef PUSH
1920 #undef POP
1924 /* For preview purposes only */
1925 /* use same algorithm as draw_line() to draw line. */
1926 static void line_gradient( int x1, int y1, int x2, int y2 )
1928 int h1, s1, v1, h2, s2, v2, r, g, b;
1929 int xerr = x2 - x1, yerr = y2 - y1, xstep, ystep;
1930 int i, delta, err;
1931 fb_data color1, color2;
1933 if( xerr == 0 && yerr == 0 )
1935 draw_pixel( x1, y1 );
1936 return;
1939 xstep = xerr > 0 ? 1 : -1;
1940 ystep = yerr > 0 ? 1 : -1;
1941 xerr = abs(xerr) << 1;
1942 yerr = abs(yerr) << 1;
1944 color1 = rp_colors[ bgdrawcolor ];
1945 color2 = rp_colors[ drawcolor ];
1947 r = RGB_UNPACK_RED( color1 );
1948 g = RGB_UNPACK_GREEN( color1 );
1949 b = RGB_UNPACK_BLUE( color1 );
1950 rgb2hsv( r, g, b, &h1, &s1, &v1 );
1952 r = RGB_UNPACK_RED( color2 );
1953 g = RGB_UNPACK_GREEN( color2 );
1954 b = RGB_UNPACK_BLUE( color2 );
1955 rgb2hsv( r, g, b, &h2, &s2, &v2 );
1957 if( xerr > yerr )
1959 err = xerr>>1;
1960 delta = err+1;
1961 /* to leave off the last pixel of the line, leave off the "+ 1" */
1962 for (i = delta; i; --i)
1964 hsv2rgb( h2+((h1-h2)*i)/delta,
1965 s2+((s1-s2)*i)/delta,
1966 v2+((v1-v2)*i)/delta,
1967 &r, &g, &b );
1968 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
1969 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1970 draw_pixel(x1, y1);
1971 x1 += xstep;
1972 err -= yerr;
1973 if (err < 0) {
1974 y1 += ystep;
1975 err += xerr;
1979 else /* yerr >= xerr */
1981 err = yerr>>1;
1982 delta = err+1;
1983 for (i = delta; i; --i)
1985 hsv2rgb( h2+((h1-h2)*i)/delta,
1986 s2+((s1-s2)*i)/delta,
1987 v2+((v1-v2)*i)/delta,
1988 &r, &g, &b );
1989 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
1990 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1991 draw_pixel(x1, y1);
1992 y1 += ystep;
1993 err -= xerr;
1994 if (err < 0) {
1995 x1 += xstep;
1996 err += yerr;
2000 rp_colors[ drawcolor ] = color2;
2003 /* macros used by linear_gradient() and radial_gradient(). */
2004 #define PUSH( _x, _y ) \
2005 save_buffer[(_x)+(_y)*COLS] = mark_color; \
2006 buffer->coord[i].x = (short)(_x); \
2007 buffer->coord[i].y = (short)(_y); \
2008 i++;
2009 #define POP( _x, _y ) \
2010 i--; \
2011 _x = (int)buffer->coord[i].x; \
2012 _y = (int)buffer->coord[i].y;
2013 #define PUSH2( _x, _y ) \
2014 j--; \
2015 buffer->coord[j].x = (short)(_x); \
2016 buffer->coord[j].y = (short)(_y);
2017 #define POP2( _x, _y ) \
2018 _x = (int)buffer->coord[j].x; \
2019 _y = (int)buffer->coord[j].y; \
2020 j++;
2022 static void linear_gradient( int x1, int y1, int x2, int y2 )
2024 int r1 = RGB_UNPACK_RED( rp_colors[ bgdrawcolor ] );
2025 int g1 = RGB_UNPACK_GREEN( rp_colors[ bgdrawcolor ] );
2026 int b1 = RGB_UNPACK_BLUE( rp_colors[ bgdrawcolor ] );
2027 int r2 = RGB_UNPACK_RED( rp_colors[ drawcolor ] );
2028 int g2 = RGB_UNPACK_GREEN( rp_colors[ drawcolor ] );
2029 int b2 = RGB_UNPACK_BLUE( rp_colors[ drawcolor ] );
2030 fb_data color = rp_colors[ drawcolor ];
2032 int h1, s1, v1, h2, s2, v2, r, g, b;
2034 /* radius^2 */
2035 int radius2 = ( x1 - x2 ) * ( x1 - x2 ) + ( y1 - y2 ) * ( y1 - y2 );
2036 int dist2, i=0, j=COLS*ROWS;
2038 /* We only propagate the gradient to neighboring pixels with the same
2039 * color as ( x1, y1 ) */
2040 fb_data prev_color = save_buffer[ x1+y1*COLS ];
2041 /* to mark pixel that the pixel is already in LIFO. */
2042 fb_data mark_color = ~prev_color;
2044 int x = x1;
2045 int y = y1;
2047 if( radius2 == 0 ) return;
2048 if( preview )
2050 line_gradient( x1, y1, x2, y2 );
2051 return;
2053 if( rp_colors[ drawcolor ] == rp_colors[ bgdrawcolor ] )
2055 draw_fill( x1, y1 );
2056 return;
2059 rgb2hsv( r1, g1, b1, &h1, &s1, &v1 );
2060 rgb2hsv( r2, g2, b2, &h2, &s2, &v2 );
2062 PUSH( x, y );
2064 while( i > 0 )
2066 POP( x, y );
2068 dist2 = ( x2 - x1 ) * ( x - x1 ) + ( y2 - y1 ) * ( y - y1 );
2069 if( dist2 <= 0 )
2071 rp_colors[ drawcolor ] = rp_colors[ bgdrawcolor ];
2073 else if( dist2 < radius2 )
2075 hsv2rgb( h1+((h2-h1)*dist2)/radius2,
2076 s1+((s2-s1)*dist2)/radius2,
2077 v1+((v2-v1)*dist2)/radius2,
2078 &r, &g, &b );
2079 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2081 else
2083 rp_colors[ drawcolor ] = color;
2085 if( rp_colors[ drawcolor ] == prev_color )
2087 /* "mark" that pixel was checked. correct color later. */
2088 PUSH2( x, y );
2089 rp_colors[ drawcolor ] = mark_color;
2091 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2092 draw_pixel( x, y );
2094 if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
2096 PUSH( x-1, y );
2098 if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
2100 PUSH( x+1, y );
2102 if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
2104 PUSH( x, y-1 );
2106 if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
2108 PUSH( x, y+1 );
2111 while (j < COLS*ROWS)
2113 /* correct color. */
2114 POP2( x, y );
2115 rp_colors[ drawcolor ] = prev_color;
2116 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2117 draw_pixel( x, y );
2119 rp_colors[ drawcolor ] = color;
2122 static void radial_gradient( int x1, int y1, int x2, int y2 )
2124 int r1 = RGB_UNPACK_RED( rp_colors[ bgdrawcolor ] );
2125 int g1 = RGB_UNPACK_GREEN( rp_colors[ bgdrawcolor ] );
2126 int b1 = RGB_UNPACK_BLUE( rp_colors[ bgdrawcolor ] );
2127 int r2 = RGB_UNPACK_RED( rp_colors[ drawcolor ] );
2128 int g2 = RGB_UNPACK_GREEN( rp_colors[ drawcolor ] );
2129 int b2 = RGB_UNPACK_BLUE( rp_colors[ drawcolor ] );
2130 fb_data color = rp_colors[ drawcolor ];
2132 int h1, s1, v1, h2, s2, v2, r, g, b;
2134 /* radius^2 */
2135 int radius2 = ( x1 - x2 ) * ( x1 - x2 ) + ( y1 - y2 ) * ( y1 - y2 );
2136 int dist2, i=0, j=COLS*ROWS;
2138 /* We only propagate the gradient to neighboring pixels with the same
2139 * color as ( x1, y1 ) */
2140 fb_data prev_color = save_buffer[ x1+y1*COLS ];
2141 /* to mark pixel that the pixel is already in LIFO. */
2142 fb_data mark_color = ~prev_color;
2144 int x = x1;
2145 int y = y1;
2147 if( radius2 == 0 ) return;
2148 if( preview )
2150 line_gradient( x1, y1, x2, y2 );
2151 return;
2153 if( rp_colors[ drawcolor ] == rp_colors[ bgdrawcolor ] )
2155 draw_fill( x1, y1 );
2156 return;
2159 rgb2hsv( r1, g1, b1, &h1, &s1, &v1 );
2160 rgb2hsv( r2, g2, b2, &h2, &s2, &v2 );
2162 PUSH( x, y );
2164 while( i > 0 )
2166 POP( x, y );
2168 dist2 = ( x - x1 ) * ( x - x1 ) + ( y - y1 ) * ( y - y1 );
2169 if( dist2 < radius2 )
2171 hsv2rgb( h1+((h2-h1)*dist2)/radius2,
2172 s1+((s2-s1)*dist2)/radius2,
2173 v1+((v2-v1)*dist2)/radius2,
2174 &r, &g, &b );
2175 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2177 else
2179 rp_colors[ drawcolor ] = color;
2181 if( rp_colors[ drawcolor ] == prev_color )
2183 /* "mark" that pixel was checked. correct color later. */
2184 PUSH2( x, y );
2185 rp_colors[ drawcolor ] = mark_color;
2187 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2188 draw_pixel( x, y );
2190 if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
2192 PUSH( x-1, y );
2194 if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
2196 PUSH( x+1, y );
2198 if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
2200 PUSH( x, y-1 );
2202 if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
2204 PUSH( x, y+1 );
2207 while (j < COLS*ROWS)
2209 /* correct color. */
2210 POP2( x, y );
2211 rp_colors[ drawcolor ] = prev_color;
2212 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2213 draw_pixel( x, y );
2215 rp_colors[ drawcolor ] = color;
2218 #undef PUSH
2219 #undef POP
2220 #undef PUSH2
2221 #undef POP2
2223 static void draw_toolbars(bool update)
2225 int i;
2226 #define TOP (LCD_HEIGHT-TB_HEIGHT)
2227 rb->lcd_set_background( COLOR_LIGHTGRAY );
2228 rb->lcd_set_foreground( COLOR_LIGHTGRAY );
2229 rb->lcd_fillrect( 0, TOP, LCD_WIDTH, TB_HEIGHT );
2230 rb->lcd_set_foreground( COLOR_BLACK );
2231 rb->lcd_drawrect( 0, TOP, LCD_WIDTH, TB_HEIGHT );
2233 rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
2234 rb->lcd_fillrect( TB_SC_BG_LEFT, TOP+TB_SC_BG_TOP,
2235 TB_SC_SIZE, TB_SC_SIZE );
2236 rb->lcd_set_foreground(ROCKPAINT_PALETTE);
2237 rb->lcd_drawrect( TB_SC_BG_LEFT, TOP+TB_SC_BG_TOP,
2238 TB_SC_SIZE, TB_SC_SIZE );
2239 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2240 rb->lcd_fillrect( TB_SC_FG_LEFT, TOP+TB_SC_FG_TOP,
2241 TB_SC_SIZE, TB_SC_SIZE );
2242 rb->lcd_set_foreground(ROCKPAINT_PALETTE);
2243 rb->lcd_drawrect( TB_SC_FG_LEFT, TOP+TB_SC_FG_TOP,
2244 TB_SC_SIZE, TB_SC_SIZE );
2246 for( i=0; i<18; i++ )
2248 rb->lcd_set_foreground( rp_colors[i] );
2249 rb->lcd_fillrect(
2250 TB_PL_LEFT+(i%9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING ),
2251 TOP+TB_PL_TOP+(i/9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING),
2252 TB_PL_COLOR_SIZE, TB_PL_COLOR_SIZE );
2253 rb->lcd_set_foreground( ROCKPAINT_PALETTE );
2254 rb->lcd_drawrect(
2255 TB_PL_LEFT+(i%9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING ),
2256 TOP+TB_PL_TOP+(i/9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING),
2257 TB_PL_COLOR_SIZE, TB_PL_COLOR_SIZE );
2260 #define SEPARATOR( x, y ) \
2261 rb->lcd_set_foreground( COLOR_WHITE ); \
2262 rb->lcd_vline( x, TOP+y, TOP+y+TB_PL_HEIGHT-1 ); \
2263 rb->lcd_set_foreground( COLOR_DARKGRAY ); \
2264 rb->lcd_vline( x+1, TOP+y, TOP+y+TB_PL_HEIGHT-1 );
2265 SEPARATOR( TB_PL_LEFT + TB_PL_WIDTH - 1 + TB_SP_MARGIN, TB_PL_TOP );
2267 rb->lcd_bitmap_transparent( rockpaint, TB_TL_LEFT, TOP+TB_TL_TOP,
2268 TB_TL_WIDTH, TB_TL_HEIGHT );
2269 rb->lcd_set_foreground(ROCKPAINT_PALETTE);
2270 rb->lcd_drawrect( TB_TL_LEFT+(TB_TL_SIZE+TB_TL_SPACING)*(tool/2),
2271 TOP+TB_TL_TOP+(TB_TL_SIZE+TB_TL_SPACING)*(tool%2),
2272 TB_TL_SIZE, TB_TL_SIZE );
2274 SEPARATOR( TB_TL_LEFT + TB_TL_WIDTH - 1 + TB_SP_MARGIN, TB_TL_TOP );
2276 rb->lcd_setfont( FONT_SYSFIXED );
2277 rb->lcd_putsxy( TB_MENU_LEFT, TOP+TB_MENU_TOP, "Menu" );
2278 rb->lcd_setfont( FONT_UI );
2279 #undef TOP
2281 if( update ) rb->lcd_update();
2284 static void toolbar( void )
2286 int button, i, j;
2287 restore_screen();
2288 draw_toolbars( false );
2289 y = LCD_HEIGHT-TB_HEIGHT/2;
2290 inv_cursor( true );
2291 while( 1 )
2293 switch( button = rb->button_get( true ) )
2295 case ROCKPAINT_DRAW:
2296 #define TOP ( LCD_HEIGHT - TB_HEIGHT )
2297 if( y >= TOP + TB_SC_FG_TOP
2298 && y < TOP + TB_SC_FG_TOP + TB_SC_SIZE
2299 && x >= TB_SC_FG_LEFT
2300 && x < TB_SC_FG_LEFT + TB_SC_SIZE )
2302 /* click on the foreground color */
2303 rp_colors[drawcolor] = color_chooser( rp_colors[drawcolor] );
2305 else if( y >= TOP + TB_SC_BG_TOP
2306 && y < TOP + TB_SC_BG_TOP + TB_SC_SIZE
2307 && x >= TB_SC_BG_LEFT
2308 && x < TB_SC_BG_LEFT + TB_SC_SIZE )
2310 /* click on the background color */
2311 i = drawcolor;
2312 drawcolor = bgdrawcolor;
2313 bgdrawcolor = i;
2315 else if( y >= TOP + TB_PL_TOP
2316 && y < TOP + TB_PL_TOP + TB_PL_HEIGHT
2317 && x >= TB_PL_LEFT
2318 && x < TB_PL_LEFT + TB_PL_WIDTH )
2320 /* click on the palette */
2321 i = (x - TB_PL_LEFT)%(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2322 j = (y - (TOP+TB_PL_TOP) )%(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2323 if( i >= TB_PL_COLOR_SIZE || j >= TB_PL_COLOR_SIZE )
2324 break;
2325 i = ( x - TB_PL_LEFT )/(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2326 j = ( y - (TOP+TB_PL_TOP) )/(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2327 drawcolor = j*(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING)+i;
2329 else if( y >= TOP+TB_TL_TOP
2330 && y < TOP + TB_TL_TOP + TB_TL_HEIGHT
2331 && x >= TB_TL_LEFT
2332 && x <= TB_TL_LEFT + TB_TL_WIDTH )
2334 /* click on the tools */
2335 i = (x - TB_TL_LEFT ) % (TB_TL_SIZE+TB_TL_SPACING);
2336 j = (y - (TOP+TB_TL_TOP) ) %(TB_TL_SIZE+TB_TL_SPACING);
2337 if( i >= TB_TL_SIZE || j >= TB_TL_SIZE ) break;
2338 i = ( x - TB_TL_LEFT )/(TB_TL_SIZE+TB_TL_SPACING);
2339 j = ( y - (TOP+TB_TL_TOP) )/(TB_TL_SIZE+TB_TL_SPACING);
2340 tool = i*2+j;
2341 reset_tool();
2342 if( tool == Text )
2344 buffer->text.initialized = false;
2347 else if( x >= TB_MENU_LEFT && y >= TOP+TB_MENU_TOP-2)
2349 /* menu button */
2350 goto_menu();
2352 #undef TOP
2353 restore_screen();
2354 draw_toolbars( false );
2355 inv_cursor( true );
2356 break;
2358 case ROCKPAINT_LEFT:
2359 case ROCKPAINT_LEFT | BUTTON_REPEAT:
2360 case ROCKPAINT_RIGHT:
2361 case ROCKPAINT_RIGHT | BUTTON_REPEAT:
2362 inv_cursor(false);
2363 incdec_value(&x, &incdec_x,
2364 (button&ROCKPAINT_RIGHT), (button&BUTTON_REPEAT));
2365 inv_cursor(true);
2366 break;
2368 case ROCKPAINT_UP:
2369 case ROCKPAINT_UP | BUTTON_REPEAT:
2370 case ROCKPAINT_DOWN:
2371 case ROCKPAINT_DOWN | BUTTON_REPEAT:
2372 inv_cursor(false);
2373 if (incdec_value(&y, &incdec_y,
2374 (button&ROCKPAINT_DOWN), (button&BUTTON_REPEAT))
2375 || y < LCD_HEIGHT-TB_HEIGHT)
2377 /* went out of region. exit toolbar. */
2378 return;
2380 inv_cursor(true);
2381 break;
2383 case ROCKPAINT_TOOLBAR:
2384 case ROCKPAINT_TOOLBAR2:
2385 return;
2387 if( quit ) return;
2391 static void inv_cursor(bool update)
2393 rb->lcd_set_foreground(COLOR_BLACK);
2394 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
2395 /* cross painting */
2396 rb->lcd_hline(x-4,x+4,y);
2397 rb->lcd_vline(x,y-4,y+4);
2398 rb->lcd_set_foreground(rp_colors[drawcolor]);
2399 rb->lcd_set_drawmode(DRMODE_SOLID);
2401 if( update ) rb->lcd_update();
2404 static void restore_screen(void)
2406 rb->lcd_bitmap( save_buffer, 0, 0, COLS, ROWS );
2407 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
2408 rb->lcd_vline( img_width, 0, ROWS );
2409 rb->lcd_hline( 0, COLS, img_height );
2410 rb->lcd_drawpixel( img_width, img_height );
2411 rb->lcd_set_drawmode(DRMODE_SOLID);
2414 static void clear_drawing(void)
2416 init_buffer();
2417 img_height = ROWS;
2418 img_width = COLS;
2419 rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
2420 rb->lcd_fillrect( 0, 0, COLS, ROWS );
2421 rb->lcd_update();
2424 static void goto_menu(void)
2426 int multi;
2427 int selected = 0;
2429 while( 1 )
2431 switch( rb->do_menu( &main_menu, &selected, NULL, false ) )
2433 case MAIN_MENU_NEW:
2434 clear_drawing();
2435 return;
2437 case MAIN_MENU_LOAD:
2438 if( browse( filename, MAX_PATH, "/" ) )
2440 if( load_bitmap( filename ) <= 0 )
2442 rb->splashf( 1*HZ, "Error while loading %s",
2443 filename );
2444 clear_drawing();
2446 else
2448 rb->splashf( 1*HZ, "Image loaded (%s)", filename );
2449 restore_screen();
2450 inv_cursor(true);
2451 return;
2454 break;
2456 case MAIN_MENU_SAVE:
2457 rb->lcd_set_foreground(COLOR_BLACK);
2458 if (!filename[0])
2459 rb->strcpy(filename,"/");
2460 if( !rb->kbd_input( filename, MAX_PATH ) )
2462 if( !check_extention( filename, ".bmp" ) )
2463 rb->strcat(filename, ".bmp");
2464 save_bitmap( filename );
2465 rb->splashf( 1*HZ, "File saved (%s)", filename );
2467 break;
2469 case MAIN_MENU_SET_WIDTH:
2470 rb->set_int( "Set Width", "px", UNIT_INT, &img_width,
2471 NULL, 1, 1, COLS, NULL );
2472 break;
2473 case MAIN_MENU_SET_HEIGHT:
2474 rb->set_int( "Set Height", "px", UNIT_INT, &img_height,
2475 NULL, 1, 1, ROWS, NULL );
2476 break;
2477 case MAIN_MENU_BRUSH_SIZE:
2478 for(multi = 0; multi<4; multi++)
2479 if(bsize == times_list[multi]) break;
2480 rb->set_option( "Brush Size", &multi, INT, times_options, 4, NULL );
2481 if( multi >= 0 )
2482 bsize = times_list[multi];
2483 break;
2485 case MAIN_MENU_BRUSH_SPEED:
2486 for(multi = 0; multi<3; multi++)
2487 if(bspeed == times_list[multi]) break;
2488 rb->set_option( "Brush Speed", &multi, INT, times_options, 3, NULL );
2489 if( multi >= 0 ) {
2490 bspeed = times_list[multi];
2491 incdec_x.step[0] = bspeed;
2492 incdec_x.step[1] = bspeed * 4;
2493 incdec_y.step[0] = bspeed;
2494 incdec_y.step[1] = bspeed * 4;
2496 break;
2498 case MAIN_MENU_COLOR:
2499 rp_colors[drawcolor] = color_chooser( rp_colors[drawcolor] );
2500 break;
2502 case MAIN_MENU_GRID_SIZE:
2503 for(multi = 0; multi<4; multi++)
2504 if(gridsize == gridsize_list[multi]) break;
2505 rb->set_option( "Grid Size", &multi, INT, gridsize_options, 4, NULL );
2506 if( multi >= 0 )
2507 gridsize = gridsize_list[multi];
2508 break;
2510 case MAIN_MENU_PLAYBACK_CONTROL:
2511 if (!audio_buf)
2512 playback_control( NULL );
2513 else
2514 rb->splash(HZ, "Cannot restart playback");
2515 break;
2517 case MAIN_MENU_EXIT:
2518 restore_screen();
2519 quit=true;
2520 return;
2522 case MAIN_MENU_RESUME:
2523 default:
2524 restore_screen();
2525 return;
2526 }/* end switch */
2527 }/* end while */
2530 static void reset_tool( void )
2532 prev_x = -1;
2533 prev_y = -1;
2534 prev_x2 = -1;
2535 prev_y2 = -1;
2536 prev_x3 = -1;
2537 prev_y3 = -1;
2538 /* reset state */
2539 state = State0;
2540 /* always preview color picker */
2541 preview = (tool == ColorPicker);
2544 /* brush tool */
2545 static void state_func_brush(void)
2547 if( state == State0 )
2549 state = State1;
2551 else
2553 state = State0;
2557 /* fill tool */
2558 static void state_func_fill(void)
2560 draw_fill( x, y );
2561 restore_screen();
2564 /* select rectangle tool */
2565 static void state_func_select(void)
2567 int mode;
2568 if( state == State0 )
2570 prev_x = x;
2571 prev_y = y;
2572 preview = true;
2573 state = State1;
2575 else if( state == State1 )
2577 mode = rb->do_menu( &select_menu, NULL, NULL, false );
2578 switch( mode )
2580 case SELECT_MENU_CUT:
2581 case SELECT_MENU_COPY:
2582 prev_x2 = x;
2583 prev_y2 = y;
2584 if( prev_x < x ) x = prev_x;
2585 if( prev_y < y ) y = prev_y;
2586 prev_x3 = abs(prev_x2 - prev_x);
2587 prev_y3 = abs(prev_y2 - prev_y);
2588 copy_to_clipboard();
2589 state = (mode == SELECT_MENU_CUT? State2: State3);
2590 break;
2592 case SELECT_MENU_INVERT:
2593 draw_invert( prev_x, prev_y, x, y );
2594 reset_tool();
2595 break;
2597 case SELECT_MENU_HFLIP:
2598 draw_hflip( prev_x, prev_y, x, y );
2599 reset_tool();
2600 break;
2602 case SELECT_MENU_VFLIP:
2603 draw_vflip( prev_x, prev_y, x, y );
2604 reset_tool();
2605 break;
2607 case SELECT_MENU_ROTATE90:
2608 draw_rot_90_deg( prev_x, prev_y, x, y, 1 );
2609 reset_tool();
2610 break;
2612 case SELECT_MENU_ROTATE180:
2613 draw_hflip( prev_x, prev_y, x, y );
2614 draw_vflip( prev_x, prev_y, x, y );
2615 reset_tool();
2616 break;
2618 case SELECT_MENU_ROTATE270:
2619 draw_rot_90_deg( prev_x, prev_y, x, y, -1 );
2620 reset_tool();
2621 break;
2623 case SELECT_MENU_CANCEL:
2624 reset_tool();
2625 break;
2627 default:
2628 break;
2630 restore_screen();
2632 else
2634 preview = false;
2635 draw_paste_rectangle( prev_x, prev_y, prev_x2, prev_y2,
2636 x, y, state == State2 );
2637 reset_tool();
2638 restore_screen();
2642 static void preview_select(void)
2644 if( state == State1 )
2646 /* we are defining the selection */
2647 draw_select_rectangle( prev_x, prev_y, x, y );
2649 else
2651 /* we are pasting the selected data */
2652 draw_paste_rectangle( prev_x, prev_y, prev_x2, prev_y2,
2653 x, y, state == State2 );
2654 draw_select_rectangle( x, y, x+prev_x3, y+prev_y3 );
2658 /* color picker tool */
2659 static void state_func_picker(void)
2661 preview = false;
2662 color_picker( x, y );
2663 reset_tool();
2666 static void preview_picker(void)
2668 color_picker( x, y );
2671 /* curve tool */
2672 static void state_func_curve(void)
2674 if( state == State0 )
2676 prev_x = x;
2677 prev_y = y;
2678 preview = true;
2679 state = State1;
2681 else if( state == State1 )
2683 prev_x2 = x;
2684 prev_y2 = y;
2685 state = State2;
2687 else if( state == State2 )
2689 prev_x3 = x;
2690 prev_y3 = y;
2691 state = State3;
2693 else
2695 preview = false;
2696 draw_curve( prev_x, prev_y, prev_x2, prev_y2,
2697 prev_x3, prev_y3, x, y );
2698 reset_tool();
2699 restore_screen();
2703 static void preview_curve(void)
2705 if( state == State1 )
2707 draw_line( prev_x, prev_y, x, y );
2709 else
2711 draw_curve( prev_x, prev_y, prev_x2, prev_y2,
2712 prev_x3, prev_y3, x, y );
2716 /* text tool */
2717 static void state_func_text(void)
2719 draw_text( x, y );
2722 /* tools which take 2 point */
2723 static void preview_2point(void);
2724 static void state_func_2point(void)
2726 if( state == State0 )
2728 prev_x = x;
2729 prev_y = y;
2730 state = State1;
2731 preview = true;
2733 else
2735 preview = false;
2736 preview_2point();
2737 reset_tool();
2738 restore_screen();
2742 static void preview_2point(void)
2744 if( state == State1 )
2746 switch( tool )
2748 case Line:
2749 draw_line( prev_x, prev_y, x, y );
2750 break;
2751 case Rectangle:
2752 draw_rect( prev_x, prev_y, x, y );
2753 break;
2754 case RectangleFull:
2755 draw_rect_full( prev_x, prev_y, x, y );
2756 break;
2757 case Oval:
2758 draw_oval_empty( prev_x, prev_y, x, y );
2759 break;
2760 case OvalFull:
2761 draw_oval_full( prev_x, prev_y, x, y );
2762 break;
2763 case LinearGradient:
2764 linear_gradient( prev_x, prev_y, x, y );
2765 break;
2766 case RadialGradient:
2767 radial_gradient( prev_x, prev_y, x, y );
2768 break;
2769 default:
2770 break;
2772 if( !preview )
2774 reset_tool();
2775 restore_screen();
2780 static const struct tool_func tools[14] = {
2781 [Brush] = { state_func_brush, NULL },
2782 [Fill] = { state_func_fill, NULL },
2783 [SelectRectangle] = { state_func_select, preview_select },
2784 [ColorPicker] = { state_func_picker, preview_picker },
2785 [Line] = { state_func_2point, preview_2point },
2786 [Unused] = { NULL, NULL },
2787 [Curve] = { state_func_curve, preview_curve },
2788 [Text] = { state_func_text, NULL },
2789 [Rectangle] = { state_func_2point, preview_2point },
2790 [RectangleFull] = { state_func_2point, preview_2point },
2791 [Oval] = { state_func_2point, preview_2point },
2792 [OvalFull] = { state_func_2point, preview_2point },
2793 [LinearGradient] = { state_func_2point, preview_2point },
2794 [RadialGradient] = { state_func_2point, preview_2point },
2797 static bool rockpaint_loop( void )
2799 int button = 0, i, j;
2800 bool bigstep;
2802 x = 10;
2803 toolbar();
2804 x = 0; y = 0;
2805 restore_screen();
2806 inv_cursor(true);
2808 while (!quit) {
2809 button = rb->button_get(true);
2810 bigstep = (button & BUTTON_REPEAT) && !(tool == Brush && state == State1);
2812 switch(button)
2814 case ROCKPAINT_QUIT:
2815 if (state != State0)
2817 reset_tool();
2818 restore_screen();
2819 inv_cursor(true);
2821 else
2823 rb->lcd_set_drawmode(DRMODE_SOLID);
2824 return PLUGIN_OK;
2826 break;
2828 case ROCKPAINT_MENU:
2829 goto_menu();
2830 restore_screen();
2831 inv_cursor(true);
2832 break;
2834 case ROCKPAINT_DRAW:
2835 if( tools[tool].state_func )
2837 inv_cursor(false);
2838 tools[tool].state_func();
2839 inv_cursor(true);
2841 break;
2843 case ROCKPAINT_DRAW|BUTTON_REPEAT:
2844 if( tool == Curve && state != State0 )
2846 /* 3 point bezier curve */
2847 preview = false;
2848 draw_curve( prev_x, prev_y, prev_x2, prev_y2,
2849 -1, -1, x, y );
2850 reset_tool();
2851 restore_screen();
2852 inv_cursor( true );
2854 break;
2856 case ROCKPAINT_TOOLBAR:
2857 case ROCKPAINT_TOOLBAR2:
2858 i = x; j = y;
2859 x = (button == ROCKPAINT_TOOLBAR2) ? 110: 10;
2860 toolbar();
2861 x = i; y = j;
2862 restore_screen();
2863 inv_cursor(true);
2864 break;
2866 case ROCKPAINT_LEFT:
2867 case ROCKPAINT_LEFT | BUTTON_REPEAT:
2868 case ROCKPAINT_RIGHT:
2869 case ROCKPAINT_RIGHT | BUTTON_REPEAT:
2870 inv_cursor(false);
2871 incdec_value(&x, &incdec_x,
2872 (button&ROCKPAINT_RIGHT), bigstep);
2873 inv_cursor(true);
2874 break;
2876 case ROCKPAINT_UP:
2877 case ROCKPAINT_UP | BUTTON_REPEAT:
2878 case ROCKPAINT_DOWN:
2879 case ROCKPAINT_DOWN | BUTTON_REPEAT:
2880 inv_cursor(false);
2881 if (incdec_value(&y, &incdec_y,
2882 (button&ROCKPAINT_DOWN), bigstep)
2883 && (button&ROCKPAINT_DOWN))
2885 toolbar();
2886 restore_screen();
2888 inv_cursor(true);
2889 break;
2891 default:
2892 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
2893 return PLUGIN_USB_CONNECTED;
2894 break;
2896 if( tool == Brush && state == State1 )
2898 inv_cursor(false);
2899 draw_brush( x, y );
2900 inv_cursor(true);
2902 if( preview && tools[tool].preview_func )
2904 restore_screen();
2905 tools[tool].preview_func();
2906 inv_cursor( true );
2908 if( gridsize > 0 )
2910 show_grid( true );
2911 show_grid( false );
2915 return PLUGIN_OK;
2918 static int load_bitmap( const char *file )
2920 struct bitmap bm;
2921 bool ret;
2922 int i, j;
2923 fb_data color = rp_colors[ bgdrawcolor ];
2925 bm.data = (char*)save_buffer;
2926 ret = rb->read_bmp_file( file, &bm, ROWS*COLS*sizeof( fb_data ),
2927 FORMAT_NATIVE, NULL );
2929 if((bm.width > COLS ) || ( bm.height > ROWS ))
2930 return -1;
2932 img_width = bm.width;
2933 img_height = bm.height;
2934 for( i = bm.height-1; i >= 0; i-- )
2936 rb->memmove( save_buffer+i*COLS, save_buffer+i*bm.width,
2937 sizeof( fb_data )*bm.width );
2938 for( j = bm.width; j < COLS; j++ )
2939 save_buffer[j+i*COLS] = color;
2941 for( i = bm.height*COLS; i < ROWS*COLS; i++ )
2942 save_buffer[i] = color;
2944 return ret;
2947 static int save_bitmap( char *file )
2949 struct bitmap bm;
2950 int i;
2951 for(i = 0; i < img_height; i++)
2953 rb->memcpy( buffer->clipboard+i*img_width, save_buffer+i*COLS,
2954 sizeof( fb_data )*img_width );
2956 bm.data = (char*)buffer->clipboard;
2957 bm.height = img_height;
2958 bm.width = img_width;
2959 bm.format = FORMAT_NATIVE;
2960 return save_bmp_file( file, &bm );
2963 enum plugin_status plugin_start(const void* parameter)
2965 size_t buffer_size;
2966 unsigned char *temp;
2967 temp = rb->plugin_get_buffer(&buffer_size);
2968 if (buffer_size < sizeof(*buffer) + 3)
2970 /* steal from audiobuffer if plugin buffer is too small */
2971 temp = rb->plugin_get_audio_buffer(&buffer_size);
2972 if (buffer_size < sizeof(*buffer) + 3)
2974 rb->splash(HZ, "Not enough memory");
2975 return PLUGIN_ERROR;
2977 audio_buf = true;
2979 buffer = (union buf*) (((uintptr_t)temp + 3) & ~3);
2981 rb->lcd_set_foreground(COLOR_WHITE);
2982 rb->lcd_set_backdrop(NULL);
2983 rb->lcd_fillrect(0,0,LCD_WIDTH,LCD_HEIGHT);
2984 rb->splash( HZ/2, "Rock Paint");
2986 rb->lcd_clear_display();
2988 filename[0] = '\0';
2990 if( parameter )
2992 if( load_bitmap( parameter ) <= 0 )
2994 rb->splash( 1*HZ, "File Open Error");
2995 clear_drawing();
2997 else
2999 rb->splashf( 1*HZ, "Image loaded (%s)", (char *)parameter );
3000 restore_screen();
3001 rb->strcpy( filename, parameter );
3004 else
3006 clear_drawing();
3008 inv_cursor(true);
3010 return rockpaint_loop();