rockpaint: change the way to handle tools to improve readability.
[maemo-rb.git] / apps / plugins / rockpaint.c
blobff708bc2263169aba441e9a0f7791e354e10dace
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
28 * - cache fonts when building the font preview (else it only works well on simulators because they have "fast" disk read)
31 #include "plugin.h"
32 #include "lib/pluginlib_bmp.h"
33 #include "lib/rgb_hsv.h"
34 #include "lib/playback_control.h"
36 #include "pluginbitmaps/rockpaint.h"
37 #include "pluginbitmaps/rockpaint_hsvrgb.h"
40 /***********************************************************************
41 * Buttons
42 ***********************************************************************/
44 #if CONFIG_KEYPAD == IRIVER_H300_PAD
45 #define ROCKPAINT_QUIT BUTTON_OFF
46 #define ROCKPAINT_DRAW BUTTON_SELECT
47 #define ROCKPAINT_MENU BUTTON_ON
48 #define ROCKPAINT_TOOLBAR BUTTON_REC
49 #define ROCKPAINT_TOOLBAR2 BUTTON_MODE
50 #define ROCKPAINT_UP BUTTON_UP
51 #define ROCKPAINT_DOWN BUTTON_DOWN
52 #define ROCKPAINT_LEFT BUTTON_LEFT
53 #define ROCKPAINT_RIGHT BUTTON_RIGHT
55 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
56 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
57 #define ROCKPAINT_QUIT ( ~BUTTON_MAIN )
58 #define ROCKPAINT_DRAW BUTTON_SELECT
59 #define ROCKPAINT_MENU ( BUTTON_SELECT | BUTTON_MENU )
60 #define ROCKPAINT_TOOLBAR ( BUTTON_MENU | BUTTON_LEFT )
61 #define ROCKPAINT_TOOLBAR2 ( BUTTON_MENU | BUTTON_RIGHT )
62 #define ROCKPAINT_UP BUTTON_MENU
63 #define ROCKPAINT_DOWN BUTTON_PLAY
64 #define ROCKPAINT_LEFT BUTTON_LEFT
65 #define ROCKPAINT_RIGHT BUTTON_RIGHT
67 #elif ( CONFIG_KEYPAD == IAUDIO_X5M5_PAD )
68 #define ROCKPAINT_QUIT BUTTON_POWER
69 #define ROCKPAINT_DRAW BUTTON_SELECT
70 #define ROCKPAINT_MENU BUTTON_PLAY
71 #define ROCKPAINT_TOOLBAR BUTTON_REC
72 #define ROCKPAINT_TOOLBAR2 ( BUTTON_REC | BUTTON_LEFT )
73 #define ROCKPAINT_UP BUTTON_UP
74 #define ROCKPAINT_DOWN BUTTON_DOWN
75 #define ROCKPAINT_LEFT BUTTON_LEFT
76 #define ROCKPAINT_RIGHT BUTTON_RIGHT
78 #elif CONFIG_KEYPAD == GIGABEAT_PAD
79 #define ROCKPAINT_QUIT BUTTON_POWER
80 #define ROCKPAINT_DRAW BUTTON_SELECT
81 #define ROCKPAINT_MENU BUTTON_MENU
82 #define ROCKPAINT_TOOLBAR BUTTON_A
83 #define ROCKPAINT_TOOLBAR2 ( BUTTON_A | BUTTON_LEFT )
84 #define ROCKPAINT_UP BUTTON_UP
85 #define ROCKPAINT_DOWN BUTTON_DOWN
86 #define ROCKPAINT_LEFT BUTTON_LEFT
87 #define ROCKPAINT_RIGHT BUTTON_RIGHT
89 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
90 (CONFIG_KEYPAD == SANSA_C200_PAD)
91 #define ROCKPAINT_QUIT BUTTON_POWER
92 #define ROCKPAINT_DRAW BUTTON_SELECT
93 #define ROCKPAINT_MENU ( BUTTON_SELECT | BUTTON_POWER )
94 #define ROCKPAINT_TOOLBAR BUTTON_REC
95 #define ROCKPAINT_TOOLBAR2 ( BUTTON_REC | BUTTON_LEFT )
96 #define ROCKPAINT_UP BUTTON_UP
97 #define ROCKPAINT_DOWN BUTTON_DOWN
98 #define ROCKPAINT_LEFT BUTTON_LEFT
99 #define ROCKPAINT_RIGHT BUTTON_RIGHT
101 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
102 #define ROCKPAINT_QUIT (BUTTON_HOME|BUTTON_REPEAT)
103 #define ROCKPAINT_DRAW BUTTON_SELECT
104 #define ROCKPAINT_MENU ( BUTTON_SELECT | BUTTON_DOWN )
105 #define ROCKPAINT_TOOLBAR ( BUTTON_SELECT | BUTTON_LEFT )
106 #define ROCKPAINT_TOOLBAR2 ( BUTTON_SELECT | BUTTON_RIGHT )
107 #define ROCKPAINT_UP BUTTON_UP
108 #define ROCKPAINT_DOWN BUTTON_DOWN
109 #define ROCKPAINT_LEFT BUTTON_LEFT
110 #define ROCKPAINT_RIGHT BUTTON_RIGHT
112 #elif ( CONFIG_KEYPAD == IRIVER_H10_PAD )
113 #define ROCKPAINT_QUIT BUTTON_POWER
114 #define ROCKPAINT_DRAW BUTTON_FF
115 #define ROCKPAINT_MENU BUTTON_PLAY
116 #define ROCKPAINT_TOOLBAR BUTTON_REW
117 #define ROCKPAINT_TOOLBAR2 ( BUTTON_REW | BUTTON_LEFT )
118 #define ROCKPAINT_UP BUTTON_SCROLL_UP
119 #define ROCKPAINT_DOWN BUTTON_SCROLL_DOWN
120 #define ROCKPAINT_LEFT BUTTON_LEFT
121 #define ROCKPAINT_RIGHT BUTTON_RIGHT
123 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
124 #define ROCKPAINT_QUIT BUTTON_BACK
125 #define ROCKPAINT_DRAW BUTTON_SELECT
126 #define ROCKPAINT_MENU BUTTON_MENU
127 #define ROCKPAINT_TOOLBAR BUTTON_PLAY
128 #define ROCKPAINT_TOOLBAR2 ( BUTTON_PLAY | BUTTON_LEFT )
129 #define ROCKPAINT_UP BUTTON_UP
130 #define ROCKPAINT_DOWN BUTTON_DOWN
131 #define ROCKPAINT_LEFT BUTTON_LEFT
132 #define ROCKPAINT_RIGHT BUTTON_RIGHT
134 #elif ( CONFIG_KEYPAD == COWON_D2_PAD )
135 #define ROCKPAINT_QUIT BUTTON_POWER
136 #define ROCKPAINT_MENU BUTTON_MENU
138 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
139 #define ROCKPAINT_QUIT BUTTON_BACK
140 #define ROCKPAINT_DRAW BUTTON_SELECT
141 #define ROCKPAINT_MENU BUTTON_MENU
142 #define ROCKPAINT_TOOLBAR BUTTON_PLAY
143 #define ROCKPAINT_TOOLBAR2 ( BUTTON_PLAY | BUTTON_LEFT )
144 #define ROCKPAINT_UP BUTTON_UP
145 #define ROCKPAINT_DOWN BUTTON_DOWN
146 #define ROCKPAINT_LEFT BUTTON_LEFT
147 #define ROCKPAINT_RIGHT BUTTON_RIGHT
149 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
150 #define ROCKPAINT_QUIT BUTTON_POWER
151 #define ROCKPAINT_DRAW BUTTON_SELECT
152 #define ROCKPAINT_MENU BUTTON_MENU
153 #define ROCKPAINT_TOOLBAR BUTTON_VIEW
154 #define ROCKPAINT_TOOLBAR2 BUTTON_PLAYLIST
155 #define ROCKPAINT_UP BUTTON_UP
156 #define ROCKPAINT_DOWN BUTTON_DOWN
157 #define ROCKPAINT_LEFT BUTTON_LEFT
158 #define ROCKPAINT_RIGHT BUTTON_RIGHT
160 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
161 #define ROCKPAINT_QUIT BUTTON_POWER
162 #define ROCKPAINT_DRAW BUTTON_PLAY
163 #define ROCKPAINT_MENU BUTTON_MENU
164 #define ROCKPAINT_TOOLBAR BUTTON_PREV
165 #define ROCKPAINT_TOOLBAR2 BUTTON_NEXT
166 #define ROCKPAINT_UP BUTTON_UP
167 #define ROCKPAINT_DOWN BUTTON_DOWN
168 #define ROCKPAINT_LEFT BUTTON_LEFT
169 #define ROCKPAINT_RIGHT BUTTON_RIGHT
171 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
172 #define ROCKPAINT_QUIT BUTTON_POWER
173 #define ROCKPAINT_DRAW BUTTON_PLAY
174 #define ROCKPAINT_MENU BUTTON_MENU
175 #define ROCKPAINT_TOOLBAR BUTTON_RIGHT
176 #define ROCKPAINT_TOOLBAR2 BUTTON_LEFT
177 #define ROCKPAINT_UP BUTTON_UP
178 #define ROCKPAINT_DOWN BUTTON_DOWN
179 #define ROCKPAINT_LEFT BUTTON_PREV
180 #define ROCKPAINT_RIGHT BUTTON_NEXT
182 #elif ( CONFIG_KEYPAD == ONDAVX747_PAD )
183 #define ROCKPAINT_QUIT BUTTON_POWER
184 #define ROCKPAINT_MENU BUTTON_MENU
186 #elif ( CONFIG_KEYPAD == ONDAVX777_PAD )
187 #define ROCKPAINT_QUIT BUTTON_POWER
189 #elif CONFIG_KEYPAD == MROBE500_PAD
190 #define ROCKPAINT_QUIT BUTTON_POWER
192 #elif ( CONFIG_KEYPAD == SAMSUNG_YH_PAD )
193 #define ROCKPAINT_QUIT BUTTON_REC
194 #define ROCKPAINT_DRAW BUTTON_PLAY
195 #define ROCKPAINT_MENU BUTTON_FFWD
196 #define ROCKPAINT_TOOLBAR BUTTON_REW
197 #define ROCKPAINT_TOOLBAR2 ( BUTTON_REW | BUTTON_LEFT )
198 #define ROCKPAINT_UP BUTTON_UP
199 #define ROCKPAINT_DOWN BUTTON_DOWN
200 #define ROCKPAINT_LEFT BUTTON_LEFT
201 #define ROCKPAINT_RIGHT BUTTON_RIGHT
203 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
204 #define ROCKPAINT_QUIT BUTTON_REC
205 #define ROCKPAINT_DRAW BUTTON_PLAY
206 #define ROCKPAINT_MENU BUTTON_MENU
207 #define ROCKPAINT_TOOLBAR BUTTON_OK
208 #define ROCKPAINT_TOOLBAR2 BUTTON_CANCEL
209 #define ROCKPAINT_UP BUTTON_UP
210 #define ROCKPAINT_DOWN BUTTON_DOWN
211 #define ROCKPAINT_LEFT BUTTON_PREV
212 #define ROCKPAINT_RIGHT BUTTON_NEXT
214 #else
215 #error "Please define keys for this keypad"
216 #endif
218 #ifdef HAVE_TOUCHSCREEN
219 #ifndef ROCKPAINT_QUIT
220 #define ROCKPAINT_QUIT BUTTON_TOPLEFT
221 #endif
222 #ifndef ROCKPAINT_DRAW
223 #define ROCKPAINT_DRAW BUTTON_CENTER
224 #endif
225 #ifndef ROCKPAINT_MENU
226 #define ROCKPAINT_MENU BUTTON_TOPRIGHT
227 #endif
228 #ifndef ROCKPAINT_TOOLBAR
229 #define ROCKPAINT_TOOLBAR BUTTON_BOTTOMLEFT
230 #endif
231 #ifndef ROCKPAINT_TOOLBAR2
232 #define ROCKPAINT_TOOLBAR2 BUTTON_BOTTOMRIGHT
233 #endif
234 #ifndef ROCKPAINT_UP
235 #define ROCKPAINT_UP BUTTON_TOPMIDDLE
236 #endif
237 #ifndef ROCKPAINT_DOWN
238 #define ROCKPAINT_DOWN BUTTON_BOTTOMMIDDLE
239 #endif
240 #ifndef ROCKPAINT_LEFT
241 #define ROCKPAINT_LEFT BUTTON_MIDLEFT
242 #endif
243 #ifndef ROCKPAINT_RIGHT
244 #define ROCKPAINT_RIGHT BUTTON_MIDRIGHT
245 #endif
246 #endif
248 /***********************************************************************
249 * Palette Default Colors
250 ***********************************************************************/
251 #define COLOR_BLACK LCD_RGBPACK(0,0,0)
252 #define COLOR_WHITE LCD_RGBPACK(255,255,255)
253 #define COLOR_DARKGRAY LCD_RGBPACK(128,128,128)
254 #define COLOR_LIGHTGRAY LCD_RGBPACK(192,192,192)
255 #define COLOR_RED LCD_RGBPACK(128,0,0)
256 #define COLOR_LIGHTRED LCD_RGBPACK(255,0,0)
257 #define COLOR_DARKYELLOW LCD_RGBPACK(128,128,0)
258 #define COLOR_YELLOW LCD_RGBPACK(255,255,0)
259 #define COLOR_GREEN LCD_RGBPACK(0,128,0)
260 #define COLOR_LIGHTGREN LCD_RGBPACK(0,255,0)
261 #define COLOR_CYAN LCD_RGBPACK(0,128,128)
262 #define COLOR_LIGHTCYAN LCD_RGBPACK(0,255,255)
263 #define COLOR_BLUE LCD_RGBPACK(0,0,128)
264 #define COLOR_LIGHTBLUE LCD_RGBPACK(0,0,255)
265 #define COLOR_PURPLE LCD_RGBPACK(128,0,128)
266 #define COLOR_PINK LCD_RGBPACK(255,0,255)
267 #define COLOR_BROWN LCD_RGBPACK(128,64,0)
268 #define COLOR_LIGHTBROWN LCD_RGBPACK(255,128,64)
270 /***********************************************************************
271 * Program Colors
272 ***********************************************************************/
273 #define ROCKPAINT_PALETTE LCD_RGBPACK(0,64,128)
274 #define ROCKPAINT_SELECTED LCD_RGBPACK(128,192,255)
276 #define ROWS LCD_HEIGHT
277 #define COLS LCD_WIDTH
280 * Toolbar positioning stuff ... don't read this unless you really need to
282 * TB Toolbar
283 * SP Separator
284 * SC Selected Color
285 * PL Palette
286 * TL Tools
289 /* Separator sizes */
290 #define TB_SP_MARGIN 3
291 #define TB_SP_WIDTH (2+2*TB_SP_MARGIN)
293 /* Selected color sizes */
294 #define TB_SC_SIZE 12
296 /* Palette sizes */
297 #define TB_PL_COLOR_SIZE 7
298 #define TB_PL_COLOR_SPACING 2
299 #define TB_PL_WIDTH ( 9 * TB_PL_COLOR_SIZE + 8 * TB_PL_COLOR_SPACING )
300 #define TB_PL_HEIGHT ( TB_PL_COLOR_SIZE * 2 + TB_PL_COLOR_SPACING )
302 /* Tools sizes */
303 #define TB_TL_SIZE 8
304 #define TB_TL_SPACING 2
305 #define TB_TL_WIDTH ( 7 * ( TB_TL_SIZE + TB_TL_SPACING ) - TB_TL_SPACING )
306 #define TB_TL_HEIGHT ( 2 * TB_TL_SIZE + TB_TL_SPACING )
308 /* Menu button size ... gruik */
309 #define TB_MENU_MIN_WIDTH 30
311 /* Selected colors position */
312 #define TB_SC_FG_TOP 2
313 #define TB_SC_FG_LEFT 2
314 #define TB_SC_BG_TOP (TB_SC_FG_TOP+TB_PL_COLOR_SIZE*2+TB_PL_COLOR_SPACING-TB_SC_SIZE)
315 #define TB_SC_BG_LEFT (TB_SC_FG_LEFT+TB_PL_COLOR_SIZE*2+TB_PL_COLOR_SPACING-TB_SC_SIZE)
317 /* Palette position */
318 #define TB_PL_TOP TB_SC_FG_TOP
319 #define TB_PL_LEFT (TB_SC_BG_LEFT + TB_SC_SIZE + TB_PL_COLOR_SPACING)
321 /* Tools position */
322 #define TB_TL_TOP TB_SC_FG_TOP
323 #define TB_TL_LEFT ( TB_PL_LEFT + TB_PL_WIDTH-1 + TB_SP_WIDTH )
325 #if TB_TL_LEFT + TB_TL_WIDTH + TB_MENU_MIN_WIDTH >= LCD_WIDTH
326 #undef TB_TL_TOP
327 #undef TB_TL_LEFT
328 #define TB_TL_TOP ( TB_PL_TOP + TB_PL_HEIGHT + 4 )
329 #define TB_TL_LEFT TB_SC_FG_LEFT
330 #endif
332 /* Menu button position */
333 #define TB_MENU_TOP ( TB_TL_TOP + (TB_TL_HEIGHT-8)/2 )
334 #define TB_MENU_LEFT ( TB_TL_LEFT + TB_TL_WIDTH-1 + TB_SP_WIDTH )
336 #define TB_HEIGHT ( TB_TL_TOP + TB_TL_HEIGHT + 1 )
339 static void draw_pixel(int x,int y);
340 static void draw_line( int x1, int y1, int x2, int y2 );
341 static void draw_rect( int x1, int y1, int x2, int y2 );
342 static void draw_rect_full( int x1, int y1, int x2, int y2 );
343 static void draw_toolbars(bool update);
344 static void inv_cursor(bool update);
345 static void restore_screen(void);
346 static void clear_drawing(void);
347 static void reset_tool(void);
348 static void goto_menu(void);
349 static int load_bitmap( const char *filename );
350 static int save_bitmap( char *filename );
352 /***********************************************************************
353 * Global variables
354 ***********************************************************************/
356 static int drawcolor=0; /* Current color (in palette) */
357 static int bgdrawcolor=9; /* Current background color (in palette) */
358 static int img_height = ROWS;
359 static int img_width = COLS;
360 bool isbg = false; /* gruik ugly hack alert */
362 static int preview=false; /* Is preview mode on ? */
364 /* TODO: clean this up */
365 static int x=0, y=0; /* cursor position */
366 static int prev_x=-1, prev_y=-1; /* previous saved cursor position */
367 static int prev_x2=-1, prev_y2=-1;
368 static int prev_x3=-1, prev_y3=-1;
371 static int bsize=1; /* brush size */
372 static int bspeed=1; /* brush speed */
374 enum Tools { Brush = 0, /* Regular brush */
375 Fill = 1, /* Fill a shape with current color */
376 SelectRectangle = 2,
377 ColorPicker = 3, /* Pick a color */
378 Line = 4, /* Draw a line between two points */
379 Unused = 5, /* THIS IS UNUSED ... */
380 Curve = 6,
381 Text = 7,
382 Rectangle = 8, /* Draw a rectangle */
383 RectangleFull = 9,
384 Oval = 10, /* Draw an oval */
385 OvalFull = 11,
386 LinearGradient = 12,
387 RadialGradient = 13
390 enum States { State0 = 0, /* initial state */
391 State1,
392 State2,
393 State3,
396 enum Tools tool = Brush;
397 enum States state = State0;
399 static bool quit=false;
400 static int gridsize=0;
402 static fb_data rp_colors[18] =
404 COLOR_BLACK, COLOR_DARKGRAY, COLOR_RED, COLOR_DARKYELLOW,
405 COLOR_GREEN, COLOR_CYAN, COLOR_BLUE, COLOR_PURPLE, COLOR_BROWN,
406 COLOR_WHITE, COLOR_LIGHTGRAY, COLOR_LIGHTRED, COLOR_YELLOW,
407 COLOR_LIGHTGREN, COLOR_LIGHTCYAN, COLOR_LIGHTBLUE, COLOR_PINK,
408 COLOR_LIGHTBROWN
411 static fb_data save_buffer[ ROWS*COLS ];
413 struct tool_func {
414 void (*state_func)(void);
415 void (*preview_func)(void);
418 struct incdec_ctx {
419 int max;
420 int step[2];
421 bool wrap;
423 struct incdec_ctx incdec_x = { COLS, { 1, 4}, true };
424 struct incdec_ctx incdec_y = { ROWS, { 1, 4}, true };
426 /* Maximum string size allowed for the text tool */
427 #define MAX_TEXT 256
429 union buf
431 /* Used by fill and gradient algorithms */
432 struct
434 short x;
435 short y;
436 } coord[ ROWS*COLS ];
438 /* Used by bezier curve algorithms */
439 struct
441 short x1, y1;
442 short x2, y2;
443 short x3, y3;
444 short x4, y4;
445 short depth;
446 } bezier[ (ROWS*COLS)/5 ]; /* We have 4.5 times more data per struct
447 * than coord ... so we divide to take
448 * less memory. */
450 /* Used to cut/copy/paste data */
451 fb_data clipboard[ ROWS*COLS ];
453 /* Used for text mode */
454 struct
456 char text[MAX_TEXT];
457 char font[MAX_PATH];
458 char old_font[MAX_PATH];
459 int fh_buf[30];
460 int fw_buf[30];
461 char fontname_buf[30][MAX_PATH];
462 } text;
465 static union buf *buffer;
466 static bool audio_buf = false;
468 /* Current filename */
469 static char filename[MAX_PATH];
471 static bool incdec_value(int *pval, struct incdec_ctx *ctx, bool inc, bool bigstep)
473 bool of = true;
474 int step = ctx->step[bigstep?1:0];
475 step = inc?step: -step;
476 *pval += step;
477 if (ctx->wrap)
479 if (*pval < 0) *pval += ctx->max;
480 else if (*pval >= ctx->max) *pval -= ctx->max;
481 else of = false;
483 else
485 if (*pval < 0) *pval = 0;
486 else if (*pval > ctx->max) *pval = ctx->max;
487 else of = false;
489 return of;
492 /* Font preview buffer */
493 //#define FONT_PREVIEW_WIDTH ((LCD_WIDTH-30)/8)
494 //#define FONT_PREVIEW_HEIGHT 1000
495 //static unsigned char fontpreview[FONT_PREVIEW_WIDTH*FONT_PREVIEW_HEIGHT];
497 /***********************************************************************
498 * Offscreen buffer/Text/Fonts handling
500 * Parts of code taken from firmware/drivers/lcd-16bit.c
501 ***********************************************************************/
502 static void buffer_mono_bitmap_part(
503 fb_data *buf, int buf_width, int buf_height,
504 const unsigned char *src, int src_x, int src_y,
505 int stride, int x, int y, int width, int height )
506 /* this function only draws the foreground part of the bitmap */
508 const unsigned char *src_end;
509 fb_data *dst, *dst_end;
510 unsigned fgcolor = rb->lcd_get_foreground();
512 /* nothing to draw? */
513 if( ( width <= 0 ) || ( height <= 0 ) || ( x >= buf_width )
514 || ( y >= buf_height ) || ( x + width <= 0 ) || ( y + height <= 0 ) )
515 return;
517 /* clipping */
518 if( x < 0 )
520 width += x;
521 src_x -= x;
522 x = 0;
524 if( y < 0 )
526 height += y;
527 src_y -= y;
528 y = 0;
530 if( x + width > buf_width )
531 width = buf_width - x;
532 if( y + height > buf_height )
533 height = buf_height - y;
535 src += stride * (src_y >> 3) + src_x; /* move starting point */
536 src_y &= 7;
537 src_end = src + width;
539 dst = buf + y*buf_width + x;
543 const unsigned char *src_col = src++;
544 unsigned data = *src_col >> src_y;
545 fb_data *dst_col = dst++;
546 int numbits = 8 - src_y;
548 dst_end = dst_col + height * buf_width;
551 if( data & 0x01 )
552 *dst_col = fgcolor; /* FIXME ? */
554 dst_col += buf_width;
556 data >>= 1;
557 if( --numbits == 0 )
559 src_col += stride;
560 data = *src_col;
561 numbits = 8;
563 } while( dst_col < dst_end );
564 } while( src < src_end );
567 static void buffer_putsxyofs( fb_data *buf, int buf_width, int buf_height,
568 int x, int y, int ofs, const unsigned char *str )
570 unsigned short ch;
571 unsigned short *ucs;
573 struct font *pf = rb->font_get( FONT_UI );
574 if( !pf ) pf = rb->font_get( FONT_SYSFIXED );
576 ucs = rb->bidi_l2v( str, 1 );
578 while( (ch = *ucs++) != 0 && x < buf_width )
580 int width;
581 const unsigned char *bits;
583 /* get proportional width and glyph bits */
584 width = rb->font_get_width( pf, ch );
586 if( ofs > width )
588 ofs -= width;
589 continue;
592 bits = rb->font_get_bits( pf, ch );
594 buffer_mono_bitmap_part( buf, buf_width, buf_height, bits, ofs, 0,
595 width, x, y, width - ofs, pf->height);
597 x += width - ofs;
598 ofs = 0;
602 /***********************************************************************
603 * Menu handling
604 ***********************************************************************/
605 enum {
606 /* Main menu */
607 MAIN_MENU_RESUME,
608 MAIN_MENU_NEW, MAIN_MENU_LOAD, MAIN_MENU_SAVE,
609 MAIN_MENU_SET_WIDTH, MAIN_MENU_SET_HEIGHT,
610 MAIN_MENU_BRUSH_SIZE, MAIN_MENU_BRUSH_SPEED, MAIN_MENU_COLOR,
611 MAIN_MENU_GRID_SIZE,
612 MAIN_MENU_PLAYBACK_CONTROL,
613 MAIN_MENU_EXIT,
615 enum {
616 /* Select action menu */
617 SELECT_MENU_CUT, SELECT_MENU_COPY,
618 SELECT_MENU_INVERT, SELECT_MENU_HFLIP, SELECT_MENU_VFLIP,
619 SELECT_MENU_ROTATE90, SELECT_MENU_ROTATE180, SELECT_MENU_ROTATE270,
620 SELECT_MENU_CANCEL,
622 enum {
623 /* Text menu */
624 TEXT_MENU_TEXT, TEXT_MENU_FONT,
625 TEXT_MENU_PREVIEW, TEXT_MENU_APPLY, TEXT_MENU_CANCEL,
628 MENUITEM_STRINGLIST(main_menu, "RockPaint", NULL,
629 "Resume", "New", "Load", "Save",
630 "Set Width", "Set Height",
631 "Brush Size", "Brush Speed",
632 "Choose Color", "Grid Size",
633 "Playback Control", "Exit");
634 MENUITEM_STRINGLIST(select_menu, "Select...", NULL,
635 "Cut", "Copy",
636 "Invert", "Horizontal Flip", "Vertical Flip",
637 "Rotate 90°", "Rotate 180°", "Rotate 270°",
638 "Cancel");
639 MENUITEM_STRINGLIST(text_menu, "Text", NULL,
640 "Set Text", "Change Font",
641 "Preview", "Apply", "Cancel");
642 static const int times_list[] = { 1, 2, 4, 8 };
643 static const int gridsize_list[] = { 0, 5, 10, 20 };
644 static const struct opt_items times_options[] = {
645 { "1x", -1 }, { "2x", -1 }, { "4x", -1 }, { "8x", -1 }
647 static const struct opt_items gridsize_options[] = {
648 { "No grid", -1 }, { "5px", -1 }, { "10px", -1 }, { "20px", -1 }
651 static int draw_window( int height, int width,
652 int *top, int *left,
653 const char *title )
655 int fh;
656 rb->lcd_getstringsize( title, NULL, &fh );
657 fh++;
659 const int _top = ( LCD_HEIGHT - height ) / 2;
660 const int _left = ( LCD_WIDTH - width ) / 2;
661 if( top ) *top = _top;
662 if( left ) *left = _left;
663 rb->lcd_set_background(COLOR_BLUE);
664 rb->lcd_set_foreground(COLOR_LIGHTGRAY);
665 rb->lcd_fillrect( _left, _top, width, height );
666 rb->lcd_set_foreground(COLOR_BLUE);
667 rb->lcd_fillrect( _left, _top, width, fh+4 );
668 rb->lcd_set_foreground(COLOR_WHITE);
669 rb->lcd_putsxy( _left+2, _top+2, title );
670 rb->lcd_set_foreground(COLOR_BLACK);
671 rb->lcd_drawrect( _left, _top, width, height );
672 return _top+fh+4;
675 /***********************************************************************
676 * File browser
677 ***********************************************************************/
679 char bbuf[MAX_PATH]; /* used by file and font browsers */
680 char bbuf_s[MAX_PATH]; /* used by file and font browsers */
681 struct tree_context *tree = NULL;
683 static bool check_extention(const char *filename, const char *ext)
685 const char *p = rb->strrchr( filename, '.' );
686 return ( p != NULL && !rb->strcasecmp( p, ext ) );
689 static const char* browse_get_name_cb(int selected_item, void *data,
690 char *buffer, size_t buffer_len)
692 int *indexes = (int *) data;
693 struct entry* dc = tree->dircache;
694 struct entry* e = &dc[indexes[selected_item]];
695 (void) buffer;
696 (void) buffer_len;
698 return e->name;
701 static bool browse( char *dst, int dst_size, const char *start )
703 struct gui_synclist browse_list;
704 int item_count = 0, selected, button;
705 struct tree_context backup;
706 struct entry *dc;
707 bool reload = true;
708 int dirfilter = SHOW_ALL;
709 int *indexes = (int *) buffer->clipboard;
711 char *a;
713 rb->strcpy( bbuf, start );
714 a = bbuf+rb->strlen(bbuf)-1;
715 if( *a != '/' )
717 a[1] = '/';
718 a[2] = '\0';
720 bbuf_s[0] = '\0';
722 rb->gui_synclist_init(&browse_list, browse_get_name_cb,
723 (void*) indexes, false, 1, NULL);
725 tree = rb->tree_get_context();
726 backup = *tree;
727 dc = tree->dircache;
728 a = backup.currdir+rb->strlen(backup.currdir)-1;
729 if( *a != '/' )
731 *++a = '/';
732 *++a = '\0';
734 rb->strcpy( a, dc[tree->selected_item].name );
735 tree->dirfilter = &dirfilter;
736 while( 1 )
738 if( reload )
740 int i;
741 rb->set_current_file(bbuf);
742 item_count = 0;
743 selected = 0;
744 for( i = 0; i < tree->filesindir ; i++)
746 /* only displayes directories and .bmp files */
747 if( ((dc[i].attr & ATTR_DIRECTORY ) &&
748 rb->strcmp( dc[i].name, "." ) &&
749 rb->strcmp( dc[i].name, ".." )) ||
750 ( !(dc[i].attr & ATTR_DIRECTORY) &&
751 check_extention( dc[i].name, ".bmp" ) ) )
753 if( !rb->strcmp( dc[i].name, bbuf_s ) )
754 selected = item_count;
755 indexes[item_count++] = i;
759 rb->gui_synclist_set_nb_items(&browse_list,item_count);
760 rb->gui_synclist_select_item(&browse_list, selected);
761 rb->gui_synclist_set_title(&browse_list, bbuf, NOICON);
762 rb->gui_synclist_draw(&browse_list);
763 reload = false;
765 button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
766 if (rb->gui_synclist_do_button(&browse_list,&button,LIST_WRAP_UNLESS_HELD))
767 continue;
768 switch( button )
770 case ACTION_STD_CANCEL:
771 if( !rb->strcmp( bbuf, "/" ) )
773 *tree = backup;
774 rb->set_current_file( backup.currdir );
775 return false;
777 rb->strcpy( bbuf_s, ".." );
778 case ACTION_STD_OK:
779 if( button == ACTION_STD_OK )
781 selected = rb->gui_synclist_get_sel_pos( &browse_list );
782 if( selected < 0 || selected >= item_count )
783 break;
784 struct entry* e = &dc[indexes[selected]];
785 rb->strlcpy( bbuf_s, e->name, sizeof( bbuf_s ) );
786 if( !( e->attr & ATTR_DIRECTORY ) )
788 *tree = backup;
789 rb->set_current_file( backup.currdir );
790 rb->snprintf( dst, dst_size, "%s%s", bbuf, bbuf_s );
791 return true;
794 if( !rb->strcmp( bbuf_s, "." ) ) break;
795 a = bbuf+rb->strlen(bbuf);
796 if( !rb->strcmp( bbuf_s, ".." ) )
798 a--;
799 if( a == bbuf ) break;
800 if( *a == '/' ) a--;
801 while( *a != '/' ) a--;
802 rb->strcpy( bbuf_s, ++a );
803 /* select parent directory */
804 bbuf_s[rb->strlen(bbuf_s)-1] = '\0';
805 *a = '\0';
806 reload = true;
807 break;
809 rb->snprintf( a, bbuf+sizeof(bbuf)-a, "%s/", bbuf_s );
810 reload = true;
811 break;
813 case ACTION_STD_MENU:
814 *tree = backup;
815 rb->set_current_file( backup.currdir );
816 return false;
821 /***********************************************************************
822 * Font browser
824 * FIXME: This still needs some work ... it currently only works fine
825 * on the simulators, disk spins too much on real targets -> rendered
826 * font buffer needed.
827 ***********************************************************************/
828 static bool browse_fonts( char *dst, int dst_size )
830 #define WIDTH ( LCD_WIDTH - 20 )
831 #define HEIGHT ( LCD_HEIGHT - 20 )
832 #define LINE_SPACE 2
833 int top, top_inside = 0, left;
835 DIR *d;
836 struct dirent *de;
837 int fvi = 0; /* first visible item */
838 int lvi = 0; /* last visible item */
839 int si = 0; /* selected item */
840 int osi = 0; /* old selected item */
841 int li = 0; /* last item */
842 int nvih = 0; /* next visible item height */
843 int i;
844 int b_need_redraw = 1; /* Do we need to redraw ? */
846 int cp = 0; /* current position */
847 int fh; /* font height */
849 #define fh_buf buffer->text.fh_buf /* 30 might not be enough ... */
850 #define fw_buf buffer->text.fw_buf
851 int fw;
852 #define fontname_buf buffer->text.fontname_buf
854 rb->snprintf( buffer->text.old_font, MAX_PATH,
855 FONT_DIR "/%s.fnt",
856 rb->global_settings->font_file );
858 while( 1 )
860 if( !b_need_redraw )
862 /* we don't need to redraw ... but we need to unselect
863 * the previously selected item */
864 cp = top_inside + LINE_SPACE;
865 for( i = 0; i+fvi < osi; i++ )
867 cp += fh_buf[i] + LINE_SPACE;
869 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
870 rb->lcd_fillrect( left+10, cp, fw_buf[i], fh_buf[i] );
871 rb->lcd_set_drawmode(DRMODE_SOLID);
874 if( b_need_redraw )
876 b_need_redraw = 0;
878 d = rb->opendir( FONT_DIR "/" );
879 if( !d )
881 return false;
883 top_inside = draw_window( HEIGHT, WIDTH, &top, &left, "Fonts" );
884 i = 0;
885 li = -1;
886 while( i < fvi )
888 rb->readdir( d );
889 i++;
891 cp = top_inside+LINE_SPACE;
893 rb->lcd_set_foreground(COLOR_BLACK);
894 rb->lcd_set_background(COLOR_LIGHTGRAY);
896 while( cp < top+HEIGHT )
898 de = rb->readdir( d );
899 if( !de )
901 li = i-1;
902 break;
904 if( !check_extention( de->d_name, ".fnt" ) )
905 continue;
906 rb->snprintf( bbuf, MAX_PATH, FONT_DIR "/%s",
907 de->d_name );
908 rb->font_load(NULL, bbuf );
909 rb->font_getstringsize( de->d_name, &fw, &fh, FONT_UI );
910 if( nvih > 0 )
912 nvih -= fh;
913 fvi++;
914 if( nvih < 0 ) nvih = 0;
915 i++;
916 continue;
918 if( cp + fh >= top+HEIGHT )
920 nvih = fh;
921 break;
923 rb->lcd_putsxy( left+10, cp, de->d_name );
924 fh_buf[i-fvi] = fh;
925 fw_buf[i-fvi] = fw;
926 cp += fh + LINE_SPACE;
927 rb->strcpy( fontname_buf[i-fvi], bbuf );
928 i++;
930 lvi = i-1;
931 if( li == -1 )
933 if( !(de = rb->readdir( d ) ) )
935 li = lvi;
937 else if( !nvih && check_extention( de->d_name, ".fnt" ) )
939 rb->snprintf( bbuf, MAX_PATH, FONT_DIR "/%s",
940 de->d_name );
941 rb->font_load(NULL, bbuf );
942 rb->font_getstringsize( de->d_name, NULL, &fh, FONT_UI );
943 nvih = fh;
946 rb->font_load(NULL, buffer->text.old_font );
947 rb->closedir( d );
950 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
951 cp = top_inside + LINE_SPACE;
952 for( i = 0; i+fvi < si; i++ )
954 cp += fh_buf[i] + LINE_SPACE;
956 rb->lcd_fillrect( left+10, cp, fw_buf[i], fh_buf[i] );
957 rb->lcd_set_drawmode(DRMODE_SOLID);
959 rb->lcd_update_rect( left, top, WIDTH, HEIGHT );
961 osi = si;
962 i = fvi;
963 switch( rb->button_get(true) )
965 case ROCKPAINT_UP:
966 case ROCKPAINT_UP|BUTTON_REPEAT:
967 if( si > 0 )
969 si--;
970 if( si<fvi )
972 fvi = si;
975 break;
977 case ROCKPAINT_DOWN:
978 case ROCKPAINT_DOWN|BUTTON_REPEAT:
979 if( li == -1 || si < li )
981 si++;
983 break;
985 case ROCKPAINT_LEFT:
986 case ROCKPAINT_QUIT:
987 return false;
989 case ROCKPAINT_RIGHT:
990 case ROCKPAINT_DRAW:
991 rb->snprintf( dst, dst_size, "%s", fontname_buf[si-fvi] );
992 return true;
995 if( i != fvi || si > lvi )
997 b_need_redraw = 1;
1000 if( si<=lvi )
1002 nvih = 0;
1005 #undef fh_buf
1006 #undef fw_buf
1007 #undef fontname_buf
1008 #undef WIDTH
1009 #undef HEIGHT
1010 #undef LINE_SPACE
1013 /***********************************************************************
1014 * HSVRGB Color chooser
1015 ***********************************************************************/
1016 static unsigned int color_chooser( unsigned int color )
1018 int red = RGB_UNPACK_RED( color );
1019 int green = RGB_UNPACK_GREEN( color );
1020 int blue = RGB_UNPACK_BLUE( color );
1021 int hue, saturation, value;
1022 int r, g, b; /* temp variables */
1023 int i, top, left;
1024 int button;
1025 int *pval;
1026 static struct incdec_ctx ctxs[] = {
1027 { 3600, { 10, 100}, true }, /* hue */
1028 { 0xff, { 1, 8}, false }, /* the others */
1031 enum BaseColor { Hue = 0, Saturation = 1, Value = 2,
1032 Red = 3, Green = 4, Blue = 5 };
1033 enum BaseColor current = Red;
1034 bool has_changed;
1036 restore_screen();
1038 rgb2hsv( red, green, blue, &hue, &saturation, &value );
1040 while( 1 )
1042 has_changed = false;
1043 color = LCD_RGBPACK( red, green, blue );
1045 #define HEIGHT ( 100 )
1046 #define WIDTH ( 150 )
1048 top = draw_window( HEIGHT, WIDTH, NULL, &left, "Color chooser" );
1049 top -= 15;
1051 for( i=0; i<100; i++ )
1053 hsv2rgb( i*36, saturation, value, &r, &g, &b );
1054 rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
1055 rb->lcd_vline( left+15+i, top+20, top+27 );
1056 hsv2rgb( hue, i*255/100, value, &r, &g, &b );
1057 rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
1058 rb->lcd_vline( left+15+i, top+30, top+37 );
1059 hsv2rgb( hue, saturation, i*255/100, &r, &g, &b );
1060 rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
1061 rb->lcd_vline( left+15+i, top+40, top+47 );
1062 rb->lcd_set_foreground( LCD_RGBPACK( i*255/100, green, blue ) );
1063 rb->lcd_vline( left+15+i, top+50, top+57 );
1064 rb->lcd_set_foreground( LCD_RGBPACK( red, i*255/100, blue ) );
1065 rb->lcd_vline( left+15+i, top+60, top+67 );
1066 rb->lcd_set_foreground( LCD_RGBPACK( red, green, i*255/100 ) );
1067 rb->lcd_vline( left+15+i, top+70, top+77 );
1070 rb->lcd_set_foreground(COLOR_BLACK);
1071 #define POSITION( a, i ) \
1072 rb->lcd_drawpixel( left+14+i, top + 19 + a ); \
1073 rb->lcd_drawpixel( left+16+i, top + 19 + a ); \
1074 rb->lcd_drawpixel( left+14+i, top + 28 + a ); \
1075 rb->lcd_drawpixel( left+16+i, top + 28 + a );
1076 POSITION( 0, hue/36 );
1077 POSITION( 10, saturation*99/255 );
1078 POSITION( 20, value*99/255 );
1079 POSITION( 30, red*99/255 );
1080 POSITION( 40, green*99/255 );
1081 POSITION( 50, blue*99/255 );
1082 #undef POSITION
1083 rb->lcd_set_background(COLOR_LIGHTGRAY);
1084 rb->lcd_setfont( FONT_SYSFIXED );
1085 rb->lcd_putsxyf( left + 117, top + 20, "%d", hue/10 );
1086 rb->lcd_putsxyf( left + 117, top + 30, "%d.%d",
1087 saturation/255, ((saturation*100)/255)%100 );
1088 rb->lcd_putsxyf( left + 117, top + 40, "%d.%d",
1089 value/255, ((value*100)/255)%100 );
1090 rb->lcd_putsxyf( left + 117, top + 50, "%d", red );
1091 rb->lcd_putsxyf( left + 117, top + 60, "%d", green );
1092 rb->lcd_putsxyf( left + 117, top + 70, "%d", blue );
1093 rb->lcd_setfont( FONT_UI );
1095 #define CURSOR( l ) \
1096 rb->lcd_bitmap_transparent_part( rockpaint_hsvrgb, 1, 1, 16, left+l+1, top+20, 6, 58 ); \
1097 rb->lcd_bitmap_transparent_part( rockpaint_hsvrgb, 8, 10*current, 16, left+l, top+19+10*current, 8, 10 );
1098 CURSOR( 5 );
1099 #undef CURSOR
1101 rb->lcd_set_foreground( color );
1102 rb->lcd_fillrect( left+15, top+85, 100, 8 );
1104 rb->lcd_update();
1106 switch( button = rb->button_get(true) )
1108 case ROCKPAINT_UP:
1109 current = ( current + 5 )%6;
1110 break;
1112 case ROCKPAINT_DOWN:
1113 current = ( current + 1 )%6;
1114 break;
1116 case ROCKPAINT_LEFT:
1117 case ROCKPAINT_LEFT|BUTTON_REPEAT:
1118 case ROCKPAINT_RIGHT:
1119 case ROCKPAINT_RIGHT|BUTTON_REPEAT:
1120 has_changed = true;
1121 switch( current )
1123 case Hue:
1124 pval = &hue;
1125 break;
1126 case Saturation:
1127 pval = &saturation;
1128 break;
1129 case Value:
1130 pval = &value;
1131 break;
1132 case Red:
1133 pval = &red;
1134 break;
1135 case Green:
1136 pval = &green;
1137 break;
1138 case Blue:
1139 pval = &blue;
1140 break;
1141 default:
1142 pval = NULL;
1143 break;
1145 if (pval)
1147 incdec_value(pval, &ctxs[(current != Hue? 1: 0)],
1148 (button&ROCKPAINT_RIGHT), (button&BUTTON_REPEAT));
1150 break;
1152 case ROCKPAINT_DRAW:
1153 return color;
1155 if( has_changed )
1157 switch( current )
1159 case Hue:
1160 case Saturation:
1161 case Value:
1162 hsv2rgb( hue, saturation, value, &red, &green, &blue );
1163 break;
1165 case Red:
1166 case Green:
1167 case Blue:
1168 rgb2hsv( red, green, blue, &hue, &saturation, &value );
1169 break;
1172 #undef HEIGHT
1173 #undef WIDTH
1177 /***********************************************************************
1178 * Misc routines
1179 ***********************************************************************/
1180 static void init_buffer(void)
1182 int i;
1183 fb_data color = rp_colors[ bgdrawcolor ];
1184 for( i = 0; i < ROWS*COLS; i++ )
1186 save_buffer[i] = color;
1190 static void draw_pixel(int x,int y)
1192 if( !preview )
1194 if( x < 0 || x >= COLS || y < 0 || y >= ROWS ) return;
1195 if( isbg )
1197 save_buffer[ x+y*COLS ] = rp_colors[bgdrawcolor];
1199 else
1201 save_buffer[ x+y*COLS ] = rp_colors[drawcolor];
1204 rb->lcd_drawpixel(x,y);
1207 static void color_picker( int x, int y )
1209 if( preview )
1211 rb->lcd_set_foreground( save_buffer[ x+y*COLS ] );
1212 #define PSIZE 12
1213 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1214 if( x >= COLS - PSIZE ) x -= PSIZE + 2;
1215 if( y >= ROWS - PSIZE ) y -= PSIZE + 2;
1216 rb->lcd_drawrect( x + 2, y + 2, PSIZE - 2, PSIZE - 2 );
1217 rb->lcd_set_drawmode(DRMODE_SOLID);
1218 rb->lcd_fillrect( x + 3, y + 3, PSIZE - 4, PSIZE - 4 );
1219 #undef PSIZE
1220 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1222 else
1224 rp_colors[ drawcolor ] = save_buffer[ x+y*COLS ];
1228 static void draw_select_rectangle( int x1, int y1, int x2, int y2 )
1229 /* This is a preview mode only function */
1231 int i,a;
1232 if( x1 > x2 )
1234 i = x1;
1235 x1 = x2;
1236 x2 = i;
1238 if( y1 > y2 )
1240 i = y1;
1241 y1 = y2;
1242 y2 = i;
1244 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1245 i = 0;
1246 for( a = x1; a < x2; a++, i++ )
1247 if( i%2 )
1248 rb->lcd_drawpixel( a, y1 );
1249 for( a = y1; a < y2; a++, i++ )
1250 if( i%2 )
1251 rb->lcd_drawpixel( x2, a );
1252 if( y2 != y1 )
1253 for( a = x2; a > x1; a--, i++ )
1254 if( i%2 )
1255 rb->lcd_drawpixel( a, y2 );
1256 if( x2 != x1 )
1257 for( a = y2; a > y1; a--, i++ )
1258 if( i%2 )
1259 rb->lcd_drawpixel( x1, a );
1260 rb->lcd_set_drawmode(DRMODE_SOLID);
1263 static void copy_to_clipboard( void )
1265 /* This needs to be optimised ... but i'm lazy ATM */
1266 rb->memcpy( buffer->clipboard, save_buffer, COLS*ROWS*sizeof( fb_data ) );
1269 /* no preview mode handling atm ... do we need it ? (one if) */
1270 static void draw_invert( 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 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1287 rb->lcd_fillrect( x1, y1, x2-x1+1, y2-y1+1 );
1288 rb->lcd_set_drawmode(DRMODE_SOLID);
1290 for( ; y1<=y2; y1++ )
1292 for( i = x1; i<=x2; i++ )
1294 save_buffer[ y1*COLS + i ] = ~save_buffer[ y1*COLS + i ];
1297 /*if( update )*/ rb->lcd_update();
1300 static void draw_hflip( int x1, int y1, int x2, int y2 )
1302 int i;
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;
1316 copy_to_clipboard();
1318 for( i = 0; i <= y2 - y1; i++ )
1320 rb->memcpy( save_buffer+(y1+i)*COLS+x1,
1321 buffer->clipboard+(y2-i)*COLS+x1,
1322 (x2-x1+1)*sizeof( fb_data ) );
1324 restore_screen();
1325 rb->lcd_update();
1328 static void draw_vflip( int x1, int y1, int x2, int y2 )
1330 int i;
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 for( ; y1 <= y2; y1++ )
1348 for( i = 0; i <= x2 - x1; i++ )
1350 save_buffer[y1*COLS+x1+i] = buffer->clipboard[y1*COLS+x2-i];
1353 restore_screen();
1354 rb->lcd_update();
1357 /* direction: -1 = left, 1 = right */
1358 static void draw_rot_90_deg( int x1, int y1, int x2, int y2, int direction )
1360 int i, j;
1361 if( x1 > x2 )
1363 i = x1;
1364 x1 = x2;
1365 x2 = i;
1367 if( y1 > y2 )
1369 i = y1;
1370 y1 = y2;
1371 y2 = i;
1374 copy_to_clipboard();
1376 fb_data color = rp_colors[ bgdrawcolor ];
1377 const int width = x2 - x1, height = y2 - y1;
1378 const int sub_half = width/2-height/2, add_half = (width+height)/2;
1379 if( width > height )
1381 for( i = 0; i <= height; i++ )
1383 for( j = 0; j < sub_half; j++ )
1384 save_buffer[(y1+i)*COLS+x1+j] = color;
1385 for( j = add_half+1; j <= width; j++ )
1386 save_buffer[(y1+i)*COLS+x1+j] = color;
1389 else if( width < height )
1391 for( j = 0; j <= width; j++ )
1393 for( i = 0; i < -sub_half; i++ )
1394 save_buffer[(y1+i)*COLS+x1+j] = color;
1395 for( i = add_half+1; i <= height; i++ )
1396 save_buffer[(y1+i)*COLS+x1+j] = color;
1399 int x3 = x1 + sub_half, y3 = y1 - sub_half;
1400 int is = x3<0?-x3:0, ie = COLS-x3-1, js = y3<0?-y3:0, je = ROWS-y3-1;
1401 if( ie > height ) ie = height;
1402 if( je > width ) je = width;
1403 for( i = is; i <= ie; i++ )
1405 for( j = js; j <= je; j++ )
1407 int x, y;
1408 if(direction > 0)
1410 x = x1+j;
1411 y = y1+height-i;
1413 else
1415 x = x1+width-j;
1416 y = y1+i;
1418 save_buffer[(y3+j)*COLS+x3+i] = buffer->clipboard[y*COLS+x];
1421 restore_screen();
1422 rb->lcd_update();
1425 static void draw_paste_rectangle( int src_x1, int src_y1, int src_x2,
1426 int src_y2, int x1, int y1, int cut )
1428 int i, width, height;
1429 if( cut )
1431 i = drawcolor;
1432 drawcolor = bgdrawcolor;
1433 draw_rect_full( src_x1, src_y1, src_x2, src_y2 );
1434 drawcolor = i;
1436 if( src_x1 > src_x2 )
1438 i = src_x1;
1439 src_x1 = src_x2;
1440 src_x2 = i;
1442 if( src_y1 > src_y2 )
1444 i = src_y1;
1445 src_y1 = src_y2;
1446 src_y2 = i;
1448 width = src_x2 - src_x1 + 1;
1449 height = src_y2 - src_y1 + 1;
1450 /* clipping */
1451 if( x1 + width > COLS )
1452 width = COLS - x1;
1453 if( y1 + height > ROWS )
1454 height = ROWS - y1;
1456 rb->lcd_bitmap_part( buffer->clipboard, src_x1, src_y1, COLS,
1457 x1, y1, width, height );
1458 if( !preview )
1460 for( i = 0; i < height; i++ )
1462 rb->memcpy( save_buffer+(y1+i)*COLS+x1,
1463 buffer->clipboard+(src_y1+i)*COLS+src_x1,
1464 width*sizeof( fb_data ) );
1469 static void show_grid( bool update )
1471 int i;
1472 if( gridsize > 0 )
1474 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1475 for( i = gridsize; i < img_width; i+= gridsize )
1477 rb->lcd_vline( i, 0, img_height-1 );
1479 for( i = gridsize; i < img_height; i+= gridsize )
1481 rb->lcd_hline( 0, img_width-1, i );
1483 rb->lcd_set_drawmode(DRMODE_SOLID);
1484 if( update ) rb->lcd_update();
1488 static void draw_text( int x, int y )
1490 int selected = 0;
1491 buffer->text.text[0] = '\0';
1492 rb->snprintf( buffer->text.old_font, MAX_PATH,
1493 FONT_DIR "/%s.fnt",
1494 rb->global_settings->font_file );
1495 while( 1 )
1497 switch( rb->do_menu( &text_menu, &selected, NULL, NULL ) )
1499 case TEXT_MENU_TEXT:
1500 rb->lcd_set_foreground(COLOR_BLACK);
1501 rb->kbd_input( buffer->text.text, MAX_TEXT );
1502 break;
1504 case TEXT_MENU_FONT:
1505 if( browse_fonts( buffer->text.font, MAX_PATH ) )
1507 rb->font_load(NULL, buffer->text.font );
1509 break;
1511 case TEXT_MENU_PREVIEW:
1512 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1513 while( 1 )
1515 int button;
1516 restore_screen();
1517 rb->lcd_putsxy( x, y, buffer->text.text );
1518 rb->lcd_update();
1519 switch( button = rb->button_get( true ) )
1521 case ROCKPAINT_LEFT:
1522 case ROCKPAINT_LEFT | BUTTON_REPEAT:
1523 case ROCKPAINT_RIGHT:
1524 case ROCKPAINT_RIGHT | BUTTON_REPEAT:
1525 incdec_value(&x, &incdec_x,
1526 (button&ROCKPAINT_RIGHT), (button&BUTTON_REPEAT));
1527 break;
1529 case ROCKPAINT_UP:
1530 case ROCKPAINT_UP | BUTTON_REPEAT:
1531 case ROCKPAINT_DOWN:
1532 case ROCKPAINT_DOWN | BUTTON_REPEAT:
1533 incdec_value(&y, &incdec_y,
1534 (button&ROCKPAINT_DOWN), (button&BUTTON_REPEAT));
1535 break;
1537 case ROCKPAINT_DRAW:
1538 break;
1539 default:
1540 if(rb->default_event_handler(button)
1541 == SYS_USB_CONNECTED)
1542 button = ROCKPAINT_DRAW;
1543 break;
1545 if( button == ROCKPAINT_DRAW ) break;
1547 break;
1549 case TEXT_MENU_APPLY:
1550 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1551 buffer_putsxyofs( save_buffer, COLS, ROWS, x, y, 0,
1552 buffer->text.text );
1553 case TEXT_MENU_CANCEL:
1554 default:
1555 restore_screen();
1556 rb->font_load(NULL, buffer->text.old_font );
1557 return;
1562 static void draw_brush( int x, int y )
1564 int i,j;
1565 for( i=-bsize/2+(bsize+1)%2; i<=bsize/2; i++ )
1567 for( j=-bsize/2+(bsize+1)%2; j<=bsize/2; j++ )
1569 draw_pixel( x+i, y+j );
1574 /* This is an implementation of Bresenham's line algorithm.
1575 * See http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm.
1577 static void draw_line( int x1, int y1, int x2, int y2 )
1579 int x = x1;
1580 int y = y1;
1581 int deltax = x2 - x1;
1582 int deltay = y2 - y1;
1583 int i;
1585 int xerr = abs(deltax);
1586 int yerr = abs(deltay);
1587 int xstep = deltax > 0 ? 1 : -1;
1588 int ystep = deltay > 0 ? 1 : -1;
1589 int err;
1591 if (yerr > xerr)
1593 /* more vertical */
1594 err = yerr;
1595 xerr <<= 1;
1596 yerr <<= 1;
1598 /* to leave off the last pixel of the line, leave off the "+ 1" */
1599 for (i = err + 1; i; --i)
1601 draw_pixel(x, y);
1602 y += ystep;
1603 err -= xerr;
1604 if (err < 0) {
1605 x += xstep;
1606 err += yerr;
1610 else
1612 /* more horizontal */
1613 err = xerr;
1614 xerr <<= 1;
1615 yerr <<= 1;
1617 for (i = err + 1; i; --i)
1619 draw_pixel(x, y);
1620 x += xstep;
1621 err -= yerr;
1622 if (err < 0) {
1623 y += ystep;
1624 err += xerr;
1630 static void draw_curve( int x1, int y1, int x2, int y2,
1631 int xa, int ya, int xb, int yb )
1633 int i = 0;
1634 short xl1, yl1;
1635 short xl2, yl2;
1636 short xl3, yl3;
1637 short xl4, yl4;
1638 short xr1, yr1;
1639 short xr2, yr2;
1640 short xr3, yr3;
1641 short xr4, yr4;
1642 short depth;
1643 short xh, yh;
1645 if( x1 == x2 && y1 == y2 )
1647 draw_pixel( x1, y1 );
1648 return;
1651 // if( preview )
1653 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1654 if( xa == -1 || ya == -1 )
1656 rb->lcd_drawline( x1, y1, xb, yb );
1657 rb->lcd_drawline( x2, y2, xb, yb );
1659 else
1661 rb->lcd_drawline( x1, y1, xa, ya );
1662 rb->lcd_drawline( x2, y2, xb, yb );
1664 rb->lcd_set_drawmode(DRMODE_SOLID);
1667 if( xa == -1 || ya == -1 )
1668 /* We only have 3 of the points
1669 * This will currently only be used in preview mode */
1671 #define PUSH( a1, b1, a2, b2, a3, b3, d ) \
1672 buffer->bezier[i].x1 = a1; \
1673 buffer->bezier[i].y1 = b1; \
1674 buffer->bezier[i].x2 = a2; \
1675 buffer->bezier[i].y2 = b2; \
1676 buffer->bezier[i].x3 = a3; \
1677 buffer->bezier[i].y3 = b3; \
1678 buffer->bezier[i].depth = d; \
1679 i++;
1680 #define POP( a1, b1, a2, b2, a3, b3, d ) \
1681 i--; \
1682 a1 = buffer->bezier[i].x1; \
1683 b1 = buffer->bezier[i].y1; \
1684 a2 = buffer->bezier[i].x2; \
1685 b2 = buffer->bezier[i].y2; \
1686 a3 = buffer->bezier[i].x3; \
1687 b3 = buffer->bezier[i].y3; \
1688 d = buffer->bezier[i].depth;
1690 PUSH( x1<<4, y1<<4, xb<<4, yb<<4, x2<<4, y2<<4, 0 );
1691 while( i )
1693 /* de Casteljau's algorithm (see wikipedia) */
1694 POP( xl1, yl1, xb, yb, xr3, yr3, depth );
1695 if( depth < 10 ) /* check that the stack's 'i' doesn't overflow */
1697 xl2 = ( xl1 + xb )>>1;
1698 yl2 = ( yl1 + yb )>>1;
1699 xr2 = ( xb + xr3 )>>1;
1700 yr2 = ( yb + yr3 )>>1;
1701 xr1 = ( xl2 + xr2 )>>1;
1702 yr1 = ( yl2 + yr2 )>>1;
1703 xl3 = xr1;
1704 yl3 = yr1;
1705 PUSH( xl1, yl1, xl2, yl2, xl3, yl3, depth+1 );
1706 PUSH( xr1, yr1, xr2, yr2, xr3, yr3, depth+1 );
1708 else
1710 draw_line( ((xl1>>3)+1)>>1, ((yl1>>3)+1)>>1,
1711 ((xr3>>3)+1)>>1, ((yr3>>3)+1)>>1 );
1714 #undef PUSH
1715 #undef POP
1717 else /* We have the 4 points */
1719 #define PUSH( a1, b1, a2, b2, a3, b3, a4, b4, d ) \
1720 buffer->bezier[i].x1 = a1; \
1721 buffer->bezier[i].y1 = b1; \
1722 buffer->bezier[i].x2 = a2; \
1723 buffer->bezier[i].y2 = b2; \
1724 buffer->bezier[i].x3 = a3; \
1725 buffer->bezier[i].y3 = b3; \
1726 buffer->bezier[i].x4 = a4; \
1727 buffer->bezier[i].y4 = b4; \
1728 buffer->bezier[i].depth = d; \
1729 i++;
1730 #define POP( a1, b1, a2, b2, a3, b3, a4, b4, d ) \
1731 i--; \
1732 a1 = buffer->bezier[i].x1; \
1733 b1 = buffer->bezier[i].y1; \
1734 a2 = buffer->bezier[i].x2; \
1735 b2 = buffer->bezier[i].y2; \
1736 a3 = buffer->bezier[i].x3; \
1737 b3 = buffer->bezier[i].y3; \
1738 a4 = buffer->bezier[i].x4; \
1739 b4 = buffer->bezier[i].y4; \
1740 d = buffer->bezier[i].depth;
1742 PUSH( x1<<4, y1<<4, xa<<4, ya<<4, xb<<4, yb<<4, x2<<4, y2<<4, 0 );
1743 while( i )
1745 /* de Casteljau's algorithm (see wikipedia) */
1746 POP( xl1, yl1, xa, ya, xb, yb, xr4, yr4, depth );
1747 if( depth < 10 ) /* check that the stack's 'i' doesn't overflow */
1749 xl2 = ( xl1 + xa )>>1;
1750 yl2 = ( yl1 + ya )>>1;
1751 xh = ( xa + xb )>>1;
1752 yh = ( ya + yb )>>1;
1753 xr3 = ( xb + xr4 )>>1;
1754 yr3 = ( yb + yr4 )>>1;
1755 xl3 = ( xl2 + xh )>>1;
1756 yl3 = ( yl2 + yh )>>1;
1757 xr2 = ( xr3 + xh )>>1;
1758 yr2 = ( yr3 + yh )>>1;
1759 xl4 = ( xl3 + xr2 )>>1;
1760 yl4 = ( yl3 + yr2 )>>1;
1761 xr1 = xl4;
1762 yr1 = yl4;
1763 PUSH( xl1, yl1, xl2, yl2, xl3, yl3, xl4, yl4, depth+1 );
1764 PUSH( xr1, yr1, xr2, yr2, xr3, yr3, xr4, yr4, depth+1 );
1766 else
1768 draw_line( ((xl1>>3)+1)>>1, ((yl1>>3)+1)>>1,
1769 ((xr4>>3)+1)>>1, ((yr4>>3)+1)>>1 );
1772 #undef PUSH
1773 #undef POP
1777 static void draw_rect( int x1, int y1, int x2, int y2 )
1779 draw_line( x1, y1, x1, y2 );
1780 draw_line( x1, y1, x2, y1 );
1781 draw_line( x1, y2, x2, y2 );
1782 draw_line( x2, y1, x2, y2 );
1785 static void togglebg( void )
1787 if( isbg )
1789 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1791 else
1793 rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
1795 isbg = !isbg;
1798 static void draw_rect_full( int x1, int y1, int x2, int y2 )
1800 /* GRUIK */
1801 int x;
1802 togglebg();
1803 if( x1 > x2 )
1805 x = x1;
1806 x1 = x2;
1807 x2 = x;
1809 x = x1;
1810 do {
1811 draw_line( x, y1, x, y2 );
1812 } while( ++x <= x2 );
1813 togglebg();
1814 draw_rect( x1, y1, x2, y2 );
1817 static void draw_oval( int x1, int y1, int x2, int y2, bool full )
1819 /* TODO: simplify :) */
1820 int cx = (x1+x2)>>1;
1821 int cy = (y1+y2)>>1;
1823 int rx = (x1-x2)>>1;
1824 int ry = (y1-y2)>>1;
1825 if( rx < 0 ) rx *= -1;
1826 if( ry < 0 ) ry *= -1;
1828 if( rx == 0 || ry == 0 )
1830 draw_line( x1, y1, x2, y2 );
1831 return;
1834 int x,y;
1835 int dst, old_dst;
1837 for( x = 0; x < rx; x++ )
1839 y = 0;
1840 dst = -0xfff;
1841 do {
1842 old_dst = dst;
1843 dst = ry * ry * x * x + rx * rx * y * y - rx * rx * ry * ry;
1844 y++;
1845 } while( dst < 0 );
1846 if( -old_dst < dst ) y--;
1847 if( full )
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 );
1852 draw_line( cx-x, cy, cx-x, cy-y );
1854 else
1856 draw_pixel( cx+x, cy+y );
1857 draw_pixel( cx+x, cy-y );
1858 draw_pixel( cx-x, cy+y );
1859 draw_pixel( cx-x, cy-y );
1862 for( y = 0; y < ry; y++ )
1864 x = 0;
1865 dst = -0xfff;
1866 do {
1867 old_dst = dst;
1868 dst = ry * ry * x * x + rx * rx * y * y - rx * rx * ry * ry;
1869 x++;
1870 } while( dst < 0 );
1871 if( -old_dst < dst ) x--;
1872 if( full )
1874 draw_line( cx+x, cy, cx+x, cy+y );
1875 draw_line( cx+x, cy, cx+x, cy-y );
1876 draw_line( cx-x, cy, cx-x, cy+y );
1877 draw_line( cx-x, cy, cx-x, cy-y );
1879 else
1881 draw_pixel( cx+x, cy+y );
1882 draw_pixel( cx+x, cy-y );
1883 draw_pixel( cx-x, cy+y );
1884 draw_pixel( cx-x, cy-y );
1889 static void draw_oval_empty( int x1, int y1, int x2, int y2 )
1891 draw_oval( x1, y1, x2, y2, false );
1894 static void draw_oval_full( int x1, int y1, int x2, int y2 )
1896 togglebg();
1897 draw_oval( x1, y1, x2, y2, true );
1898 togglebg();
1899 draw_oval( x1, y1, x2, y2, false );
1902 static void draw_fill( int x0, int y0 )
1904 #define PUSH( a, b ) \
1905 draw_pixel( (int)a, (int)b ); \
1906 buffer->coord[i].x = a; \
1907 buffer->coord[i].y = b; \
1908 i++;
1909 #define POP( a, b ) \
1910 i--; \
1911 a = buffer->coord[i].x; \
1912 b = buffer->coord[i].y;
1914 unsigned int i=0;
1915 short x = x0;
1916 short y = y0;
1917 unsigned int prev_color = save_buffer[ x0+y0*COLS ];
1919 if( preview )
1920 return;
1921 if( prev_color == rp_colors[ drawcolor ] ) return;
1923 PUSH( x, y );
1925 while( i != 0 )
1927 POP( x, y );
1928 if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
1930 PUSH( x-1, y );
1932 if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
1934 PUSH( x+1, y );
1936 if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
1938 PUSH( x, y-1 );
1940 if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
1942 PUSH( x, y+1 );
1945 #undef PUSH
1946 #undef POP
1950 /* For preview purposes only */
1951 /* use same algorithm as draw_line() to draw line. */
1952 static void line_gradient( int x1, int y1, int x2, int y2 )
1954 int h1, s1, v1, h2, s2, v2, r, g, b;
1955 int xerr = x2 - x1, yerr = y2 - y1, xstep, ystep;
1956 int i, delta, err;
1957 fb_data color1, color2;
1959 if( xerr == 0 && yerr == 0 )
1961 draw_pixel( x1, y1 );
1962 return;
1965 xstep = xerr > 0 ? 1 : -1;
1966 ystep = yerr > 0 ? 1 : -1;
1967 xerr = abs(xerr) << 1;
1968 yerr = abs(yerr) << 1;
1970 color1 = rp_colors[ bgdrawcolor ];
1971 color2 = rp_colors[ drawcolor ];
1973 r = RGB_UNPACK_RED( color1 );
1974 g = RGB_UNPACK_GREEN( color1 );
1975 b = RGB_UNPACK_BLUE( color1 );
1976 rgb2hsv( r, g, b, &h1, &s1, &v1 );
1978 r = RGB_UNPACK_RED( color2 );
1979 g = RGB_UNPACK_GREEN( color2 );
1980 b = RGB_UNPACK_BLUE( color2 );
1981 rgb2hsv( r, g, b, &h2, &s2, &v2 );
1983 if( xerr > yerr )
1985 err = xerr>>1;
1986 delta = err+1;
1987 /* to leave off the last pixel of the line, leave off the "+ 1" */
1988 for (i = delta; i; --i)
1990 hsv2rgb( h2+((h1-h2)*i)/delta,
1991 s2+((s1-s2)*i)/delta,
1992 v2+((v1-v2)*i)/delta,
1993 &r, &g, &b );
1994 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
1995 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1996 draw_pixel(x1, y1);
1997 x1 += xstep;
1998 err -= yerr;
1999 if (err < 0) {
2000 y1 += ystep;
2001 err += xerr;
2005 else /* yerr >= xerr */
2007 err = yerr>>1;
2008 delta = err+1;
2009 for (i = delta; i; --i)
2011 hsv2rgb( h2+((h1-h2)*i)/delta,
2012 s2+((s1-s2)*i)/delta,
2013 v2+((v1-v2)*i)/delta,
2014 &r, &g, &b );
2015 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2016 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2017 draw_pixel(x1, y1);
2018 y1 += ystep;
2019 err -= xerr;
2020 if (err < 0) {
2021 x1 += xstep;
2022 err += yerr;
2026 rp_colors[ drawcolor ] = color2;
2029 /* macros used by linear_gradient() and radial_gradient(). */
2030 #define PUSH( _x, _y ) \
2031 save_buffer[(_x)+(_y)*COLS] = mark_color; \
2032 buffer->coord[i].x = (short)(_x); \
2033 buffer->coord[i].y = (short)(_y); \
2034 i++;
2035 #define POP( _x, _y ) \
2036 i--; \
2037 _x = (int)buffer->coord[i].x; \
2038 _y = (int)buffer->coord[i].y;
2039 #define PUSH2( _x, _y ) \
2040 j--; \
2041 buffer->coord[j].x = (short)(_x); \
2042 buffer->coord[j].y = (short)(_y);
2043 #define POP2( _x, _y ) \
2044 _x = (int)buffer->coord[j].x; \
2045 _y = (int)buffer->coord[j].y; \
2046 j++;
2048 static void linear_gradient( int x1, int y1, int x2, int y2 )
2050 int r1 = RGB_UNPACK_RED( rp_colors[ bgdrawcolor ] );
2051 int g1 = RGB_UNPACK_GREEN( rp_colors[ bgdrawcolor ] );
2052 int b1 = RGB_UNPACK_BLUE( rp_colors[ bgdrawcolor ] );
2053 int r2 = RGB_UNPACK_RED( rp_colors[ drawcolor ] );
2054 int g2 = RGB_UNPACK_GREEN( rp_colors[ drawcolor ] );
2055 int b2 = RGB_UNPACK_BLUE( rp_colors[ drawcolor ] );
2056 fb_data color = rp_colors[ drawcolor ];
2058 int h1, s1, v1, h2, s2, v2, r, g, b;
2060 /* radius^2 */
2061 int radius2 = ( x1 - x2 ) * ( x1 - x2 ) + ( y1 - y2 ) * ( y1 - y2 );
2062 int dist2, i=0, j=COLS*ROWS;
2064 /* We only propagate the gradient to neighboring pixels with the same
2065 * color as ( x1, y1 ) */
2066 fb_data prev_color = save_buffer[ x1+y1*COLS ];
2067 /* to mark pixel that the pixel is already in LIFO. */
2068 fb_data mark_color = ~prev_color;
2070 int x = x1;
2071 int y = y1;
2073 if( radius2 == 0 ) return;
2074 if( preview )
2076 line_gradient( x1, y1, x2, y2 );
2077 return;
2079 if( rp_colors[ drawcolor ] == rp_colors[ bgdrawcolor ] )
2081 draw_fill( x1, y1 );
2082 return;
2085 rgb2hsv( r1, g1, b1, &h1, &s1, &v1 );
2086 rgb2hsv( r2, g2, b2, &h2, &s2, &v2 );
2088 PUSH( x, y );
2090 while( i > 0 )
2092 POP( x, y );
2094 dist2 = ( x2 - x1 ) * ( x - x1 ) + ( y2 - y1 ) * ( y - y1 );
2095 if( dist2 <= 0 )
2097 rp_colors[ drawcolor ] = rp_colors[ bgdrawcolor ];
2099 else if( dist2 < radius2 )
2101 hsv2rgb( h1+((h2-h1)*dist2)/radius2,
2102 s1+((s2-s1)*dist2)/radius2,
2103 v1+((v2-v1)*dist2)/radius2,
2104 &r, &g, &b );
2105 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2107 else
2109 rp_colors[ drawcolor ] = color;
2111 if( rp_colors[ drawcolor ] == prev_color )
2113 /* "mark" that pixel was checked. correct color later. */
2114 PUSH2( x, y );
2115 rp_colors[ drawcolor ] = mark_color;
2117 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2118 draw_pixel( x, y );
2120 if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
2122 PUSH( x-1, y );
2124 if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
2126 PUSH( x+1, y );
2128 if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
2130 PUSH( x, y-1 );
2132 if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
2134 PUSH( x, y+1 );
2137 while (j < COLS*ROWS)
2139 /* correct color. */
2140 POP2( x, y );
2141 rp_colors[ drawcolor ] = prev_color;
2142 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2143 draw_pixel( x, y );
2145 rp_colors[ drawcolor ] = color;
2148 static void radial_gradient( int x1, int y1, int x2, int y2 )
2150 int r1 = RGB_UNPACK_RED( rp_colors[ bgdrawcolor ] );
2151 int g1 = RGB_UNPACK_GREEN( rp_colors[ bgdrawcolor ] );
2152 int b1 = RGB_UNPACK_BLUE( rp_colors[ bgdrawcolor ] );
2153 int r2 = RGB_UNPACK_RED( rp_colors[ drawcolor ] );
2154 int g2 = RGB_UNPACK_GREEN( rp_colors[ drawcolor ] );
2155 int b2 = RGB_UNPACK_BLUE( rp_colors[ drawcolor ] );
2156 fb_data color = rp_colors[ drawcolor ];
2158 int h1, s1, v1, h2, s2, v2, r, g, b;
2160 /* radius^2 */
2161 int radius2 = ( x1 - x2 ) * ( x1 - x2 ) + ( y1 - y2 ) * ( y1 - y2 );
2162 int dist2, i=0, j=COLS*ROWS;
2164 /* We only propagate the gradient to neighboring pixels with the same
2165 * color as ( x1, y1 ) */
2166 fb_data prev_color = save_buffer[ x1+y1*COLS ];
2167 /* to mark pixel that the pixel is already in LIFO. */
2168 fb_data mark_color = ~prev_color;
2170 int x = x1;
2171 int y = y1;
2173 if( radius2 == 0 ) return;
2174 if( preview )
2176 line_gradient( x1, y1, x2, y2 );
2177 return;
2179 if( rp_colors[ drawcolor ] == rp_colors[ bgdrawcolor ] )
2181 draw_fill( x1, y1 );
2182 return;
2185 rgb2hsv( r1, g1, b1, &h1, &s1, &v1 );
2186 rgb2hsv( r2, g2, b2, &h2, &s2, &v2 );
2188 PUSH( x, y );
2190 while( i > 0 )
2192 POP( x, y );
2194 dist2 = ( x - x1 ) * ( x - x1 ) + ( y - y1 ) * ( y - y1 );
2195 if( dist2 < radius2 )
2197 hsv2rgb( h1+((h2-h1)*dist2)/radius2,
2198 s1+((s2-s1)*dist2)/radius2,
2199 v1+((v2-v1)*dist2)/radius2,
2200 &r, &g, &b );
2201 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2203 else
2205 rp_colors[ drawcolor ] = color;
2207 if( rp_colors[ drawcolor ] == prev_color )
2209 /* "mark" that pixel was checked. correct color later. */
2210 PUSH2( x, y );
2211 rp_colors[ drawcolor ] = mark_color;
2213 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2214 draw_pixel( x, y );
2216 if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
2218 PUSH( x-1, y );
2220 if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
2222 PUSH( x+1, y );
2224 if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
2226 PUSH( x, y-1 );
2228 if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
2230 PUSH( x, y+1 );
2233 while (j < COLS*ROWS)
2235 /* correct color. */
2236 POP2( x, y );
2237 rp_colors[ drawcolor ] = prev_color;
2238 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2239 draw_pixel( x, y );
2241 rp_colors[ drawcolor ] = color;
2244 #undef PUSH
2245 #undef POP
2246 #undef PUSH2
2247 #undef POP2
2249 static void draw_toolbars(bool update)
2251 int i;
2252 #define TOP (LCD_HEIGHT-TB_HEIGHT)
2253 rb->lcd_set_background( COLOR_LIGHTGRAY );
2254 rb->lcd_set_foreground( COLOR_LIGHTGRAY );
2255 rb->lcd_fillrect( 0, TOP, LCD_WIDTH, TB_HEIGHT );
2256 rb->lcd_set_foreground( COLOR_BLACK );
2257 rb->lcd_drawrect( 0, TOP, LCD_WIDTH, TB_HEIGHT );
2259 rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
2260 rb->lcd_fillrect( TB_SC_BG_LEFT, TOP+TB_SC_BG_TOP,
2261 TB_SC_SIZE, TB_SC_SIZE );
2262 rb->lcd_set_foreground(ROCKPAINT_PALETTE);
2263 rb->lcd_drawrect( TB_SC_BG_LEFT, TOP+TB_SC_BG_TOP,
2264 TB_SC_SIZE, TB_SC_SIZE );
2265 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2266 rb->lcd_fillrect( TB_SC_FG_LEFT, TOP+TB_SC_FG_TOP,
2267 TB_SC_SIZE, TB_SC_SIZE );
2268 rb->lcd_set_foreground(ROCKPAINT_PALETTE);
2269 rb->lcd_drawrect( TB_SC_FG_LEFT, TOP+TB_SC_FG_TOP,
2270 TB_SC_SIZE, TB_SC_SIZE );
2272 for( i=0; i<18; i++ )
2274 rb->lcd_set_foreground( rp_colors[i] );
2275 rb->lcd_fillrect(
2276 TB_PL_LEFT+(i%9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING ),
2277 TOP+TB_PL_TOP+(i/9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING),
2278 TB_PL_COLOR_SIZE, TB_PL_COLOR_SIZE );
2279 rb->lcd_set_foreground( ROCKPAINT_PALETTE );
2280 rb->lcd_drawrect(
2281 TB_PL_LEFT+(i%9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING ),
2282 TOP+TB_PL_TOP+(i/9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING),
2283 TB_PL_COLOR_SIZE, TB_PL_COLOR_SIZE );
2286 #define SEPARATOR( x, y ) \
2287 rb->lcd_set_foreground( COLOR_WHITE ); \
2288 rb->lcd_vline( x, TOP+y, TOP+y+TB_PL_HEIGHT-1 ); \
2289 rb->lcd_set_foreground( COLOR_DARKGRAY ); \
2290 rb->lcd_vline( x+1, TOP+y, TOP+y+TB_PL_HEIGHT-1 );
2291 SEPARATOR( TB_PL_LEFT + TB_PL_WIDTH - 1 + TB_SP_MARGIN, TB_PL_TOP );
2293 rb->lcd_bitmap_transparent( rockpaint, TB_TL_LEFT, TOP+TB_TL_TOP,
2294 TB_TL_WIDTH, TB_TL_HEIGHT );
2295 rb->lcd_set_foreground(ROCKPAINT_PALETTE);
2296 rb->lcd_drawrect( TB_TL_LEFT+(TB_TL_SIZE+TB_TL_SPACING)*(tool/2),
2297 TOP+TB_TL_TOP+(TB_TL_SIZE+TB_TL_SPACING)*(tool%2),
2298 TB_TL_SIZE, TB_TL_SIZE );
2300 SEPARATOR( TB_TL_LEFT + TB_TL_WIDTH - 1 + TB_SP_MARGIN, TB_TL_TOP );
2302 rb->lcd_setfont( FONT_SYSFIXED );
2303 rb->lcd_putsxy( TB_MENU_LEFT, TOP+TB_MENU_TOP, "Menu" );
2304 rb->lcd_setfont( FONT_UI );
2305 #undef TOP
2307 if( update ) rb->lcd_update();
2310 static void toolbar( void )
2312 int button, i, j;
2313 restore_screen();
2314 draw_toolbars( false );
2315 y = LCD_HEIGHT-TB_HEIGHT/2;
2316 inv_cursor( true );
2317 while( 1 )
2319 switch( button = rb->button_get( true ) )
2321 case ROCKPAINT_DRAW:
2322 #define TOP ( LCD_HEIGHT - TB_HEIGHT )
2323 if( y >= TOP + TB_SC_FG_TOP
2324 && y < TOP + TB_SC_FG_TOP + TB_SC_SIZE
2325 && x >= TB_SC_FG_LEFT
2326 && x < TB_SC_FG_LEFT + TB_SC_SIZE )
2328 /* click on the foreground color */
2329 rp_colors[drawcolor] = color_chooser( rp_colors[drawcolor] );
2331 else if( y >= TOP + TB_SC_BG_TOP
2332 && y < TOP + TB_SC_BG_TOP + TB_SC_SIZE
2333 && x >= TB_SC_BG_LEFT
2334 && x < TB_SC_BG_LEFT + TB_SC_SIZE )
2336 /* click on the background color */
2337 i = drawcolor;
2338 drawcolor = bgdrawcolor;
2339 bgdrawcolor = i;
2341 else if( y >= TOP + TB_PL_TOP
2342 && y < TOP + TB_PL_TOP + TB_PL_HEIGHT
2343 && x >= TB_PL_LEFT
2344 && x < TB_PL_LEFT + TB_PL_WIDTH )
2346 /* click on the palette */
2347 i = (x - TB_PL_LEFT)%(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2348 j = (y - (TOP+TB_PL_TOP) )%(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2349 if( i >= TB_PL_COLOR_SIZE || j >= TB_PL_COLOR_SIZE )
2350 break;
2351 i = ( x - TB_PL_LEFT )/(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2352 j = ( y - (TOP+TB_PL_TOP) )/(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2353 drawcolor = j*(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING)+i;
2355 else if( y >= TOP+TB_TL_TOP
2356 && y < TOP + TB_TL_TOP + TB_TL_HEIGHT
2357 && x >= TB_TL_LEFT
2358 && x <= TB_TL_LEFT + TB_TL_WIDTH )
2360 /* click on the tools */
2361 i = (x - TB_TL_LEFT ) % (TB_TL_SIZE+TB_TL_SPACING);
2362 j = (y - (TOP+TB_TL_TOP) ) %(TB_TL_SIZE+TB_TL_SPACING);
2363 if( i >= TB_TL_SIZE || j >= TB_TL_SIZE ) break;
2364 i = ( x - TB_TL_LEFT )/(TB_TL_SIZE+TB_TL_SPACING);
2365 j = ( y - (TOP+TB_TL_TOP) )/(TB_TL_SIZE+TB_TL_SPACING);
2366 tool = i*2+j;
2367 reset_tool();
2369 else if( x >= TB_MENU_LEFT && y >= TOP+TB_MENU_TOP-2)
2371 /* menu button */
2372 goto_menu();
2374 #undef TOP
2375 restore_screen();
2376 draw_toolbars( false );
2377 inv_cursor( true );
2378 break;
2380 case ROCKPAINT_LEFT:
2381 case ROCKPAINT_LEFT | BUTTON_REPEAT:
2382 case ROCKPAINT_RIGHT:
2383 case ROCKPAINT_RIGHT | BUTTON_REPEAT:
2384 inv_cursor(false);
2385 incdec_value(&x, &incdec_x,
2386 (button&ROCKPAINT_RIGHT), (button&BUTTON_REPEAT));
2387 inv_cursor(true);
2388 break;
2390 case ROCKPAINT_UP:
2391 case ROCKPAINT_UP | BUTTON_REPEAT:
2392 case ROCKPAINT_DOWN:
2393 case ROCKPAINT_DOWN | BUTTON_REPEAT:
2394 inv_cursor(false);
2395 if (incdec_value(&y, &incdec_y,
2396 (button&ROCKPAINT_DOWN), (button&BUTTON_REPEAT))
2397 || y < LCD_HEIGHT-TB_HEIGHT)
2399 /* went out of region. exit toolbar. */
2400 return;
2402 inv_cursor(true);
2403 break;
2405 case ROCKPAINT_TOOLBAR:
2406 case ROCKPAINT_TOOLBAR2:
2407 return;
2409 if( quit ) return;
2413 static void inv_cursor(bool update)
2415 rb->lcd_set_foreground(COLOR_BLACK);
2416 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
2417 /* cross painting */
2418 rb->lcd_hline(x-4,x+4,y);
2419 rb->lcd_vline(x,y-4,y+4);
2420 rb->lcd_set_foreground(rp_colors[drawcolor]);
2421 rb->lcd_set_drawmode(DRMODE_SOLID);
2423 if( update ) rb->lcd_update();
2426 static void restore_screen(void)
2428 rb->lcd_bitmap( save_buffer, 0, 0, COLS, ROWS );
2429 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
2430 rb->lcd_vline( img_width, 0, ROWS );
2431 rb->lcd_hline( 0, COLS, img_height );
2432 rb->lcd_drawpixel( img_width, img_height );
2433 rb->lcd_set_drawmode(DRMODE_SOLID);
2436 static void clear_drawing(void)
2438 init_buffer();
2439 img_height = ROWS;
2440 img_width = COLS;
2441 rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
2442 rb->lcd_fillrect( 0, 0, COLS, ROWS );
2443 rb->lcd_update();
2446 static void goto_menu(void)
2448 int multi;
2449 int selected = 0;
2451 while( 1 )
2453 switch( rb->do_menu( &main_menu, &selected, NULL, false ) )
2455 case MAIN_MENU_NEW:
2456 clear_drawing();
2457 return;
2459 case MAIN_MENU_LOAD:
2460 if( browse( filename, MAX_PATH, "/" ) )
2462 if( load_bitmap( filename ) <= 0 )
2464 rb->splashf( 1*HZ, "Error while loading %s",
2465 filename );
2466 clear_drawing();
2468 else
2470 rb->splashf( 1*HZ, "Image loaded (%s)", filename );
2471 restore_screen();
2472 inv_cursor(true);
2473 return;
2476 break;
2478 case MAIN_MENU_SAVE:
2479 rb->lcd_set_foreground(COLOR_BLACK);
2480 if (!filename[0])
2481 rb->strcpy(filename,"/");
2482 if( !rb->kbd_input( filename, MAX_PATH ) )
2484 if( !check_extention( filename, ".bmp" ) )
2485 rb->strcat(filename, ".bmp");
2486 save_bitmap( filename );
2487 rb->splashf( 1*HZ, "File saved (%s)", filename );
2489 break;
2491 case MAIN_MENU_SET_WIDTH:
2492 rb->set_int( "Set Width", "px", UNIT_INT, &img_width,
2493 NULL, 1, 1, COLS, NULL );
2494 break;
2495 case MAIN_MENU_SET_HEIGHT:
2496 rb->set_int( "Set Height", "px", UNIT_INT, &img_height,
2497 NULL, 1, 1, ROWS, NULL );
2498 break;
2499 case MAIN_MENU_BRUSH_SIZE:
2500 for(multi = 0; multi<4; multi++)
2501 if(bsize == times_list[multi]) break;
2502 rb->set_option( "Brush Size", &multi, INT, times_options, 4, NULL );
2503 if( multi >= 0 )
2504 bsize = times_list[multi];
2505 break;
2507 case MAIN_MENU_BRUSH_SPEED:
2508 for(multi = 0; multi<3; multi++)
2509 if(bspeed == times_list[multi]) break;
2510 rb->set_option( "Brush Speed", &multi, INT, times_options, 3, NULL );
2511 if( multi >= 0 ) {
2512 bspeed = times_list[multi];
2513 incdec_x.step[0] = bspeed;
2514 incdec_x.step[1] = bspeed * 4;
2515 incdec_y.step[0] = bspeed;
2516 incdec_y.step[1] = bspeed * 4;
2518 break;
2520 case MAIN_MENU_COLOR:
2521 rp_colors[drawcolor] = color_chooser( rp_colors[drawcolor] );
2522 break;
2524 case MAIN_MENU_GRID_SIZE:
2525 for(multi = 0; multi<4; multi++)
2526 if(gridsize == gridsize_list[multi]) break;
2527 rb->set_option( "Grid Size", &multi, INT, gridsize_options, 4, NULL );
2528 if( multi >= 0 )
2529 gridsize = gridsize_list[multi];
2530 break;
2532 case MAIN_MENU_PLAYBACK_CONTROL:
2533 if (!audio_buf)
2534 playback_control( NULL );
2535 else
2536 rb->splash(HZ, "Cannot restart playback");
2537 break;
2539 case MAIN_MENU_EXIT:
2540 restore_screen();
2541 quit=true;
2542 return;
2544 case MAIN_MENU_RESUME:
2545 default:
2546 restore_screen();
2547 return;
2548 }/* end switch */
2549 }/* end while */
2552 static void reset_tool( void )
2554 prev_x = -1;
2555 prev_y = -1;
2556 prev_x2 = -1;
2557 prev_y2 = -1;
2558 prev_x3 = -1;
2559 prev_y3 = -1;
2560 /* reset state */
2561 state = State0;
2562 /* always preview color picker */
2563 preview = (tool == ColorPicker);
2566 /* brush tool */
2567 static void state_func_brush(void)
2569 if( state == State0 )
2571 state = State1;
2573 else
2575 state = State0;
2579 /* fill tool */
2580 static void state_func_fill(void)
2582 draw_fill( x, y );
2583 restore_screen();
2586 /* select rectangle tool */
2587 static void state_func_select(void)
2589 int mode;
2590 if( state == State0 )
2592 prev_x = x;
2593 prev_y = y;
2594 preview = true;
2595 state = State1;
2597 else if( state == State1 )
2599 mode = rb->do_menu( &select_menu, NULL, NULL, false );
2600 switch( mode )
2602 case SELECT_MENU_CUT:
2603 case SELECT_MENU_COPY:
2604 prev_x2 = x;
2605 prev_y2 = y;
2606 if( prev_x < x ) x = prev_x;
2607 if( prev_y < y ) y = prev_y;
2608 prev_x3 = abs(prev_x2 - prev_x);
2609 prev_y3 = abs(prev_y2 - prev_y);
2610 copy_to_clipboard();
2611 state = (mode == SELECT_MENU_CUT? State2: State3);
2612 break;
2614 case SELECT_MENU_INVERT:
2615 draw_invert( prev_x, prev_y, x, y );
2616 reset_tool();
2617 break;
2619 case SELECT_MENU_HFLIP:
2620 draw_hflip( prev_x, prev_y, x, y );
2621 reset_tool();
2622 break;
2624 case SELECT_MENU_VFLIP:
2625 draw_vflip( prev_x, prev_y, x, y );
2626 reset_tool();
2627 break;
2629 case SELECT_MENU_ROTATE90:
2630 draw_rot_90_deg( prev_x, prev_y, x, y, 1 );
2631 reset_tool();
2632 break;
2634 case SELECT_MENU_ROTATE180:
2635 draw_hflip( prev_x, prev_y, x, y );
2636 draw_vflip( prev_x, prev_y, x, y );
2637 reset_tool();
2638 break;
2640 case SELECT_MENU_ROTATE270:
2641 draw_rot_90_deg( prev_x, prev_y, x, y, -1 );
2642 reset_tool();
2643 break;
2645 case SELECT_MENU_CANCEL:
2646 reset_tool();
2647 break;
2649 default:
2650 break;
2652 restore_screen();
2654 else
2656 preview = false;
2657 draw_paste_rectangle( prev_x, prev_y, prev_x2, prev_y2,
2658 x, y, state == State2 );
2659 reset_tool();
2660 restore_screen();
2664 static void preview_select(void)
2666 if( state == State1 )
2668 /* we are defining the selection */
2669 draw_select_rectangle( prev_x, prev_y, x, y );
2671 else
2673 /* we are pasting the selected data */
2674 draw_paste_rectangle( prev_x, prev_y, prev_x2, prev_y2,
2675 x, y, state == State2 );
2676 draw_select_rectangle( x, y, x+prev_x3, y+prev_y3 );
2680 /* color picker tool */
2681 static void state_func_picker(void)
2683 preview = false;
2684 color_picker( x, y );
2685 reset_tool();
2688 static void preview_picker(void)
2690 color_picker( x, y );
2693 /* curve tool */
2694 static void state_func_curve(void)
2696 if( state == State0 )
2698 prev_x = x;
2699 prev_y = y;
2700 preview = true;
2701 state = State1;
2703 else if( state == State1 )
2705 prev_x2 = x;
2706 prev_y2 = y;
2707 state = State2;
2709 else if( state == State2 )
2711 prev_x3 = x;
2712 prev_y3 = y;
2713 state = State3;
2715 else
2717 preview = false;
2718 draw_curve( prev_x, prev_y, prev_x2, prev_y2,
2719 prev_x3, prev_y3, x, y );
2720 reset_tool();
2721 restore_screen();
2725 static void preview_curve(void)
2727 if( state == State1 )
2729 draw_line( prev_x, prev_y, x, y );
2731 else
2733 draw_curve( prev_x, prev_y, prev_x2, prev_y2,
2734 prev_x3, prev_y3, x, y );
2738 /* text tool */
2739 static void state_func_text(void)
2741 draw_text( x, y );
2744 /* tools which take 2 point */
2745 static void preview_2point(void);
2746 static void state_func_2point(void)
2748 if( state == State0 )
2750 prev_x = x;
2751 prev_y = y;
2752 state = State1;
2753 preview = true;
2755 else
2757 preview = false;
2758 preview_2point();
2759 reset_tool();
2760 restore_screen();
2764 static void preview_2point(void)
2766 if( state == State1 )
2768 switch( tool )
2770 case Line:
2771 draw_line( prev_x, prev_y, x, y );
2772 break;
2773 case Rectangle:
2774 draw_rect( prev_x, prev_y, x, y );
2775 break;
2776 case RectangleFull:
2777 draw_rect_full( prev_x, prev_y, x, y );
2778 break;
2779 case Oval:
2780 draw_oval_empty( prev_x, prev_y, x, y );
2781 break;
2782 case OvalFull:
2783 draw_oval_full( prev_x, prev_y, x, y );
2784 break;
2785 case LinearGradient:
2786 linear_gradient( prev_x, prev_y, x, y );
2787 break;
2788 case RadialGradient:
2789 radial_gradient( prev_x, prev_y, x, y );
2790 break;
2791 default:
2792 break;
2794 if( !preview )
2796 reset_tool();
2797 restore_screen();
2802 static const struct tool_func tools[14] = {
2803 [Brush] = { state_func_brush, NULL },
2804 [Fill] = { state_func_fill, NULL },
2805 [SelectRectangle] = { state_func_select, preview_select },
2806 [ColorPicker] = { state_func_picker, preview_picker },
2807 [Line] = { state_func_2point, preview_2point },
2808 [Unused] = { NULL, NULL },
2809 [Curve] = { state_func_curve, preview_curve },
2810 [Text] = { state_func_text, NULL },
2811 [Rectangle] = { state_func_2point, preview_2point },
2812 [RectangleFull] = { state_func_2point, preview_2point },
2813 [Oval] = { state_func_2point, preview_2point },
2814 [OvalFull] = { state_func_2point, preview_2point },
2815 [LinearGradient] = { state_func_2point, preview_2point },
2816 [RadialGradient] = { state_func_2point, preview_2point },
2819 static bool rockpaint_loop( void )
2821 int button = 0, i, j;
2822 bool bigstep;
2824 x = 10;
2825 toolbar();
2826 x = 0; y = 0;
2827 restore_screen();
2828 inv_cursor(true);
2830 while (!quit) {
2831 button = rb->button_get(true);
2832 bigstep = (button & BUTTON_REPEAT) && !(tool == Brush && state == State1);
2834 switch(button)
2836 case ROCKPAINT_QUIT:
2837 if (state != State0)
2839 reset_tool();
2840 restore_screen();
2841 inv_cursor(true);
2843 else
2845 rb->lcd_set_drawmode(DRMODE_SOLID);
2846 return PLUGIN_OK;
2848 break;
2850 case ROCKPAINT_MENU:
2851 goto_menu();
2852 restore_screen();
2853 inv_cursor(true);
2854 break;
2856 case ROCKPAINT_DRAW:
2857 if( tools[tool].state_func )
2859 inv_cursor(false);
2860 tools[tool].state_func();
2861 inv_cursor(true);
2863 break;
2865 case ROCKPAINT_DRAW|BUTTON_REPEAT:
2866 if( tool == Curve && state != State0 )
2868 /* 3 point bezier curve */
2869 preview = false;
2870 draw_curve( prev_x, prev_y, prev_x2, prev_y2,
2871 -1, -1, x, y );
2872 reset_tool();
2873 restore_screen();
2874 inv_cursor( true );
2876 break;
2878 case ROCKPAINT_TOOLBAR:
2879 case ROCKPAINT_TOOLBAR2:
2880 i = x; j = y;
2881 x = (button == ROCKPAINT_TOOLBAR2) ? 110: 10;
2882 toolbar();
2883 x = i; y = j;
2884 restore_screen();
2885 inv_cursor(true);
2886 break;
2888 case ROCKPAINT_LEFT:
2889 case ROCKPAINT_LEFT | BUTTON_REPEAT:
2890 case ROCKPAINT_RIGHT:
2891 case ROCKPAINT_RIGHT | BUTTON_REPEAT:
2892 inv_cursor(false);
2893 incdec_value(&x, &incdec_x,
2894 (button&ROCKPAINT_RIGHT), bigstep);
2895 inv_cursor(true);
2896 break;
2898 case ROCKPAINT_UP:
2899 case ROCKPAINT_UP | BUTTON_REPEAT:
2900 case ROCKPAINT_DOWN:
2901 case ROCKPAINT_DOWN | BUTTON_REPEAT:
2902 inv_cursor(false);
2903 if (incdec_value(&y, &incdec_y,
2904 (button&ROCKPAINT_DOWN), bigstep)
2905 && (button&ROCKPAINT_DOWN))
2907 toolbar();
2908 restore_screen();
2910 inv_cursor(true);
2911 break;
2913 default:
2914 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
2915 return PLUGIN_USB_CONNECTED;
2916 break;
2918 if( tool == Brush && state == State1 )
2920 inv_cursor(false);
2921 draw_brush( x, y );
2922 inv_cursor(true);
2924 if( preview && tools[tool].preview_func )
2926 restore_screen();
2927 tools[tool].preview_func();
2928 inv_cursor( true );
2930 if( gridsize > 0 )
2932 show_grid( true );
2933 show_grid( false );
2937 return PLUGIN_OK;
2940 static int load_bitmap( const char *file )
2942 struct bitmap bm;
2943 bool ret;
2944 int i, j;
2945 fb_data color = rp_colors[ bgdrawcolor ];
2947 bm.data = (char*)save_buffer;
2948 ret = rb->read_bmp_file( file, &bm, ROWS*COLS*sizeof( fb_data ),
2949 FORMAT_NATIVE, NULL );
2951 if((bm.width > COLS ) || ( bm.height > ROWS ))
2952 return -1;
2954 img_width = bm.width;
2955 img_height = bm.height;
2956 for( i = bm.height-1; i >= 0; i-- )
2958 rb->memmove( save_buffer+i*COLS, save_buffer+i*bm.width,
2959 sizeof( fb_data )*bm.width );
2960 for( j = bm.width; j < COLS; j++ )
2961 save_buffer[j+i*COLS] = color;
2963 for( i = bm.height*COLS; i < ROWS*COLS; i++ )
2964 save_buffer[i] = color;
2966 return ret;
2969 static int save_bitmap( char *file )
2971 struct bitmap bm;
2972 int i;
2973 for(i = 0; i < img_height; i++)
2975 rb->memcpy( buffer->clipboard+i*img_width, save_buffer+i*COLS,
2976 sizeof( fb_data )*img_width );
2978 bm.data = (char*)buffer->clipboard;
2979 bm.height = img_height;
2980 bm.width = img_width;
2981 bm.format = FORMAT_NATIVE;
2982 return save_bmp_file( file, &bm );
2985 enum plugin_status plugin_start(const void* parameter)
2987 size_t buffer_size;
2988 unsigned char *temp;
2989 temp = rb->plugin_get_buffer(&buffer_size);
2990 if (buffer_size < sizeof(*buffer) + 3)
2992 /* steal from audiobuffer if plugin buffer is too small */
2993 temp = rb->plugin_get_audio_buffer(&buffer_size);
2994 if (buffer_size < sizeof(*buffer) + 3)
2996 rb->splash(HZ, "Not enough memory");
2997 return PLUGIN_ERROR;
2999 audio_buf = true;
3001 buffer = (union buf*) (((uintptr_t)temp + 3) & ~3);
3003 rb->lcd_set_foreground(COLOR_WHITE);
3004 rb->lcd_set_backdrop(NULL);
3005 rb->lcd_fillrect(0,0,LCD_WIDTH,LCD_HEIGHT);
3006 rb->splash( HZ/2, "Rock Paint");
3008 rb->lcd_clear_display();
3010 filename[0] = '\0';
3012 if( parameter )
3014 if( load_bitmap( parameter ) <= 0 )
3016 rb->splash( 1*HZ, "File Open Error");
3017 clear_drawing();
3019 else
3021 rb->splashf( 1*HZ, "Image loaded (%s)", (char *)parameter );
3022 restore_screen();
3023 rb->strcpy( filename, parameter );
3026 else
3028 clear_drawing();
3030 inv_cursor(true);
3032 return rockpaint_loop();