FS#12076 - DB stats resurrection: If the filename was changed, require
[kugel-rb.git] / apps / plugins / rockpaint.c
blob1fef0e9f5f3fa710ce81d41a83508f674284a0f5
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 /* draw alpha bitmap for anti-alias font */
566 #define ALPHA_COLOR_FONT_DEPTH 2
567 #define ALPHA_COLOR_LOOKUP_SHIFT (1 << ALPHA_COLOR_FONT_DEPTH)
568 #define ALPHA_COLOR_LOOKUP_SIZE ((1 << ALPHA_COLOR_LOOKUP_SHIFT) - 1)
569 #define ALPHA_COLOR_PIXEL_PER_BYTE (8 >> ALPHA_COLOR_FONT_DEPTH)
570 #define ALPHA_COLOR_PIXEL_PER_WORD (32 >> ALPHA_COLOR_FONT_DEPTH)
571 #ifdef CPU_ARM
572 #define BLEND_INIT do {} while (0)
573 #define BLEND_START(acc, color, alpha) \
574 asm volatile("mul %0, %1, %2" : "=&r" (acc) : "r" (color), "r" (alpha))
575 #define BLEND_CONT(acc, color, alpha) \
576 asm volatile("mla %0, %1, %2, %0" : "+&r" (acc) : "r" (color), "r" (alpha))
577 #define BLEND_OUT(acc) do {} while (0)
578 #elif defined(CPU_COLDFIRE)
579 #define ALPHA_BITMAP_READ_WORDS
580 #define BLEND_INIT coldfire_set_macsr(EMAC_UNSIGNED)
581 #define BLEND_START(acc, color, alpha) \
582 asm volatile("mac.l %0, %1, %%acc0" :: "%d" (color), "d" (alpha))
583 #define BLEND_CONT BLEND_START
584 #define BLEND_OUT(acc) asm volatile("movclr.l %%acc0, %0" : "=d" (acc))
585 #else
586 #define BLEND_INIT do {} while (0)
587 #define BLEND_START(acc, color, alpha) ((acc) = (color) * (alpha))
588 #define BLEND_CONT(acc, color, alpha) ((acc) += (color) * (alpha))
589 #define BLEND_OUT(acc) do {} while (0)
590 #endif
592 /* Blend the given two colors */
593 static inline unsigned blend_two_colors(unsigned c1, unsigned c2, unsigned a)
595 a += a >> (ALPHA_COLOR_LOOKUP_SHIFT - 1);
596 #if (LCD_PIXELFORMAT == RGB565SWAPPED)
597 c1 = swap16(c1);
598 c2 = swap16(c2);
599 #endif
600 unsigned c1l = (c1 | (c1 << 16)) & 0x07e0f81f;
601 unsigned c2l = (c2 | (c2 << 16)) & 0x07e0f81f;
602 unsigned p;
603 BLEND_START(p, c1l, a);
604 BLEND_CONT(p, c2l, ALPHA_COLOR_LOOKUP_SIZE + 1 - a);
605 BLEND_OUT(p);
606 p = (p >> ALPHA_COLOR_LOOKUP_SHIFT) & 0x07e0f81f;
607 p |= (p >> 16);
608 #if (LCD_PIXELFORMAT == RGB565SWAPPED)
609 return swap16(p);
610 #else
611 return p;
612 #endif
615 static void buffer_alpha_bitmap_part(
616 fb_data *buf, int buf_width, int buf_height,
617 const unsigned char *src, int src_x, int src_y,
618 int stride, int x, int y, int width, int height )
620 fb_data *dst;
621 unsigned fg_pattern = rb->lcd_get_foreground();
623 /* nothing to draw? */
624 if ((width <= 0) || (height <= 0) || (x >= buf_width) ||
625 (y >= buf_height) || (x + width <= 0) || (y + height <= 0))
626 return;
628 /* initialize blending */
629 BLEND_INIT;
631 /* clipping */
632 if (x < 0)
634 width += x;
635 src_x -= x;
636 x = 0;
638 if (y < 0)
640 height += y;
641 src_y -= y;
642 y = 0;
644 if (x + width > buf_width)
645 width = buf_width - x;
646 if (y + height > buf_height)
647 height = buf_height - y;
649 dst = buf + y*buf_width + x;
651 int col, row = height;
652 unsigned data, pixels;
653 unsigned skip_end = (stride - width);
654 unsigned skip_start = src_y * stride + src_x;
656 #ifdef ALPHA_BITMAP_READ_WORDS
657 uint32_t *src_w = (uint32_t *)((uintptr_t)src & ~3);
658 skip_start += ALPHA_COLOR_PIXEL_PER_BYTE * ((uintptr_t)src & 3);
659 src_w += skip_start / ALPHA_COLOR_PIXEL_PER_WORD;
660 data = letoh32(*src_w++);
661 #else
662 src += skip_start / ALPHA_COLOR_PIXEL_PER_BYTE;
663 data = *src;
664 #endif
665 pixels = skip_start % ALPHA_COLOR_PIXEL_PER_WORD;
666 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
667 #ifdef ALPHA_BITMAP_READ_WORDS
668 pixels = 8 - pixels;
669 #endif
673 col = width;
674 #ifdef ALPHA_BITMAP_READ_WORDS
675 #define UPDATE_SRC_ALPHA do { \
676 if (--pixels) \
677 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
678 else \
680 data = letoh32(*src_w++); \
681 pixels = ALPHA_COLOR_PIXEL_PER_WORD; \
683 } while (0)
684 #elif ALPHA_COLOR_PIXEL_PER_BYTE == 2
685 #define UPDATE_SRC_ALPHA do { \
686 if (pixels ^= 1) \
687 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
688 else \
689 data = *(++src); \
690 } while (0)
691 #else
692 #define UPDATE_SRC_ALPHA do { \
693 if (pixels = (++pixels % ALPHA_COLOR_PIXEL_PER_BYTE)) \
694 data >>= ALPHA_COLOR_LOOKUP_SHIFT; \
695 else \
696 data = *(++src); \
697 } while (0)
698 #endif
702 *dst=blend_two_colors(*dst, fg_pattern,
703 data & ALPHA_COLOR_LOOKUP_SIZE );
704 dst++;
705 UPDATE_SRC_ALPHA;
707 while (--col);
708 #ifdef ALPHA_BITMAP_READ_WORDS
709 if (skip_end < pixels)
711 pixels -= skip_end;
712 data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
713 } else {
714 pixels = skip_end - pixels;
715 src_w += pixels / ALPHA_COLOR_PIXEL_PER_WORD;
716 pixels %= ALPHA_COLOR_PIXEL_PER_WORD;
717 data = letoh32(*src_w++);
718 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
719 pixels = 8 - pixels;
721 #else
722 if (skip_end)
724 pixels += skip_end;
725 if (pixels >= ALPHA_COLOR_PIXEL_PER_BYTE)
727 src += pixels / ALPHA_COLOR_PIXEL_PER_BYTE;
728 pixels %= ALPHA_COLOR_PIXEL_PER_BYTE;
729 data = *src;
730 data >>= pixels * ALPHA_COLOR_LOOKUP_SHIFT;
731 } else
732 data >>= skip_end * ALPHA_COLOR_LOOKUP_SHIFT;
734 #endif
735 dst += LCD_WIDTH - width;
736 } while (--row);
739 static void buffer_putsxyofs( fb_data *buf, int buf_width, int buf_height,
740 int x, int y, int ofs, const unsigned char *str )
742 unsigned short ch;
743 unsigned short *ucs;
745 struct font *pf = rb->font_get( FONT_UI );
746 if( !pf ) pf = rb->font_get( FONT_SYSFIXED );
748 ucs = rb->bidi_l2v( str, 1 );
750 while( (ch = *ucs++) != 0 && x < buf_width )
752 int width;
753 const unsigned char *bits;
755 /* get proportional width and glyph bits */
756 width = rb->font_get_width( pf, ch );
758 if( ofs > width )
760 ofs -= width;
761 continue;
764 bits = rb->font_get_bits( pf, ch );
766 if (pf->depth)
767 buffer_alpha_bitmap_part( buf, buf_width, buf_height, bits, ofs, 0,
768 width, x, y, width - ofs, pf->height);
769 else
770 buffer_mono_bitmap_part( buf, buf_width, buf_height, bits, ofs, 0,
771 width, x, y, width - ofs, pf->height);
773 x += width - ofs;
774 ofs = 0;
778 /***********************************************************************
779 * Menu handling
780 ***********************************************************************/
781 enum {
782 /* Main menu */
783 MAIN_MENU_RESUME,
784 MAIN_MENU_NEW, MAIN_MENU_LOAD, MAIN_MENU_SAVE,
785 MAIN_MENU_SET_WIDTH, MAIN_MENU_SET_HEIGHT,
786 MAIN_MENU_BRUSH_SIZE, MAIN_MENU_BRUSH_SPEED, MAIN_MENU_COLOR,
787 MAIN_MENU_GRID_SIZE,
788 MAIN_MENU_PLAYBACK_CONTROL,
789 MAIN_MENU_EXIT,
791 enum {
792 /* Select action menu */
793 SELECT_MENU_CUT, SELECT_MENU_COPY,
794 SELECT_MENU_INVERT, SELECT_MENU_HFLIP, SELECT_MENU_VFLIP,
795 SELECT_MENU_ROTATE90, SELECT_MENU_ROTATE180, SELECT_MENU_ROTATE270,
796 SELECT_MENU_CANCEL,
798 enum {
799 /* Text menu */
800 TEXT_MENU_TEXT, TEXT_MENU_FONT,
801 TEXT_MENU_PREVIEW, TEXT_MENU_APPLY, TEXT_MENU_CANCEL,
804 MENUITEM_STRINGLIST(main_menu, "RockPaint", NULL,
805 "Resume", "New", "Load", "Save",
806 "Set Width", "Set Height",
807 "Brush Size", "Brush Speed",
808 "Choose Color", "Grid Size",
809 "Playback Control", "Exit");
810 MENUITEM_STRINGLIST(select_menu, "Select...", NULL,
811 "Cut", "Copy",
812 "Invert", "Horizontal Flip", "Vertical Flip",
813 "Rotate 90°", "Rotate 180°", "Rotate 270°",
814 "Cancel");
815 MENUITEM_STRINGLIST(text_menu, "Text", NULL,
816 "Set Text", "Change Font",
817 "Preview", "Apply", "Cancel");
818 static const int times_list[] = { 1, 2, 4, 8 };
819 static const int gridsize_list[] = { 0, 5, 10, 20 };
820 static const struct opt_items times_options[] = {
821 { "1x", -1 }, { "2x", -1 }, { "4x", -1 }, { "8x", -1 }
823 static const struct opt_items gridsize_options[] = {
824 { "No grid", -1 }, { "5px", -1 }, { "10px", -1 }, { "20px", -1 }
827 static int draw_window( int height, int width,
828 int *top, int *left,
829 const char *title )
831 int fh;
832 rb->lcd_getstringsize( title, NULL, &fh );
833 fh++;
835 const int _top = ( LCD_HEIGHT - height ) / 2;
836 const int _left = ( LCD_WIDTH - width ) / 2;
837 if( top ) *top = _top;
838 if( left ) *left = _left;
839 rb->lcd_set_background(COLOR_BLUE);
840 rb->lcd_set_foreground(COLOR_LIGHTGRAY);
841 rb->lcd_fillrect( _left, _top, width, height );
842 rb->lcd_set_foreground(COLOR_BLUE);
843 rb->lcd_fillrect( _left, _top, width, fh+4 );
844 rb->lcd_set_foreground(COLOR_WHITE);
845 rb->lcd_putsxy( _left+2, _top+2, title );
846 rb->lcd_set_foreground(COLOR_BLACK);
847 rb->lcd_drawrect( _left, _top, width, height );
848 return _top+fh+4;
851 /***********************************************************************
852 * File browser
853 ***********************************************************************/
855 char bbuf[MAX_PATH]; /* used by file and font browsers */
856 char bbuf_s[MAX_PATH]; /* used by file and font browsers */
857 struct tree_context *tree = NULL;
859 static bool check_extention(const char *filename, const char *ext)
861 const char *p = rb->strrchr( filename, '.' );
862 return ( p != NULL && !rb->strcasecmp( p, ext ) );
865 /* only displayes directories and .bmp files */
866 static bool callback_show_item(char *name, int attr, struct tree_context *tc)
868 (void) tc;
869 if( ( attr & ATTR_DIRECTORY ) ||
870 ( !(attr & ATTR_DIRECTORY) && check_extention( name, ".bmp" ) ) )
872 return true;
874 return false;
877 static bool browse( char *dst, int dst_size, const char *start )
879 struct browse_context browse;
881 rb->browse_context_init(&browse, SHOW_ALL,
882 BROWSE_SELECTONLY|BROWSE_NO_CONTEXT_MENU,
883 NULL, NOICON, start, NULL);
885 browse.callback_show_item = callback_show_item;
886 browse.buf = dst;
887 browse.bufsize = dst_size;
889 rb->rockbox_browse(&browse);
891 return (browse.flags & BROWSE_SELECTED);
894 /***********************************************************************
895 * Font browser
897 * FIXME: This still needs some work ... it currently only works fine
898 * on the simulators, disk spins too much on real targets -> rendered
899 * font buffer needed.
900 ***********************************************************************/
902 * cache font preview handling assumes:
903 * - fvi doesn't decrease by more than 1.
904 * In other words, cache_first-1 must be cached before cache_first-2 is cached.
905 * - there is enough space to store all preview currently displayed.
907 static bool browse_fonts( char *dst, int dst_size )
909 #define LINE_SPACE 2
910 #define PREVIEW_SIZE(x) ((x)->size)
911 #define PREVIEW_NEXT(x) (struct font_preview *)((char*)(x) + PREVIEW_SIZE(x))
913 struct tree_context backup;
914 struct entry *dc, *e;
915 int dirfilter = SHOW_FONT;
917 struct font_preview {
918 unsigned short width;
919 unsigned short height;
920 size_t size; /* to avoid calculating size each time. */
921 fb_data preview[0];
922 } *font_preview = NULL;
924 int top = 0;
926 int fvi = 0; /* first visible item */
927 int lvi = 0; /* last visible item */
928 int si = 0; /* selected item */
929 int li = 0; /* last item */
930 int nvih = 0; /* next visible item height */
931 int i;
932 bool need_redraw = true; /* Do we need to redraw ? */
933 bool reset_font = false;
934 bool ret = false;
936 int cp = 0; /* current position */
937 int sp = 0; /* selected position */
938 int fh, fw; /* font height, width */
940 unsigned char *cache = (unsigned char *) buffer + sizeof(buffer->text);
941 size_t cache_size = sizeof(*buffer) - sizeof(buffer->text);
942 size_t cache_used = 0;
943 int cache_first = 0, cache_last = -1;
944 char *a;
946 rb->snprintf( bbuf_s, MAX_PATH, FONT_DIR "/%s.fnt",
947 rb->global_settings->font_file );
949 tree = rb->tree_get_context();
950 backup = *tree;
951 dc = tree->dircache;
952 a = backup.currdir+rb->strlen(backup.currdir)-1;
953 if( *a != '/' )
955 *++a = '/';
957 rb->strcpy( a+1, dc[tree->selected_item].name );
958 tree->dirfilter = &dirfilter;
959 tree->browse = NULL;
960 rb->strcpy( bbuf, FONT_DIR "/" );
961 rb->set_current_file( bbuf );
963 if( buffer->text.initialized )
965 cache_used = buffer->text.cache_used;
966 cache_first = buffer->text.cache_first;
967 cache_last = buffer->text.cache_last;
968 fvi = buffer->text.fvi;
969 si = buffer->text.si;
971 buffer->text.initialized = true;
973 while( 1 )
975 if( !need_redraw )
977 /* we don't need to redraw ... but we need to unselect
978 * the previously selected item */
979 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
980 rb->lcd_fillrect( 10, sp, LCD_WIDTH-10, font_preview->height );
981 rb->lcd_set_drawmode(DRMODE_SOLID);
984 if( need_redraw )
986 need_redraw = false;
988 rb->lcd_set_foreground(COLOR_BLACK);
989 rb->lcd_set_background(COLOR_LIGHTGRAY);
990 rb->lcd_clear_display();
992 rb->font_getstringsize( "Fonts", NULL, &fh, FONT_UI );
993 rb->lcd_putsxy( 2, 2, "Fonts" );
994 top = fh + 4 + LINE_SPACE;
996 font_preview = (struct font_preview *) cache;
997 /* get first font preview to be displayed. */
998 for( i = cache_first; i < cache_last && i < fvi; i++ )
1000 font_preview = PREVIEW_NEXT(font_preview);
1002 for( ; fvi < lvi && nvih > 0; fvi++ )
1004 nvih -= font_preview->height + LINE_SPACE;
1005 font_preview = PREVIEW_NEXT(font_preview);
1007 nvih = 0;
1008 i = fvi;
1010 cp = top;
1011 while( cp <= LCD_HEIGHT+LINE_SPACE && i < tree->filesindir )
1013 e = &dc[i];
1014 if( i < cache_first || i > cache_last )
1016 size_t siz;
1017 reset_font = true;
1018 rb->snprintf( bbuf, MAX_PATH, FONT_DIR "/%s", e->name );
1019 rb->font_load(NULL, bbuf );
1020 rb->font_getstringsize( e->name, &fw, &fh, FONT_UI );
1021 if( fw > LCD_WIDTH ) fw = LCD_WIDTH;
1022 siz = (sizeof(struct font_preview) + fw*fh*FB_DATA_SZ+3) & ~3;
1023 if( i < cache_first )
1025 /* insert font preview to the top. */
1026 cache_used = 0;
1027 for( ; cache_first <= cache_last; cache_first++ )
1029 font_preview = (struct font_preview *) (cache + cache_used);
1030 size_t size = PREVIEW_SIZE(font_preview);
1031 if( cache_used + size >= cache_size - siz )
1032 break;
1033 cache_used += size;
1035 cache_last = cache_first-1;
1036 cache_first = i;
1037 rb->memmove( cache+siz, cache, cache_used );
1038 font_preview = (struct font_preview *) cache;
1040 else /* i > cache_last */
1042 /* add font preview to the bottom. */
1043 font_preview = (struct font_preview *) cache;
1044 while( cache_used >= cache_size - siz )
1046 cache_used -= PREVIEW_SIZE(font_preview);
1047 font_preview = PREVIEW_NEXT(font_preview);
1048 cache_first++;
1050 cache_last = i;
1051 rb->memmove( cache, font_preview, cache_used );
1052 font_preview = (struct font_preview *) (cache + cache_used);
1054 cache_used += siz;
1055 /* create preview cache. */
1056 font_preview->width = fw;
1057 font_preview->height = fh;
1058 font_preview->size = siz;
1059 /* clear with background. */
1060 for( siz = fw*fh; siz > 0; )
1062 font_preview->preview[--siz] = COLOR_LIGHTGRAY;
1064 buffer_putsxyofs( font_preview->preview,
1065 fw, fh, 0, 0, 0, e->name );
1067 else
1069 fw = font_preview->width;
1070 fh = font_preview->height;
1072 if( cp + fh >= LCD_HEIGHT )
1074 nvih = fh;
1075 break;
1077 rb->lcd_bitmap( font_preview->preview, 10, cp, fw, fh );
1078 cp += fh + LINE_SPACE;
1079 i++;
1080 font_preview = PREVIEW_NEXT(font_preview);
1082 lvi = i-1;
1083 li = tree->filesindir-1;
1084 if( reset_font )
1086 rb->font_load(NULL, bbuf_s );
1087 reset_font = false;
1089 if( lvi-fvi+1 < tree->filesindir )
1091 rb->gui_scrollbar_draw( rb->screens[SCREEN_MAIN], 0, top,
1092 9, LCD_HEIGHT-top,
1093 tree->filesindir, fvi, lvi+1, VERTICAL );
1097 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1098 sp = top;
1099 font_preview = (struct font_preview *) cache;
1100 for( i = cache_first; i < si; i++ )
1102 if( i >= fvi )
1103 sp += font_preview->height + LINE_SPACE;
1104 font_preview = PREVIEW_NEXT(font_preview);
1106 rb->lcd_fillrect( 10, sp, LCD_WIDTH-10, font_preview->height );
1107 rb->lcd_set_drawmode(DRMODE_SOLID);
1109 rb->lcd_update();
1111 switch( rb->button_get(true) )
1113 case ROCKPAINT_UP:
1114 case ROCKPAINT_UP|BUTTON_REPEAT:
1115 if( si > 0 )
1117 si--;
1118 if( si < fvi )
1120 fvi = si;
1121 nvih = 0;
1122 need_redraw = true;
1125 break;
1127 case ROCKPAINT_DOWN:
1128 case ROCKPAINT_DOWN|BUTTON_REPEAT:
1129 if( si < li )
1131 si++;
1132 if( si > lvi )
1134 need_redraw = true;
1137 break;
1139 case ROCKPAINT_RIGHT:
1140 case ROCKPAINT_DRAW:
1141 ret = true;
1142 rb->snprintf( dst, dst_size, FONT_DIR "/%s", dc[si].name );
1143 /* fall through */
1144 case ROCKPAINT_LEFT:
1145 case ROCKPAINT_QUIT:
1146 buffer->text.cache_used = cache_used;
1147 buffer->text.cache_first = cache_first;
1148 buffer->text.cache_last = cache_last;
1149 buffer->text.fvi = fvi;
1150 buffer->text.si = si;
1151 *tree = backup;
1152 rb->set_current_file( backup.currdir );
1153 return ret;
1156 #undef LINE_SPACE
1157 #undef PREVIEW_SIZE
1158 #undef PREVIEW_NEXT
1161 /***********************************************************************
1162 * HSVRGB Color chooser
1163 ***********************************************************************/
1164 static unsigned int color_chooser( unsigned int color )
1166 int red = RGB_UNPACK_RED( color );
1167 int green = RGB_UNPACK_GREEN( color );
1168 int blue = RGB_UNPACK_BLUE( color );
1169 int hue, saturation, value;
1170 int r, g, b; /* temp variables */
1171 int i, top, left;
1172 int button;
1173 int *pval;
1174 static struct incdec_ctx ctxs[] = {
1175 { 3600, { 10, 100}, true }, /* hue */
1176 { 0xff, { 1, 8}, false }, /* the others */
1179 enum BaseColor { Hue = 0, Saturation = 1, Value = 2,
1180 Red = 3, Green = 4, Blue = 5 };
1181 enum BaseColor current = Red;
1182 bool has_changed;
1184 restore_screen();
1186 rgb2hsv( red, green, blue, &hue, &saturation, &value );
1188 while( 1 )
1190 has_changed = false;
1191 color = LCD_RGBPACK( red, green, blue );
1193 #define HEIGHT ( 100 )
1194 #define WIDTH ( 150 )
1196 top = draw_window( HEIGHT, WIDTH, NULL, &left, "Color chooser" );
1197 top -= 15;
1199 for( i=0; i<100; i++ )
1201 hsv2rgb( i*36, saturation, value, &r, &g, &b );
1202 rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
1203 rb->lcd_vline( left+15+i, top+20, top+27 );
1204 hsv2rgb( hue, i*255/100, value, &r, &g, &b );
1205 rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
1206 rb->lcd_vline( left+15+i, top+30, top+37 );
1207 hsv2rgb( hue, saturation, i*255/100, &r, &g, &b );
1208 rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
1209 rb->lcd_vline( left+15+i, top+40, top+47 );
1210 rb->lcd_set_foreground( LCD_RGBPACK( i*255/100, green, blue ) );
1211 rb->lcd_vline( left+15+i, top+50, top+57 );
1212 rb->lcd_set_foreground( LCD_RGBPACK( red, i*255/100, blue ) );
1213 rb->lcd_vline( left+15+i, top+60, top+67 );
1214 rb->lcd_set_foreground( LCD_RGBPACK( red, green, i*255/100 ) );
1215 rb->lcd_vline( left+15+i, top+70, top+77 );
1218 rb->lcd_set_foreground(COLOR_BLACK);
1219 #define POSITION( a, i ) \
1220 rb->lcd_drawpixel( left+14+i, top + 19 + a ); \
1221 rb->lcd_drawpixel( left+16+i, top + 19 + a ); \
1222 rb->lcd_drawpixel( left+14+i, top + 28 + a ); \
1223 rb->lcd_drawpixel( left+16+i, top + 28 + a );
1224 POSITION( 0, hue/36 );
1225 POSITION( 10, saturation*99/255 );
1226 POSITION( 20, value*99/255 );
1227 POSITION( 30, red*99/255 );
1228 POSITION( 40, green*99/255 );
1229 POSITION( 50, blue*99/255 );
1230 #undef POSITION
1231 rb->lcd_set_background(COLOR_LIGHTGRAY);
1232 rb->lcd_setfont( FONT_SYSFIXED );
1233 rb->lcd_putsxyf( left + 117, top + 20, "%d", hue/10 );
1234 rb->lcd_putsxyf( left + 117, top + 30, "%d.%d",
1235 saturation/255, ((saturation*100)/255)%100 );
1236 rb->lcd_putsxyf( left + 117, top + 40, "%d.%d",
1237 value/255, ((value*100)/255)%100 );
1238 rb->lcd_putsxyf( left + 117, top + 50, "%d", red );
1239 rb->lcd_putsxyf( left + 117, top + 60, "%d", green );
1240 rb->lcd_putsxyf( left + 117, top + 70, "%d", blue );
1241 rb->lcd_setfont( FONT_UI );
1243 #define CURSOR( l ) \
1244 rb->lcd_bitmap_transparent_part( rockpaint_hsvrgb, 1, 1, 16, left+l+1, top+20, 6, 58 ); \
1245 rb->lcd_bitmap_transparent_part( rockpaint_hsvrgb, 8, 10*current, 16, left+l, top+19+10*current, 8, 10 );
1246 CURSOR( 5 );
1247 #undef CURSOR
1249 rb->lcd_set_foreground( color );
1250 rb->lcd_fillrect( left+15, top+85, 100, 8 );
1252 rb->lcd_update();
1254 switch( button = rb->button_get(true) )
1256 case ROCKPAINT_UP:
1257 current = ( current + 5 )%6;
1258 break;
1260 case ROCKPAINT_DOWN:
1261 current = ( current + 1 )%6;
1262 break;
1264 case ROCKPAINT_LEFT:
1265 case ROCKPAINT_LEFT|BUTTON_REPEAT:
1266 case ROCKPAINT_RIGHT:
1267 case ROCKPAINT_RIGHT|BUTTON_REPEAT:
1268 has_changed = true;
1269 switch( current )
1271 case Hue:
1272 pval = &hue;
1273 break;
1274 case Saturation:
1275 pval = &saturation;
1276 break;
1277 case Value:
1278 pval = &value;
1279 break;
1280 case Red:
1281 pval = &red;
1282 break;
1283 case Green:
1284 pval = &green;
1285 break;
1286 case Blue:
1287 pval = &blue;
1288 break;
1289 default:
1290 pval = NULL;
1291 break;
1293 if (pval)
1295 incdec_value(pval, &ctxs[(current != Hue? 1: 0)],
1296 (button&ROCKPAINT_RIGHT), (button&BUTTON_REPEAT));
1298 break;
1300 case ROCKPAINT_DRAW:
1301 return color;
1303 if( has_changed )
1305 switch( current )
1307 case Hue:
1308 case Saturation:
1309 case Value:
1310 hsv2rgb( hue, saturation, value, &red, &green, &blue );
1311 break;
1313 case Red:
1314 case Green:
1315 case Blue:
1316 rgb2hsv( red, green, blue, &hue, &saturation, &value );
1317 break;
1320 #undef HEIGHT
1321 #undef WIDTH
1325 /***********************************************************************
1326 * Misc routines
1327 ***********************************************************************/
1328 static void init_buffer(void)
1330 int i;
1331 fb_data color = rp_colors[ bgdrawcolor ];
1332 for( i = 0; i < ROWS*COLS; i++ )
1334 save_buffer[i] = color;
1338 static void draw_pixel(int x,int y)
1340 if( !preview )
1342 if( x < 0 || x >= COLS || y < 0 || y >= ROWS ) return;
1343 if( isbg )
1345 save_buffer[ x+y*COLS ] = rp_colors[bgdrawcolor];
1347 else
1349 save_buffer[ x+y*COLS ] = rp_colors[drawcolor];
1352 rb->lcd_drawpixel(x,y);
1355 static void color_picker( int x, int y )
1357 if( preview )
1359 rb->lcd_set_foreground( save_buffer[ x+y*COLS ] );
1360 #define PSIZE 12
1361 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1362 if( x >= COLS - PSIZE ) x -= PSIZE + 2;
1363 if( y >= ROWS - PSIZE ) y -= PSIZE + 2;
1364 rb->lcd_drawrect( x + 2, y + 2, PSIZE - 2, PSIZE - 2 );
1365 rb->lcd_set_drawmode(DRMODE_SOLID);
1366 rb->lcd_fillrect( x + 3, y + 3, PSIZE - 4, PSIZE - 4 );
1367 #undef PSIZE
1368 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1370 else
1372 rp_colors[ drawcolor ] = save_buffer[ x+y*COLS ];
1376 static void draw_select_rectangle( int x1, int y1, int x2, int y2 )
1377 /* This is a preview mode only function */
1379 int i,a;
1380 if( x1 > x2 )
1382 i = x1;
1383 x1 = x2;
1384 x2 = i;
1386 if( y1 > y2 )
1388 i = y1;
1389 y1 = y2;
1390 y2 = i;
1392 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1393 i = 0;
1394 for( a = x1; a < x2; a++, i++ )
1395 if( i%2 )
1396 rb->lcd_drawpixel( a, y1 );
1397 for( a = y1; a < y2; a++, i++ )
1398 if( i%2 )
1399 rb->lcd_drawpixel( x2, a );
1400 if( y2 != y1 )
1401 for( a = x2; a > x1; a--, i++ )
1402 if( i%2 )
1403 rb->lcd_drawpixel( a, y2 );
1404 if( x2 != x1 )
1405 for( a = y2; a > y1; a--, i++ )
1406 if( i%2 )
1407 rb->lcd_drawpixel( x1, a );
1408 rb->lcd_set_drawmode(DRMODE_SOLID);
1411 static void copy_to_clipboard( void )
1413 /* This needs to be optimised ... but i'm lazy ATM */
1414 rb->memcpy( buffer->clipboard, save_buffer, COLS*ROWS*sizeof( fb_data ) );
1417 /* no preview mode handling atm ... do we need it ? (one if) */
1418 static void draw_invert( int x1, int y1, int x2, int y2 )
1420 int i;
1421 if( x1 > x2 )
1423 i = x1;
1424 x1 = x2;
1425 x2 = i;
1427 if( y1 > y2 )
1429 i = y1;
1430 y1 = y2;
1431 y2 = i;
1434 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1435 rb->lcd_fillrect( x1, y1, x2-x1+1, y2-y1+1 );
1436 rb->lcd_set_drawmode(DRMODE_SOLID);
1438 for( ; y1<=y2; y1++ )
1440 for( i = x1; i<=x2; i++ )
1442 save_buffer[ y1*COLS + i ] = ~save_buffer[ y1*COLS + i ];
1445 /*if( update )*/ rb->lcd_update();
1448 static void draw_hflip( int x1, int y1, int x2, int y2 )
1450 int i;
1451 if( x1 > x2 )
1453 i = x1;
1454 x1 = x2;
1455 x2 = i;
1457 if( y1 > y2 )
1459 i = y1;
1460 y1 = y2;
1461 y2 = i;
1464 copy_to_clipboard();
1466 for( i = 0; i <= y2 - y1; i++ )
1468 rb->memcpy( save_buffer+(y1+i)*COLS+x1,
1469 buffer->clipboard+(y2-i)*COLS+x1,
1470 (x2-x1+1)*sizeof( fb_data ) );
1472 restore_screen();
1473 rb->lcd_update();
1476 static void draw_vflip( int x1, int y1, int x2, int y2 )
1478 int i;
1479 if( x1 > x2 )
1481 i = x1;
1482 x1 = x2;
1483 x2 = i;
1485 if( y1 > y2 )
1487 i = y1;
1488 y1 = y2;
1489 y2 = i;
1492 copy_to_clipboard();
1494 for( ; y1 <= y2; y1++ )
1496 for( i = 0; i <= x2 - x1; i++ )
1498 save_buffer[y1*COLS+x1+i] = buffer->clipboard[y1*COLS+x2-i];
1501 restore_screen();
1502 rb->lcd_update();
1505 /* direction: -1 = left, 1 = right */
1506 static void draw_rot_90_deg( int x1, int y1, int x2, int y2, int direction )
1508 int i, j;
1509 if( x1 > x2 )
1511 i = x1;
1512 x1 = x2;
1513 x2 = i;
1515 if( y1 > y2 )
1517 i = y1;
1518 y1 = y2;
1519 y2 = i;
1522 copy_to_clipboard();
1524 fb_data color = rp_colors[ bgdrawcolor ];
1525 const int width = x2 - x1, height = y2 - y1;
1526 const int sub_half = width/2-height/2, add_half = (width+height)/2;
1527 if( width > height )
1529 for( i = 0; i <= height; i++ )
1531 for( j = 0; j < sub_half; j++ )
1532 save_buffer[(y1+i)*COLS+x1+j] = color;
1533 for( j = add_half+1; j <= width; j++ )
1534 save_buffer[(y1+i)*COLS+x1+j] = color;
1537 else if( width < height )
1539 for( j = 0; j <= width; j++ )
1541 for( i = 0; i < -sub_half; i++ )
1542 save_buffer[(y1+i)*COLS+x1+j] = color;
1543 for( i = add_half+1; i <= height; i++ )
1544 save_buffer[(y1+i)*COLS+x1+j] = color;
1547 int x3 = x1 + sub_half, y3 = y1 - sub_half;
1548 int is = x3<0?-x3:0, ie = COLS-x3-1, js = y3<0?-y3:0, je = ROWS-y3-1;
1549 if( ie > height ) ie = height;
1550 if( je > width ) je = width;
1551 for( i = is; i <= ie; i++ )
1553 for( j = js; j <= je; j++ )
1555 int x, y;
1556 if(direction > 0)
1558 x = x1+j;
1559 y = y1+height-i;
1561 else
1563 x = x1+width-j;
1564 y = y1+i;
1566 save_buffer[(y3+j)*COLS+x3+i] = buffer->clipboard[y*COLS+x];
1569 restore_screen();
1570 rb->lcd_update();
1573 static void draw_paste_rectangle( int src_x1, int src_y1, int src_x2,
1574 int src_y2, int x1, int y1, int cut )
1576 int i, width, height;
1577 if( cut )
1579 i = drawcolor;
1580 drawcolor = bgdrawcolor;
1581 draw_rect_full( src_x1, src_y1, src_x2, src_y2 );
1582 drawcolor = i;
1584 if( src_x1 > src_x2 )
1586 i = src_x1;
1587 src_x1 = src_x2;
1588 src_x2 = i;
1590 if( src_y1 > src_y2 )
1592 i = src_y1;
1593 src_y1 = src_y2;
1594 src_y2 = i;
1596 width = src_x2 - src_x1 + 1;
1597 height = src_y2 - src_y1 + 1;
1598 /* clipping */
1599 if( x1 + width > COLS )
1600 width = COLS - x1;
1601 if( y1 + height > ROWS )
1602 height = ROWS - y1;
1604 rb->lcd_bitmap_part( buffer->clipboard, src_x1, src_y1, COLS,
1605 x1, y1, width, height );
1606 if( !preview )
1608 for( i = 0; i < height; i++ )
1610 rb->memcpy( save_buffer+(y1+i)*COLS+x1,
1611 buffer->clipboard+(src_y1+i)*COLS+src_x1,
1612 width*sizeof( fb_data ) );
1617 static void show_grid( bool update )
1619 int i;
1620 if( gridsize > 0 )
1622 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1623 for( i = gridsize; i < img_width; i+= gridsize )
1625 rb->lcd_vline( i, 0, img_height-1 );
1627 for( i = gridsize; i < img_height; i+= gridsize )
1629 rb->lcd_hline( 0, img_width-1, i );
1631 rb->lcd_set_drawmode(DRMODE_SOLID);
1632 if( update ) rb->lcd_update();
1636 static void draw_text( int x, int y )
1638 int selected = 0;
1639 buffer->text.text[0] = '\0';
1640 buffer->text.font[0] = '\0';
1641 while( 1 )
1643 switch( rb->do_menu( &text_menu, &selected, NULL, NULL ) )
1645 case TEXT_MENU_TEXT:
1646 rb->lcd_set_foreground(COLOR_BLACK);
1647 rb->kbd_input( buffer->text.text, MAX_TEXT );
1648 break;
1650 case TEXT_MENU_FONT:
1651 if( browse_fonts( buffer->text.font, MAX_PATH ) )
1653 rb->font_load(NULL, buffer->text.font );
1655 break;
1657 case TEXT_MENU_PREVIEW:
1658 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1659 while( 1 )
1661 int button;
1662 restore_screen();
1663 rb->lcd_putsxy( x, y, buffer->text.text );
1664 rb->lcd_update();
1665 switch( button = rb->button_get( true ) )
1667 case ROCKPAINT_LEFT:
1668 case ROCKPAINT_LEFT | BUTTON_REPEAT:
1669 case ROCKPAINT_RIGHT:
1670 case ROCKPAINT_RIGHT | BUTTON_REPEAT:
1671 incdec_value(&x, &incdec_x,
1672 (button&ROCKPAINT_RIGHT), (button&BUTTON_REPEAT));
1673 break;
1675 case ROCKPAINT_UP:
1676 case ROCKPAINT_UP | BUTTON_REPEAT:
1677 case ROCKPAINT_DOWN:
1678 case ROCKPAINT_DOWN | BUTTON_REPEAT:
1679 incdec_value(&y, &incdec_y,
1680 (button&ROCKPAINT_DOWN), (button&BUTTON_REPEAT));
1681 break;
1683 case ROCKPAINT_DRAW:
1684 break;
1685 default:
1686 if(rb->default_event_handler(button)
1687 == SYS_USB_CONNECTED)
1688 button = ROCKPAINT_DRAW;
1689 break;
1691 if( button == ROCKPAINT_DRAW ) break;
1693 break;
1695 case TEXT_MENU_APPLY:
1696 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1697 buffer_putsxyofs( save_buffer, COLS, ROWS, x, y, 0,
1698 buffer->text.text );
1699 case TEXT_MENU_CANCEL:
1700 default:
1701 restore_screen();
1702 if( buffer->text.font[0] )
1704 rb->snprintf( buffer->text.font, MAX_PATH,
1705 FONT_DIR "/%s.fnt",
1706 rb->global_settings->font_file );
1707 rb->font_load(NULL, buffer->text.font );
1709 return;
1714 static void draw_brush( int x, int y )
1716 int i,j;
1717 for( i=-bsize/2+(bsize+1)%2; i<=bsize/2; i++ )
1719 for( j=-bsize/2+(bsize+1)%2; j<=bsize/2; j++ )
1721 draw_pixel( x+i, y+j );
1726 /* This is an implementation of Bresenham's line algorithm.
1727 * See http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm.
1729 static void draw_line( int x1, int y1, int x2, int y2 )
1731 int x = x1;
1732 int y = y1;
1733 int deltax = x2 - x1;
1734 int deltay = y2 - y1;
1735 int i;
1737 int xerr = abs(deltax);
1738 int yerr = abs(deltay);
1739 int xstep = deltax > 0 ? 1 : -1;
1740 int ystep = deltay > 0 ? 1 : -1;
1741 int err;
1743 if (yerr > xerr)
1745 /* more vertical */
1746 err = yerr;
1747 xerr <<= 1;
1748 yerr <<= 1;
1750 /* to leave off the last pixel of the line, leave off the "+ 1" */
1751 for (i = err + 1; i; --i)
1753 draw_pixel(x, y);
1754 y += ystep;
1755 err -= xerr;
1756 if (err < 0) {
1757 x += xstep;
1758 err += yerr;
1762 else
1764 /* more horizontal */
1765 err = xerr;
1766 xerr <<= 1;
1767 yerr <<= 1;
1769 for (i = err + 1; i; --i)
1771 draw_pixel(x, y);
1772 x += xstep;
1773 err -= yerr;
1774 if (err < 0) {
1775 y += ystep;
1776 err += xerr;
1782 static void draw_curve( int x1, int y1, int x2, int y2,
1783 int xa, int ya, int xb, int yb )
1785 int i = 0;
1786 short xl1, yl1;
1787 short xl2, yl2;
1788 short xl3, yl3;
1789 short xl4, yl4;
1790 short xr1, yr1;
1791 short xr2, yr2;
1792 short xr3, yr3;
1793 short xr4, yr4;
1794 short depth;
1795 short xh, yh;
1797 if( x1 == x2 && y1 == y2 )
1799 draw_pixel( x1, y1 );
1800 return;
1803 // if( preview )
1805 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1806 if( xa == -1 || ya == -1 )
1808 rb->lcd_drawline( x1, y1, xb, yb );
1809 rb->lcd_drawline( x2, y2, xb, yb );
1811 else
1813 rb->lcd_drawline( x1, y1, xa, ya );
1814 rb->lcd_drawline( x2, y2, xb, yb );
1816 rb->lcd_set_drawmode(DRMODE_SOLID);
1819 if( xa == -1 || ya == -1 )
1820 /* We only have 3 of the points
1821 * This will currently only be used in preview mode */
1823 #define PUSH( a1, b1, a2, b2, a3, b3, d ) \
1824 buffer->bezier[i].x1 = a1; \
1825 buffer->bezier[i].y1 = b1; \
1826 buffer->bezier[i].x2 = a2; \
1827 buffer->bezier[i].y2 = b2; \
1828 buffer->bezier[i].x3 = a3; \
1829 buffer->bezier[i].y3 = b3; \
1830 buffer->bezier[i].depth = d; \
1831 i++;
1832 #define POP( a1, b1, a2, b2, a3, b3, d ) \
1833 i--; \
1834 a1 = buffer->bezier[i].x1; \
1835 b1 = buffer->bezier[i].y1; \
1836 a2 = buffer->bezier[i].x2; \
1837 b2 = buffer->bezier[i].y2; \
1838 a3 = buffer->bezier[i].x3; \
1839 b3 = buffer->bezier[i].y3; \
1840 d = buffer->bezier[i].depth;
1842 PUSH( x1<<4, y1<<4, xb<<4, yb<<4, x2<<4, y2<<4, 0 );
1843 while( i )
1845 /* de Casteljau's algorithm (see wikipedia) */
1846 POP( xl1, yl1, xb, yb, xr3, yr3, depth );
1847 if( depth < 10 ) /* check that the stack's 'i' doesn't overflow */
1849 xl2 = ( xl1 + xb )>>1;
1850 yl2 = ( yl1 + yb )>>1;
1851 xr2 = ( xb + xr3 )>>1;
1852 yr2 = ( yb + yr3 )>>1;
1853 xr1 = ( xl2 + xr2 )>>1;
1854 yr1 = ( yl2 + yr2 )>>1;
1855 xl3 = xr1;
1856 yl3 = yr1;
1857 PUSH( xl1, yl1, xl2, yl2, xl3, yl3, depth+1 );
1858 PUSH( xr1, yr1, xr2, yr2, xr3, yr3, depth+1 );
1860 else
1862 draw_line( ((xl1>>3)+1)>>1, ((yl1>>3)+1)>>1,
1863 ((xr3>>3)+1)>>1, ((yr3>>3)+1)>>1 );
1866 #undef PUSH
1867 #undef POP
1869 else /* We have the 4 points */
1871 #define PUSH( a1, b1, a2, b2, a3, b3, a4, b4, d ) \
1872 buffer->bezier[i].x1 = a1; \
1873 buffer->bezier[i].y1 = b1; \
1874 buffer->bezier[i].x2 = a2; \
1875 buffer->bezier[i].y2 = b2; \
1876 buffer->bezier[i].x3 = a3; \
1877 buffer->bezier[i].y3 = b3; \
1878 buffer->bezier[i].x4 = a4; \
1879 buffer->bezier[i].y4 = b4; \
1880 buffer->bezier[i].depth = d; \
1881 i++;
1882 #define POP( a1, b1, a2, b2, a3, b3, a4, b4, d ) \
1883 i--; \
1884 a1 = buffer->bezier[i].x1; \
1885 b1 = buffer->bezier[i].y1; \
1886 a2 = buffer->bezier[i].x2; \
1887 b2 = buffer->bezier[i].y2; \
1888 a3 = buffer->bezier[i].x3; \
1889 b3 = buffer->bezier[i].y3; \
1890 a4 = buffer->bezier[i].x4; \
1891 b4 = buffer->bezier[i].y4; \
1892 d = buffer->bezier[i].depth;
1894 PUSH( x1<<4, y1<<4, xa<<4, ya<<4, xb<<4, yb<<4, x2<<4, y2<<4, 0 );
1895 while( i )
1897 /* de Casteljau's algorithm (see wikipedia) */
1898 POP( xl1, yl1, xa, ya, xb, yb, xr4, yr4, depth );
1899 if( depth < 10 ) /* check that the stack's 'i' doesn't overflow */
1901 xl2 = ( xl1 + xa )>>1;
1902 yl2 = ( yl1 + ya )>>1;
1903 xh = ( xa + xb )>>1;
1904 yh = ( ya + yb )>>1;
1905 xr3 = ( xb + xr4 )>>1;
1906 yr3 = ( yb + yr4 )>>1;
1907 xl3 = ( xl2 + xh )>>1;
1908 yl3 = ( yl2 + yh )>>1;
1909 xr2 = ( xr3 + xh )>>1;
1910 yr2 = ( yr3 + yh )>>1;
1911 xl4 = ( xl3 + xr2 )>>1;
1912 yl4 = ( yl3 + yr2 )>>1;
1913 xr1 = xl4;
1914 yr1 = yl4;
1915 PUSH( xl1, yl1, xl2, yl2, xl3, yl3, xl4, yl4, depth+1 );
1916 PUSH( xr1, yr1, xr2, yr2, xr3, yr3, xr4, yr4, depth+1 );
1918 else
1920 draw_line( ((xl1>>3)+1)>>1, ((yl1>>3)+1)>>1,
1921 ((xr4>>3)+1)>>1, ((yr4>>3)+1)>>1 );
1924 #undef PUSH
1925 #undef POP
1929 static void draw_rect( int x1, int y1, int x2, int y2 )
1931 draw_line( x1, y1, x1, y2 );
1932 draw_line( x1, y1, x2, y1 );
1933 draw_line( x1, y2, x2, y2 );
1934 draw_line( x2, y1, x2, y2 );
1937 static void togglebg( void )
1939 if( isbg )
1941 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
1943 else
1945 rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
1947 isbg = !isbg;
1950 static void draw_rect_full( int x1, int y1, int x2, int y2 )
1952 /* GRUIK */
1953 int x;
1954 togglebg();
1955 if( x1 > x2 )
1957 x = x1;
1958 x1 = x2;
1959 x2 = x;
1961 x = x1;
1962 do {
1963 draw_line( x, y1, x, y2 );
1964 } while( ++x <= x2 );
1965 togglebg();
1966 draw_rect( x1, y1, x2, y2 );
1969 static void draw_oval( int x1, int y1, int x2, int y2, bool full )
1971 /* TODO: simplify :) */
1972 int cx = (x1+x2)>>1;
1973 int cy = (y1+y2)>>1;
1975 int rx = (x1-x2)>>1;
1976 int ry = (y1-y2)>>1;
1977 if( rx < 0 ) rx *= -1;
1978 if( ry < 0 ) ry *= -1;
1980 if( rx == 0 || ry == 0 )
1982 draw_line( x1, y1, x2, y2 );
1983 return;
1986 int x,y;
1987 int dst, old_dst;
1989 for( x = 0; x < rx; x++ )
1991 y = 0;
1992 dst = -0xfff;
1993 do {
1994 old_dst = dst;
1995 dst = ry * ry * x * x + rx * rx * y * y - rx * rx * ry * ry;
1996 y++;
1997 } while( dst < 0 );
1998 if( -old_dst < dst ) y--;
1999 if( full )
2001 draw_line( cx+x, cy, cx+x, cy+y );
2002 draw_line( cx+x, cy, cx+x, cy-y );
2003 draw_line( cx-x, cy, cx-x, cy+y );
2004 draw_line( cx-x, cy, cx-x, cy-y );
2006 else
2008 draw_pixel( cx+x, cy+y );
2009 draw_pixel( cx+x, cy-y );
2010 draw_pixel( cx-x, cy+y );
2011 draw_pixel( cx-x, cy-y );
2014 for( y = 0; y < ry; y++ )
2016 x = 0;
2017 dst = -0xfff;
2018 do {
2019 old_dst = dst;
2020 dst = ry * ry * x * x + rx * rx * y * y - rx * rx * ry * ry;
2021 x++;
2022 } while( dst < 0 );
2023 if( -old_dst < dst ) x--;
2024 if( full )
2026 draw_line( cx+x, cy, cx+x, cy+y );
2027 draw_line( cx+x, cy, cx+x, cy-y );
2028 draw_line( cx-x, cy, cx-x, cy+y );
2029 draw_line( cx-x, cy, cx-x, cy-y );
2031 else
2033 draw_pixel( cx+x, cy+y );
2034 draw_pixel( cx+x, cy-y );
2035 draw_pixel( cx-x, cy+y );
2036 draw_pixel( cx-x, cy-y );
2041 static void draw_oval_empty( int x1, int y1, int x2, int y2 )
2043 draw_oval( x1, y1, x2, y2, false );
2046 static void draw_oval_full( int x1, int y1, int x2, int y2 )
2048 togglebg();
2049 draw_oval( x1, y1, x2, y2, true );
2050 togglebg();
2051 draw_oval( x1, y1, x2, y2, false );
2054 static void draw_fill( int x0, int y0 )
2056 #define PUSH( a, b ) \
2057 draw_pixel( (int)a, (int)b ); \
2058 buffer->coord[i].x = a; \
2059 buffer->coord[i].y = b; \
2060 i++;
2061 #define POP( a, b ) \
2062 i--; \
2063 a = buffer->coord[i].x; \
2064 b = buffer->coord[i].y;
2066 unsigned int i=0;
2067 short x = x0;
2068 short y = y0;
2069 unsigned int prev_color = save_buffer[ x0+y0*COLS ];
2071 if( preview )
2072 return;
2073 if( prev_color == rp_colors[ drawcolor ] ) return;
2075 PUSH( x, y );
2077 while( i != 0 )
2079 POP( x, y );
2080 if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
2082 PUSH( x-1, y );
2084 if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
2086 PUSH( x+1, y );
2088 if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
2090 PUSH( x, y-1 );
2092 if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
2094 PUSH( x, y+1 );
2097 #undef PUSH
2098 #undef POP
2102 /* For preview purposes only */
2103 /* use same algorithm as draw_line() to draw line. */
2104 static void line_gradient( int x1, int y1, int x2, int y2 )
2106 int h1, s1, v1, h2, s2, v2, r, g, b;
2107 int xerr = x2 - x1, yerr = y2 - y1, xstep, ystep;
2108 int i, delta, err;
2109 fb_data color1, color2;
2111 if( xerr == 0 && yerr == 0 )
2113 draw_pixel( x1, y1 );
2114 return;
2117 xstep = xerr > 0 ? 1 : -1;
2118 ystep = yerr > 0 ? 1 : -1;
2119 xerr = abs(xerr) << 1;
2120 yerr = abs(yerr) << 1;
2122 color1 = rp_colors[ bgdrawcolor ];
2123 color2 = rp_colors[ drawcolor ];
2125 r = RGB_UNPACK_RED( color1 );
2126 g = RGB_UNPACK_GREEN( color1 );
2127 b = RGB_UNPACK_BLUE( color1 );
2128 rgb2hsv( r, g, b, &h1, &s1, &v1 );
2130 r = RGB_UNPACK_RED( color2 );
2131 g = RGB_UNPACK_GREEN( color2 );
2132 b = RGB_UNPACK_BLUE( color2 );
2133 rgb2hsv( r, g, b, &h2, &s2, &v2 );
2135 if( xerr > yerr )
2137 err = xerr>>1;
2138 delta = err+1;
2139 /* to leave off the last pixel of the line, leave off the "+ 1" */
2140 for (i = delta; i; --i)
2142 hsv2rgb( h2+((h1-h2)*i)/delta,
2143 s2+((s1-s2)*i)/delta,
2144 v2+((v1-v2)*i)/delta,
2145 &r, &g, &b );
2146 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2147 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2148 draw_pixel(x1, y1);
2149 x1 += xstep;
2150 err -= yerr;
2151 if (err < 0) {
2152 y1 += ystep;
2153 err += xerr;
2157 else /* yerr >= xerr */
2159 err = yerr>>1;
2160 delta = err+1;
2161 for (i = delta; i; --i)
2163 hsv2rgb( h2+((h1-h2)*i)/delta,
2164 s2+((s1-s2)*i)/delta,
2165 v2+((v1-v2)*i)/delta,
2166 &r, &g, &b );
2167 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2168 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2169 draw_pixel(x1, y1);
2170 y1 += ystep;
2171 err -= xerr;
2172 if (err < 0) {
2173 x1 += xstep;
2174 err += yerr;
2178 rp_colors[ drawcolor ] = color2;
2181 /* macros used by linear_gradient() and radial_gradient(). */
2182 #define PUSH( _x, _y ) \
2183 save_buffer[(_x)+(_y)*COLS] = mark_color; \
2184 buffer->coord[i].x = (short)(_x); \
2185 buffer->coord[i].y = (short)(_y); \
2186 i++;
2187 #define POP( _x, _y ) \
2188 i--; \
2189 _x = (int)buffer->coord[i].x; \
2190 _y = (int)buffer->coord[i].y;
2191 #define PUSH2( _x, _y ) \
2192 j--; \
2193 buffer->coord[j].x = (short)(_x); \
2194 buffer->coord[j].y = (short)(_y);
2195 #define POP2( _x, _y ) \
2196 _x = (int)buffer->coord[j].x; \
2197 _y = (int)buffer->coord[j].y; \
2198 j++;
2200 static void linear_gradient( int x1, int y1, int x2, int y2 )
2202 int r1 = RGB_UNPACK_RED( rp_colors[ bgdrawcolor ] );
2203 int g1 = RGB_UNPACK_GREEN( rp_colors[ bgdrawcolor ] );
2204 int b1 = RGB_UNPACK_BLUE( rp_colors[ bgdrawcolor ] );
2205 int r2 = RGB_UNPACK_RED( rp_colors[ drawcolor ] );
2206 int g2 = RGB_UNPACK_GREEN( rp_colors[ drawcolor ] );
2207 int b2 = RGB_UNPACK_BLUE( rp_colors[ drawcolor ] );
2208 fb_data color = rp_colors[ drawcolor ];
2210 int h1, s1, v1, h2, s2, v2, r, g, b;
2212 /* radius^2 */
2213 int radius2 = ( x1 - x2 ) * ( x1 - x2 ) + ( y1 - y2 ) * ( y1 - y2 );
2214 int dist2, i=0, j=COLS*ROWS;
2216 /* We only propagate the gradient to neighboring pixels with the same
2217 * color as ( x1, y1 ) */
2218 fb_data prev_color = save_buffer[ x1+y1*COLS ];
2219 /* to mark pixel that the pixel is already in LIFO. */
2220 fb_data mark_color = ~prev_color;
2222 int x = x1;
2223 int y = y1;
2225 if( radius2 == 0 ) return;
2226 if( preview )
2228 line_gradient( x1, y1, x2, y2 );
2229 return;
2231 if( rp_colors[ drawcolor ] == rp_colors[ bgdrawcolor ] )
2233 draw_fill( x1, y1 );
2234 return;
2237 rgb2hsv( r1, g1, b1, &h1, &s1, &v1 );
2238 rgb2hsv( r2, g2, b2, &h2, &s2, &v2 );
2240 PUSH( x, y );
2242 while( i > 0 )
2244 POP( x, y );
2246 dist2 = ( x2 - x1 ) * ( x - x1 ) + ( y2 - y1 ) * ( y - y1 );
2247 if( dist2 <= 0 )
2249 rp_colors[ drawcolor ] = rp_colors[ bgdrawcolor ];
2251 else if( dist2 < radius2 )
2253 hsv2rgb( h1+((h2-h1)*dist2)/radius2,
2254 s1+((s2-s1)*dist2)/radius2,
2255 v1+((v2-v1)*dist2)/radius2,
2256 &r, &g, &b );
2257 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2259 else
2261 rp_colors[ drawcolor ] = color;
2263 if( rp_colors[ drawcolor ] == prev_color )
2265 /* "mark" that pixel was checked. correct color later. */
2266 PUSH2( x, y );
2267 rp_colors[ drawcolor ] = mark_color;
2269 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2270 draw_pixel( x, y );
2272 if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
2274 PUSH( x-1, y );
2276 if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
2278 PUSH( x+1, y );
2280 if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
2282 PUSH( x, y-1 );
2284 if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
2286 PUSH( x, y+1 );
2289 while (j < COLS*ROWS)
2291 /* correct color. */
2292 POP2( x, y );
2293 rp_colors[ drawcolor ] = prev_color;
2294 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2295 draw_pixel( x, y );
2297 rp_colors[ drawcolor ] = color;
2300 static void radial_gradient( int x1, int y1, int x2, int y2 )
2302 int r1 = RGB_UNPACK_RED( rp_colors[ bgdrawcolor ] );
2303 int g1 = RGB_UNPACK_GREEN( rp_colors[ bgdrawcolor ] );
2304 int b1 = RGB_UNPACK_BLUE( rp_colors[ bgdrawcolor ] );
2305 int r2 = RGB_UNPACK_RED( rp_colors[ drawcolor ] );
2306 int g2 = RGB_UNPACK_GREEN( rp_colors[ drawcolor ] );
2307 int b2 = RGB_UNPACK_BLUE( rp_colors[ drawcolor ] );
2308 fb_data color = rp_colors[ drawcolor ];
2310 int h1, s1, v1, h2, s2, v2, r, g, b;
2312 /* radius^2 */
2313 int radius2 = ( x1 - x2 ) * ( x1 - x2 ) + ( y1 - y2 ) * ( y1 - y2 );
2314 int dist2, i=0, j=COLS*ROWS;
2316 /* We only propagate the gradient to neighboring pixels with the same
2317 * color as ( x1, y1 ) */
2318 fb_data prev_color = save_buffer[ x1+y1*COLS ];
2319 /* to mark pixel that the pixel is already in LIFO. */
2320 fb_data mark_color = ~prev_color;
2322 int x = x1;
2323 int y = y1;
2325 if( radius2 == 0 ) return;
2326 if( preview )
2328 line_gradient( x1, y1, x2, y2 );
2329 return;
2331 if( rp_colors[ drawcolor ] == rp_colors[ bgdrawcolor ] )
2333 draw_fill( x1, y1 );
2334 return;
2337 rgb2hsv( r1, g1, b1, &h1, &s1, &v1 );
2338 rgb2hsv( r2, g2, b2, &h2, &s2, &v2 );
2340 PUSH( x, y );
2342 while( i > 0 )
2344 POP( x, y );
2346 dist2 = ( x - x1 ) * ( x - x1 ) + ( y - y1 ) * ( y - y1 );
2347 if( dist2 < radius2 )
2349 hsv2rgb( h1+((h2-h1)*dist2)/radius2,
2350 s1+((s2-s1)*dist2)/radius2,
2351 v1+((v2-v1)*dist2)/radius2,
2352 &r, &g, &b );
2353 rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
2355 else
2357 rp_colors[ drawcolor ] = color;
2359 if( rp_colors[ drawcolor ] == prev_color )
2361 /* "mark" that pixel was checked. correct color later. */
2362 PUSH2( x, y );
2363 rp_colors[ drawcolor ] = mark_color;
2365 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2366 draw_pixel( x, y );
2368 if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
2370 PUSH( x-1, y );
2372 if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
2374 PUSH( x+1, y );
2376 if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
2378 PUSH( x, y-1 );
2380 if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
2382 PUSH( x, y+1 );
2385 while (j < COLS*ROWS)
2387 /* correct color. */
2388 POP2( x, y );
2389 rp_colors[ drawcolor ] = prev_color;
2390 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2391 draw_pixel( x, y );
2393 rp_colors[ drawcolor ] = color;
2396 #undef PUSH
2397 #undef POP
2398 #undef PUSH2
2399 #undef POP2
2401 static void draw_toolbars(bool update)
2403 int i;
2404 #define TOP (LCD_HEIGHT-TB_HEIGHT)
2405 rb->lcd_set_background( COLOR_LIGHTGRAY );
2406 rb->lcd_set_foreground( COLOR_LIGHTGRAY );
2407 rb->lcd_fillrect( 0, TOP, LCD_WIDTH, TB_HEIGHT );
2408 rb->lcd_set_foreground( COLOR_BLACK );
2409 rb->lcd_drawrect( 0, TOP, LCD_WIDTH, TB_HEIGHT );
2411 rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
2412 rb->lcd_fillrect( TB_SC_BG_LEFT, TOP+TB_SC_BG_TOP,
2413 TB_SC_SIZE, TB_SC_SIZE );
2414 rb->lcd_set_foreground(ROCKPAINT_PALETTE);
2415 rb->lcd_drawrect( TB_SC_BG_LEFT, TOP+TB_SC_BG_TOP,
2416 TB_SC_SIZE, TB_SC_SIZE );
2417 rb->lcd_set_foreground( rp_colors[ drawcolor ] );
2418 rb->lcd_fillrect( TB_SC_FG_LEFT, TOP+TB_SC_FG_TOP,
2419 TB_SC_SIZE, TB_SC_SIZE );
2420 rb->lcd_set_foreground(ROCKPAINT_PALETTE);
2421 rb->lcd_drawrect( TB_SC_FG_LEFT, TOP+TB_SC_FG_TOP,
2422 TB_SC_SIZE, TB_SC_SIZE );
2424 for( i=0; i<18; i++ )
2426 rb->lcd_set_foreground( rp_colors[i] );
2427 rb->lcd_fillrect(
2428 TB_PL_LEFT+(i%9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING ),
2429 TOP+TB_PL_TOP+(i/9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING),
2430 TB_PL_COLOR_SIZE, TB_PL_COLOR_SIZE );
2431 rb->lcd_set_foreground( ROCKPAINT_PALETTE );
2432 rb->lcd_drawrect(
2433 TB_PL_LEFT+(i%9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING ),
2434 TOP+TB_PL_TOP+(i/9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING),
2435 TB_PL_COLOR_SIZE, TB_PL_COLOR_SIZE );
2438 #define SEPARATOR( x, y ) \
2439 rb->lcd_set_foreground( COLOR_WHITE ); \
2440 rb->lcd_vline( x, TOP+y, TOP+y+TB_PL_HEIGHT-1 ); \
2441 rb->lcd_set_foreground( COLOR_DARKGRAY ); \
2442 rb->lcd_vline( x+1, TOP+y, TOP+y+TB_PL_HEIGHT-1 );
2443 SEPARATOR( TB_PL_LEFT + TB_PL_WIDTH - 1 + TB_SP_MARGIN, TB_PL_TOP );
2445 rb->lcd_bitmap_transparent( rockpaint, TB_TL_LEFT, TOP+TB_TL_TOP,
2446 TB_TL_WIDTH, TB_TL_HEIGHT );
2447 rb->lcd_set_foreground(ROCKPAINT_PALETTE);
2448 rb->lcd_drawrect( TB_TL_LEFT+(TB_TL_SIZE+TB_TL_SPACING)*(tool/2),
2449 TOP+TB_TL_TOP+(TB_TL_SIZE+TB_TL_SPACING)*(tool%2),
2450 TB_TL_SIZE, TB_TL_SIZE );
2452 SEPARATOR( TB_TL_LEFT + TB_TL_WIDTH - 1 + TB_SP_MARGIN, TB_TL_TOP );
2454 rb->lcd_setfont( FONT_SYSFIXED );
2455 rb->lcd_putsxy( TB_MENU_LEFT, TOP+TB_MENU_TOP, "Menu" );
2456 rb->lcd_setfont( FONT_UI );
2457 #undef TOP
2459 if( update ) rb->lcd_update();
2462 static void toolbar( void )
2464 int button, i, j;
2465 restore_screen();
2466 draw_toolbars( false );
2467 y = LCD_HEIGHT-TB_HEIGHT/2;
2468 inv_cursor( true );
2469 while( 1 )
2471 switch( button = rb->button_get( true ) )
2473 case ROCKPAINT_DRAW:
2474 #define TOP ( LCD_HEIGHT - TB_HEIGHT )
2475 if( y >= TOP + TB_SC_FG_TOP
2476 && y < TOP + TB_SC_FG_TOP + TB_SC_SIZE
2477 && x >= TB_SC_FG_LEFT
2478 && x < TB_SC_FG_LEFT + TB_SC_SIZE )
2480 /* click on the foreground color */
2481 rp_colors[drawcolor] = color_chooser( rp_colors[drawcolor] );
2483 else if( y >= TOP + TB_SC_BG_TOP
2484 && y < TOP + TB_SC_BG_TOP + TB_SC_SIZE
2485 && x >= TB_SC_BG_LEFT
2486 && x < TB_SC_BG_LEFT + TB_SC_SIZE )
2488 /* click on the background color */
2489 i = drawcolor;
2490 drawcolor = bgdrawcolor;
2491 bgdrawcolor = i;
2493 else if( y >= TOP + TB_PL_TOP
2494 && y < TOP + TB_PL_TOP + TB_PL_HEIGHT
2495 && x >= TB_PL_LEFT
2496 && x < TB_PL_LEFT + TB_PL_WIDTH )
2498 /* click on the palette */
2499 i = (x - TB_PL_LEFT)%(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2500 j = (y - (TOP+TB_PL_TOP) )%(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2501 if( i >= TB_PL_COLOR_SIZE || j >= TB_PL_COLOR_SIZE )
2502 break;
2503 i = ( x - TB_PL_LEFT )/(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2504 j = ( y - (TOP+TB_PL_TOP) )/(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
2505 drawcolor = j*(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING)+i;
2507 else if( y >= TOP+TB_TL_TOP
2508 && y < TOP + TB_TL_TOP + TB_TL_HEIGHT
2509 && x >= TB_TL_LEFT
2510 && x <= TB_TL_LEFT + TB_TL_WIDTH )
2512 /* click on the tools */
2513 i = (x - TB_TL_LEFT ) % (TB_TL_SIZE+TB_TL_SPACING);
2514 j = (y - (TOP+TB_TL_TOP) ) %(TB_TL_SIZE+TB_TL_SPACING);
2515 if( i >= TB_TL_SIZE || j >= TB_TL_SIZE ) break;
2516 i = ( x - TB_TL_LEFT )/(TB_TL_SIZE+TB_TL_SPACING);
2517 j = ( y - (TOP+TB_TL_TOP) )/(TB_TL_SIZE+TB_TL_SPACING);
2518 tool = i*2+j;
2519 reset_tool();
2520 if( tool == Text )
2522 buffer->text.initialized = false;
2525 else if( x >= TB_MENU_LEFT && y >= TOP+TB_MENU_TOP-2)
2527 /* menu button */
2528 goto_menu();
2530 #undef TOP
2531 restore_screen();
2532 draw_toolbars( false );
2533 inv_cursor( true );
2534 break;
2536 case ROCKPAINT_LEFT:
2537 case ROCKPAINT_LEFT | BUTTON_REPEAT:
2538 case ROCKPAINT_RIGHT:
2539 case ROCKPAINT_RIGHT | BUTTON_REPEAT:
2540 inv_cursor(false);
2541 incdec_value(&x, &incdec_x,
2542 (button&ROCKPAINT_RIGHT), (button&BUTTON_REPEAT));
2543 inv_cursor(true);
2544 break;
2546 case ROCKPAINT_UP:
2547 case ROCKPAINT_UP | BUTTON_REPEAT:
2548 case ROCKPAINT_DOWN:
2549 case ROCKPAINT_DOWN | BUTTON_REPEAT:
2550 inv_cursor(false);
2551 if (incdec_value(&y, &incdec_y,
2552 (button&ROCKPAINT_DOWN), (button&BUTTON_REPEAT))
2553 || y < LCD_HEIGHT-TB_HEIGHT)
2555 /* went out of region. exit toolbar. */
2556 return;
2558 inv_cursor(true);
2559 break;
2561 case ROCKPAINT_TOOLBAR:
2562 case ROCKPAINT_TOOLBAR2:
2563 return;
2565 if( quit ) return;
2569 static void inv_cursor(bool update)
2571 rb->lcd_set_foreground(COLOR_BLACK);
2572 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
2573 /* cross painting */
2574 rb->lcd_hline(x-4,x+4,y);
2575 rb->lcd_vline(x,y-4,y+4);
2576 rb->lcd_set_foreground(rp_colors[drawcolor]);
2577 rb->lcd_set_drawmode(DRMODE_SOLID);
2579 if( update ) rb->lcd_update();
2582 static void restore_screen(void)
2584 rb->lcd_bitmap( save_buffer, 0, 0, COLS, ROWS );
2585 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
2586 rb->lcd_vline( img_width, 0, ROWS );
2587 rb->lcd_hline( 0, COLS, img_height );
2588 rb->lcd_drawpixel( img_width, img_height );
2589 rb->lcd_set_drawmode(DRMODE_SOLID);
2592 static void clear_drawing(void)
2594 init_buffer();
2595 img_height = ROWS;
2596 img_width = COLS;
2597 rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
2598 rb->lcd_fillrect( 0, 0, COLS, ROWS );
2599 rb->lcd_update();
2602 static void goto_menu(void)
2604 int multi;
2605 int selected = 0;
2607 while( 1 )
2609 switch( rb->do_menu( &main_menu, &selected, NULL, false ) )
2611 case MAIN_MENU_NEW:
2612 clear_drawing();
2613 return;
2615 case MAIN_MENU_LOAD:
2616 if( browse( filename, MAX_PATH, "/" ) )
2618 if( load_bitmap( filename ) <= 0 )
2620 rb->splashf( 1*HZ, "Error while loading %s",
2621 filename );
2622 clear_drawing();
2624 else
2626 rb->splashf( 1*HZ, "Image loaded (%s)", filename );
2627 restore_screen();
2628 inv_cursor(true);
2629 return;
2632 break;
2634 case MAIN_MENU_SAVE:
2635 rb->lcd_set_foreground(COLOR_BLACK);
2636 if (!filename[0])
2637 rb->strcpy(filename,"/");
2638 if( !rb->kbd_input( filename, MAX_PATH ) )
2640 if( !check_extention( filename, ".bmp" ) )
2641 rb->strcat(filename, ".bmp");
2642 save_bitmap( filename );
2643 rb->splashf( 1*HZ, "File saved (%s)", filename );
2645 break;
2647 case MAIN_MENU_SET_WIDTH:
2648 rb->set_int( "Set Width", "px", UNIT_INT, &img_width,
2649 NULL, 1, 1, COLS, NULL );
2650 break;
2651 case MAIN_MENU_SET_HEIGHT:
2652 rb->set_int( "Set Height", "px", UNIT_INT, &img_height,
2653 NULL, 1, 1, ROWS, NULL );
2654 break;
2655 case MAIN_MENU_BRUSH_SIZE:
2656 for(multi = 0; multi<4; multi++)
2657 if(bsize == times_list[multi]) break;
2658 rb->set_option( "Brush Size", &multi, INT, times_options, 4, NULL );
2659 if( multi >= 0 )
2660 bsize = times_list[multi];
2661 break;
2663 case MAIN_MENU_BRUSH_SPEED:
2664 for(multi = 0; multi<3; multi++)
2665 if(bspeed == times_list[multi]) break;
2666 rb->set_option( "Brush Speed", &multi, INT, times_options, 3, NULL );
2667 if( multi >= 0 ) {
2668 bspeed = times_list[multi];
2669 incdec_x.step[0] = bspeed;
2670 incdec_x.step[1] = bspeed * 4;
2671 incdec_y.step[0] = bspeed;
2672 incdec_y.step[1] = bspeed * 4;
2674 break;
2676 case MAIN_MENU_COLOR:
2677 rp_colors[drawcolor] = color_chooser( rp_colors[drawcolor] );
2678 break;
2680 case MAIN_MENU_GRID_SIZE:
2681 for(multi = 0; multi<4; multi++)
2682 if(gridsize == gridsize_list[multi]) break;
2683 rb->set_option( "Grid Size", &multi, INT, gridsize_options, 4, NULL );
2684 if( multi >= 0 )
2685 gridsize = gridsize_list[multi];
2686 break;
2688 case MAIN_MENU_PLAYBACK_CONTROL:
2689 if (!audio_buf)
2690 playback_control( NULL );
2691 else
2692 rb->splash(HZ, "Cannot restart playback");
2693 break;
2695 case MAIN_MENU_EXIT:
2696 restore_screen();
2697 quit=true;
2698 return;
2700 case MAIN_MENU_RESUME:
2701 default:
2702 restore_screen();
2703 return;
2704 }/* end switch */
2705 }/* end while */
2708 static void reset_tool( void )
2710 prev_x = -1;
2711 prev_y = -1;
2712 prev_x2 = -1;
2713 prev_y2 = -1;
2714 prev_x3 = -1;
2715 prev_y3 = -1;
2716 /* reset state */
2717 state = State0;
2718 /* always preview color picker */
2719 preview = (tool == ColorPicker);
2722 /* brush tool */
2723 static void state_func_brush(void)
2725 if( state == State0 )
2727 state = State1;
2729 else
2731 state = State0;
2735 /* fill tool */
2736 static void state_func_fill(void)
2738 draw_fill( x, y );
2739 restore_screen();
2742 /* select rectangle tool */
2743 static void state_func_select(void)
2745 int mode;
2746 if( state == State0 )
2748 prev_x = x;
2749 prev_y = y;
2750 preview = true;
2751 state = State1;
2753 else if( state == State1 )
2755 mode = rb->do_menu( &select_menu, NULL, NULL, false );
2756 switch( mode )
2758 case SELECT_MENU_CUT:
2759 case SELECT_MENU_COPY:
2760 prev_x2 = x;
2761 prev_y2 = y;
2762 if( prev_x < x ) x = prev_x;
2763 if( prev_y < y ) y = prev_y;
2764 prev_x3 = abs(prev_x2 - prev_x);
2765 prev_y3 = abs(prev_y2 - prev_y);
2766 copy_to_clipboard();
2767 state = (mode == SELECT_MENU_CUT? State2: State3);
2768 break;
2770 case SELECT_MENU_INVERT:
2771 draw_invert( prev_x, prev_y, x, y );
2772 reset_tool();
2773 break;
2775 case SELECT_MENU_HFLIP:
2776 draw_hflip( prev_x, prev_y, x, y );
2777 reset_tool();
2778 break;
2780 case SELECT_MENU_VFLIP:
2781 draw_vflip( prev_x, prev_y, x, y );
2782 reset_tool();
2783 break;
2785 case SELECT_MENU_ROTATE90:
2786 draw_rot_90_deg( prev_x, prev_y, x, y, 1 );
2787 reset_tool();
2788 break;
2790 case SELECT_MENU_ROTATE180:
2791 draw_hflip( prev_x, prev_y, x, y );
2792 draw_vflip( prev_x, prev_y, x, y );
2793 reset_tool();
2794 break;
2796 case SELECT_MENU_ROTATE270:
2797 draw_rot_90_deg( prev_x, prev_y, x, y, -1 );
2798 reset_tool();
2799 break;
2801 case SELECT_MENU_CANCEL:
2802 reset_tool();
2803 break;
2805 default:
2806 break;
2808 restore_screen();
2810 else
2812 preview = false;
2813 draw_paste_rectangle( prev_x, prev_y, prev_x2, prev_y2,
2814 x, y, state == State2 );
2815 reset_tool();
2816 restore_screen();
2820 static void preview_select(void)
2822 if( state == State1 )
2824 /* we are defining the selection */
2825 draw_select_rectangle( prev_x, prev_y, x, y );
2827 else
2829 /* we are pasting the selected data */
2830 draw_paste_rectangle( prev_x, prev_y, prev_x2, prev_y2,
2831 x, y, state == State2 );
2832 draw_select_rectangle( x, y, x+prev_x3, y+prev_y3 );
2836 /* color picker tool */
2837 static void state_func_picker(void)
2839 preview = false;
2840 color_picker( x, y );
2841 reset_tool();
2844 static void preview_picker(void)
2846 color_picker( x, y );
2849 /* curve tool */
2850 static void state_func_curve(void)
2852 if( state == State0 )
2854 prev_x = x;
2855 prev_y = y;
2856 preview = true;
2857 state = State1;
2859 else if( state == State1 )
2861 prev_x2 = x;
2862 prev_y2 = y;
2863 state = State2;
2865 else if( state == State2 )
2867 prev_x3 = x;
2868 prev_y3 = y;
2869 state = State3;
2871 else
2873 preview = false;
2874 draw_curve( prev_x, prev_y, prev_x2, prev_y2,
2875 prev_x3, prev_y3, x, y );
2876 reset_tool();
2877 restore_screen();
2881 static void preview_curve(void)
2883 if( state == State1 )
2885 draw_line( prev_x, prev_y, x, y );
2887 else
2889 draw_curve( prev_x, prev_y, prev_x2, prev_y2,
2890 prev_x3, prev_y3, x, y );
2894 /* text tool */
2895 static void state_func_text(void)
2897 draw_text( x, y );
2900 /* tools which take 2 point */
2901 static void preview_2point(void);
2902 static void state_func_2point(void)
2904 if( state == State0 )
2906 prev_x = x;
2907 prev_y = y;
2908 state = State1;
2909 preview = true;
2911 else
2913 preview = false;
2914 preview_2point();
2915 reset_tool();
2916 restore_screen();
2920 static void preview_2point(void)
2922 if( state == State1 )
2924 switch( tool )
2926 case Line:
2927 draw_line( prev_x, prev_y, x, y );
2928 break;
2929 case Rectangle:
2930 draw_rect( prev_x, prev_y, x, y );
2931 break;
2932 case RectangleFull:
2933 draw_rect_full( prev_x, prev_y, x, y );
2934 break;
2935 case Oval:
2936 draw_oval_empty( prev_x, prev_y, x, y );
2937 break;
2938 case OvalFull:
2939 draw_oval_full( prev_x, prev_y, x, y );
2940 break;
2941 case LinearGradient:
2942 linear_gradient( prev_x, prev_y, x, y );
2943 break;
2944 case RadialGradient:
2945 radial_gradient( prev_x, prev_y, x, y );
2946 break;
2947 default:
2948 break;
2950 if( !preview )
2952 reset_tool();
2953 restore_screen();
2958 static const struct tool_func tools[14] = {
2959 [Brush] = { state_func_brush, NULL },
2960 [Fill] = { state_func_fill, NULL },
2961 [SelectRectangle] = { state_func_select, preview_select },
2962 [ColorPicker] = { state_func_picker, preview_picker },
2963 [Line] = { state_func_2point, preview_2point },
2964 [Unused] = { NULL, NULL },
2965 [Curve] = { state_func_curve, preview_curve },
2966 [Text] = { state_func_text, NULL },
2967 [Rectangle] = { state_func_2point, preview_2point },
2968 [RectangleFull] = { state_func_2point, preview_2point },
2969 [Oval] = { state_func_2point, preview_2point },
2970 [OvalFull] = { state_func_2point, preview_2point },
2971 [LinearGradient] = { state_func_2point, preview_2point },
2972 [RadialGradient] = { state_func_2point, preview_2point },
2975 static bool rockpaint_loop( void )
2977 int button = 0, i, j;
2978 bool bigstep;
2980 x = 10;
2981 toolbar();
2982 x = 0; y = 0;
2983 restore_screen();
2984 inv_cursor(true);
2986 while (!quit) {
2987 button = rb->button_get(true);
2988 bigstep = (button & BUTTON_REPEAT) && !(tool == Brush && state == State1);
2990 switch(button)
2992 case ROCKPAINT_QUIT:
2993 if (state != State0)
2995 reset_tool();
2996 restore_screen();
2997 inv_cursor(true);
2999 else
3001 rb->lcd_set_drawmode(DRMODE_SOLID);
3002 return PLUGIN_OK;
3004 break;
3006 case ROCKPAINT_MENU:
3007 goto_menu();
3008 restore_screen();
3009 inv_cursor(true);
3010 break;
3012 case ROCKPAINT_DRAW:
3013 if( tools[tool].state_func )
3015 inv_cursor(false);
3016 tools[tool].state_func();
3017 inv_cursor(true);
3019 break;
3021 case ROCKPAINT_DRAW|BUTTON_REPEAT:
3022 if( tool == Curve && state != State0 )
3024 /* 3 point bezier curve */
3025 preview = false;
3026 draw_curve( prev_x, prev_y, prev_x2, prev_y2,
3027 -1, -1, x, y );
3028 reset_tool();
3029 restore_screen();
3030 inv_cursor( true );
3032 break;
3034 case ROCKPAINT_TOOLBAR:
3035 case ROCKPAINT_TOOLBAR2:
3036 i = x; j = y;
3037 x = (button == ROCKPAINT_TOOLBAR2) ? 110: 10;
3038 toolbar();
3039 x = i; y = j;
3040 restore_screen();
3041 inv_cursor(true);
3042 break;
3044 case ROCKPAINT_LEFT:
3045 case ROCKPAINT_LEFT | BUTTON_REPEAT:
3046 case ROCKPAINT_RIGHT:
3047 case ROCKPAINT_RIGHT | BUTTON_REPEAT:
3048 inv_cursor(false);
3049 incdec_value(&x, &incdec_x,
3050 (button&ROCKPAINT_RIGHT), bigstep);
3051 inv_cursor(true);
3052 break;
3054 case ROCKPAINT_UP:
3055 case ROCKPAINT_UP | BUTTON_REPEAT:
3056 case ROCKPAINT_DOWN:
3057 case ROCKPAINT_DOWN | BUTTON_REPEAT:
3058 inv_cursor(false);
3059 if (incdec_value(&y, &incdec_y,
3060 (button&ROCKPAINT_DOWN), bigstep)
3061 && (button&ROCKPAINT_DOWN))
3063 toolbar();
3064 restore_screen();
3066 inv_cursor(true);
3067 break;
3069 default:
3070 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
3071 return PLUGIN_USB_CONNECTED;
3072 break;
3074 if( tool == Brush && state == State1 )
3076 inv_cursor(false);
3077 draw_brush( x, y );
3078 inv_cursor(true);
3080 if( preview && tools[tool].preview_func )
3082 restore_screen();
3083 tools[tool].preview_func();
3084 inv_cursor( true );
3086 if( gridsize > 0 )
3088 show_grid( true );
3089 show_grid( false );
3093 return PLUGIN_OK;
3096 static int load_bitmap( const char *file )
3098 struct bitmap bm;
3099 bool ret;
3100 int i, j;
3101 fb_data color = rp_colors[ bgdrawcolor ];
3103 bm.data = (char*)save_buffer;
3104 ret = rb->read_bmp_file( file, &bm, ROWS*COLS*sizeof( fb_data ),
3105 FORMAT_NATIVE, NULL );
3107 if((bm.width > COLS ) || ( bm.height > ROWS ))
3108 return -1;
3110 img_width = bm.width;
3111 img_height = bm.height;
3112 for( i = bm.height-1; i >= 0; i-- )
3114 rb->memmove( save_buffer+i*COLS, save_buffer+i*bm.width,
3115 sizeof( fb_data )*bm.width );
3116 for( j = bm.width; j < COLS; j++ )
3117 save_buffer[j+i*COLS] = color;
3119 for( i = bm.height*COLS; i < ROWS*COLS; i++ )
3120 save_buffer[i] = color;
3122 return ret;
3125 static int save_bitmap( char *file )
3127 struct bitmap bm;
3128 int i;
3129 for(i = 0; i < img_height; i++)
3131 rb->memcpy( buffer->clipboard+i*img_width, save_buffer+i*COLS,
3132 sizeof( fb_data )*img_width );
3134 bm.data = (char*)buffer->clipboard;
3135 bm.height = img_height;
3136 bm.width = img_width;
3137 bm.format = FORMAT_NATIVE;
3138 return save_bmp_file( file, &bm );
3141 enum plugin_status plugin_start(const void* parameter)
3143 size_t buffer_size;
3144 unsigned char *temp;
3145 temp = rb->plugin_get_buffer(&buffer_size);
3146 if (buffer_size < sizeof(*buffer) + 3)
3148 /* steal from audiobuffer if plugin buffer is too small */
3149 temp = rb->plugin_get_audio_buffer(&buffer_size);
3150 if (buffer_size < sizeof(*buffer) + 3)
3152 rb->splash(HZ, "Not enough memory");
3153 return PLUGIN_ERROR;
3155 audio_buf = true;
3157 buffer = (union buf*) (((uintptr_t)temp + 3) & ~3);
3159 rb->lcd_set_foreground(COLOR_WHITE);
3160 rb->lcd_set_backdrop(NULL);
3161 rb->lcd_fillrect(0,0,LCD_WIDTH,LCD_HEIGHT);
3162 rb->splash( HZ/2, "Rock Paint");
3164 rb->lcd_clear_display();
3166 filename[0] = '\0';
3168 if( parameter )
3170 if( load_bitmap( parameter ) <= 0 )
3172 rb->splash( 1*HZ, "File Open Error");
3173 clear_drawing();
3175 else
3177 rb->splashf( 1*HZ, "Image loaded (%s)", (char *)parameter );
3178 restore_screen();
3179 rb->strcpy( filename, parameter );
3182 else
3184 clear_drawing();
3186 inv_cursor(true);
3188 return rockpaint_loop();