rockbox/apps/plugins/rockpaint.c
Teruaki Kawashima e2e7ecf350 add playback control to more menu of plugins.
although it doesn't make much sense for some plugins like dice as the menu is only shown when you start that plugin.
change star plugin to go back to menu when exit game instead of closing plugin so that above change makes useful.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@22171 a1c6a512-1295-4272-9138-f99709370657
2009-08-05 14:29:29 +00:00

3003 lines
90 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006 Antoine Cellerier <dionoea -at- videolan -dot- org>
* Based on parts of rockpaint 0.45, Copyright (C) 2005 Eli Sherer
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
/**
* TODO:
* - implement 2 layers with alpha colors
* - take brush width into account when drawing shapes
* - handle bigger than screen bitmaps
* - cache fonts when building the font preview (else it only works well on simulators because they have "fast" disk read)
*/
#include "plugin.h"
#include "lib/pluginlib_bmp.h"
#include "lib/rgb_hsv.h"
#include "lib/playback_control.h"
PLUGIN_HEADER
/***********************************************************************
* Buttons
***********************************************************************/
#if CONFIG_KEYPAD == IRIVER_H300_PAD
#define ROCKPAINT_QUIT BUTTON_OFF
#define ROCKPAINT_DRAW BUTTON_SELECT
#define ROCKPAINT_MENU BUTTON_ON
#define ROCKPAINT_TOOLBAR BUTTON_REC
#define ROCKPAINT_TOOLBAR2 BUTTON_MODE
#define ROCKPAINT_UP BUTTON_UP
#define ROCKPAINT_DOWN BUTTON_DOWN
#define ROCKPAINT_LEFT BUTTON_LEFT
#define ROCKPAINT_RIGHT BUTTON_RIGHT
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
#define ROCKPAINT_QUIT ( ~BUTTON_MAIN )
#define ROCKPAINT_DRAW BUTTON_SELECT
#define ROCKPAINT_MENU ( BUTTON_SELECT | BUTTON_MENU )
#define ROCKPAINT_TOOLBAR ( BUTTON_MENU | BUTTON_LEFT )
#define ROCKPAINT_TOOLBAR2 ( BUTTON_MENU | BUTTON_RIGHT )
#define ROCKPAINT_UP BUTTON_MENU
#define ROCKPAINT_DOWN BUTTON_PLAY
#define ROCKPAINT_LEFT BUTTON_LEFT
#define ROCKPAINT_RIGHT BUTTON_RIGHT
#elif ( CONFIG_KEYPAD == IAUDIO_X5M5_PAD )
#define ROCKPAINT_QUIT BUTTON_POWER
#define ROCKPAINT_DRAW BUTTON_SELECT
#define ROCKPAINT_MENU BUTTON_PLAY
#define ROCKPAINT_TOOLBAR BUTTON_REC
#define ROCKPAINT_TOOLBAR2 ( BUTTON_REC | BUTTON_LEFT )
#define ROCKPAINT_UP BUTTON_UP
#define ROCKPAINT_DOWN BUTTON_DOWN
#define ROCKPAINT_LEFT BUTTON_LEFT
#define ROCKPAINT_RIGHT BUTTON_RIGHT
#elif CONFIG_KEYPAD == GIGABEAT_PAD
#define ROCKPAINT_QUIT BUTTON_POWER
#define ROCKPAINT_DRAW BUTTON_SELECT
#define ROCKPAINT_MENU BUTTON_MENU
#define ROCKPAINT_TOOLBAR BUTTON_A
#define ROCKPAINT_TOOLBAR2 ( BUTTON_A | BUTTON_LEFT )
#define ROCKPAINT_UP BUTTON_UP
#define ROCKPAINT_DOWN BUTTON_DOWN
#define ROCKPAINT_LEFT BUTTON_LEFT
#define ROCKPAINT_RIGHT BUTTON_RIGHT
#elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
(CONFIG_KEYPAD == SANSA_C200_PAD)
#define ROCKPAINT_QUIT BUTTON_POWER
#define ROCKPAINT_DRAW BUTTON_SELECT
#define ROCKPAINT_MENU ( BUTTON_SELECT | BUTTON_POWER )
#define ROCKPAINT_TOOLBAR BUTTON_REC
#define ROCKPAINT_TOOLBAR2 ( BUTTON_REC | BUTTON_LEFT )
#define ROCKPAINT_UP BUTTON_UP
#define ROCKPAINT_DOWN BUTTON_DOWN
#define ROCKPAINT_LEFT BUTTON_LEFT
#define ROCKPAINT_RIGHT BUTTON_RIGHT
#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
#define ROCKPAINT_QUIT (BUTTON_HOME|BUTTON_REPEAT)
#define ROCKPAINT_DRAW BUTTON_SELECT
#define ROCKPAINT_MENU ( BUTTON_SELECT | BUTTON_DOWN )
#define ROCKPAINT_TOOLBAR ( BUTTON_SELECT | BUTTON_LEFT )
#define ROCKPAINT_TOOLBAR2 ( BUTTON_SELECT | BUTTON_RIGHT )
#define ROCKPAINT_UP BUTTON_UP
#define ROCKPAINT_DOWN BUTTON_DOWN
#define ROCKPAINT_LEFT BUTTON_LEFT
#define ROCKPAINT_RIGHT BUTTON_RIGHT
#elif ( CONFIG_KEYPAD == IRIVER_H10_PAD )
#define ROCKPAINT_QUIT BUTTON_POWER
#define ROCKPAINT_DRAW BUTTON_FF
#define ROCKPAINT_MENU BUTTON_PLAY
#define ROCKPAINT_TOOLBAR BUTTON_REW
#define ROCKPAINT_TOOLBAR2 ( BUTTON_REW | BUTTON_LEFT )
#define ROCKPAINT_UP BUTTON_SCROLL_UP
#define ROCKPAINT_DOWN BUTTON_SCROLL_DOWN
#define ROCKPAINT_LEFT BUTTON_LEFT
#define ROCKPAINT_RIGHT BUTTON_RIGHT
#elif CONFIG_KEYPAD == GIGABEAT_S_PAD
#define ROCKPAINT_QUIT BUTTON_BACK
#define ROCKPAINT_DRAW BUTTON_SELECT
#define ROCKPAINT_MENU BUTTON_MENU
#define ROCKPAINT_TOOLBAR BUTTON_PLAY
#define ROCKPAINT_TOOLBAR2 ( BUTTON_PLAY | BUTTON_LEFT )
#define ROCKPAINT_UP BUTTON_UP
#define ROCKPAINT_DOWN BUTTON_DOWN
#define ROCKPAINT_LEFT BUTTON_LEFT
#define ROCKPAINT_RIGHT BUTTON_RIGHT
#elif ( CONFIG_KEYPAD == COWOND2_PAD )
#define ROCKPAINT_QUIT BUTTON_POWER
#define ROCKPAINT_MENU BUTTON_MENU
#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
#define ROCKPAINT_QUIT BUTTON_BACK
#define ROCKPAINT_DRAW BUTTON_SELECT
#define ROCKPAINT_MENU BUTTON_MENU
#define ROCKPAINT_TOOLBAR BUTTON_PLAY
#define ROCKPAINT_TOOLBAR2 ( BUTTON_PLAY | BUTTON_LEFT )
#define ROCKPAINT_UP BUTTON_UP
#define ROCKPAINT_DOWN BUTTON_DOWN
#define ROCKPAINT_LEFT BUTTON_LEFT
#define ROCKPAINT_RIGHT BUTTON_RIGHT
#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
#define ROCKPAINT_QUIT BUTTON_POWER
#define ROCKPAINT_DRAW BUTTON_SELECT
#define ROCKPAINT_MENU BUTTON_MENU
#define ROCKPAINT_TOOLBAR BUTTON_VIEW
#define ROCKPAINT_TOOLBAR2 BUTTON_PLAYLIST
#define ROCKPAINT_UP BUTTON_UP
#define ROCKPAINT_DOWN BUTTON_DOWN
#define ROCKPAINT_LEFT BUTTON_LEFT
#define ROCKPAINT_RIGHT BUTTON_RIGHT
#elif ( CONFIG_KEYPAD == ONDAVX747_PAD )
#define ROCKPAINT_QUIT BUTTON_POWER
#define ROCKPAINT_MENU BUTTON_MENU
#elif CONFIG_KEYPAD == MROBE500_PAD
#define ROCKPAINT_QUIT BUTTON_POWER
#elif ( CONFIG_KEYPAD == SAMSUNG_YH_PAD )
#define ROCKPAINT_QUIT BUTTON_REC
#define ROCKPAINT_DRAW BUTTON_PLAY
#define ROCKPAINT_MENU BUTTON_FFWD
#define ROCKPAINT_TOOLBAR BUTTON_REW
#define ROCKPAINT_TOOLBAR2 ( BUTTON_REW | BUTTON_LEFT )
#define ROCKPAINT_UP BUTTON_UP
#define ROCKPAINT_DOWN BUTTON_DOWN
#define ROCKPAINT_LEFT BUTTON_LEFT
#define ROCKPAINT_RIGHT BUTTON_RIGHT
#else
#error "Please define keys for this keypad"
#endif
#ifdef HAVE_TOUCHSCREEN
#ifndef ROCKPAINT_QUIT
#define ROCKPAINT_QUIT BUTTON_TOPLEFT
#endif
#ifndef ROCKPAINT_DRAW
#define ROCKPAINT_DRAW BUTTON_CENTER
#endif
#ifndef ROCKPAINT_MENU
#define ROCKPAINT_MENU BUTTON_TOPRIGHT
#endif
#ifndef ROCKPAINT_TOOLBAR
#define ROCKPAINT_TOOLBAR BUTTON_BOTTOMLEFT
#endif
#ifndef ROCKPAINT_TOOLBAR2
#define ROCKPAINT_TOOLBAR2 BUTTON_BOTTOMRIGHT
#endif
#ifndef ROCKPAINT_UP
#define ROCKPAINT_UP BUTTON_TOPMIDDLE
#endif
#ifndef ROCKPAINT_DOWN
#define ROCKPAINT_DOWN BUTTON_BOTTOMMIDDLE
#endif
#ifndef ROCKPAINT_LEFT
#define ROCKPAINT_LEFT BUTTON_MIDLEFT
#endif
#ifndef ROCKPAINT_RIGHT
#define ROCKPAINT_RIGHT BUTTON_MIDRIGHT
#endif
#endif
/***********************************************************************
* Palette Default Colors
***********************************************************************/
#define COLOR_BLACK LCD_RGBPACK(0,0,0)
#define COLOR_WHITE LCD_RGBPACK(255,255,255)
#define COLOR_DARKGRAY LCD_RGBPACK(128,128,128)
#define COLOR_LIGHTGRAY LCD_RGBPACK(192,192,192)
#define COLOR_RED LCD_RGBPACK(128,0,0)
#define COLOR_LIGHTRED LCD_RGBPACK(255,0,0)
#define COLOR_DARKYELLOW LCD_RGBPACK(128,128,0)
#define COLOR_YELLOW LCD_RGBPACK(255,255,0)
#define COLOR_GREEN LCD_RGBPACK(0,128,0)
#define COLOR_LIGHTGREN LCD_RGBPACK(0,255,0)
#define COLOR_CYAN LCD_RGBPACK(0,128,128)
#define COLOR_LIGHTCYAN LCD_RGBPACK(0,255,255)
#define COLOR_BLUE LCD_RGBPACK(0,0,128)
#define COLOR_LIGHTBLUE LCD_RGBPACK(0,0,255)
#define COLOR_PURPLE LCD_RGBPACK(128,0,128)
#define COLOR_PINK LCD_RGBPACK(255,0,255)
#define COLOR_BROWN LCD_RGBPACK(128,64,0)
#define COLOR_LIGHTBROWN LCD_RGBPACK(255,128,64)
#define SPLASH_SCREEN PLUGIN_APPS_DIR "/rockpaint/splash.bmp"
#define ROCKPAINT_TITLE_FONT 2
/***********************************************************************
* Program Colors
***********************************************************************/
#define ROCKPAINT_PALETTE LCD_RGBPACK(0,64,128)
#define ROCKPAINT_SELECTED LCD_RGBPACK(128,192,255)
#define ROWS LCD_HEIGHT
#define COLS LCD_WIDTH
/**
* Toolbar positioning stuff ... don't read this unless you really need to
*
* TB Toolbar
* SP Separator
* SC Selected Color
* PL Palette
* TL Tools
*/
/* Separator sizes */
#define TB_SP_MARGIN 3
#define TB_SP_WIDTH (2+2*TB_SP_MARGIN)
/* Selected color sizes */
#define TB_SC_SIZE 12
/* Palette sizes */
#define TB_PL_COLOR_SIZE 7
#define TB_PL_COLOR_SPACING 2
#define TB_PL_WIDTH ( 9 * TB_PL_COLOR_SIZE + 8 * TB_PL_COLOR_SPACING )
#define TB_PL_HEIGHT ( TB_PL_COLOR_SIZE * 2 + TB_PL_COLOR_SPACING )
/* Tools sizes */
#define TB_TL_SIZE 8
#define TB_TL_SPACING 2
#define TB_TL_WIDTH ( 7 * ( TB_TL_SIZE + TB_TL_SPACING ) - TB_TL_SPACING )
#define TB_TL_HEIGHT ( 2 * TB_TL_SIZE + TB_TL_SPACING )
/* Menu button size ... gruik */
#define TB_MENU_MIN_WIDTH 30
/* Selected colors position */
#define TB_SC_FG_TOP 2
#define TB_SC_FG_LEFT 2
#define TB_SC_BG_TOP (TB_SC_FG_TOP+TB_PL_COLOR_SIZE*2+TB_PL_COLOR_SPACING-TB_SC_SIZE)
#define TB_SC_BG_LEFT (TB_SC_FG_LEFT+TB_PL_COLOR_SIZE*2+TB_PL_COLOR_SPACING-TB_SC_SIZE)
/* Palette position */
#define TB_PL_TOP TB_SC_FG_TOP
#define TB_PL_LEFT (TB_SC_BG_LEFT + TB_SC_SIZE + TB_PL_COLOR_SPACING)
/* Tools position */
#define TB_TL_TOP TB_SC_FG_TOP
#define TB_TL_LEFT ( TB_PL_LEFT + TB_PL_WIDTH-1 + TB_SP_WIDTH )
#if TB_TL_LEFT + TB_TL_WIDTH + TB_MENU_MIN_WIDTH >= LCD_WIDTH
#undef TB_TL_TOP
#undef TB_TL_LEFT
#define TB_TL_TOP ( TB_PL_TOP + TB_PL_HEIGHT + 4 )
#define TB_TL_LEFT TB_SC_FG_LEFT
#endif
/* Menu button position */
#define TB_MENU_TOP ( TB_TL_TOP + (TB_TL_HEIGHT-8)/2 )
#define TB_MENU_LEFT ( TB_TL_LEFT + TB_TL_WIDTH-1 + TB_SP_WIDTH )
#define TB_HEIGHT ( TB_TL_TOP + TB_TL_HEIGHT + 1 )
static void draw_pixel(int x,int y);
static void draw_line( int x1, int y1, int x2, int y2 );
static void draw_rect( int x1, int y1, int x2, int y2 );
static void draw_toolbars(bool update);
static void inv_cursor(bool update);
static void restore_screen(void);
static void clear_drawing(void);
static void goto_menu(void);
static int load_bitmap( const char *filename );
static int save_bitmap( char *filename );
static void draw_rect_full( int x1, int y1, int x2, int y2 );
extern int errno;
/***********************************************************************
* Global variables
***********************************************************************/
#if !defined(SIMULATOR) || defined(__MINGW32__) || defined(__CYGWIN__)
int errno;
#endif
static int drawcolor=0; /* Current color (in palette) */
static int bgdrawcolor=9; /* Current background color (in palette) */
bool isbg = false; /* gruik ugly hack alert */
static int preview=false; /* Is preview mode on ? */
/* TODO: clean this up */
static int x=0, y=0; /* cursor position */
static int prev_x=-1, prev_y=-1; /* previous saved cursor position */
static int prev_x2=-1, prev_y2=-1;
static int prev_x3=-1, prev_y3=-1;
static int tool_mode=-1;
static int bsize=1; /* brush size */
static int bspeed=1; /* brush speed */
enum Tools { Brush = 0, /* Regular brush */
Fill = 1, /* Fill a shape with current color */
SelectRectangle = 2,
ColorPicker = 3, /* Pick a color */
Line = 4, /* Draw a line between two points */
Unused = 5, /* THIS IS UNUSED ... */
Curve = 6,
Text = 7,
Rectangle = 8, /* Draw a rectangle */
RectangleFull = 9,
Oval = 10, /* Draw an oval */
OvalFull = 11,
LinearGradient = 12,
RadialGradient = 13
};
enum Tools tool = Brush;
static bool quit=false;
static int gridsize=0;
static fb_data rp_colors[18] =
{
COLOR_BLACK, COLOR_DARKGRAY, COLOR_RED, COLOR_DARKYELLOW,
COLOR_GREEN, COLOR_CYAN, COLOR_BLUE, COLOR_PURPLE, COLOR_BROWN,
COLOR_WHITE, COLOR_LIGHTGRAY, COLOR_LIGHTRED, COLOR_YELLOW,
COLOR_LIGHTGREN, COLOR_LIGHTCYAN, COLOR_LIGHTBLUE, COLOR_PINK,
COLOR_LIGHTBROWN
};
static fb_data save_buffer[ ROWS*COLS ];
extern fb_data rockpaint[];
extern fb_data rockpaint_hsvrgb[];
/* Maximum string size allowed for the text tool */
#define MAX_TEXT 255
static union
{
/* Used by fill and gradient algorithms */
struct
{
short x;
short y;
} coord[ ROWS*COLS ];
/* Used by bezier curve algorithms */
struct
{
short x1, y1;
short x2, y2;
short x3, y3;
short x4, y4;
short depth;
} bezier[ (ROWS*COLS)/5 ]; /* We have 4.5 times more data per struct
* than coord ... so we divide to take
* less memory. */
/* Used to cut/copy/paste data */
fb_data clipboard[ ROWS*COLS ];
/* Used for text mode */
struct
{
char text[MAX_TEXT+1];
char font[MAX_PATH+1];
char old_font[MAX_PATH+1];
int fh_buf[30];
int fw_buf[30];
char fontname_buf[30][MAX_PATH];
} text;
} buffer;
/* Current filename */
static char filename[MAX_PATH+1];
/* Font preview buffer */
//#define FONT_PREVIEW_WIDTH ((LCD_WIDTH-30)/8)
//#define FONT_PREVIEW_HEIGHT 1000
//static unsigned char fontpreview[FONT_PREVIEW_WIDTH*FONT_PREVIEW_HEIGHT];
/***********************************************************************
* Offscreen buffer/Text/Fonts handling
*
* Parts of code taken from firmware/drivers/lcd-16bit.c
***********************************************************************/
static void buffer_mono_bitmap_part(
fb_data *buf, int buf_width, int buf_height,
const unsigned char *src, int src_x, int src_y,
int stride, int x, int y, int width, int height )
/* this function only draws the foreground part of the bitmap */
{
const unsigned char *src_end;
fb_data *dst, *dst_end;
unsigned fgcolor = rb->lcd_get_foreground();
/* nothing to draw? */
if( ( width <= 0 ) || ( height <= 0 ) || ( x >= buf_width )
|| ( y >= buf_height ) || ( x + width <= 0 ) || ( y + height <= 0 ) )
return;
/* clipping */
if( x < 0 )
{
width += x;
src_x -= x;
x = 0;
}
if( y < 0 )
{
height += y;
src_y -= y;
y = 0;
}
if( x + width > buf_width )
width = buf_width - x;
if( y + height > buf_height )
height = buf_height - y;
src += stride * (src_y >> 3) + src_x; /* move starting point */
src_y &= 7;
src_end = src + width;
dst = buf + y*buf_width + x;
do
{
const unsigned char *src_col = src++;
unsigned data = *src_col >> src_y;
fb_data *dst_col = dst++;
int numbits = 8 - src_y;
dst_end = dst_col + height * buf_width;
do
{
if( data & 0x01 )
*dst_col = fgcolor; /* FIXME ? */
dst_col += buf_width;
data >>= 1;
if( --numbits == 0 )
{
src_col += stride;
data = *src_col;
numbits = 8;
}
} while( dst_col < dst_end );
} while( src < src_end );
}
static void buffer_putsxyofs( fb_data *buf, int buf_width, int buf_height,
int x, int y, int ofs, const unsigned char *str )
{
unsigned short ch;
unsigned short *ucs;
struct font *pf = rb->font_get( FONT_UI );
if( !pf ) pf = rb->font_get( FONT_SYSFIXED );
ucs = rb->bidi_l2v( str, 1 );
while( (ch = *ucs++) != 0 && x < buf_width )
{
int width;
const unsigned char *bits;
/* get proportional width and glyph bits */
width = rb->font_get_width( pf, ch );
if( ofs > width )
{
ofs -= width;
continue;
}
bits = rb->font_get_bits( pf, ch );
buffer_mono_bitmap_part( buf, buf_width, buf_height, bits, ofs, 0, width, x, y, width - ofs, pf->height);
x += width - ofs;
ofs = 0;
}
}
/***********************************************************************
* Menu handling
***********************************************************************/
enum {
/* Main menu */
MAIN_MENU_RESUME,
MAIN_MENU_NEW, MAIN_MENU_LOAD, MAIN_MENU_SAVE,
MAIN_MENU_BRUSH_SIZE, MAIN_MENU_BRUSH_SPEED, MAIN_MENU_COLOR,
MAIN_MENU_GRID_SIZE,
MAIN_MENU_PLAYBACK_CONTROL,
MAIN_MENU_EXIT,
};
enum {
/* Select action menu */
SELECT_MENU_CUT, SELECT_MENU_COPY, SELECT_MENU_INVERT,
SELECT_MENU_HFLIP, SELECT_MENU_VFLIP, SELECT_MENU_ROTATE90,
SELECT_MENU_ROTATE180, SELECT_MENU_ROTATE270,
SELECT_MENU_CANCEL,
};
enum {
/* Text menu */
TEXT_MENU_TEXT, TEXT_MENU_FONT,
TEXT_MENU_PREVIEW, TEXT_MENU_APPLY, TEXT_MENU_CANCEL,
};
MENUITEM_STRINGLIST(main_menu, "RockPaint", NULL,
"Resume", "New", "Load", "Save",
"Brush Size", "Brush Speed",
"Choose Color", "Grid Size",
"Playback Control", "Exit");
MENUITEM_STRINGLIST(size_menu, "Choose Size", NULL,
"1x", "2x","4x", "8x");
MENUITEM_STRINGLIST(speed_menu, "Choose Speed", NULL,
"1x", "2x","4x");
MENUITEM_STRINGLIST(gridsize_menu, "Grid Size", NULL,
"No grid", "5px", "10px", "20px");
MENUITEM_STRINGLIST(select_menu, "Select...", NULL,
"Cut", "Copy", "Invert", "Horizontal Flip" ,
"Vertical Flip", "Rotate 90°",
"Rotate 180°", "Rotate 270°", "Cancel");
MENUITEM_STRINGLIST(text_menu, "Text", NULL,
"Set Text", "Change Font",
"Preview", "Apply", "Cancel");
static const int times_list[] = { 1, 2, 4, 8 };
static const int gridsize_list[] = { 0, 5, 10, 20 };
static int draw_window( int height, int width,
int *top, int *left,
const char *title )
{
int fh;
rb->lcd_getstringsize( title, NULL, &fh );
fh++;
const int _top = ( LCD_HEIGHT - height ) / 2;
const int _left = ( LCD_WIDTH - width ) / 2;
if( top ) *top = _top;
if( left ) *left = _left;
rb->lcd_set_background(COLOR_BLUE);
rb->lcd_set_foreground(COLOR_LIGHTGRAY);
rb->lcd_fillrect( _left, _top, width, height );
rb->lcd_set_foreground(COLOR_BLUE);
rb->lcd_fillrect( _left, _top, width, fh+4 );
rb->lcd_set_foreground(COLOR_WHITE);
rb->lcd_putsxy( _left+2, _top+2, title );
rb->lcd_set_foreground(COLOR_BLACK);
rb->lcd_drawrect( _left, _top, width, height );
return _top+fh+4;
}
/***********************************************************************
* File browser
***********************************************************************/
char bbuf[MAX_PATH+1]; /* used by file and font browsers */
char bbuf_s[MAX_PATH+1]; /* used by file and font browsers */
struct tree_context *tree = NULL;
static char * browse_get_name_cb( int selected_item, void *data,
char *buffer, size_t buffer_len )
{
int *indexes = (int *) data;
struct entry* dc = tree->dircache;
struct entry* e = &dc[indexes[selected_item]];
(void) buffer;
(void) buffer_len;
return (e->name);
}
static bool browse( char *dst, int dst_size, const char *start )
{
struct gui_synclist browse_list;
int item_count = 0, selected, button;
struct tree_context backup;
struct entry *dc;
bool reload = true;
int dirfilter = SHOW_ALL;
int *indexes = (int *) buffer.clipboard;
char *a;
rb->strcpy( bbuf, start );
a = bbuf+rb->strlen(bbuf)-1;
if( *a != '/' )
{
a[1] = '/';
a[2] = '\0';
}
bbuf_s[0] = '\0';
rb->gui_synclist_init( &browse_list, browse_get_name_cb,
(void*) indexes, false, 1, NULL );
tree = rb->tree_get_context();
backup = *tree;
dc = tree->dircache;
a = backup.currdir+rb->strlen(backup.currdir)-1;
if( *a != '/' )
{
*++a = '/';
*++a = '\0';
}
rb->strcpy( a, dc[tree->selected_item].name );
tree->dirfilter = &dirfilter;
while( 1 )
{
if( reload )
{
int i;
rb->set_current_file(bbuf);
item_count = 0;
selected = 0;
for( i = 0; i < tree->filesindir ; i++)
{
/* only displayes directories and .bmp files */
if( ((dc[i].attr & ATTR_DIRECTORY ) &&
rb->strcmp( dc[i].name, "." ) &&
rb->strcmp( dc[i].name, ".." )) ||
( !(dc[i].attr & ATTR_DIRECTORY) &&
(a = rb->strrchr( dc[i].name,'.' )) &&
!rb->strcmp( a, ".bmp" ) ))
{
if( !rb->strcmp( dc[i].name, bbuf_s ) )
selected = item_count;
indexes[item_count++] = i;
}
}
rb->gui_synclist_set_nb_items(&browse_list,item_count);
rb->gui_synclist_select_item(&browse_list, selected);
rb->gui_synclist_set_title(&browse_list, bbuf, NOICON);
rb->gui_synclist_draw(&browse_list);
reload = false;
}
button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
if (rb->gui_synclist_do_button(&browse_list,&button,LIST_WRAP_UNLESS_HELD))
continue;
switch( button )
{
case ACTION_STD_CANCEL:
if( !rb->strcmp( bbuf, "/" ) )
{
*tree = backup;
rb->set_current_file( backup.currdir );
return false;
}
rb->strcpy( bbuf_s, ".." );
case ACTION_STD_OK:
if( button == ACTION_STD_OK )
{
selected = rb->gui_synclist_get_sel_pos( &browse_list );
if( selected < 0 || selected >= item_count )
break;
struct entry* e = &dc[indexes[selected]];
rb->strlcpy( bbuf_s, e->name, sizeof( bbuf_s ) );
if( !( e->attr & ATTR_DIRECTORY ) )
{
*tree = backup;
rb->set_current_file( backup.currdir );
rb->snprintf( dst, dst_size, "%s%s", bbuf, bbuf_s );
return true;
}
}
if( !rb->strcmp( bbuf_s, "." ) ) break;
a = bbuf+rb->strlen(bbuf);
if( !rb->strcmp( bbuf_s, ".." ) )
{
a--;
if( a == bbuf ) break;
if( *a == '/' ) a--;
while( *a != '/' ) a--;
rb->strcpy( bbuf_s, ++a );
/* select parent directory */
bbuf_s[rb->strlen(bbuf_s)-1] = '\0';
*a = '\0';
reload = true;
break;
}
rb->snprintf( a, bbuf+sizeof(bbuf)-a, "%s/", bbuf_s );
reload = true;
break;
case ACTION_STD_MENU:
*tree = backup;
rb->set_current_file( backup.currdir );
return false;
}
}
}
/***********************************************************************
* Font browser
*
* FIXME: This still needs some work ... it currently only works fine
* on the simulators, disk spins too much on real targets -> rendered
* font buffer needed.
***********************************************************************/
static bool browse_fonts( char *dst, int dst_size )
{
#define WIDTH ( LCD_WIDTH - 20 )
#define HEIGHT ( LCD_HEIGHT - 20 )
#define LINE_SPACE 2
int top, top_inside = 0, left;
DIR *d;
struct dirent *de;
int fvi = 0; /* first visible item */
int lvi = 0; /* last visible item */
int si = 0; /* selected item */
int osi = 0; /* old selected item */
int li = 0; /* last item */
int nvih = 0; /* next visible item height */
int i;
int b_need_redraw = 1; /* Do we need to redraw ? */
int cp = 0; /* current position */
int fh; /* font height */
#define fh_buf buffer.text.fh_buf /* 30 might not be enough ... */
#define fw_buf buffer.text.fw_buf
int fw;
#define fontname_buf buffer.text.fontname_buf
rb->snprintf( buffer.text.old_font, MAX_PATH,
FONT_DIR "/%s.fnt",
rb->global_settings->font_file );
while( 1 )
{
if( !b_need_redraw )
{
/* we don't need to redraw ... but we need to unselect
* the previously selected item */
cp = top_inside + LINE_SPACE;
for( i = 0; i+fvi < osi; i++ )
{
cp += fh_buf[i] + LINE_SPACE;
}
rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
rb->lcd_fillrect( left+10, cp, fw_buf[i], fh_buf[i] );
rb->lcd_set_drawmode(DRMODE_SOLID);
}
if( b_need_redraw )
{
b_need_redraw = 0;
d = rb->opendir( FONT_DIR "/" );
if( !d )
{
return false;
}
top_inside = draw_window( HEIGHT, WIDTH, &top, &left, "Fonts" );
i = 0;
li = -1;
while( i < fvi )
{
rb->readdir( d );
i++;
}
cp = top_inside+LINE_SPACE;
rb->lcd_set_foreground(COLOR_BLACK);
rb->lcd_set_background(COLOR_LIGHTGRAY);
while( cp < top+HEIGHT )
{
de = rb->readdir( d );
if( !de )
{
li = i-1;
break;
}
if( rb->strlen( de->d_name ) < 4
|| rb->strcmp( de->d_name + rb->strlen( de->d_name ) - 4,
".fnt" ) )
continue;
rb->snprintf( bbuf, MAX_PATH, FONT_DIR "/%s",
de->d_name );
rb->font_load( bbuf );
rb->font_getstringsize( de->d_name, &fw, &fh, FONT_UI );
if( nvih > 0 )
{
nvih -= fh;
fvi++;
if( nvih < 0 ) nvih = 0;
i++;
continue;
}
if( cp + fh >= top+HEIGHT )
{
nvih = fh;
break;
}
rb->lcd_putsxy( left+10, cp, de->d_name );
fh_buf[i-fvi] = fh;
fw_buf[i-fvi] = fw;
cp += fh + LINE_SPACE;
rb->strcpy( fontname_buf[i-fvi], bbuf );
i++;
}
lvi = i-1;
if( li == -1 )
{
if( !(de = rb->readdir( d ) ) )
{
li = lvi;
}
else if( !nvih && !rb->strlen( de->d_name ) < 4
&& !rb->strcmp( de->d_name + rb->strlen( de->d_name ) - 4,
".fnt" ) )
{
rb->snprintf( bbuf, MAX_PATH, FONT_DIR "/%s",
de->d_name );
rb->font_load( bbuf );
rb->font_getstringsize( de->d_name, NULL, &fh, FONT_UI );
nvih = fh;
}
}
rb->font_load( buffer.text.old_font );
rb->closedir( d );
}
rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
cp = top_inside + LINE_SPACE;
for( i = 0; i+fvi < si; i++ )
{
cp += fh_buf[i] + LINE_SPACE;
}
rb->lcd_fillrect( left+10, cp, fw_buf[i], fh_buf[i] );
rb->lcd_set_drawmode(DRMODE_SOLID);
rb->lcd_update_rect( left, top, WIDTH, HEIGHT );
osi = si;
i = fvi;
switch( rb->button_get(true) )
{
case ROCKPAINT_UP:
case ROCKPAINT_UP|BUTTON_REPEAT:
if( si > 0 )
{
si--;
if( si<fvi )
{
fvi = si;
}
}
break;
case ROCKPAINT_DOWN:
case ROCKPAINT_DOWN|BUTTON_REPEAT:
if( li == -1 || si < li )
{
si++;
}
break;
case ROCKPAINT_LEFT:
return false;
case ROCKPAINT_RIGHT:
case ROCKPAINT_DRAW:
rb->snprintf( dst, dst_size, "%s", fontname_buf[si-fvi] );
return true;
}
if( i != fvi || si > lvi )
{
b_need_redraw = 1;
}
if( si<=lvi )
{
nvih = 0;
}
}
#undef fh_buf
#undef fw_buf
#undef fontname_buf
#undef WIDTH
#undef HEIGHT
#undef LINE_SPACE
}
/***********************************************************************
* HSVRGB Color chooser
***********************************************************************/
static unsigned int color_chooser( unsigned int color )
{
int red = RGB_UNPACK_RED( color );
int green = RGB_UNPACK_GREEN( color );
int blue = RGB_UNPACK_BLUE( color );
int hue, saturation, value;
int r, g, b; /* temp variables */
int i, top, left;
enum BaseColor { Hue = 0, Saturation = 1, Value = 2,
Red = 3, Green = 4, Blue = 5 };
enum BaseColor current = Red;
bool has_changed;
char str[6] = "";
restore_screen();
rgb2hsv( red, green, blue, &hue, &saturation, &value );
while( 1 )
{
has_changed = false;
color = LCD_RGBPACK( red, green, blue );
#define HEIGHT ( 100 )
#define WIDTH ( 150 )
top = draw_window( HEIGHT, WIDTH, NULL, &left, "Color chooser" );
top -= 15;
for( i=0; i<100; i++ )
{
hsv2rgb( i*36, saturation, value, &r, &g, &b );
rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
rb->lcd_vline( left+15+i, top+20, top+27 );
hsv2rgb( hue, i*255/100, value, &r, &g, &b );
rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
rb->lcd_vline( left+15+i, top+30, top+37 );
hsv2rgb( hue, saturation, i*255/100, &r, &g, &b );
rb->lcd_set_foreground( LCD_RGBPACK( r, g, b ) );
rb->lcd_vline( left+15+i, top+40, top+47 );
rb->lcd_set_foreground( LCD_RGBPACK( i*255/100, green, blue ) );
rb->lcd_vline( left+15+i, top+50, top+57 );
rb->lcd_set_foreground( LCD_RGBPACK( red, i*255/100, blue ) );
rb->lcd_vline( left+15+i, top+60, top+67 );
rb->lcd_set_foreground( LCD_RGBPACK( red, green, i*255/100 ) );
rb->lcd_vline( left+15+i, top+70, top+77 );
}
rb->lcd_set_foreground(COLOR_BLACK);
#define POSITION( a, i ) \
rb->lcd_drawpixel( left+14+i, top + 19 + a ); \
rb->lcd_drawpixel( left+16+i, top + 19 + a ); \
rb->lcd_drawpixel( left+14+i, top + 28 + a ); \
rb->lcd_drawpixel( left+16+i, top + 28 + a );
POSITION( 0, hue/36 );
POSITION( 10, saturation*99/255 );
POSITION( 20, value*99/255 );
POSITION( 30, red*99/255 );
POSITION( 40, green*99/255 );
POSITION( 50, blue*99/255 );
#undef POSITION
rb->lcd_set_background(COLOR_LIGHTGRAY);
rb->lcd_setfont( FONT_SYSFIXED );
rb->snprintf( str, 6, "%d", hue/10 );
rb->lcd_putsxy( left + 117, top + 20, str );
rb->snprintf( str, 6, "%d.%d", saturation/255, ((saturation*100)/255)%100 );
rb->lcd_putsxy( left + 117, top + 30, str );
rb->snprintf( str, 6, "%d.%d", value/255, ((value*100)/255)%100 );
rb->lcd_putsxy( left + 117, top + 40, str );
rb->snprintf( str, 6, "%d", red );
rb->lcd_putsxy( left + 117, top + 50, str );
rb->snprintf( str, 6, "%d", green );
rb->lcd_putsxy( left + 117, top + 60, str );
rb->snprintf( str, 6, "%d", blue );
rb->lcd_putsxy( left + 117, top + 70, str );
rb->lcd_setfont( FONT_UI );
#define CURSOR( l ) \
rb->lcd_bitmap_transparent_part( rockpaint_hsvrgb, 1, 1, 16, left+l+1, top+20, 6, 58 ); \
rb->lcd_bitmap_transparent_part( rockpaint_hsvrgb, 8, 10*current, 16, left+l, top+19+10*current, 8, 10 );
CURSOR( 5 );
#undef CURSOR
rb->lcd_set_foreground( color );
rb->lcd_fillrect( left+15, top+85, 100, 8 );
rb->lcd_update();
switch( rb->button_get(true) )
{
case ROCKPAINT_UP:
current = ( current + 5 )%6;
break;
case ROCKPAINT_DOWN:
current = (current + 1 )%6;
break;
case ROCKPAINT_LEFT:
has_changed = true;
switch( current )
{
case Hue:
hue = ( hue + 3600 - 10 )%3600;
break;
case Saturation:
if( saturation ) saturation--;
break;
case Value:
if( value ) value--;
break;
case Red:
if( red ) red--;
break;
case Green:
if( green ) green--;
break;
case Blue:
if( blue ) blue--;
break;
}
break;
case ROCKPAINT_LEFT|BUTTON_REPEAT:
has_changed = true;
switch( current )
{
case Hue:
hue = ( hue + 3600 - 100 )%3600;
break;
case Saturation:
if( saturation >= 8 ) saturation-=8;
else saturation = 0;
break;
case Value:
if( value >= 8 ) value-=8;
else value = 0;
break;
case Red:
if( red >= 8 ) red-=8;
else red = 0;
break;
case Green:
if( green >= 8 ) green-=8;
else green = 0;
break;
case Blue:
if( blue >= 8 ) blue-=8;
else blue = 0;
break;
}
break;
case ROCKPAINT_RIGHT:
has_changed = true;
switch( current )
{
case Hue:
hue = ( hue + 10 )%3600;
break;
case Saturation:
if( saturation < 0xff ) saturation++;
break;
case Value:
if( value < 0xff ) value++;
break;
case Red:
if( red < 0xff ) red++;
break;
case Green:
if( green < 0xff ) green++;
break;
case Blue:
if( blue < 0xff ) blue++;
break;
}
break;
case ROCKPAINT_RIGHT|BUTTON_REPEAT:
has_changed = true;
switch( current )
{
case Hue:
hue = ( hue + 100 )%3600;
break;
case Saturation:
if( saturation < 0xff - 8 ) saturation+=8;
else saturation = 0xff;
break;
case Value:
if( value < 0xff - 8 ) value+=8;
else value = 0xff;
break;
case Red:
if( red < 0xff - 8 ) red+=8;
else red = 0xff;
break;
case Green:
if( green < 0xff - 8 ) green+=8;
else green = 0xff;
break;
case Blue:
if( blue < 0xff - 8 ) blue+=8;
else blue = 0xff;
break;
}
break;
case ROCKPAINT_DRAW:
return color;
}
if( has_changed )
{
switch( current )
{
case Hue:
case Saturation:
case Value:
hsv2rgb( hue, saturation, value, &red, &green, &blue );
break;
case Red:
case Green:
case Blue:
rgb2hsv( red, green, blue, &hue, &saturation, &value );
break;
}
}
#undef HEIGHT
#undef WIDTH
}
}
/***********************************************************************
* Misc routines
***********************************************************************/
static void init_buffer(void)
{
int i;
fb_data color = rp_colors[ bgdrawcolor ];
for( i = 0; i < ROWS*COLS; i++ )
{
save_buffer[i] = color;
}
}
static void draw_pixel(int x,int y)
{
if( !preview )
{
if( x < 0 || x >= COLS || y < 0 || y >= ROWS ) return;
if( isbg )
{
save_buffer[ x+y*COLS ] = rp_colors[bgdrawcolor];
}
else
{
save_buffer[ x+y*COLS ] = rp_colors[drawcolor];
}
}
rb->lcd_drawpixel(x,y);
}
static void color_picker( int x, int y )
{
if( preview )
{
rb->lcd_set_foreground( save_buffer[ x+y*COLS ] );
#define PSIZE 12
rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
rb->lcd_drawrect( x<COLS-PSIZE ? x + 2 : x - PSIZE, y<ROWS-PSIZE ? y + 2: y - PSIZE, PSIZE - 2, PSIZE - 2 );
rb->lcd_set_drawmode(DRMODE_SOLID);
rb->lcd_fillrect( x<COLS-PSIZE ? x+3 : x - PSIZE+1, y<ROWS-PSIZE ? y +3: y - PSIZE+1, PSIZE-4, PSIZE-4 );
#undef PSIZE
rb->lcd_set_foreground( rp_colors[ drawcolor ] );
}
else
{
rp_colors[ drawcolor ] = save_buffer[ x+y*COLS ];
}
}
static void draw_select_rectangle( int x1, int y1, int x2, int y2 )
/* This is a preview mode only function */
{
int i,a;
if( x1 > x2 )
{
i = x1;
x1 = x2;
x2 = i;
}
if( y1 > y2 )
{
i = y1;
y1 = y2;
y2 = i;
}
rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
i = 0;
for( a = x1; a < x2; a++, i++ )
if( i%2 )
rb->lcd_drawpixel( a, y1 );
for( a = y1; a < y2; a++, i++ )
if( i%2 )
rb->lcd_drawpixel( x2, a );
if( y2 != y1 )
for( a = x2; a > x1; a--, i++ )
if( i%2 )
rb->lcd_drawpixel( a, y2 );
if( x2 != x1 )
for( a = y2; a > y1; a--, i++ )
if( i%2 )
rb->lcd_drawpixel( x1, a );
rb->lcd_set_drawmode(DRMODE_SOLID);
}
static void copy_to_clipboard( void )
{
/* This needs to be optimised ... but i'm lazy ATM */
rb->memcpy( buffer.clipboard, save_buffer, COLS*ROWS*sizeof( fb_data ) );
}
/* no preview mode handling atm ... do we need it ? (one if) */
static void draw_invert( int x1, int y1, int x2, int y2 )
{
int i;
if( x1 > x2 )
{
i = x1;
x1 = x2;
x2 = i;
}
if( y1 > y2 )
{
i = y1;
y1 = y2;
y2 = i;
}
rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
rb->lcd_fillrect( x1, y1, x2-x1+1, y2-y1+1 );
rb->lcd_set_drawmode(DRMODE_SOLID);
for( ; y1<=y2; y1++ )
{
for( i = x1; i<=x2; i++ )
{
save_buffer[ y1*COLS + i ] = ~save_buffer[ y1*COLS + i ];
}
}
/*if( update )*/ rb->lcd_update();
}
static void draw_hflip( int x1, int y1, int x2, int y2 )
{
int i;
if( x1 > x2 )
{
i = x1;
x1 = x2;
x2 = i;
}
if( y1 > y2 )
{
i = y1;
y1 = y2;
y2 = i;
}
copy_to_clipboard();
for( i = 0; i <= y2 - y1; i++ )
{
rb->memcpy( save_buffer+(y1+i)*COLS+x1,
buffer.clipboard+(y2-i)*COLS+x1,
(x2-x1+1)*sizeof( fb_data ) );
}
restore_screen();
rb->lcd_update();
}
static void draw_vflip( int x1, int y1, int x2, int y2 )
{
int i;
if( x1 > x2 )
{
i = x1;
x1 = x2;
x2 = i;
}
if( y1 > y2 )
{
i = y1;
y1 = y2;
y2 = i;
}
copy_to_clipboard();
for( ; y1 <= y2; y1++ )
{
for( i = 0; i <= x2 - x1; i++ )
{
save_buffer[y1*COLS+x1+i] = buffer.clipboard[y1*COLS+x2-i];
}
}
restore_screen();
rb->lcd_update();
}
/* direction: -1 = left, 1 = right */
static void draw_rot_90_deg( int x1, int y1, int x2, int y2, int direction )
{
int i, j;
if( x1 > x2 )
{
i = x1;
x1 = x2;
x2 = i;
}
if( y1 > y2 )
{
i = y1;
y1 = y2;
y2 = i;
}
copy_to_clipboard();
fb_data color = rp_colors[ bgdrawcolor ];
const int width = x2 - x1, height = y2 - y1;
const int sub_half = width/2-height/2, add_half = (width+height)/2;
if( width > height )
{
for( i = 0; i <= height; i++ )
{
for( j = 0; j < sub_half; j++ )
save_buffer[(y1+i)*COLS+x1+j] = color;
for( j = add_half+1; j <= width; j++ )
save_buffer[(y1+i)*COLS+x1+j] = color;
}
}
else if( width < height )
{
for( j = 0; j <= width; j++ )
{
for( i = 0; i < -sub_half; i++ )
save_buffer[(y1+i)*COLS+x1+j] = color;
for( i = add_half+1; i <= height; i++ )
save_buffer[(y1+i)*COLS+x1+j] = color;
}
}
int x3 = x1 + sub_half, y3 = y1 - sub_half;
int is = x3<0?-x3:0, ie = COLS-x3-1, js = y3<0?-y3:0, je = ROWS-y3-1;
if( ie > height ) ie = height;
if( je > width ) je = width;
for( i = is; i <= ie; i++ )
{
for( j = js; j <= je; j++ )
{
int x, y;
if(direction > 0)
{
x = x1+j;
y = y1+height-i;
}
else
{
x = x1+width-j;
y = y1+i;
}
save_buffer[(y3+j)*COLS+x3+i] = buffer.clipboard[y*COLS+x];
}
}
restore_screen();
rb->lcd_update();
}
static void draw_paste_rectangle( int src_x1, int src_y1, int src_x2,
int src_y2, int x1, int y1, int mode )
{
int i;
if( mode == SELECT_MENU_CUT )
{
i = drawcolor;
drawcolor = bgdrawcolor;
draw_rect_full( src_x1, src_y1, src_x2, src_y2 );
drawcolor = i;
}
if( src_x1 > src_x2 )
{
i = src_x1;
src_x1 = src_x2;
src_x2 = i;
}
if( src_y1 > src_y2 )
{
i = src_y1;
src_y1 = src_y2;
src_y2 = i;
}
rb->lcd_bitmap_part( buffer.clipboard, src_x1, src_y1, COLS,
x1, y1, src_x2-src_x1+1, src_y2-src_y1+1 );
if( !preview )
{
for( i = 0; i <= src_y2 - src_y1; i++ )
{
rb->memcpy( save_buffer+(y1+i)*COLS+x1,
buffer.clipboard+(src_y1+i)*COLS+src_x1,
(src_x2 - src_x1 + 1)*sizeof( fb_data ) );
}
}
}
static void show_grid( bool update )
{
int i;
if( gridsize > 0 )
{
rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
for( i = gridsize; i < COLS; i+= gridsize )
{
rb->lcd_vline( i, 0, ROWS-1 );
}
for( i = gridsize; i < ROWS; i+= gridsize )
{
rb->lcd_hline( 0, COLS-1, i );
}
rb->lcd_set_drawmode(DRMODE_SOLID);
if( update ) rb->lcd_update();
}
}
static void draw_text( int x, int y )
{
int selected = 0;
buffer.text.text[0] = '\0';
rb->snprintf( buffer.text.old_font, MAX_PATH,
FONT_DIR "/%s.fnt",
rb->global_settings->font_file );
while( 1 )
{
switch( rb->do_menu( &text_menu, &selected, NULL, NULL ) )
{
case TEXT_MENU_TEXT:
rb->lcd_set_foreground(COLOR_BLACK);
rb->kbd_input( buffer.text.text, MAX_TEXT );
break;
case TEXT_MENU_FONT:
if( browse_fonts( buffer.text.font, MAX_PATH ) )
{
rb->font_load( buffer.text.font );
}
break;
case TEXT_MENU_PREVIEW:
rb->lcd_set_foreground( rp_colors[ drawcolor ] );
while( 1 )
{
int button;
restore_screen();
rb->lcd_putsxy( x, y, buffer.text.text );
rb->lcd_update();
switch( button = rb->button_get( true ) )
{
case ROCKPAINT_LEFT:
case ROCKPAINT_LEFT | BUTTON_REPEAT:
x-=bspeed * ( button & BUTTON_REPEAT ? 4 : 1 );
if (x<0) x=COLS-1;
break;
case ROCKPAINT_RIGHT:
case ROCKPAINT_RIGHT | BUTTON_REPEAT:
x+=bspeed * ( button & BUTTON_REPEAT ? 4 : 1 );
if (x>=COLS) x=0;
break;
case ROCKPAINT_UP:
case ROCKPAINT_UP | BUTTON_REPEAT:
y-=bspeed * ( button & BUTTON_REPEAT ? 4 : 1 );
if (y<0) y=ROWS-1;
break;
case ROCKPAINT_DOWN:
case ROCKPAINT_DOWN | BUTTON_REPEAT:
y+=bspeed * ( button & BUTTON_REPEAT ? 4 : 1 );
if (y>=ROWS-1) y=0;
break;
case ROCKPAINT_DRAW:
break;
default:
if(rb->default_event_handler(button)
== SYS_USB_CONNECTED)
button = ROCKPAINT_DRAW;
break;
}
if( button == ROCKPAINT_DRAW ) break;
}
break;
case TEXT_MENU_APPLY:
rb->lcd_set_foreground( rp_colors[ drawcolor ] );
buffer_putsxyofs( save_buffer, COLS, ROWS, x, y, 0,
buffer.text.text );
case TEXT_MENU_CANCEL:
default:
restore_screen();
rb->font_load( buffer.text.old_font );
return;
}
}
}
static void draw_brush( int x, int y )
{
int i,j;
for( i=-bsize/2+(bsize+1)%2; i<=bsize/2; i++ )
{
for( j=-bsize/2+(bsize+1)%2; j<=bsize/2; j++ )
{
draw_pixel( x+i, y+j );
}
}
}
/* This is an implementation of Bresenham's line algorithm.
* See http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm.
*/
static void draw_line( int x1, int y1, int x2, int y2 )
{
int x = x1;
int y = y1;
int deltax = x2 - x1;
int deltay = y2 - y1;
int i;
int xerr = abs(deltax);
int yerr = abs(deltay);
int xstep = deltax > 0 ? 1 : -1;
int ystep = deltay > 0 ? 1 : -1;
int err;
if (yerr > xerr)
{
/* more vertical */
err = yerr;
xerr <<= 1;
yerr <<= 1;
/* to leave off the last pixel of the line, leave off the "+ 1" */
for (i = abs(deltay) + 1; i; --i)
{
draw_pixel(x, y);
y += ystep;
err -= xerr;
if (err < 0) {
x += xstep;
err += yerr;
}
}
}
else
{
/* more horizontal */
err = xerr;
xerr <<= 1;
yerr <<= 1;
for (i = abs(deltax) + 1; i; --i)
{
draw_pixel(x, y);
x += xstep;
err -= yerr;
if (err < 0) {
y += ystep;
err += xerr;
}
}
}
}
static void draw_curve( int x1, int y1, int x2, int y2,
int xa, int ya, int xb, int yb )
{
int i = 0;
short xl1, yl1;
short xl2, yl2;
short xl3, yl3;
short xl4, yl4;
short xr1, yr1;
short xr2, yr2;
short xr3, yr3;
short xr4, yr4;
short depth;
short xh, yh;
if( x1 == x2 && y1 == y2 )
{
draw_pixel( x1, y1 );
return;
}
// if( preview )
{
rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
if( xa == -1 || ya == -1 )
{
rb->lcd_drawline( x1, y1, xb, yb );
rb->lcd_drawline( x2, y2, xb, yb );
}
else
{
rb->lcd_drawline( x1, y1, xa, ya );
rb->lcd_drawline( x2, y2, xb, yb );
}
rb->lcd_set_drawmode(DRMODE_SOLID);
}
if( xa == -1 || ya == -1 )
/* We only have 3 of the points
* This will currently only be used in preview mode */
{
#define PUSH( a1, b1, a2, b2, a3, b3, d ) \
buffer.bezier[i].x1 = a1; \
buffer.bezier[i].y1 = b1; \
buffer.bezier[i].x2 = a2; \
buffer.bezier[i].y2 = b2; \
buffer.bezier[i].x3 = a3; \
buffer.bezier[i].y3 = b3; \
buffer.bezier[i].depth = d; \
i++;
#define POP( a1, b1, a2, b2, a3, b3, d ) \
i--; \
a1 = buffer.bezier[i].x1; \
b1 = buffer.bezier[i].y1; \
a2 = buffer.bezier[i].x2; \
b2 = buffer.bezier[i].y2; \
a3 = buffer.bezier[i].x3; \
b3 = buffer.bezier[i].y3; \
d = buffer.bezier[i].depth;
PUSH( x1<<4, y1<<4, xb<<4, yb<<4, x2<<4, y2<<4, 0 );
while( i )
{
/* de Casteljau's algorithm (see wikipedia) */
POP( xl1, yl1, xb, yb, xr3, yr3, depth );
if( depth < 10 ) /* check that the stack's 'i' doesn't overflow */
{
xl2 = ( xl1 + xb )>>1;
yl2 = ( yl1 + yb )>>1;
xr2 = ( xb + xr3 )>>1;
yr2 = ( yb + yr3 )>>1;
xr1 = ( xl2 + xr2 )>>1;
yr1 = ( yl2 + yr2 )>>1;
xl3 = xr1;
yl3 = yr1;
PUSH( xl1, yl1, xl2, yl2, xl3, yl3, depth+1 );
PUSH( xr1, yr1, xr2, yr2, xr3, yr3, depth+1 );
}
else
{
draw_line( ((xl1>>3)+1)>>1, ((yl1>>3)+1)>>1,
((xr3>>3)+1)>>1, ((yr3>>3)+1)>>1 );
}
}
#undef PUSH
#undef POP
}
else /* We have the 4 points */
{
#define PUSH( a1, b1, a2, b2, a3, b3, a4, b4, d ) \
buffer.bezier[i].x1 = a1; \
buffer.bezier[i].y1 = b1; \
buffer.bezier[i].x2 = a2; \
buffer.bezier[i].y2 = b2; \
buffer.bezier[i].x3 = a3; \
buffer.bezier[i].y3 = b3; \
buffer.bezier[i].x4 = a4; \
buffer.bezier[i].y4 = b4; \
buffer.bezier[i].depth = d; \
i++;
#define POP( a1, b1, a2, b2, a3, b3, a4, b4, d ) \
i--; \
a1 = buffer.bezier[i].x1; \
b1 = buffer.bezier[i].y1; \
a2 = buffer.bezier[i].x2; \
b2 = buffer.bezier[i].y2; \
a3 = buffer.bezier[i].x3; \
b3 = buffer.bezier[i].y3; \
a4 = buffer.bezier[i].x4; \
b4 = buffer.bezier[i].y4; \
d = buffer.bezier[i].depth;
PUSH( x1<<4, y1<<4, xa<<4, ya<<4, xb<<4, yb<<4, x2<<4, y2<<4, 0 );
while( i )
{
/* de Casteljau's algorithm (see wikipedia) */
POP( xl1, yl1, xa, ya, xb, yb, xr4, yr4, depth );
if( depth < 10 ) /* check that the stack's 'i' doesn't overflow */
{
xl2 = ( xl1 + xa )>>1;
yl2 = ( yl1 + ya )>>1;
xh = ( xa + xb )>>1;
yh = ( ya + yb )>>1;
xr3 = ( xb + xr4 )>>1;
yr3 = ( yb + yr4 )>>1;
xl3 = ( xl2 + xh )>>1;
yl3 = ( yl2 + yh )>>1;
xr2 = ( xr3 + xh )>>1;
yr2 = ( yr3 + yh )>>1;
xl4 = ( xl3 + xr2 )>>1;
yl4 = ( yl3 + yr2 )>>1;
xr1 = xl4;
yr1 = yl4;
PUSH( xl1, yl1, xl2, yl2, xl3, yl3, xl4, yl4, depth+1 );
PUSH( xr1, yr1, xr2, yr2, xr3, yr3, xr4, yr4, depth+1 );
}
else
{
draw_line( ((xl1>>3)+1)>>1, ((yl1>>3)+1)>>1,
((xr4>>3)+1)>>1, ((yr4>>3)+1)>>1 );
}
}
#undef PUSH
#undef POP
}
}
static void draw_rect( int x1, int y1, int x2, int y2 )
{
draw_line( x1, y1, x1, y2 );
draw_line( x1, y1, x2, y1 );
draw_line( x1, y2, x2, y2 );
draw_line( x2, y1, x2, y2 );
}
static void togglebg( void )
{
if( isbg )
{
rb->lcd_set_foreground( rp_colors[ drawcolor ] );
}
else
{
rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
}
isbg = !isbg;
}
static void draw_rect_full( int x1, int y1, int x2, int y2 )
{
/* GRUIK */
int x = x1;
togglebg();
if( x < x2 )
{
do {
draw_line( x, y1, x, y2 );
} while( ++x <= x2 );
}
else
{
do {
draw_line( x, y1, x, y2 );
} while( --x >= x2 );
}
togglebg();
draw_rect( x1, y1, x2, y2 );
}
static void draw_oval( int x1, int y1, int x2, int y2, bool full )
{
/* TODO: simplify :) */
int cx = (x1+x2)>>1;
int cy = (y1+y2)>>1;
int rx = (x1-x2)>>1;
int ry = (y1-y2)>>1;
if( rx < 0 ) rx *= -1;
if( ry < 0 ) ry *= -1;
if( rx == 0 || ry == 0 )
{
draw_line( x1, y1, x2, y2 );
return;
}
int x,y;
int dst, old_dst;
for( x = 0; x < rx; x++ )
{
y = 0;
dst = -0xfff;
do {
old_dst = dst;
dst = ry * ry * x * x + rx * rx * y * y - rx * rx * ry * ry;
y++;
} while( dst < 0 );
if( -old_dst < dst ) y--;
if( full )
{
draw_line( cx+x, cy, cx+x, cy+y );
draw_line( cx+x, cy, cx+x, cy-y );
draw_line( cx-x, cy, cx-x, cy+y );
draw_line( cx-x, cy, cx-x, cy-y );
}
else
{
draw_pixel( cx+x, cy+y );
draw_pixel( cx+x, cy-y );
draw_pixel( cx-x, cy+y );
draw_pixel( cx-x, cy-y );
}
}
for( y = 0; y < ry; y++ )
{
x = 0;
dst = -0xfff;
do {
old_dst = dst;
dst = ry * ry * x * x + rx * rx * y * y - rx * rx * ry * ry;
x++;
} while( dst < 0 );
if( -old_dst < dst ) x--;
if( full )
{
draw_line( cx+x, cy, cx+x, cy+y );
draw_line( cx+x, cy, cx+x, cy-y );
draw_line( cx-x, cy, cx-x, cy+y );
draw_line( cx-x, cy, cx-x, cy-y );
}
else
{
draw_pixel( cx+x, cy+y );
draw_pixel( cx+x, cy-y );
draw_pixel( cx-x, cy+y );
draw_pixel( cx-x, cy-y );
}
}
}
static void draw_oval_empty( int x1, int y1, int x2, int y2 )
{
draw_oval( x1, y1, x2, y2, false );
}
static void draw_oval_full( int x1, int y1, int x2, int y2 )
{
togglebg();
draw_oval( x1, y1, x2, y2, true );
togglebg();
draw_oval( x1, y1, x2, y2, false );
}
static void draw_fill( int x0, int y0 )
{
#define PUSH( a, b ) \
draw_pixel( (int)a, (int)b ); \
buffer.coord[i].x = a; \
buffer.coord[i].y = b; \
i++;
#define POP( a, b ) \
i--; \
a = buffer.coord[i].x; \
b = buffer.coord[i].y;
unsigned int i=0;
short x = x0;
short y = y0;
unsigned int prev_color = save_buffer[ x0+y0*COLS ];
if( prev_color == rp_colors[ drawcolor ] ) return;
PUSH( x, y );
while( i != 0 )
{
POP( x, y );
if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
{
PUSH( x-1, y );
}
if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
{
PUSH( x+1, y );
}
if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
{
PUSH( x, y-1 );
}
if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
{
PUSH( x, y+1 );
}
}
#undef PUSH
#undef POP
}
/* For preview purposes only */
static void line_gradient( int x1, int y1, int x2, int y2 )
{
int r1, g1, b1;
int r2, g2, b2;
int h1, s1, v1, h2, s2, v2, r, g, b;
int w, h, x, y;
bool a = false;
x1 <<= 1;
y1 <<= 1;
x2 <<= 1;
y2 <<= 1;
w = x1 - x2;
h = y1 - y2;
if( w == 0 && h == 0 )
{
draw_pixel( x1>>1, y1>>1 );
return;
}
r1 = RGB_UNPACK_RED( rp_colors[ bgdrawcolor ] );
g1 = RGB_UNPACK_GREEN( rp_colors[ bgdrawcolor ] );
b1 = RGB_UNPACK_BLUE( rp_colors[ bgdrawcolor ] );
r2 = RGB_UNPACK_RED( rp_colors[ drawcolor ] );
g2 = RGB_UNPACK_GREEN( rp_colors[ drawcolor ] );
b2 = RGB_UNPACK_BLUE( rp_colors[ drawcolor ] );
if( w < 0 )
{
w *= -1;
a = true;
}
if( h < 0 )
{
h *= -1;
a = !a;
}
if( a )
{
r = r1;
r1 = r2;
r2 = r;
g = g1;
g1 = g2;
g2 = g;
b = b1;
b1 = b2;
b2 = b;
}
rgb2hsv( r1, g1, b1, &h1, &s1, &v1 );
rgb2hsv( r2, g2, b2, &h2, &s2, &v2 );
if( w > h )
{
if( x1 > x2 )
{
x = x2;
y = y2;
x2 = x1;
y2 = y1;
x1 = x;
y1 = y;
}
w = x1 - x2;
h = y1 - y2;
while( x1 <= x2 )
{
hsv2rgb( h1+((h2-h1)*(x1-x2))/w,
s1+((s2-s1)*(x1-x2))/w,
v1+((v2-v1)*(x1-x2))/w,
&r, &g, &b );
rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
rb->lcd_set_foreground( rp_colors[ drawcolor ] );
draw_pixel( (x1+1)>>1, (y1+1)>>1 );
x1+=2;
y1 = y2 - ( x2 - x1 ) * h / w;
}
}
else /* h > w */
{
if( y1 > y2 )
{
x = x2;
y = y2;
x2 = x1;
y2 = y1;
x1 = x;
y1 = y;
}
w = x1 - x2;
h = y1 - y2;
while( y1 <= y2 )
{
hsv2rgb( h1+((h2-h1)*(y1-y2))/h,
s1+((s2-s1)*(y1-y2))/h,
v1+((v2-v1)*(y1-y2))/h,
&r, &g, &b );
rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
rb->lcd_set_foreground( rp_colors[ drawcolor ] );
draw_pixel( (x1+1)>>1, (y1+1)>>1 );
y1+=2;
x1 = x2 - ( y2 - y1 ) * w / h;
}
}
if( a )
{
rp_colors[ drawcolor ] = LCD_RGBPACK( r1, g1, b1 );
}
else
{
rp_colors[ drawcolor ] = LCD_RGBPACK( r2, g2, b2 );
}
}
static void linear_gradient( int x1, int y1, int x2, int y2 )
{
int r1 = RGB_UNPACK_RED( rp_colors[ bgdrawcolor ] );
int g1 = RGB_UNPACK_GREEN( rp_colors[ bgdrawcolor ] );
int b1 = RGB_UNPACK_BLUE( rp_colors[ bgdrawcolor ] );
int r2 = RGB_UNPACK_RED( rp_colors[ drawcolor ] );
int g2 = RGB_UNPACK_GREEN( rp_colors[ drawcolor ] );
int b2 = RGB_UNPACK_BLUE( rp_colors[ drawcolor ] );
int h1, s1, v1, h2, s2, v2, r, g, b;
/* radius^2 */
int radius2 = ( x1 - x2 ) * ( x1 - x2 ) + ( y1 - y2 ) * ( y1 - y2 );
int dist2, i=0;
/* We only propagate the gradient to neighboring pixels with the same
* color as ( x1, y1 ) */
unsigned int prev_color = save_buffer[ x1+y1*COLS ];
int x = x1;
int y = y1;
if( radius2 == 0 ) return;
if( preview )
{
line_gradient( x1, y1, x2, y2 );
}
rgb2hsv( r1, g1, b1, &h1, &s1, &v1 );
rgb2hsv( r2, g2, b2, &h2, &s2, &v2 );
#define PUSH( x0, y0 ) \
buffer.coord[i].x = (short)(x0); \
buffer.coord[i].y = (short)(y0); \
i++;
#define POP( a, b ) \
i--; \
a = (int)buffer.coord[i].x; \
b = (int)buffer.coord[i].y;
PUSH( x, y );
while( i != 0 )
{
POP( x, y );
dist2 = ( x2 - x1 ) * ( x - x1 ) + ( y2 - y1 ) * ( y - y1 );
if( dist2 <= 0 )
{
rp_colors[ drawcolor ] = rp_colors[ bgdrawcolor ];
}
else if( dist2 < radius2 )
{
hsv2rgb( h1+((h2-h1)*dist2)/radius2,
s1+((s2-s1)*dist2)/radius2,
v1+((v2-v1)*dist2)/radius2,
&r, &g, &b );
rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
}
else
{
rp_colors[ drawcolor ] = LCD_RGBPACK( r2, g2, b2 );
}
if( rp_colors[ drawcolor ] == prev_color )
{
if( rp_colors[ drawcolor ])
rp_colors[ drawcolor ]--; /* GRUIK */
else
rp_colors[ drawcolor ]++; /* GRUIK */
}
rb->lcd_set_foreground( rp_colors[ drawcolor ] );
draw_pixel( x, y );
if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
{
PUSH( x-1, y );
}
if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
{
PUSH( x+1, y );
}
if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
{
PUSH( x, y-1 );
}
if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
{
PUSH( x, y+1 );
}
}
#undef PUSH
#undef POP
rp_colors[ drawcolor ] = LCD_RGBPACK( r2, g2, b2 );
}
static void radial_gradient( int x1, int y1, int x2, int y2 )
{
int r1 = RGB_UNPACK_RED( rp_colors[ bgdrawcolor ] );
int g1 = RGB_UNPACK_GREEN( rp_colors[ bgdrawcolor ] );
int b1 = RGB_UNPACK_BLUE( rp_colors[ bgdrawcolor ] );
int r2 = RGB_UNPACK_RED( rp_colors[ drawcolor ] );
int g2 = RGB_UNPACK_GREEN( rp_colors[ drawcolor ] );
int b2 = RGB_UNPACK_BLUE( rp_colors[ drawcolor ] );
int h1, s1, v1, h2, s2, v2, r, g, b;
/* radius^2 */
int radius2 = ( x1 - x2 ) * ( x1 - x2 ) + ( y1 - y2 ) * ( y1 - y2 );
int dist2, i=0;
/* We only propagate the gradient to neighboring pixels with the same
* color as ( x1, y1 ) */
unsigned int prev_color = save_buffer[ x1+y1*COLS ];
int x = x1;
int y = y1;
if( radius2 == 0 ) return;
if( preview )
{
line_gradient( x1, y1, x2, y2 );
}
rgb2hsv( r1, g1, b1, &h1, &s1, &v1 );
rgb2hsv( r2, g2, b2, &h2, &s2, &v2 );
#define PUSH( x0, y0 ) \
buffer.coord[i].x = (short)(x0); \
buffer.coord[i].y = (short)(y0); \
i++;
#define POP( a, b ) \
i--; \
a = (int)buffer.coord[i].x; \
b = (int)buffer.coord[i].y;
PUSH( x, y );
while( i != 0 )
{
POP( x, y );
if( ( dist2 = (x1-(x))*(x1-(x))+(y1-(y))*(y1-(y)) ) < radius2 )
{
hsv2rgb( h1+((h2-h1)*dist2)/radius2,
s1+((s2-s1)*dist2)/radius2,
v1+((v2-v1)*dist2)/radius2,
&r, &g, &b );
rp_colors[ drawcolor ] = LCD_RGBPACK( r, g, b );
}
else
{
rp_colors[ drawcolor ] = LCD_RGBPACK( r2, g2, b2 );
}
if( rp_colors[ drawcolor ] == prev_color )
{
if( rp_colors[ drawcolor ])
rp_colors[ drawcolor ]--; /* GRUIK */
else
rp_colors[ drawcolor ]++; /* GRUIK */
}
rb->lcd_set_foreground( rp_colors[ drawcolor ] );
draw_pixel( x, y );
if( x > 0 && save_buffer[x-1+y*COLS] == prev_color )
{
PUSH( x-1, y );
}
if( x < COLS-1 && save_buffer[x+1+y*COLS] == prev_color )
{
PUSH( x+1, y );
}
if( y > 0 && save_buffer[x+(y-1)*COLS] == prev_color )
{
PUSH( x, y-1 );
}
if( y < ROWS - 1 && save_buffer[x+(y+1)*COLS] == prev_color )
{
PUSH( x, y+1 );
}
}
#undef PUSH
#undef POP
rp_colors[ drawcolor ] = LCD_RGBPACK( r2, g2, b2 );
}
static void draw_toolbars(bool update)
{
int i;
#define TOP (LCD_HEIGHT-TB_HEIGHT)
rb->lcd_set_background( COLOR_LIGHTGRAY );
rb->lcd_set_foreground( COLOR_LIGHTGRAY );
rb->lcd_fillrect( 0, TOP, LCD_WIDTH, TB_HEIGHT );
rb->lcd_set_foreground( COLOR_BLACK );
rb->lcd_drawrect( 0, TOP, LCD_WIDTH, TB_HEIGHT );
rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
rb->lcd_fillrect( TB_SC_BG_LEFT, TOP+TB_SC_BG_TOP,
TB_SC_SIZE, TB_SC_SIZE );
rb->lcd_set_foreground(ROCKPAINT_PALETTE);
rb->lcd_drawrect( TB_SC_BG_LEFT, TOP+TB_SC_BG_TOP,
TB_SC_SIZE, TB_SC_SIZE );
rb->lcd_set_foreground( rp_colors[ drawcolor ] );
rb->lcd_fillrect( TB_SC_FG_LEFT, TOP+TB_SC_FG_TOP,
TB_SC_SIZE, TB_SC_SIZE );
rb->lcd_set_foreground(ROCKPAINT_PALETTE);
rb->lcd_drawrect( TB_SC_FG_LEFT, TOP+TB_SC_FG_TOP,
TB_SC_SIZE, TB_SC_SIZE );
for( i=0; i<18; i++ )
{
rb->lcd_set_foreground( rp_colors[i] );
rb->lcd_fillrect(
TB_PL_LEFT+(i%9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING ),
TOP+TB_PL_TOP+(i/9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING),
TB_PL_COLOR_SIZE, TB_PL_COLOR_SIZE );
rb->lcd_set_foreground( ROCKPAINT_PALETTE );
rb->lcd_drawrect(
TB_PL_LEFT+(i%9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING ),
TOP+TB_PL_TOP+(i/9)*( TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING),
TB_PL_COLOR_SIZE, TB_PL_COLOR_SIZE );
}
#define SEPARATOR( x, y ) \
rb->lcd_set_foreground( COLOR_WHITE ); \
rb->lcd_vline( x, TOP+y, TOP+y+TB_PL_HEIGHT-1 ); \
rb->lcd_set_foreground( COLOR_DARKGRAY ); \
rb->lcd_vline( x+1, TOP+y, TOP+y+TB_PL_HEIGHT-1 );
SEPARATOR( TB_PL_LEFT + TB_PL_WIDTH - 1 + TB_SP_MARGIN, TB_PL_TOP );
rb->lcd_bitmap_transparent( rockpaint, TB_TL_LEFT, TOP+TB_TL_TOP,
TB_TL_WIDTH, TB_TL_HEIGHT );
rb->lcd_set_foreground(ROCKPAINT_PALETTE);
rb->lcd_drawrect( TB_TL_LEFT+(TB_TL_SIZE+TB_TL_SPACING)*(tool/2),
TOP+TB_TL_TOP+(TB_TL_SIZE+TB_TL_SPACING)*(tool%2),
TB_TL_SIZE, TB_TL_SIZE );
SEPARATOR( TB_TL_LEFT + TB_TL_WIDTH - 1 + TB_SP_MARGIN, TB_TL_TOP );
rb->lcd_setfont( FONT_SYSFIXED );
rb->lcd_putsxy( TB_MENU_LEFT, TOP+TB_MENU_TOP, "Menu" );
rb->lcd_setfont( FONT_UI );
#undef TOP
if( update ) rb->lcd_update();
}
static void toolbar( void )
{
int button, i, j;
restore_screen();
draw_toolbars( false );
y = LCD_HEIGHT-TB_HEIGHT/2;
inv_cursor( true );
while( 1 )
{
switch( button = rb->button_get( true ) )
{
case ROCKPAINT_DRAW:
#define TOP ( LCD_HEIGHT - TB_HEIGHT )
if( y >= TOP + TB_SC_FG_TOP
&& y < TOP + TB_SC_FG_TOP + TB_SC_SIZE
&& x >= TB_SC_FG_LEFT
&& x < TB_SC_FG_LEFT + TB_SC_SIZE )
{
/* click on the foreground color */
rp_colors[drawcolor] = color_chooser( rp_colors[drawcolor] );
}
else if( y >= TOP + TB_SC_BG_TOP
&& y < TOP + TB_SC_BG_TOP + TB_SC_SIZE
&& x >= TB_SC_BG_LEFT
&& x < TB_SC_BG_LEFT + TB_SC_SIZE )
{
/* click on the background color */
i = drawcolor;
drawcolor = bgdrawcolor;
bgdrawcolor = i;
}
else if( y >= TOP + TB_PL_TOP
&& y < TOP + TB_PL_TOP + TB_PL_HEIGHT
&& x >= TB_PL_LEFT
&& x < TB_PL_LEFT + TB_PL_WIDTH )
{
/* click on the palette */
i = (x - TB_PL_LEFT)%(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
j = (y - (TOP+TB_PL_TOP) )%(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
if( i >= TB_PL_COLOR_SIZE || j >= TB_PL_COLOR_SIZE )
break;
i = ( x - TB_PL_LEFT )/(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
j = ( y - (TOP+TB_PL_TOP) )/(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING);
drawcolor = j*(TB_PL_COLOR_SIZE+TB_PL_COLOR_SPACING)+i;
}
else if( y >= TOP+TB_TL_TOP
&& y < TOP + TB_TL_TOP + TB_TL_HEIGHT
&& x >= TB_TL_LEFT
&& x <= TB_TL_LEFT + TB_TL_WIDTH )
{
/* click on the tools */
i = (x - TB_TL_LEFT ) % (TB_TL_SIZE+TB_TL_SPACING);
j = (y - (TOP+TB_TL_TOP) ) %(TB_TL_SIZE+TB_TL_SPACING);
if( i >= TB_TL_SIZE || j >= TB_TL_SIZE ) break;
i = ( x - TB_TL_LEFT )/(TB_TL_SIZE+TB_TL_SPACING);
j = ( y - (TOP+TB_TL_TOP) )/(TB_TL_SIZE+TB_TL_SPACING);
tool = i*2+j;
prev_x = -1;
prev_y = -1;
prev_x2 = -1;
prev_y2 = -1;
prev_x3 = -1;
prev_y3 = -1;
preview = false;
}
else if( x >= TB_MENU_LEFT && y >= TOP+TB_MENU_TOP-2)
{
/* menu button */
goto_menu();
}
#undef TOP
restore_screen();
draw_toolbars( false );
inv_cursor( true );
break;
case ROCKPAINT_LEFT:
case ROCKPAINT_LEFT | BUTTON_REPEAT:
inv_cursor(false);
x-=bspeed * ( button & BUTTON_REPEAT ? 4 : 1 );
if (x<0) x=COLS-1;
inv_cursor(true);
break;
case ROCKPAINT_RIGHT:
case ROCKPAINT_RIGHT | BUTTON_REPEAT:
inv_cursor(false);
x+=bspeed * ( button & BUTTON_REPEAT ? 4 : 1 );
if (x>=COLS) x=0;
inv_cursor(true);
break;
case ROCKPAINT_UP:
case ROCKPAINT_UP | BUTTON_REPEAT:
inv_cursor(false);
y-=bspeed * ( button & BUTTON_REPEAT ? 4 : 1 );
if (y<LCD_HEIGHT-TB_HEIGHT)
{
return;
}
inv_cursor(true);
break;
case ROCKPAINT_DOWN:
case ROCKPAINT_DOWN | BUTTON_REPEAT:
inv_cursor(false);
y+=bspeed * ( button & BUTTON_REPEAT ? 4 : 1 );
if (y>=LCD_HEIGHT)
{
y = 0;
return;
}
inv_cursor(true);
break;
case ROCKPAINT_TOOLBAR:
case ROCKPAINT_TOOLBAR2:
return;
}
if( quit ) return;
}
}
static void inv_cursor(bool update)
{
rb->lcd_set_foreground(COLOR_BLACK);
rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
/* cross painting */
rb->lcd_hline(x-4,x-1,y);
rb->lcd_hline(x+1,x+4,y);
rb->lcd_vline(x,y-4,y-1);
rb->lcd_vline(x,y+1,y+4);
rb->lcd_set_foreground(rp_colors[drawcolor]);
rb->lcd_set_drawmode(DRMODE_SOLID);
if( update ) rb->lcd_update();
}
static void restore_screen(void)
{
rb->lcd_bitmap( save_buffer, 0, 0, COLS, ROWS );
}
static void clear_drawing(void)
{
init_buffer();
rb->lcd_set_foreground( rp_colors[ bgdrawcolor ] );
rb->lcd_fillrect( 0, 0, COLS, ROWS );
rb->lcd_update();
}
static void goto_menu(void)
{
int multi;
int selected = 0;
while( 1 )
{
switch( rb->do_menu( &main_menu, &selected, NULL, false ) )
{
case MAIN_MENU_NEW:
clear_drawing();
return;
case MAIN_MENU_LOAD:
if( browse( filename, MAX_PATH, "/" ) )
{
if( load_bitmap( filename ) <= 0 )
{
rb->splashf( 1*HZ, "Error while loading %s",
filename );
}
else
{
rb->splashf( 1*HZ, "Image loaded (%s)", filename );
restore_screen();
inv_cursor(true);
return;
}
}
break;
case MAIN_MENU_SAVE:
rb->lcd_set_foreground(COLOR_BLACK);
if (!filename[0])
rb->strcpy(filename,"/");
if( !rb->kbd_input( filename, MAX_PATH ) )
{
if(rb->strlen(filename) <= 4 ||
rb->strcasecmp(&filename[rb->strlen(filename)-4], ".bmp"))
rb->strcat(filename, ".bmp");
save_bitmap( filename );
rb->splashf( 1*HZ, "File saved (%s)", filename );
}
break;
case MAIN_MENU_BRUSH_SIZE:
for(multi = 0; multi<4; multi++)
if(bsize == times_list[multi]) break;
rb->do_menu( &size_menu, &multi, NULL, false );
if( multi >= 0 )
bsize = times_list[multi];
break;
case MAIN_MENU_BRUSH_SPEED:
for(multi = 0; multi<3; multi++)
if(bspeed == times_list[multi]) break;
rb->do_menu( &speed_menu, &multi, NULL, false );
if( multi >= 0 )
bspeed = times_list[multi];
break;
case MAIN_MENU_COLOR:
rp_colors[drawcolor] = color_chooser( rp_colors[drawcolor] );
break;
case MAIN_MENU_GRID_SIZE:
for(multi = 0; multi<4; multi++)
if(gridsize == gridsize_list[multi]) break;
rb->do_menu( &gridsize_menu, &multi, NULL, false );
if( multi >= 0 )
gridsize = gridsize_list[multi];
break;
case MAIN_MENU_PLAYBACK_CONTROL:
playback_control( NULL );
break;
case MAIN_MENU_EXIT:
restore_screen();
quit=true;
return;
case MAIN_MENU_RESUME:
default:
restore_screen();
return;
}/* end switch */
}/* end while */
}
static void reset_tool( void )
{
prev_x = -1;
prev_y = -1;
prev_x2 = -1;
prev_y2 = -1;
prev_x3 = -1;
prev_y3 = -1;
tool_mode = -1;
preview = false;
}
static bool rockpaint_loop( void )
{
int button=0,i,j;
int accelaration;
toolbar();
restore_screen();
inv_cursor(true);
while (!quit) {
button = rb->button_get(true);
if( tool == Brush && prev_x != -1 )
{
accelaration = 1;
}
else if( button & BUTTON_REPEAT )
{
accelaration = 4;
}
else
{
accelaration = 1;
}
switch(button)
{
case ROCKPAINT_QUIT:
rb->lcd_set_drawmode(DRMODE_SOLID);
return PLUGIN_OK;
case ROCKPAINT_MENU:
inv_cursor(false);
goto_menu();
restore_screen();
inv_cursor(true);
break;
case ROCKPAINT_DRAW:
inv_cursor(false);
switch( tool )
{
case Brush:
if( prev_x == -1 ) prev_x = 1;
else prev_x = -1;
break;
case SelectRectangle:
case Line:
case Curve:
case Rectangle:
case RectangleFull:
case Oval:
case OvalFull:
case LinearGradient:
case RadialGradient:
/* Curve uses 4 points, others use 2 */
if( prev_x == -1 || prev_y == -1 )
{
prev_x = x;
prev_y = y;
preview = true;
}
else if( tool == Curve
&& ( prev_x2 == -1 || prev_y2 == -1 ) )
{
prev_x2 = x;
prev_y2 = y;
}
else if( tool == SelectRectangle
&& ( prev_x2 == -1 || prev_y2 == -1 ) )
{
tool_mode = rb->do_menu( &select_menu,
NULL, NULL, false );
switch( tool_mode )
{
case SELECT_MENU_CUT:
case SELECT_MENU_COPY:
prev_x2 = x;
prev_y2 = y;
copy_to_clipboard();
if( prev_x < x ) x = prev_x;
if( prev_y < y ) y = prev_y;
break;
case SELECT_MENU_INVERT:
draw_invert( prev_x, prev_y, x, y );
reset_tool();
break;
case SELECT_MENU_HFLIP:
draw_hflip( prev_x, prev_y, x, y );
reset_tool();
break;
case SELECT_MENU_VFLIP:
draw_vflip( prev_x, prev_y, x, y );
reset_tool();
break;
case SELECT_MENU_ROTATE90:
draw_rot_90_deg( prev_x, prev_y, x, y, 1 );
reset_tool();
break;
case SELECT_MENU_ROTATE180:
draw_hflip( prev_x, prev_y, x, y );
draw_vflip( prev_x, prev_y, x, y );
reset_tool();
break;
case SELECT_MENU_ROTATE270:
draw_rot_90_deg( prev_x, prev_y, x, y, -1 );
reset_tool();
break;
case SELECT_MENU_CANCEL:
reset_tool();
break;
default:
break;
}
restore_screen();
}
else if( tool == Curve
&& ( prev_x3 == -1 || prev_y3 == -1 ) )
{
prev_x3 = x;
prev_y3 = y;
}
else
{
preview = false;
switch( tool )
{
case SelectRectangle:
draw_paste_rectangle( prev_x, prev_y,
prev_x2, prev_y2,
x, y, tool_mode );
break;
case Line:
draw_line( prev_x, prev_y, x, y );
break;
case Curve:
draw_curve( prev_x, prev_y,
prev_x2, prev_y2,
prev_x3, prev_y3,
x, y );
break;
case Rectangle:
draw_rect( prev_x, prev_y, x, y );
break;
case RectangleFull:
draw_rect_full( prev_x, prev_y, x, y );
break;
case Oval:
draw_oval_empty( prev_x, prev_y, x, y );
break;
case OvalFull:
draw_oval_full( prev_x, prev_y, x, y );
break;
case LinearGradient:
linear_gradient( prev_x, prev_y, x, y );
break;
case RadialGradient:
radial_gradient( prev_x, prev_y, x, y );
break;
default:
break;
}
reset_tool();
}
break;
case Fill:
draw_fill( x, y );
break;
case ColorPicker:
color_picker( x, y );
break;
case Text:
draw_text( x, y );
break;
default:
break;
}
inv_cursor(true);
break;
case ROCKPAINT_DRAW|BUTTON_REPEAT:
if( tool == Curve )
{
/* 3 point bezier curve */
preview = false;
draw_curve( prev_x, prev_y,
prev_x2, prev_y2,
-1, -1,
x, y );
reset_tool();
restore_screen();
inv_cursor( true );
}
break;
case ROCKPAINT_TOOLBAR:
i = x; j = y;
x = 10;
toolbar();
x = i; y = j;
restore_screen();
inv_cursor(true);
break;
case ROCKPAINT_TOOLBAR2:
i = x; j = y;
x = 110;
toolbar();
x = i; y = j;
restore_screen();
inv_cursor(true);
break;
case ROCKPAINT_LEFT:
case ROCKPAINT_LEFT | BUTTON_REPEAT:
inv_cursor(false);
x-=bspeed * accelaration;
if (x<0) x=COLS-1;
inv_cursor(true);
break;
case ROCKPAINT_RIGHT:
case ROCKPAINT_RIGHT | BUTTON_REPEAT:
inv_cursor(false);
x+=bspeed * accelaration;
if (x>=COLS) x=0;
inv_cursor(true);
break;
case ROCKPAINT_UP:
case ROCKPAINT_UP | BUTTON_REPEAT:
inv_cursor(false);
y-=bspeed * accelaration;
if (y<0) y=ROWS-1;
inv_cursor(true);
break;
case ROCKPAINT_DOWN:
case ROCKPAINT_DOWN | BUTTON_REPEAT:
inv_cursor(false);
y+=bspeed * accelaration;
if (y>=ROWS)
{
toolbar();
restore_screen();
}
inv_cursor(true);
break;
default:
if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
return PLUGIN_USB_CONNECTED;
break;
}
if( tool == Brush && prev_x == 1 )
{
inv_cursor(false);
draw_brush( x, y );
inv_cursor(true);
}
if( preview || tool == ColorPicker )
/* always preview color picker */
{
restore_screen();
switch( tool )
{
case SelectRectangle:
if( prev_x2 == -1 || prev_y2 == -1 )
{
/* we are defining the selection */
draw_select_rectangle( prev_x, prev_y, x, y );
}
else
{
/* we are pasting the selected data */
draw_paste_rectangle( prev_x, prev_y, prev_x2,
prev_y2, x, y, tool_mode );
prev_x3 = prev_x2-prev_x;
if( prev_x3 < 0 ) prev_x3 *= -1;
prev_y3 = prev_y2-prev_y;
if( prev_y3 < 0 ) prev_y3 *= -1;
draw_select_rectangle( x, y, x+prev_x3, y+prev_y3 );
prev_x3 = -1;
prev_y3 = -1;
}
break;
case Brush:
break;
case Line:
draw_line( prev_x, prev_y, x, y );
break;
case Curve:
if( prev_x2 == -1 || prev_y2 == -1 )
{
draw_line( prev_x, prev_y, x, y );
}
else
{
draw_curve( prev_x, prev_y,
prev_x2, prev_y2,
prev_x3, prev_y3,
x, y );
}
break;
case Rectangle:
draw_rect( prev_x, prev_y, x, y );
break;
case RectangleFull:
draw_rect_full( prev_x, prev_y, x, y );
break;
case Oval:
draw_oval_empty( prev_x, prev_y, x, y );
break;
case OvalFull:
draw_oval_full( prev_x, prev_y, x, y );
break;
case Fill:
break;
case ColorPicker:
preview = true;
color_picker( x, y );
preview = false;
break;
case LinearGradient:
line_gradient( prev_x, prev_y, x, y );
break;
case RadialGradient:
line_gradient( prev_x, prev_y, x, y );
break;
case Text:
default:
break;
}
inv_cursor( true );
}
if( gridsize > 0 )
{
show_grid( true );
show_grid( false );
}
}
return PLUGIN_OK;
}
static int load_bitmap( const char *file )
{
struct bitmap bm;
bool ret;
int l;
bm.data = (char*)save_buffer;
ret = rb->read_bmp_file( file, &bm, ROWS*COLS*sizeof( fb_data ),
FORMAT_NATIVE, NULL );
if((bm.width > COLS ) || ( bm.height > ROWS ))
return -1;
for( l = bm.height-1; l > 0; l-- )
{
rb->memmove( save_buffer+l*COLS, save_buffer+l*bm.width,
sizeof( fb_data )*bm.width );
}
for( l = 0; l < bm.height; l++ )
{
rb->memset( save_buffer+l*COLS+bm.width, rp_colors[ bgdrawcolor ],
sizeof( fb_data )*(COLS-bm.width) );
}
rb->memset( save_buffer+COLS*bm.height, rp_colors[ bgdrawcolor ],
sizeof( fb_data )*COLS*(ROWS-bm.height) );
return ret;
}
static int save_bitmap( char *file )
{
struct bitmap bm;
bm.data = (char*)save_buffer;
bm.height = ROWS;
bm.width = COLS;
bm.format = FORMAT_NATIVE;
return save_bmp_file( file, &bm );
}
enum plugin_status plugin_start(const void* parameter)
{
rb->lcd_set_foreground(COLOR_WHITE);
rb->lcd_set_backdrop(NULL);
rb->lcd_fillrect(0,0,LCD_WIDTH,LCD_HEIGHT);
rb->splash( HZ/2, "Rock Paint");
rb->lcd_clear_display();
filename[0] = '\0';
if( parameter )
{
if( load_bitmap( parameter ) <= 0 )
{
rb->splash( 1*HZ, "File Open Error");
clear_drawing();
}
else
{
rb->splashf( 1*HZ, "Image loaded (%s)", (char *)parameter );
restore_screen();
rb->strcpy( filename, parameter );
}
}
else
{
clear_drawing();
}
inv_cursor(true);
return rockpaint_loop();
}