Re-add the lseek to the beginning of the file which was accidentally removed.
[kugel-rb.git] / apps / plugins / rockpaint.c
blob4ef139a526c467c33c5a37058217f0c0e0ddf7ee
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 static const char* browse_get_name_cb(int selected_item, void *data,
688 char *buffer, size_t buffer_len)
690 int *indexes = (int *) data;
691 struct entry* dc = tree->dircache;
692 struct entry* e = &dc[indexes[selected_item]];
693 (void) buffer;
694 (void) buffer_len;
696 return e->name;
699 static bool browse( char *dst, int dst_size, const char *start )
701 struct gui_synclist browse_list;
702 int item_count = 0, selected, button;
703 struct tree_context backup;
704 struct entry *dc, *e;
705 bool reload = true;
706 int dirfilter = SHOW_ALL;
707 int *indexes = (int *) buffer;
708 size_t bbuf_len, len;
710 char *a;
712 rb->strcpy( bbuf, start );
713 bbuf_len = rb->strlen(bbuf);
714 if( bbuf[bbuf_len-1] != '/' )
716 bbuf[bbuf_len++] = '/';
717 bbuf[bbuf_len] = '\0';
719 bbuf_s[0] = '\0';
721 rb->gui_synclist_init(&browse_list, browse_get_name_cb,
722 (void*) indexes, false, 1, NULL);
724 tree = rb->tree_get_context();
725 backup = *tree;
726 dc = tree->dircache;
727 a = backup.currdir+rb->strlen(backup.currdir)-1;
728 if( *a != '/' )
730 *++a = '/';
732 rb->strcpy( a+1, dc[tree->selected_item].name );
733 tree->dirfilter = &dirfilter;
734 while( 1 )
736 if( reload )
738 int i;
739 rb->set_current_file(bbuf);
740 item_count = 0;
741 selected = 0;
742 for( i = 0; i < tree->filesindir ; i++)
744 e = &dc[i];
745 /* only displayes directories and .bmp files */
746 if( ( e->attr & ATTR_DIRECTORY ) ||
747 ( !(e->attr & ATTR_DIRECTORY) &&
748 check_extention( e->name, ".bmp" ) ) )
750 if( bbuf_s[0] && !rb->strcmp( e->name, bbuf_s ) )
751 selected = item_count;
752 indexes[item_count++] = i;
756 rb->gui_synclist_set_nb_items(&browse_list,item_count);
757 rb->gui_synclist_select_item(&browse_list, selected);
758 rb->gui_synclist_set_title(&browse_list, bbuf, NOICON);
759 rb->gui_synclist_draw(&browse_list);
760 reload = false;
762 button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
763 if (rb->gui_synclist_do_button(&browse_list,&button,LIST_WRAP_UNLESS_HELD))
764 continue;
765 switch( button )
767 case ACTION_STD_CANCEL:
768 if( !rb->strcmp( bbuf, "/" ) )
770 *tree = backup;
771 rb->set_current_file( backup.currdir );
772 return false;
774 a = bbuf + bbuf_len - 1;
775 if( a == bbuf ) break;
776 while( *a == '/' ) a--;
777 *(a+1) = '\0';
778 while( *a != '/' ) a--;
779 /* select parent directory */
780 rb->strcpy( bbuf_s, ++a );
781 *a = '\0';
782 bbuf_len = a - bbuf;
783 reload = true;
784 break;
786 case ACTION_STD_OK:
787 selected = rb->gui_synclist_get_sel_pos( &browse_list );
788 if( selected < 0 || selected >= item_count )
789 break;
790 e = &dc[indexes[selected]];
791 if( !( e->attr & ATTR_DIRECTORY ) )
793 rb->snprintf( dst, dst_size, "%s%s", bbuf, e->name );
794 *tree = backup;
795 rb->set_current_file( backup.currdir );
796 return true;
798 len = rb->strlen(e->name);
799 if( bbuf_len + len + 2 < (int)sizeof(bbuf) )
801 bbuf_s[0] = '\0';
802 rb->strcpy( bbuf+bbuf_len, e->name );
803 bbuf_len += len;
804 bbuf[bbuf_len++] = '/';
805 bbuf[bbuf_len] = '\0';
806 reload = true;
808 break;
810 case ACTION_STD_MENU:
811 *tree = backup;
812 rb->set_current_file( backup.currdir );
813 return false;
818 /***********************************************************************
819 * Font browser
821 * FIXME: This still needs some work ... it currently only works fine
822 * on the simulators, disk spins too much on real targets -> rendered
823 * font buffer needed.
824 ***********************************************************************/
826 * cache font preview handling assumes:
827 * - fvi doesn't decrease by more than 1.
828 * In other words, cache_first-1 must be cached before cache_first-2 is cached.
829 * - there is enough space to store all preview currently displayed.
831 static bool browse_fonts( char *dst, int dst_size )
833 #define LINE_SPACE 2
834 #define PREVIEW_SIZE(x) ((x)->size)
835 #define PREVIEW_NEXT(x) (struct font_preview *)((char*)(x) + PREVIEW_SIZE(x))
837 struct tree_context backup;
838 struct entry *dc, *e;
839 int dirfilter = SHOW_FONT;
841 struct font_preview {
842 unsigned short width;
843 unsigned short height;
844 size_t size; /* to avoid calculating size each time. */
845 fb_data preview[0];
846 } *font_preview = NULL;
848 int top = 0;
850 int fvi = 0; /* first visible item */
851 int lvi = 0; /* last visible item */
852 int si = 0; /* selected item */
853 int li = 0; /* last item */
854 int nvih = 0; /* next visible item height */
855 int i;
856 bool need_redraw = true; /* Do we need to redraw ? */
857 bool reset_font = false;
858 bool ret = false;
860 int cp = 0; /* current position */
861 int sp = 0; /* selected position */
862 int fh, fw; /* font height, width */
864 unsigned char *cache = (unsigned char *) buffer + sizeof(buffer->text);
865 size_t cache_size = sizeof(*buffer) - sizeof(buffer->text);
866 size_t cache_used = 0;
867 int cache_first = 0, cache_last = -1;
868 char *a;
870 rb->snprintf( bbuf_s, MAX_PATH, FONT_DIR "/%s.fnt",
871 rb->global_settings->font_file );
873 tree = rb->tree_get_context();
874 backup = *tree;
875 dc = tree->dircache;
876 a = backup.currdir+rb->strlen(backup.currdir)-1;
877 if( *a != '/' )
879 *++a = '/';
881 rb->strcpy( a+1, dc[tree->selected_item].name );
882 tree->dirfilter = &dirfilter;
883 rb->strcpy( bbuf, FONT_DIR "/" );
884 rb->set_current_file( bbuf );
886 if( buffer->text.initialized )
888 cache_used = buffer->text.cache_used;
889 cache_first = buffer->text.cache_first;
890 cache_last = buffer->text.cache_last;
891 fvi = buffer->text.fvi;
892 si = buffer->text.si;
894 buffer->text.initialized = true;
896 while( 1 )
898 if( !need_redraw )
900 /* we don't need to redraw ... but we need to unselect
901 * the previously selected item */
902 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
903 rb->lcd_fillrect( 10, sp, LCD_WIDTH-10, font_preview->height );
904 rb->lcd_set_drawmode(DRMODE_SOLID);
907 if( need_redraw )
909 need_redraw = false;
911 rb->lcd_set_foreground(COLOR_BLACK);
912 rb->lcd_set_background(COLOR_LIGHTGRAY);
913 rb->lcd_clear_display();
915 rb->font_getstringsize( "Fonts", NULL, &fh, FONT_UI );
916 rb->lcd_putsxy( 2, 2, "Fonts" );
917 top = fh + 4 + LINE_SPACE;
919 font_preview = (struct font_preview *) cache;
920 /* get first font preview to be displayed. */
921 for( i = cache_first; i < cache_last && i < fvi; i++ )
923 font_preview = PREVIEW_NEXT(font_preview);
925 for( ; fvi < lvi && nvih > 0; fvi++ )
927 nvih -= font_preview->height + LINE_SPACE;
928 font_preview = PREVIEW_NEXT(font_preview);
930 nvih = 0;
931 i = fvi;
933 cp = top;
934 while( cp <= LCD_HEIGHT+LINE_SPACE && i < tree->filesindir )
936 e = &dc[i];
937 if( i < cache_first || i > cache_last )
939 size_t siz;
940 reset_font = true;
941 rb->snprintf( bbuf, MAX_PATH, FONT_DIR "/%s", e->name );
942 rb->font_load(NULL, bbuf );
943 rb->font_getstringsize( e->name, &fw, &fh, FONT_UI );
944 if( fw > LCD_WIDTH ) fw = LCD_WIDTH;
945 siz = (sizeof(struct font_preview) + fw*fh*FB_DATA_SZ+3) & ~3;
946 if( i < cache_first )
948 /* insert font preview to the top. */
949 cache_used = 0;
950 for( ; cache_first <= cache_last; cache_first++ )
952 font_preview = (struct font_preview *) (cache + cache_used);
953 size_t size = PREVIEW_SIZE(font_preview);
954 if( cache_used + size >= cache_size - siz )
955 break;
956 cache_used += size;
958 cache_last = cache_first-1;
959 cache_first = i;
960 rb->memmove( cache+siz, cache, cache_used );
961 font_preview = (struct font_preview *) cache;
963 else /* i > cache_last */
965 /* add font preview to the bottom. */
966 font_preview = (struct font_preview *) cache;
967 while( cache_used >= cache_size - siz )
969 cache_used -= PREVIEW_SIZE(font_preview);
970 font_preview = PREVIEW_NEXT(font_preview);
971 cache_first++;
973 cache_last = i;
974 rb->memmove( cache, font_preview, cache_used );
975 font_preview = (struct font_preview *) (cache + cache_used);
977 cache_used += siz;
978 /* create preview cache. */
979 font_preview->width = fw;
980 font_preview->height = fh;
981 font_preview->size = siz;
982 /* clear with background. */
983 for( siz = fw*fh; siz > 0; )
985 font_preview->preview[--siz] = COLOR_LIGHTGRAY;
987 buffer_putsxyofs( font_preview->preview,
988 fw, fh, 0, 0, 0, e->name );
990 else
992 fw = font_preview->width;
993 fh = font_preview->height;
995 if( cp + fh >= LCD_HEIGHT )
997 nvih = fh;
998 break;
1000 rb->lcd_bitmap( font_preview->preview, 10, cp, fw, fh );
1001 cp += fh + LINE_SPACE;
1002 i++;
1003 font_preview = PREVIEW_NEXT(font_preview);
1005 lvi = i-1;
1006 li = tree->filesindir-1;
1007 if( reset_font )
1009 rb->font_load(NULL, bbuf_s );
1010 reset_font = false;
1012 if( lvi-fvi+1 < tree->filesindir )
1014 rb->gui_scrollbar_draw( rb->screens[SCREEN_MAIN], 0, top,
1015 9, LCD_HEIGHT-top,
1016 tree->filesindir, fvi, lvi+1, VERTICAL );
1020 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1021 sp = top;
1022 font_preview = (struct font_preview *) cache;
1023 for( i = cache_first; i < si; i++ )
1025 if( i >= fvi )
1026 sp += font_preview->height + LINE_SPACE;
1027 font_preview = PREVIEW_NEXT(font_preview);
1029 rb->lcd_fillrect( 10, sp, LCD_WIDTH-10, font_preview->height );
1030 rb->lcd_set_drawmode(DRMODE_SOLID);
1032 rb->lcd_update();
1034 switch( rb->button_get(true) )
1036 case ROCKPAINT_UP:
1037 case ROCKPAINT_UP|BUTTON_REPEAT:
1038 if( si > 0 )
1040 si--;
1041 if( si < fvi )
1043 fvi = si;
1044 nvih = 0;
1045 need_redraw = true;
1048 break;
1050 case ROCKPAINT_DOWN:
1051 case ROCKPAINT_DOWN|BUTTON_REPEAT:
1052 if( si < li )
1054 si++;
1055 if( si > lvi )
1057 need_redraw = true;
1060 break;
1062 case ROCKPAINT_RIGHT:
1063 case ROCKPAINT_DRAW:
1064 ret = true;
1065 rb->snprintf( dst, dst_size, FONT_DIR "/%s", dc[si].name );
1066 /* fall through */
1067 case ROCKPAINT_LEFT:
1068 case ROCKPAINT_QUIT:
1069 buffer->text.cache_used = cache_used;
1070 buffer->text.cache_first = cache_first;
1071 buffer->text.cache_last = cache_last;
1072 buffer->text.fvi = fvi;
1073 buffer->text.si = si;
1074 *tree = backup;
1075 rb->set_current_file( backup.currdir );
1076 return ret;
1079 #undef LINE_SPACE
1080 #undef PREVIEW_SIZE
1081 #undef PREVIEW_NEXT
1084 /***********************************************************************
1085 * HSVRGB Color chooser
1086 ***********************************************************************/
1087 static unsigned int color_chooser( unsigned int color )
1089 int red = RGB_UNPACK_RED( color );
1090 int green = RGB_UNPACK_GREEN( color );
1091 int blue = RGB_UNPACK_BLUE( color );
1092 int hue, saturation, value;
1093 int r, g, b; /* temp variables */
1094 int i, top, left;
1095 int button;
1096 int *pval;
1097 static struct incdec_ctx ctxs[] = {
1098 { 3600, { 10, 100}, true }, /* hue */
1099 { 0xff, { 1, 8}, false }, /* the others */
1102 enum BaseColor { Hue = 0, Saturation = 1, Value = 2,
1103 Red = 3, Green = 4, Blue = 5 };
1104 enum BaseColor current = Red;
1105 bool has_changed;
1107 restore_screen();
1109 rgb2hsv( red, green, blue, &hue, &saturation, &value );
1111 while( 1 )
1113 has_changed = false;
1114 color = LCD_RGBPACK( red, green, blue );
1116 #define HEIGHT ( 100 )
1117 #define WIDTH ( 150 )
1119 top = draw_window( HEIGHT, WIDTH, NULL, &left, "Color chooser" );
1120 top -= 15;
1122 for( i=0; i<100; i++ )
1124 hsv2rgb( i*36, saturation, value, &r, &g, &b );
1125 rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
1126 rb->lcd_vline( left+15+i, top+20, top+27 );
1127 hsv2rgb( hue, i*255/100, value, &r, &g, &b );
1128 rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
1129 rb->lcd_vline( left+15+i, top+30, top+37 );
1130 hsv2rgb( hue, saturation, i*255/100, &r, &g, &b );
1131 rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
1132 rb->lcd_vline( left+15+i, top+40, top+47 );
1133 rb->lcd_set_foreground( LCD_RGBPACK( i*255/100, green, blue ) );
1134 rb->lcd_vline( left+15+i, top+50, top+57 );
1135 rb->lcd_set_foreground( LCD_RGBPACK( red, i*255/100, blue ) );
1136 rb->lcd_vline( left+15+i, top+60, top+67 );
1137 rb->lcd_set_foreground( LCD_RGBPACK( red, green, i*255/100 ) );
1138 rb->lcd_vline( left+15+i, top+70, top+77 );
1141 rb->lcd_set_foreground(COLOR_BLACK);
1142 #define POSITION( a, i ) \
1143 rb->lcd_drawpixel( left+14+i, top + 19 + a ); \
1144 rb->lcd_drawpixel( left+16+i, top + 19 + a ); \
1145 rb->lcd_drawpixel( left+14+i, top + 28 + a ); \
1146 rb->lcd_drawpixel( left+16+i, top + 28 + a );
1147 POSITION( 0, hue/36 );
1148 POSITION( 10, saturation*99/255 );
1149 POSITION( 20, value*99/255 );
1150 POSITION( 30, red*99/255 );
1151 POSITION( 40, green*99/255 );
1152 POSITION( 50, blue*99/255 );
1153 #undef POSITION
1154 rb->lcd_set_background(COLOR_LIGHTGRAY);
1155 rb->lcd_setfont( FONT_SYSFIXED );
1156 rb->lcd_putsxyf( left + 117, top + 20, "%d", hue/10 );
1157 rb->lcd_putsxyf( left + 117, top + 30, "%d.%d",
1158 saturation/255, ((saturation*100)/255)%100 );
1159 rb->lcd_putsxyf( left + 117, top + 40, "%d.%d",
1160 value/255, ((value*100)/255)%100 );
1161 rb->lcd_putsxyf( left + 117, top + 50, "%d", red );
1162 rb->lcd_putsxyf( left + 117, top + 60, "%d", green );
1163 rb->lcd_putsxyf( left + 117, top + 70, "%d", blue );
1164 rb->lcd_setfont( FONT_UI );
1166 #define CURSOR( l ) \
1167 rb->lcd_bitmap_transparent_part( rockpaint_hsvrgb, 1, 1, 16, left+l+1, top+20, 6, 58 ); \
1168 rb->lcd_bitmap_transparent_part( rockpaint_hsvrgb, 8, 10*current, 16, left+l, top+19+10*current, 8, 10 );
1169 CURSOR( 5 );
1170 #undef CURSOR
1172 rb->lcd_set_foreground( color );
1173 rb->lcd_fillrect( left+15, top+85, 100, 8 );
1175 rb->lcd_update();
1177 switch( button = rb->button_get(true) )
1179 case ROCKPAINT_UP:
1180 current = ( current + 5 )%6;
1181 break;
1183 case ROCKPAINT_DOWN:
1184 current = ( current + 1 )%6;
1185 break;
1187 case ROCKPAINT_LEFT:
1188 case ROCKPAINT_LEFT|BUTTON_REPEAT:
1189 case ROCKPAINT_RIGHT:
1190 case ROCKPAINT_RIGHT|BUTTON_REPEAT:
1191 has_changed = true;
1192 switch( current )
1194 case Hue:
1195 pval = &hue;
1196 break;
1197 case Saturation:
1198 pval = &saturation;
1199 break;
1200 case Value:
1201 pval = &value;
1202 break;
1203 case Red:
1204 pval = &red;
1205 break;
1206 case Green:
1207 pval = &green;
1208 break;
1209 case Blue:
1210 pval = &blue;
1211 break;
1212 default:
1213 pval = NULL;
1214 break;
1216 if (pval)
1218 incdec_value(pval, &ctxs[(current != Hue? 1: 0)],
1219 (button&ROCKPAINT_RIGHT), (button&BUTTON_REPEAT));
1221 break;
1223 case ROCKPAINT_DRAW:
1224 return color;
1226 if( has_changed )
1228 switch( current )
1230 case Hue:
1231 case Saturation:
1232 case Value:
1233 hsv2rgb( hue, saturation, value, &red, &green, &blue );
1234 break;
1236 case Red:
1237 case Green:
1238 case Blue:
1239 rgb2hsv( red, green, blue, &hue, &saturation, &value );
1240 break;
1243 #undef HEIGHT
1244 #undef WIDTH
1248 /***********************************************************************
1249 * Misc routines
1250 ***********************************************************************/
1251 static void init_buffer(void)
1253 int i;
1254 fb_data color = rp_colors[ bgdrawcolor ];
1255 for( i = 0; i < ROWS*COLS; i++ )
1257 save_buffer[i] = color;
1261 static void draw_pixel(int x,int y)
1263 if( !preview )
1265 if( x < 0 || x >= COLS || y < 0 || y >= ROWS ) return;
1266 if( isbg )
1268 save_buffer[ x+y*COLS ] = rp_colors[bgdrawcolor];
1270 else
1272 save_buffer[ x+y*COLS ] = rp_colors[drawcolor];
1275 rb->lcd_drawpixel(x,y);
1278 static void color_picker( int x, int y )
1280 if( preview )
1282 rb->lcd_set_foreground( save_buffer[ x+y*COLS ] );
1283 #define PSIZE 12
1284 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1285 if( x >= COLS - PSIZE ) x -= PSIZE + 2;
1286 if( y >= ROWS - PSIZE ) y -= PSIZE + 2;
1287 rb->lcd_drawrect( x + 2, y + 2, PSIZE - 2, PSIZE - 2 );
1288 rb->lcd_set_drawmode(DRMODE_SOLID);
1289 rb->lcd_fillrect( x + 3, y + 3, PSIZE - 4, PSIZE - 4 );
1290 #undef PSIZE
1291 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1293 else
1295 rp_colors[ drawcolor ] = save_buffer[ x+y*COLS ];
1299 static void draw_select_rectangle( int x1, int y1, int x2, int y2 )
1300 /* This is a preview mode only function */
1302 int i,a;
1303 if( x1 > x2 )
1305 i = x1;
1306 x1 = x2;
1307 x2 = i;
1309 if( y1 > y2 )
1311 i = y1;
1312 y1 = y2;
1313 y2 = i;
1315 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1316 i = 0;
1317 for( a = x1; a < x2; a++, i++ )
1318 if( i%2 )
1319 rb->lcd_drawpixel( a, y1 );
1320 for( a = y1; a < y2; a++, i++ )
1321 if( i%2 )
1322 rb->lcd_drawpixel( x2, a );
1323 if( y2 != y1 )
1324 for( a = x2; a > x1; a--, i++ )
1325 if( i%2 )
1326 rb->lcd_drawpixel( a, y2 );
1327 if( x2 != x1 )
1328 for( a = y2; a > y1; a--, i++ )
1329 if( i%2 )
1330 rb->lcd_drawpixel( x1, a );
1331 rb->lcd_set_drawmode(DRMODE_SOLID);
1334 static void copy_to_clipboard( void )
1336 /* This needs to be optimised ... but i'm lazy ATM */
1337 rb->memcpy( buffer->clipboard, save_buffer, COLS*ROWS*sizeof( fb_data ) );
1340 /* no preview mode handling atm ... do we need it ? (one if) */
1341 static void draw_invert( int x1, int y1, int x2, int y2 )
1343 int i;
1344 if( x1 > x2 )
1346 i = x1;
1347 x1 = x2;
1348 x2 = i;
1350 if( y1 > y2 )
1352 i = y1;
1353 y1 = y2;
1354 y2 = i;
1357 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1358 rb->lcd_fillrect( x1, y1, x2-x1+1, y2-y1+1 );
1359 rb->lcd_set_drawmode(DRMODE_SOLID);
1361 for( ; y1<=y2; y1++ )
1363 for( i = x1; i<=x2; i++ )
1365 save_buffer[ y1*COLS + i ] = ~save_buffer[ y1*COLS + i ];
1368 /*if( update )*/ rb->lcd_update();
1371 static void draw_hflip( int x1, int y1, int x2, int y2 )
1373 int i;
1374 if( x1 > x2 )
1376 i = x1;
1377 x1 = x2;
1378 x2 = i;
1380 if( y1 > y2 )
1382 i = y1;
1383 y1 = y2;
1384 y2 = i;
1387 copy_to_clipboard();
1389 for( i = 0; i <= y2 - y1; i++ )
1391 rb->memcpy( save_buffer+(y1+i)*COLS+x1,
1392 buffer->clipboard+(y2-i)*COLS+x1,
1393 (x2-x1+1)*sizeof( fb_data ) );
1395 restore_screen();
1396 rb->lcd_update();
1399 static void draw_vflip( int x1, int y1, int x2, int y2 )
1401 int i;
1402 if( x1 > x2 )
1404 i = x1;
1405 x1 = x2;
1406 x2 = i;
1408 if( y1 > y2 )
1410 i = y1;
1411 y1 = y2;
1412 y2 = i;
1415 copy_to_clipboard();
1417 for( ; y1 <= y2; y1++ )
1419 for( i = 0; i <= x2 - x1; i++ )
1421 save_buffer[y1*COLS+x1+i] = buffer->clipboard[y1*COLS+x2-i];
1424 restore_screen();
1425 rb->lcd_update();
1428 /* direction: -1 = left, 1 = right */
1429 static void draw_rot_90_deg( int x1, int y1, int x2, int y2, int direction )
1431 int i, j;
1432 if( x1 > x2 )
1434 i = x1;
1435 x1 = x2;
1436 x2 = i;
1438 if( y1 > y2 )
1440 i = y1;
1441 y1 = y2;
1442 y2 = i;
1445 copy_to_clipboard();
1447 fb_data color = rp_colors[ bgdrawcolor ];
1448 const int width = x2 - x1, height = y2 - y1;
1449 const int sub_half = width/2-height/2, add_half = (width+height)/2;
1450 if( width > height )
1452 for( i = 0; i <= height; i++ )
1454 for( j = 0; j < sub_half; j++ )
1455 save_buffer[(y1+i)*COLS+x1+j] = color;
1456 for( j = add_half+1; j <= width; j++ )
1457 save_buffer[(y1+i)*COLS+x1+j] = color;
1460 else if( width < height )
1462 for( j = 0; j <= width; j++ )
1464 for( i = 0; i < -sub_half; i++ )
1465 save_buffer[(y1+i)*COLS+x1+j] = color;
1466 for( i = add_half+1; i <= height; i++ )
1467 save_buffer[(y1+i)*COLS+x1+j] = color;
1470 int x3 = x1 + sub_half, y3 = y1 - sub_half;
1471 int is = x3<0?-x3:0, ie = COLS-x3-1, js = y3<0?-y3:0, je = ROWS-y3-1;
1472 if( ie > height ) ie = height;
1473 if( je > width ) je = width;
1474 for( i = is; i <= ie; i++ )
1476 for( j = js; j <= je; j++ )
1478 int x, y;
1479 if(direction > 0)
1481 x = x1+j;
1482 y = y1+height-i;
1484 else
1486 x = x1+width-j;
1487 y = y1+i;
1489 save_buffer[(y3+j)*COLS+x3+i] = buffer->clipboard[y*COLS+x];
1492 restore_screen();
1493 rb->lcd_update();
1496 static void draw_paste_rectangle( int src_x1, int src_y1, int src_x2,
1497 int src_y2, int x1, int y1, int cut )
1499 int i, width, height;
1500 if( cut )
1502 i = drawcolor;
1503 drawcolor = bgdrawcolor;
1504 draw_rect_full( src_x1, src_y1, src_x2, src_y2 );
1505 drawcolor = i;
1507 if( src_x1 > src_x2 )
1509 i = src_x1;
1510 src_x1 = src_x2;
1511 src_x2 = i;
1513 if( src_y1 > src_y2 )
1515 i = src_y1;
1516 src_y1 = src_y2;
1517 src_y2 = i;
1519 width = src_x2 - src_x1 + 1;
1520 height = src_y2 - src_y1 + 1;
1521 /* clipping */
1522 if( x1 + width > COLS )
1523 width = COLS - x1;
1524 if( y1 + height > ROWS )
1525 height = ROWS - y1;
1527 rb->lcd_bitmap_part( buffer->clipboard, src_x1, src_y1, COLS,
1528 x1, y1, width, height );
1529 if( !preview )
1531 for( i = 0; i < height; i++ )
1533 rb->memcpy( save_buffer+(y1+i)*COLS+x1,
1534 buffer->clipboard+(src_y1+i)*COLS+src_x1,
1535 width*sizeof( fb_data ) );
1540 static void show_grid( bool update )
1542 int i;
1543 if( gridsize > 0 )
1545 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1546 for( i = gridsize; i < img_width; i+= gridsize )
1548 rb->lcd_vline( i, 0, img_height-1 );
1550 for( i = gridsize; i < img_height; i+= gridsize )
1552 rb->lcd_hline( 0, img_width-1, i );
1554 rb->lcd_set_drawmode(DRMODE_SOLID);
1555 if( update ) rb->lcd_update();
1559 static void draw_text( int x, int y )
1561 int selected = 0;
1562 buffer->text.text[0] = '\0';
1563 buffer->text.font[0] = '\0';
1564 while( 1 )
1566 switch( rb->do_menu( &text_menu, &selected, NULL, NULL ) )
1568 case TEXT_MENU_TEXT:
1569 rb->lcd_set_foreground(COLOR_BLACK);
1570 rb->kbd_input( buffer->text.text, MAX_TEXT );
1571 break;
1573 case TEXT_MENU_FONT:
1574 if( browse_fonts( buffer->text.font, MAX_PATH ) )
1576 rb->font_load(NULL, buffer->text.font );
1578 break;
1580 case TEXT_MENU_PREVIEW:
1581 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1582 while( 1 )
1584 int button;
1585 restore_screen();
1586 rb->lcd_putsxy( x, y, buffer->text.text );
1587 rb->lcd_update();
1588 switch( button = rb->button_get( true ) )
1590 case ROCKPAINT_LEFT:
1591 case ROCKPAINT_LEFT | BUTTON_REPEAT:
1592 case ROCKPAINT_RIGHT:
1593 case ROCKPAINT_RIGHT | BUTTON_REPEAT:
1594 incdec_value(&x, &incdec_x,
1595 (button&ROCKPAINT_RIGHT), (button&BUTTON_REPEAT));
1596 break;
1598 case ROCKPAINT_UP:
1599 case ROCKPAINT_UP | BUTTON_REPEAT:
1600 case ROCKPAINT_DOWN:
1601 case ROCKPAINT_DOWN | BUTTON_REPEAT:
1602 incdec_value(&y, &incdec_y,
1603 (button&ROCKPAINT_DOWN), (button&BUTTON_REPEAT));
1604 break;
1606 case ROCKPAINT_DRAW:
1607 break;
1608 default:
1609 if(rb->default_event_handler(button)
1610 == SYS_USB_CONNECTED)
1611 button = ROCKPAINT_DRAW;
1612 break;
1614 if( button == ROCKPAINT_DRAW ) break;
1616 break;
1618 case TEXT_MENU_APPLY:
1619 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1620 buffer_putsxyofs( save_buffer, COLS, ROWS, x, y, 0,
1621 buffer->text.text );
1622 case TEXT_MENU_CANCEL:
1623 default:
1624 restore_screen();
1625 if( buffer->text.font[0] )
1627 rb->snprintf( buffer->text.font, MAX_PATH,
1628 FONT_DIR "/%s.fnt",
1629 rb->global_settings->font_file );
1630 rb->font_load(NULL, buffer->text.font );
1632 return;
1637 static void draw_brush( int x, int y )
1639 int i,j;
1640 for( i=-bsize/2+(bsize+1)%2; i<=bsize/2; i++ )
1642 for( j=-bsize/2+(bsize+1)%2; j<=bsize/2; j++ )
1644 draw_pixel( x+i, y+j );
1649 /* This is an implementation of Bresenham's line algorithm.
1650 * See http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm.
1652 static void draw_line( int x1, int y1, int x2, int y2 )
1654 int x = x1;
1655 int y = y1;
1656 int deltax = x2 - x1;
1657 int deltay = y2 - y1;
1658 int i;
1660 int xerr = abs(deltax);
1661 int yerr = abs(deltay);
1662 int xstep = deltax > 0 ? 1 : -1;
1663 int ystep = deltay > 0 ? 1 : -1;
1664 int err;
1666 if (yerr > xerr)
1668 /* more vertical */
1669 err = yerr;
1670 xerr <<= 1;
1671 yerr <<= 1;
1673 /* to leave off the last pixel of the line, leave off the "+ 1" */
1674 for (i = err + 1; i; --i)
1676 draw_pixel(x, y);
1677 y += ystep;
1678 err -= xerr;
1679 if (err < 0) {
1680 x += xstep;
1681 err += yerr;
1685 else
1687 /* more horizontal */
1688 err = xerr;
1689 xerr <<= 1;
1690 yerr <<= 1;
1692 for (i = err + 1; i; --i)
1694 draw_pixel(x, y);
1695 x += xstep;
1696 err -= yerr;
1697 if (err < 0) {
1698 y += ystep;
1699 err += xerr;
1705 static void draw_curve( int x1, int y1, int x2, int y2,
1706 int xa, int ya, int xb, int yb )
1708 int i = 0;
1709 short xl1, yl1;
1710 short xl2, yl2;
1711 short xl3, yl3;
1712 short xl4, yl4;
1713 short xr1, yr1;
1714 short xr2, yr2;
1715 short xr3, yr3;
1716 short xr4, yr4;
1717 short depth;
1718 short xh, yh;
1720 if( x1 == x2 && y1 == y2 )
1722 draw_pixel( x1, y1 );
1723 return;
1726 // if( preview )
1728 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1729 if( xa == -1 || ya == -1 )
1731 rb->lcd_drawline( x1, y1, xb, yb );
1732 rb->lcd_drawline( x2, y2, xb, yb );
1734 else
1736 rb->lcd_drawline( x1, y1, xa, ya );
1737 rb->lcd_drawline( x2, y2, xb, yb );
1739 rb->lcd_set_drawmode(DRMODE_SOLID);
1742 if( xa == -1 || ya == -1 )
1743 /* We only have 3 of the points
1744 * This will currently only be used in preview mode */
1746 #define PUSH( a1, b1, a2, b2, a3, b3, d ) \
1747 buffer->bezier[i].x1 = a1; \
1748 buffer->bezier[i].y1 = b1; \
1749 buffer->bezier[i].x2 = a2; \
1750 buffer->bezier[i].y2 = b2; \
1751 buffer->bezier[i].x3 = a3; \
1752 buffer->bezier[i].y3 = b3; \
1753 buffer->bezier[i].depth = d; \
1754 i++;
1755 #define POP( a1, b1, a2, b2, a3, b3, d ) \
1756 i--; \
1757 a1 = buffer->bezier[i].x1; \
1758 b1 = buffer->bezier[i].y1; \
1759 a2 = buffer->bezier[i].x2; \
1760 b2 = buffer->bezier[i].y2; \
1761 a3 = buffer->bezier[i].x3; \
1762 b3 = buffer->bezier[i].y3; \
1763 d = buffer->bezier[i].depth;
1765 PUSH( x1<<4, y1<<4, xb<<4, yb<<4, x2<<4, y2<<4, 0 );
1766 while( i )
1768 /* de Casteljau's algorithm (see wikipedia) */
1769 POP( xl1, yl1, xb, yb, xr3, yr3, depth );
1770 if( depth < 10 ) /* check that the stack's 'i' doesn't overflow */
1772 xl2 = ( xl1 + xb )>>1;
1773 yl2 = ( yl1 + yb )>>1;
1774 xr2 = ( xb + xr3 )>>1;
1775 yr2 = ( yb + yr3 )>>1;
1776 xr1 = ( xl2 + xr2 )>>1;
1777 yr1 = ( yl2 + yr2 )>>1;
1778 xl3 = xr1;
1779 yl3 = yr1;
1780 PUSH( xl1, yl1, xl2, yl2, xl3, yl3, depth+1 );
1781 PUSH( xr1, yr1, xr2, yr2, xr3, yr3, depth+1 );
1783 else
1785 draw_line( ((xl1>>3)+1)>>1, ((yl1>>3)+1)>>1,
1786 ((xr3>>3)+1)>>1, ((yr3>>3)+1)>>1 );
1789 #undef PUSH
1790 #undef POP
1792 else /* We have the 4 points */
1794 #define PUSH( a1, b1, a2, b2, a3, b3, a4, b4, d ) \
1795 buffer->bezier[i].x1 = a1; \
1796 buffer->bezier[i].y1 = b1; \
1797 buffer->bezier[i].x2 = a2; \
1798 buffer->bezier[i].y2 = b2; \
1799 buffer->bezier[i].x3 = a3; \
1800 buffer->bezier[i].y3 = b3; \
1801 buffer->bezier[i].x4 = a4; \
1802 buffer->bezier[i].y4 = b4; \
1803 buffer->bezier[i].depth = d; \
1804 i++;
1805 #define POP( a1, b1, a2, b2, a3, b3, a4, b4, d ) \
1806 i--; \
1807 a1 = buffer->bezier[i].x1; \
1808 b1 = buffer->bezier[i].y1; \
1809 a2 = buffer->bezier[i].x2; \
1810 b2 = buffer->bezier[i].y2; \
1811 a3 = buffer->bezier[i].x3; \
1812 b3 = buffer->bezier[i].y3; \
1813 a4 = buffer->bezier[i].x4; \
1814 b4 = buffer->bezier[i].y4; \
1815 d = buffer->bezier[i].depth;
1817 PUSH( x1<<4, y1<<4, xa<<4, ya<<4, xb<<4, yb<<4, x2<<4, y2<<4, 0 );
1818 while( i )
1820 /* de Casteljau's algorithm (see wikipedia) */
1821 POP( xl1, yl1, xa, ya, xb, yb, xr4, yr4, depth );
1822 if( depth < 10 ) /* check that the stack's 'i' doesn't overflow */
1824 xl2 = ( xl1 + xa )>>1;
1825 yl2 = ( yl1 + ya )>>1;
1826 xh = ( xa + xb )>>1;
1827 yh = ( ya + yb )>>1;
1828 xr3 = ( xb + xr4 )>>1;
1829 yr3 = ( yb + yr4 )>>1;
1830 xl3 = ( xl2 + xh )>>1;
1831 yl3 = ( yl2 + yh )>>1;
1832 xr2 = ( xr3 + xh )>>1;
1833 yr2 = ( yr3 + yh )>>1;
1834 xl4 = ( xl3 + xr2 )>>1;
1835 yl4 = ( yl3 + yr2 )>>1;
1836 xr1 = xl4;
1837 yr1 = yl4;
1838 PUSH( xl1, yl1, xl2, yl2, xl3, yl3, xl4, yl4, depth+1 );
1839 PUSH( xr1, yr1, xr2, yr2, xr3, yr3, xr4, yr4, depth+1 );
1841 else
1843 draw_line( ((xl1>>3)+1)>>1, ((yl1>>3)+1)>>1,
1844 ((xr4>>3)+1)>>1, ((yr4>>3)+1)>>1 );
1847 #undef PUSH
1848 #undef POP
1852 static void draw_rect( int x1, int y1, int x2, int y2 )
1854 draw_line( x1, y1, x1, y2 );
1855 draw_line( x1, y1, x2, y1 );
1856 draw_line( x1, y2, x2, y2 );
1857 draw_line( x2, y1, x2, y2 );
1860 static void togglebg( void )
1862 if( isbg )
1864 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1866 else
1868 rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
1870 isbg = !isbg;
1873 static void draw_rect_full( int x1, int y1, int x2, int y2 )
1875 /* GRUIK */
1876 int x;
1877 togglebg();
1878 if( x1 > x2 )
1880 x = x1;
1881 x1 = x2;
1882 x2 = x;
1884 x = x1;
1885 do {
1886 draw_line( x, y1, x, y2 );
1887 } while( ++x <= x2 );
1888 togglebg();
1889 draw_rect( x1, y1, x2, y2 );
1892 static void draw_oval( int x1, int y1, int x2, int y2, bool full )
1894 /* TODO: simplify :) */
1895 int cx = (x1+x2)>>1;
1896 int cy = (y1+y2)>>1;
1898 int rx = (x1-x2)>>1;
1899 int ry = (y1-y2)>>1;
1900 if( rx < 0 ) rx *= -1;
1901 if( ry < 0 ) ry *= -1;
1903 if( rx == 0 || ry == 0 )
1905 draw_line( x1, y1, x2, y2 );
1906 return;
1909 int x,y;
1910 int dst, old_dst;
1912 for( x = 0; x < rx; x++ )
1914 y = 0;
1915 dst = -0xfff;
1916 do {
1917 old_dst = dst;
1918 dst = ry * ry * x * x + rx * rx * y * y - rx * rx * ry * ry;
1919 y++;
1920 } while( dst < 0 );
1921 if( -old_dst < dst ) y--;
1922 if( full )
1924 draw_line( cx+x, cy, cx+x, cy+y );
1925 draw_line( cx+x, cy, cx+x, cy-y );
1926 draw_line( cx-x, cy, cx-x, cy+y );
1927 draw_line( cx-x, cy, cx-x, cy-y );
1929 else
1931 draw_pixel( cx+x, cy+y );
1932 draw_pixel( cx+x, cy-y );
1933 draw_pixel( cx-x, cy+y );
1934 draw_pixel( cx-x, cy-y );
1937 for( y = 0; y < ry; y++ )
1939 x = 0;
1940 dst = -0xfff;
1941 do {
1942 old_dst = dst;
1943 dst = ry * ry * x * x + rx * rx * y * y - rx * rx * ry * ry;
1944 x++;
1945 } while( dst < 0 );
1946 if( -old_dst < dst ) x--;
1947 if( full )
1949 draw_line( cx+x, cy, cx+x, cy+y );
1950 draw_line( cx+x, cy, cx+x, cy-y );
1951 draw_line( cx-x, cy, cx-x, cy+y );
1952 draw_line( cx-x, cy, cx-x, cy-y );
1954 else
1956 draw_pixel( cx+x, cy+y );
1957 draw_pixel( cx+x, cy-y );
1958 draw_pixel( cx-x, cy+y );
1959 draw_pixel( cx-x, cy-y );
1964 static void draw_oval_empty( int x1, int y1, int x2, int y2 )
1966 draw_oval( x1, y1, x2, y2, false );
1969 static void draw_oval_full( int x1, int y1, int x2, int y2 )
1971 togglebg();
1972 draw_oval( x1, y1, x2, y2, true );
1973 togglebg();
1974 draw_oval( x1, y1, x2, y2, false );
1977 static void draw_fill( int x0, int y0 )
1979 #define PUSH( a, b ) \
1980 draw_pixel( (int)a, (int)b ); \
1981 buffer->coord[i].x = a; \
1982 buffer->coord[i].y = b; \
1983 i++;
1984 #define POP( a, b ) \
1985 i--; \
1986 a = buffer->coord[i].x; \
1987 b = buffer->coord[i].y;
1989 unsigned int i=0;
1990 short x = x0;
1991 short y = y0;
1992 unsigned int prev_color = save_buffer[ x0+y0*COLS ];
1994 if( preview )
1995 return;
1996 if( prev_color == rp_colors[ drawcolor ] ) return;
1998 PUSH( x, y );
2000 while( i != 0 )
2002 POP( x, y );
2003 if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
2005 PUSH( x-1, y );
2007 if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
2009 PUSH( x+1, y );
2011 if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
2013 PUSH( x, y-1 );
2015 if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
2017 PUSH( x, y+1 );
2020 #undef PUSH
2021 #undef POP
2025 /* For preview purposes only */
2026 /* use same algorithm as draw_line() to draw line. */
2027 static void line_gradient( int x1, int y1, int x2, int y2 )
2029 int h1, s1, v1, h2, s2, v2, r, g, b;
2030 int xerr = x2 - x1, yerr = y2 - y1, xstep, ystep;
2031 int i, delta, err;
2032 fb_data color1, color2;
2034 if( xerr == 0 && yerr == 0 )
2036 draw_pixel( x1, y1 );
2037 return;
2040 xstep = xerr > 0 ? 1 : -1;
2041 ystep = yerr > 0 ? 1 : -1;
2042 xerr = abs(xerr) << 1;
2043 yerr = abs(yerr) << 1;
2045 color1 = rp_colors[ bgdrawcolor ];
2046 color2 = rp_colors[ drawcolor ];
2048 r = RGB_UNPACK_RED( color1 );
2049 g = RGB_UNPACK_GREEN( color1 );
2050 b = RGB_UNPACK_BLUE( color1 );
2051 rgb2hsv( r, g, b, &h1, &s1, &v1 );
2053 r = RGB_UNPACK_RED( color2 );
2054 g = RGB_UNPACK_GREEN( color2 );
2055 b = RGB_UNPACK_BLUE( color2 );
2056 rgb2hsv( r, g, b, &h2, &s2, &v2 );
2058 if( xerr > yerr )
2060 err = xerr>>1;
2061 delta = err+1;
2062 /* to leave off the last pixel of the line, leave off the "+ 1" */
2063 for (i = delta; i; --i)
2065 hsv2rgb( h2+((h1-h2)*i)/delta,
2066 s2+((s1-s2)*i)/delta,
2067 v2+((v1-v2)*i)/delta,
2068 &r, &g, &b );
2069 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2070 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2071 draw_pixel(x1, y1);
2072 x1 += xstep;
2073 err -= yerr;
2074 if (err < 0) {
2075 y1 += ystep;
2076 err += xerr;
2080 else /* yerr >= xerr */
2082 err = yerr>>1;
2083 delta = err+1;
2084 for (i = delta; i; --i)
2086 hsv2rgb( h2+((h1-h2)*i)/delta,
2087 s2+((s1-s2)*i)/delta,
2088 v2+((v1-v2)*i)/delta,
2089 &r, &g, &b );
2090 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2091 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2092 draw_pixel(x1, y1);
2093 y1 += ystep;
2094 err -= xerr;
2095 if (err < 0) {
2096 x1 += xstep;
2097 err += yerr;
2101 rp_colors[ drawcolor ] = color2;
2104 /* macros used by linear_gradient() and radial_gradient(). */
2105 #define PUSH( _x, _y ) \
2106 save_buffer[(_x)+(_y)*COLS] = mark_color; \
2107 buffer->coord[i].x = (short)(_x); \
2108 buffer->coord[i].y = (short)(_y); \
2109 i++;
2110 #define POP( _x, _y ) \
2111 i--; \
2112 _x = (int)buffer->coord[i].x; \
2113 _y = (int)buffer->coord[i].y;
2114 #define PUSH2( _x, _y ) \
2115 j--; \
2116 buffer->coord[j].x = (short)(_x); \
2117 buffer->coord[j].y = (short)(_y);
2118 #define POP2( _x, _y ) \
2119 _x = (int)buffer->coord[j].x; \
2120 _y = (int)buffer->coord[j].y; \
2121 j++;
2123 static void linear_gradient( int x1, int y1, int x2, int y2 )
2125 int r1 = RGB_UNPACK_RED( rp_colors[ bgdrawcolor ] );
2126 int g1 = RGB_UNPACK_GREEN( rp_colors[ bgdrawcolor ] );
2127 int b1 = RGB_UNPACK_BLUE( rp_colors[ bgdrawcolor ] );
2128 int r2 = RGB_UNPACK_RED( rp_colors[ drawcolor ] );
2129 int g2 = RGB_UNPACK_GREEN( rp_colors[ drawcolor ] );
2130 int b2 = RGB_UNPACK_BLUE( rp_colors[ drawcolor ] );
2131 fb_data color = rp_colors[ drawcolor ];
2133 int h1, s1, v1, h2, s2, v2, r, g, b;
2135 /* radius^2 */
2136 int radius2 = ( x1 - x2 ) * ( x1 - x2 ) + ( y1 - y2 ) * ( y1 - y2 );
2137 int dist2, i=0, j=COLS*ROWS;
2139 /* We only propagate the gradient to neighboring pixels with the same
2140 * color as ( x1, y1 ) */
2141 fb_data prev_color = save_buffer[ x1+y1*COLS ];
2142 /* to mark pixel that the pixel is already in LIFO. */
2143 fb_data mark_color = ~prev_color;
2145 int x = x1;
2146 int y = y1;
2148 if( radius2 == 0 ) return;
2149 if( preview )
2151 line_gradient( x1, y1, x2, y2 );
2152 return;
2154 if( rp_colors[ drawcolor ] == rp_colors[ bgdrawcolor ] )
2156 draw_fill( x1, y1 );
2157 return;
2160 rgb2hsv( r1, g1, b1, &h1, &s1, &v1 );
2161 rgb2hsv( r2, g2, b2, &h2, &s2, &v2 );
2163 PUSH( x, y );
2165 while( i > 0 )
2167 POP( x, y );
2169 dist2 = ( x2 - x1 ) * ( x - x1 ) + ( y2 - y1 ) * ( y - y1 );
2170 if( dist2 <= 0 )
2172 rp_colors[ drawcolor ] = rp_colors[ bgdrawcolor ];
2174 else if( dist2 < radius2 )
2176 hsv2rgb( h1+((h2-h1)*dist2)/radius2,
2177 s1+((s2-s1)*dist2)/radius2,
2178 v1+((v2-v1)*dist2)/radius2,
2179 &r, &g, &b );
2180 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2182 else
2184 rp_colors[ drawcolor ] = color;
2186 if( rp_colors[ drawcolor ] == prev_color )
2188 /* "mark" that pixel was checked. correct color later. */
2189 PUSH2( x, y );
2190 rp_colors[ drawcolor ] = mark_color;
2192 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2193 draw_pixel( x, y );
2195 if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
2197 PUSH( x-1, y );
2199 if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
2201 PUSH( x+1, y );
2203 if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
2205 PUSH( x, y-1 );
2207 if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
2209 PUSH( x, y+1 );
2212 while (j < COLS*ROWS)
2214 /* correct color. */
2215 POP2( x, y );
2216 rp_colors[ drawcolor ] = prev_color;
2217 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2218 draw_pixel( x, y );
2220 rp_colors[ drawcolor ] = color;
2223 static void radial_gradient( int x1, int y1, int x2, int y2 )
2225 int r1 = RGB_UNPACK_RED( rp_colors[ bgdrawcolor ] );
2226 int g1 = RGB_UNPACK_GREEN( rp_colors[ bgdrawcolor ] );
2227 int b1 = RGB_UNPACK_BLUE( rp_colors[ bgdrawcolor ] );
2228 int r2 = RGB_UNPACK_RED( rp_colors[ drawcolor ] );
2229 int g2 = RGB_UNPACK_GREEN( rp_colors[ drawcolor ] );
2230 int b2 = RGB_UNPACK_BLUE( rp_colors[ drawcolor ] );
2231 fb_data color = rp_colors[ drawcolor ];
2233 int h1, s1, v1, h2, s2, v2, r, g, b;
2235 /* radius^2 */
2236 int radius2 = ( x1 - x2 ) * ( x1 - x2 ) + ( y1 - y2 ) * ( y1 - y2 );
2237 int dist2, i=0, j=COLS*ROWS;
2239 /* We only propagate the gradient to neighboring pixels with the same
2240 * color as ( x1, y1 ) */
2241 fb_data prev_color = save_buffer[ x1+y1*COLS ];
2242 /* to mark pixel that the pixel is already in LIFO. */
2243 fb_data mark_color = ~prev_color;
2245 int x = x1;
2246 int y = y1;
2248 if( radius2 == 0 ) return;
2249 if( preview )
2251 line_gradient( x1, y1, x2, y2 );
2252 return;
2254 if( rp_colors[ drawcolor ] == rp_colors[ bgdrawcolor ] )
2256 draw_fill( x1, y1 );
2257 return;
2260 rgb2hsv( r1, g1, b1, &h1, &s1, &v1 );
2261 rgb2hsv( r2, g2, b2, &h2, &s2, &v2 );
2263 PUSH( x, y );
2265 while( i > 0 )
2267 POP( x, y );
2269 dist2 = ( x - x1 ) * ( x - x1 ) + ( y - y1 ) * ( y - y1 );
2270 if( dist2 < radius2 )
2272 hsv2rgb( h1+((h2-h1)*dist2)/radius2,
2273 s1+((s2-s1)*dist2)/radius2,
2274 v1+((v2-v1)*dist2)/radius2,
2275 &r, &g, &b );
2276 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2278 else
2280 rp_colors[ drawcolor ] = color;
2282 if( rp_colors[ drawcolor ] == prev_color )
2284 /* "mark" that pixel was checked. correct color later. */
2285 PUSH2( x, y );
2286 rp_colors[ drawcolor ] = mark_color;
2288 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2289 draw_pixel( x, y );
2291 if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
2293 PUSH( x-1, y );
2295 if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
2297 PUSH( x+1, y );
2299 if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
2301 PUSH( x, y-1 );
2303 if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
2305 PUSH( x, y+1 );
2308 while (j < COLS*ROWS)
2310 /* correct color. */
2311 POP2( x, y );
2312 rp_colors[ drawcolor ] = prev_color;
2313 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2314 draw_pixel( x, y );
2316 rp_colors[ drawcolor ] = color;
2319 #undef PUSH
2320 #undef POP
2321 #undef PUSH2
2322 #undef POP2
2324 static void draw_toolbars(bool update)
2326 int i;
2327 #define TOP (LCD_HEIGHT-TB_HEIGHT)
2328 rb->lcd_set_background( COLOR_LIGHTGRAY );
2329 rb->lcd_set_foreground( COLOR_LIGHTGRAY );
2330 rb->lcd_fillrect( 0, TOP, LCD_WIDTH, TB_HEIGHT );
2331 rb->lcd_set_foreground( COLOR_BLACK );
2332 rb->lcd_drawrect( 0, TOP, LCD_WIDTH, TB_HEIGHT );
2334 rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
2335 rb->lcd_fillrect( TB_SC_BG_LEFT, TOP+TB_SC_BG_TOP,
2336 TB_SC_SIZE, TB_SC_SIZE );
2337 rb->lcd_set_foreground(ROCKPAINT_PALETTE);
2338 rb->lcd_drawrect( TB_SC_BG_LEFT, TOP+TB_SC_BG_TOP,
2339 TB_SC_SIZE, TB_SC_SIZE );
2340 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2341 rb->lcd_fillrect( TB_SC_FG_LEFT, TOP+TB_SC_FG_TOP,
2342 TB_SC_SIZE, TB_SC_SIZE );
2343 rb->lcd_set_foreground(ROCKPAINT_PALETTE);
2344 rb->lcd_drawrect( TB_SC_FG_LEFT, TOP+TB_SC_FG_TOP,
2345 TB_SC_SIZE, TB_SC_SIZE );
2347 for( i=0; i<18; i++ )
2349 rb->lcd_set_foreground( rp_colors[i] );
2350 rb->lcd_fillrect(
2351 TB_PL_LEFT+(i%9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING ),
2352 TOP+TB_PL_TOP+(i/9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING),
2353 TB_PL_COLOR_SIZE, TB_PL_COLOR_SIZE );
2354 rb->lcd_set_foreground( ROCKPAINT_PALETTE );
2355 rb->lcd_drawrect(
2356 TB_PL_LEFT+(i%9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING ),
2357 TOP+TB_PL_TOP+(i/9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING),
2358 TB_PL_COLOR_SIZE, TB_PL_COLOR_SIZE );
2361 #define SEPARATOR( x, y ) \
2362 rb->lcd_set_foreground( COLOR_WHITE ); \
2363 rb->lcd_vline( x, TOP+y, TOP+y+TB_PL_HEIGHT-1 ); \
2364 rb->lcd_set_foreground( COLOR_DARKGRAY ); \
2365 rb->lcd_vline( x+1, TOP+y, TOP+y+TB_PL_HEIGHT-1 );
2366 SEPARATOR( TB_PL_LEFT + TB_PL_WIDTH - 1 + TB_SP_MARGIN, TB_PL_TOP );
2368 rb->lcd_bitmap_transparent( rockpaint, TB_TL_LEFT, TOP+TB_TL_TOP,
2369 TB_TL_WIDTH, TB_TL_HEIGHT );
2370 rb->lcd_set_foreground(ROCKPAINT_PALETTE);
2371 rb->lcd_drawrect( TB_TL_LEFT+(TB_TL_SIZE+TB_TL_SPACING)*(tool/2),
2372 TOP+TB_TL_TOP+(TB_TL_SIZE+TB_TL_SPACING)*(tool%2),
2373 TB_TL_SIZE, TB_TL_SIZE );
2375 SEPARATOR( TB_TL_LEFT + TB_TL_WIDTH - 1 + TB_SP_MARGIN, TB_TL_TOP );
2377 rb->lcd_setfont( FONT_SYSFIXED );
2378 rb->lcd_putsxy( TB_MENU_LEFT, TOP+TB_MENU_TOP, "Menu" );
2379 rb->lcd_setfont( FONT_UI );
2380 #undef TOP
2382 if( update ) rb->lcd_update();
2385 static void toolbar( void )
2387 int button, i, j;
2388 restore_screen();
2389 draw_toolbars( false );
2390 y = LCD_HEIGHT-TB_HEIGHT/2;
2391 inv_cursor( true );
2392 while( 1 )
2394 switch( button = rb->button_get( true ) )
2396 case ROCKPAINT_DRAW:
2397 #define TOP ( LCD_HEIGHT - TB_HEIGHT )
2398 if( y >= TOP + TB_SC_FG_TOP
2399 && y < TOP + TB_SC_FG_TOP + TB_SC_SIZE
2400 && x >= TB_SC_FG_LEFT
2401 && x < TB_SC_FG_LEFT + TB_SC_SIZE )
2403 /* click on the foreground color */
2404 rp_colors[drawcolor] = color_chooser( rp_colors[drawcolor] );
2406 else if( y >= TOP + TB_SC_BG_TOP
2407 && y < TOP + TB_SC_BG_TOP + TB_SC_SIZE
2408 && x >= TB_SC_BG_LEFT
2409 && x < TB_SC_BG_LEFT + TB_SC_SIZE )
2411 /* click on the background color */
2412 i = drawcolor;
2413 drawcolor = bgdrawcolor;
2414 bgdrawcolor = i;
2416 else if( y >= TOP + TB_PL_TOP
2417 && y < TOP + TB_PL_TOP + TB_PL_HEIGHT
2418 && x >= TB_PL_LEFT
2419 && x < TB_PL_LEFT + TB_PL_WIDTH )
2421 /* click on the palette */
2422 i = (x - TB_PL_LEFT)%(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2423 j = (y - (TOP+TB_PL_TOP) )%(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2424 if( i >= TB_PL_COLOR_SIZE || j >= TB_PL_COLOR_SIZE )
2425 break;
2426 i = ( x - TB_PL_LEFT )/(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2427 j = ( y - (TOP+TB_PL_TOP) )/(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2428 drawcolor = j*(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING)+i;
2430 else if( y >= TOP+TB_TL_TOP
2431 && y < TOP + TB_TL_TOP + TB_TL_HEIGHT
2432 && x >= TB_TL_LEFT
2433 && x <= TB_TL_LEFT + TB_TL_WIDTH )
2435 /* click on the tools */
2436 i = (x - TB_TL_LEFT ) % (TB_TL_SIZE+TB_TL_SPACING);
2437 j = (y - (TOP+TB_TL_TOP) ) %(TB_TL_SIZE+TB_TL_SPACING);
2438 if( i >= TB_TL_SIZE || j >= TB_TL_SIZE ) break;
2439 i = ( x - TB_TL_LEFT )/(TB_TL_SIZE+TB_TL_SPACING);
2440 j = ( y - (TOP+TB_TL_TOP) )/(TB_TL_SIZE+TB_TL_SPACING);
2441 tool = i*2+j;
2442 reset_tool();
2443 if( tool == Text )
2445 buffer->text.initialized = false;
2448 else if( x >= TB_MENU_LEFT && y >= TOP+TB_MENU_TOP-2)
2450 /* menu button */
2451 goto_menu();
2453 #undef TOP
2454 restore_screen();
2455 draw_toolbars( false );
2456 inv_cursor( true );
2457 break;
2459 case ROCKPAINT_LEFT:
2460 case ROCKPAINT_LEFT | BUTTON_REPEAT:
2461 case ROCKPAINT_RIGHT:
2462 case ROCKPAINT_RIGHT | BUTTON_REPEAT:
2463 inv_cursor(false);
2464 incdec_value(&x, &incdec_x,
2465 (button&ROCKPAINT_RIGHT), (button&BUTTON_REPEAT));
2466 inv_cursor(true);
2467 break;
2469 case ROCKPAINT_UP:
2470 case ROCKPAINT_UP | BUTTON_REPEAT:
2471 case ROCKPAINT_DOWN:
2472 case ROCKPAINT_DOWN | BUTTON_REPEAT:
2473 inv_cursor(false);
2474 if (incdec_value(&y, &incdec_y,
2475 (button&ROCKPAINT_DOWN), (button&BUTTON_REPEAT))
2476 || y < LCD_HEIGHT-TB_HEIGHT)
2478 /* went out of region. exit toolbar. */
2479 return;
2481 inv_cursor(true);
2482 break;
2484 case ROCKPAINT_TOOLBAR:
2485 case ROCKPAINT_TOOLBAR2:
2486 return;
2488 if( quit ) return;
2492 static void inv_cursor(bool update)
2494 rb->lcd_set_foreground(COLOR_BLACK);
2495 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
2496 /* cross painting */
2497 rb->lcd_hline(x-4,x+4,y);
2498 rb->lcd_vline(x,y-4,y+4);
2499 rb->lcd_set_foreground(rp_colors[drawcolor]);
2500 rb->lcd_set_drawmode(DRMODE_SOLID);
2502 if( update ) rb->lcd_update();
2505 static void restore_screen(void)
2507 rb->lcd_bitmap( save_buffer, 0, 0, COLS, ROWS );
2508 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
2509 rb->lcd_vline( img_width, 0, ROWS );
2510 rb->lcd_hline( 0, COLS, img_height );
2511 rb->lcd_drawpixel( img_width, img_height );
2512 rb->lcd_set_drawmode(DRMODE_SOLID);
2515 static void clear_drawing(void)
2517 init_buffer();
2518 img_height = ROWS;
2519 img_width = COLS;
2520 rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
2521 rb->lcd_fillrect( 0, 0, COLS, ROWS );
2522 rb->lcd_update();
2525 static void goto_menu(void)
2527 int multi;
2528 int selected = 0;
2530 while( 1 )
2532 switch( rb->do_menu( &main_menu, &selected, NULL, false ) )
2534 case MAIN_MENU_NEW:
2535 clear_drawing();
2536 return;
2538 case MAIN_MENU_LOAD:
2539 if( browse( filename, MAX_PATH, "/" ) )
2541 if( load_bitmap( filename ) <= 0 )
2543 rb->splashf( 1*HZ, "Error while loading %s",
2544 filename );
2545 clear_drawing();
2547 else
2549 rb->splashf( 1*HZ, "Image loaded (%s)", filename );
2550 restore_screen();
2551 inv_cursor(true);
2552 return;
2555 break;
2557 case MAIN_MENU_SAVE:
2558 rb->lcd_set_foreground(COLOR_BLACK);
2559 if (!filename[0])
2560 rb->strcpy(filename,"/");
2561 if( !rb->kbd_input( filename, MAX_PATH ) )
2563 if( !check_extention( filename, ".bmp" ) )
2564 rb->strcat(filename, ".bmp");
2565 save_bitmap( filename );
2566 rb->splashf( 1*HZ, "File saved (%s)", filename );
2568 break;
2570 case MAIN_MENU_SET_WIDTH:
2571 rb->set_int( "Set Width", "px", UNIT_INT, &img_width,
2572 NULL, 1, 1, COLS, NULL );
2573 break;
2574 case MAIN_MENU_SET_HEIGHT:
2575 rb->set_int( "Set Height", "px", UNIT_INT, &img_height,
2576 NULL, 1, 1, ROWS, NULL );
2577 break;
2578 case MAIN_MENU_BRUSH_SIZE:
2579 for(multi = 0; multi<4; multi++)
2580 if(bsize == times_list[multi]) break;
2581 rb->set_option( "Brush Size", &multi, INT, times_options, 4, NULL );
2582 if( multi >= 0 )
2583 bsize = times_list[multi];
2584 break;
2586 case MAIN_MENU_BRUSH_SPEED:
2587 for(multi = 0; multi<3; multi++)
2588 if(bspeed == times_list[multi]) break;
2589 rb->set_option( "Brush Speed", &multi, INT, times_options, 3, NULL );
2590 if( multi >= 0 ) {
2591 bspeed = times_list[multi];
2592 incdec_x.step[0] = bspeed;
2593 incdec_x.step[1] = bspeed * 4;
2594 incdec_y.step[0] = bspeed;
2595 incdec_y.step[1] = bspeed * 4;
2597 break;
2599 case MAIN_MENU_COLOR:
2600 rp_colors[drawcolor] = color_chooser( rp_colors[drawcolor] );
2601 break;
2603 case MAIN_MENU_GRID_SIZE:
2604 for(multi = 0; multi<4; multi++)
2605 if(gridsize == gridsize_list[multi]) break;
2606 rb->set_option( "Grid Size", &multi, INT, gridsize_options, 4, NULL );
2607 if( multi >= 0 )
2608 gridsize = gridsize_list[multi];
2609 break;
2611 case MAIN_MENU_PLAYBACK_CONTROL:
2612 if (!audio_buf)
2613 playback_control( NULL );
2614 else
2615 rb->splash(HZ, "Cannot restart playback");
2616 break;
2618 case MAIN_MENU_EXIT:
2619 restore_screen();
2620 quit=true;
2621 return;
2623 case MAIN_MENU_RESUME:
2624 default:
2625 restore_screen();
2626 return;
2627 }/* end switch */
2628 }/* end while */
2631 static void reset_tool( void )
2633 prev_x = -1;
2634 prev_y = -1;
2635 prev_x2 = -1;
2636 prev_y2 = -1;
2637 prev_x3 = -1;
2638 prev_y3 = -1;
2639 /* reset state */
2640 state = State0;
2641 /* always preview color picker */
2642 preview = (tool == ColorPicker);
2645 /* brush tool */
2646 static void state_func_brush(void)
2648 if( state == State0 )
2650 state = State1;
2652 else
2654 state = State0;
2658 /* fill tool */
2659 static void state_func_fill(void)
2661 draw_fill( x, y );
2662 restore_screen();
2665 /* select rectangle tool */
2666 static void state_func_select(void)
2668 int mode;
2669 if( state == State0 )
2671 prev_x = x;
2672 prev_y = y;
2673 preview = true;
2674 state = State1;
2676 else if( state == State1 )
2678 mode = rb->do_menu( &select_menu, NULL, NULL, false );
2679 switch( mode )
2681 case SELECT_MENU_CUT:
2682 case SELECT_MENU_COPY:
2683 prev_x2 = x;
2684 prev_y2 = y;
2685 if( prev_x < x ) x = prev_x;
2686 if( prev_y < y ) y = prev_y;
2687 prev_x3 = abs(prev_x2 - prev_x);
2688 prev_y3 = abs(prev_y2 - prev_y);
2689 copy_to_clipboard();
2690 state = (mode == SELECT_MENU_CUT? State2: State3);
2691 break;
2693 case SELECT_MENU_INVERT:
2694 draw_invert( prev_x, prev_y, x, y );
2695 reset_tool();
2696 break;
2698 case SELECT_MENU_HFLIP:
2699 draw_hflip( prev_x, prev_y, x, y );
2700 reset_tool();
2701 break;
2703 case SELECT_MENU_VFLIP:
2704 draw_vflip( prev_x, prev_y, x, y );
2705 reset_tool();
2706 break;
2708 case SELECT_MENU_ROTATE90:
2709 draw_rot_90_deg( prev_x, prev_y, x, y, 1 );
2710 reset_tool();
2711 break;
2713 case SELECT_MENU_ROTATE180:
2714 draw_hflip( prev_x, prev_y, x, y );
2715 draw_vflip( prev_x, prev_y, x, y );
2716 reset_tool();
2717 break;
2719 case SELECT_MENU_ROTATE270:
2720 draw_rot_90_deg( prev_x, prev_y, x, y, -1 );
2721 reset_tool();
2722 break;
2724 case SELECT_MENU_CANCEL:
2725 reset_tool();
2726 break;
2728 default:
2729 break;
2731 restore_screen();
2733 else
2735 preview = false;
2736 draw_paste_rectangle( prev_x, prev_y, prev_x2, prev_y2,
2737 x, y, state == State2 );
2738 reset_tool();
2739 restore_screen();
2743 static void preview_select(void)
2745 if( state == State1 )
2747 /* we are defining the selection */
2748 draw_select_rectangle( prev_x, prev_y, x, y );
2750 else
2752 /* we are pasting the selected data */
2753 draw_paste_rectangle( prev_x, prev_y, prev_x2, prev_y2,
2754 x, y, state == State2 );
2755 draw_select_rectangle( x, y, x+prev_x3, y+prev_y3 );
2759 /* color picker tool */
2760 static void state_func_picker(void)
2762 preview = false;
2763 color_picker( x, y );
2764 reset_tool();
2767 static void preview_picker(void)
2769 color_picker( x, y );
2772 /* curve tool */
2773 static void state_func_curve(void)
2775 if( state == State0 )
2777 prev_x = x;
2778 prev_y = y;
2779 preview = true;
2780 state = State1;
2782 else if( state == State1 )
2784 prev_x2 = x;
2785 prev_y2 = y;
2786 state = State2;
2788 else if( state == State2 )
2790 prev_x3 = x;
2791 prev_y3 = y;
2792 state = State3;
2794 else
2796 preview = false;
2797 draw_curve( prev_x, prev_y, prev_x2, prev_y2,
2798 prev_x3, prev_y3, x, y );
2799 reset_tool();
2800 restore_screen();
2804 static void preview_curve(void)
2806 if( state == State1 )
2808 draw_line( prev_x, prev_y, x, y );
2810 else
2812 draw_curve( prev_x, prev_y, prev_x2, prev_y2,
2813 prev_x3, prev_y3, x, y );
2817 /* text tool */
2818 static void state_func_text(void)
2820 draw_text( x, y );
2823 /* tools which take 2 point */
2824 static void preview_2point(void);
2825 static void state_func_2point(void)
2827 if( state == State0 )
2829 prev_x = x;
2830 prev_y = y;
2831 state = State1;
2832 preview = true;
2834 else
2836 preview = false;
2837 preview_2point();
2838 reset_tool();
2839 restore_screen();
2843 static void preview_2point(void)
2845 if( state == State1 )
2847 switch( tool )
2849 case Line:
2850 draw_line( prev_x, prev_y, x, y );
2851 break;
2852 case Rectangle:
2853 draw_rect( prev_x, prev_y, x, y );
2854 break;
2855 case RectangleFull:
2856 draw_rect_full( prev_x, prev_y, x, y );
2857 break;
2858 case Oval:
2859 draw_oval_empty( prev_x, prev_y, x, y );
2860 break;
2861 case OvalFull:
2862 draw_oval_full( prev_x, prev_y, x, y );
2863 break;
2864 case LinearGradient:
2865 linear_gradient( prev_x, prev_y, x, y );
2866 break;
2867 case RadialGradient:
2868 radial_gradient( prev_x, prev_y, x, y );
2869 break;
2870 default:
2871 break;
2873 if( !preview )
2875 reset_tool();
2876 restore_screen();
2881 static const struct tool_func tools[14] = {
2882 [Brush] = { state_func_brush, NULL },
2883 [Fill] = { state_func_fill, NULL },
2884 [SelectRectangle] = { state_func_select, preview_select },
2885 [ColorPicker] = { state_func_picker, preview_picker },
2886 [Line] = { state_func_2point, preview_2point },
2887 [Unused] = { NULL, NULL },
2888 [Curve] = { state_func_curve, preview_curve },
2889 [Text] = { state_func_text, NULL },
2890 [Rectangle] = { state_func_2point, preview_2point },
2891 [RectangleFull] = { state_func_2point, preview_2point },
2892 [Oval] = { state_func_2point, preview_2point },
2893 [OvalFull] = { state_func_2point, preview_2point },
2894 [LinearGradient] = { state_func_2point, preview_2point },
2895 [RadialGradient] = { state_func_2point, preview_2point },
2898 static bool rockpaint_loop( void )
2900 int button = 0, i, j;
2901 bool bigstep;
2903 x = 10;
2904 toolbar();
2905 x = 0; y = 0;
2906 restore_screen();
2907 inv_cursor(true);
2909 while (!quit) {
2910 button = rb->button_get(true);
2911 bigstep = (button & BUTTON_REPEAT) && !(tool == Brush && state == State1);
2913 switch(button)
2915 case ROCKPAINT_QUIT:
2916 if (state != State0)
2918 reset_tool();
2919 restore_screen();
2920 inv_cursor(true);
2922 else
2924 rb->lcd_set_drawmode(DRMODE_SOLID);
2925 return PLUGIN_OK;
2927 break;
2929 case ROCKPAINT_MENU:
2930 goto_menu();
2931 restore_screen();
2932 inv_cursor(true);
2933 break;
2935 case ROCKPAINT_DRAW:
2936 if( tools[tool].state_func )
2938 inv_cursor(false);
2939 tools[tool].state_func();
2940 inv_cursor(true);
2942 break;
2944 case ROCKPAINT_DRAW|BUTTON_REPEAT:
2945 if( tool == Curve && state != State0 )
2947 /* 3 point bezier curve */
2948 preview = false;
2949 draw_curve( prev_x, prev_y, prev_x2, prev_y2,
2950 -1, -1, x, y );
2951 reset_tool();
2952 restore_screen();
2953 inv_cursor( true );
2955 break;
2957 case ROCKPAINT_TOOLBAR:
2958 case ROCKPAINT_TOOLBAR2:
2959 i = x; j = y;
2960 x = (button == ROCKPAINT_TOOLBAR2) ? 110: 10;
2961 toolbar();
2962 x = i; y = j;
2963 restore_screen();
2964 inv_cursor(true);
2965 break;
2967 case ROCKPAINT_LEFT:
2968 case ROCKPAINT_LEFT | BUTTON_REPEAT:
2969 case ROCKPAINT_RIGHT:
2970 case ROCKPAINT_RIGHT | BUTTON_REPEAT:
2971 inv_cursor(false);
2972 incdec_value(&x, &incdec_x,
2973 (button&ROCKPAINT_RIGHT), bigstep);
2974 inv_cursor(true);
2975 break;
2977 case ROCKPAINT_UP:
2978 case ROCKPAINT_UP | BUTTON_REPEAT:
2979 case ROCKPAINT_DOWN:
2980 case ROCKPAINT_DOWN | BUTTON_REPEAT:
2981 inv_cursor(false);
2982 if (incdec_value(&y, &incdec_y,
2983 (button&ROCKPAINT_DOWN), bigstep)
2984 && (button&ROCKPAINT_DOWN))
2986 toolbar();
2987 restore_screen();
2989 inv_cursor(true);
2990 break;
2992 default:
2993 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
2994 return PLUGIN_USB_CONNECTED;
2995 break;
2997 if( tool == Brush && state == State1 )
2999 inv_cursor(false);
3000 draw_brush( x, y );
3001 inv_cursor(true);
3003 if( preview && tools[tool].preview_func )
3005 restore_screen();
3006 tools[tool].preview_func();
3007 inv_cursor( true );
3009 if( gridsize > 0 )
3011 show_grid( true );
3012 show_grid( false );
3016 return PLUGIN_OK;
3019 static int load_bitmap( const char *file )
3021 struct bitmap bm;
3022 bool ret;
3023 int i, j;
3024 fb_data color = rp_colors[ bgdrawcolor ];
3026 bm.data = (char*)save_buffer;
3027 ret = rb->read_bmp_file( file, &bm, ROWS*COLS*sizeof( fb_data ),
3028 FORMAT_NATIVE, NULL );
3030 if((bm.width > COLS ) || ( bm.height > ROWS ))
3031 return -1;
3033 img_width = bm.width;
3034 img_height = bm.height;
3035 for( i = bm.height-1; i >= 0; i-- )
3037 rb->memmove( save_buffer+i*COLS, save_buffer+i*bm.width,
3038 sizeof( fb_data )*bm.width );
3039 for( j = bm.width; j < COLS; j++ )
3040 save_buffer[j+i*COLS] = color;
3042 for( i = bm.height*COLS; i < ROWS*COLS; i++ )
3043 save_buffer[i] = color;
3045 return ret;
3048 static int save_bitmap( char *file )
3050 struct bitmap bm;
3051 int i;
3052 for(i = 0; i < img_height; i++)
3054 rb->memcpy( buffer->clipboard+i*img_width, save_buffer+i*COLS,
3055 sizeof( fb_data )*img_width );
3057 bm.data = (char*)buffer->clipboard;
3058 bm.height = img_height;
3059 bm.width = img_width;
3060 bm.format = FORMAT_NATIVE;
3061 return save_bmp_file( file, &bm );
3064 enum plugin_status plugin_start(const void* parameter)
3066 size_t buffer_size;
3067 unsigned char *temp;
3068 temp = rb->plugin_get_buffer(&buffer_size);
3069 if (buffer_size < sizeof(*buffer) + 3)
3071 /* steal from audiobuffer if plugin buffer is too small */
3072 temp = rb->plugin_get_audio_buffer(&buffer_size);
3073 if (buffer_size < sizeof(*buffer) + 3)
3075 rb->splash(HZ, "Not enough memory");
3076 return PLUGIN_ERROR;
3078 audio_buf = true;
3080 buffer = (union buf*) (((uintptr_t)temp + 3) & ~3);
3082 rb->lcd_set_foreground(COLOR_WHITE);
3083 rb->lcd_set_backdrop(NULL);
3084 rb->lcd_fillrect(0,0,LCD_WIDTH,LCD_HEIGHT);
3085 rb->splash( HZ/2, "Rock Paint");
3087 rb->lcd_clear_display();
3089 filename[0] = '\0';
3091 if( parameter )
3093 if( load_bitmap( parameter ) <= 0 )
3095 rb->splash( 1*HZ, "File Open Error");
3096 clear_drawing();
3098 else
3100 rb->splashf( 1*HZ, "Image loaded (%s)", (char *)parameter );
3101 restore_screen();
3102 rb->strcpy( filename, parameter );
3105 else
3107 clear_drawing();
3109 inv_cursor(true);
3111 return rockpaint_loop();