From dd3bd4c2a8203dc0a2199df2b65a1f32f66ae507 Mon Sep 17 00:00:00 2001 From: bernie Date: Wed, 19 Jul 2006 13:00:01 +0000 Subject: [PATCH] Import into DevLib. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@672 38d2e660-2303-0410-9eaa-f027e97ec537 --- gfx/fillpoly.cpp | 380 +++++++++++++++++++++++++++++++++++++++++++++++ gui/levelbar.c | 106 +++++++++++++ gui/levelbar.h | 41 +++++ gui/leveledit.c | 305 +++++++++++++++++++++++++++++++++++++ gui/leveledit.h | 64 ++++++++ 5 files changed, 896 insertions(+) create mode 100755 gfx/fillpoly.cpp create mode 100755 gui/levelbar.c create mode 100755 gui/levelbar.h create mode 100755 gui/leveledit.c create mode 100755 gui/leveledit.h diff --git a/gfx/fillpoly.cpp b/gfx/fillpoly.cpp new file mode 100755 index 00000000..25be11a0 --- /dev/null +++ b/gfx/fillpoly.cpp @@ -0,0 +1,380 @@ +/** + * \file + * + * + * \version $Id$ + * \author Massimiliano Corsini + * + * + * \brief Low-level drawing routines. + * + * This file contains the implementation of the low-level drawing routines + * to draw fill rectangle, fill triangle and so on. + * + */ + +/*#* + *#* $Log$ + *#* Revision 1.1 2006/07/19 13:00:01 bernie + *#* Import into DevLib. + *#* + *#* Revision 1.10 2005/10/15 15:03:43 rasky + *#* Remove per-pixel clipping from line(). + *#* Use clipLine() also for a-scope. + *#* + *#* Revision 1.9 2005/10/14 15:21:32 eldes + *#* Implement the cohen-sutherland clipping on the buffer + *#* + *#* Revision 1.8 2005/09/27 13:28:10 rasky + *#* Add clipping capabilities to line() + *#* Fix off-by-one computation of rectangles of drawing. + *#* + *#* Revision 1.7 2005/09/27 10:41:35 rasky + *#* Import line-drawing routine from Devlib + *#* + *#* Revision 1.6 2005/09/19 16:36:05 chad + *#* Fix doxygen autobrief + *#* + *#* Revision 1.5 2005/07/06 12:51:47 chad + *#* Make the fillRectangle() independent of the order of the points of the rectangle + *#* + *#* Revision 1.4 2005/06/17 15:06:36 chad + *#* Remove conversion warning + *#* + *#* Revision 1.3 2005/06/17 15:04:47 chad + *#* Add line clipping capability + *#* + *#* Revision 1.2 2005/06/15 14:04:43 chad + *#* Add line routine + *#* + *#* Revision 1.1 2005/06/15 13:34:34 chad + *#* Low-level drawing routines + *#* + *#*/ + +// Qt-specific headers +#include + + +/** + * Low-level routine to draw a line. + * + * This routine is based on the Bresenham Line-Drawing Algorithm. + * + * The \a stride represents the width of the image buffer. + * (\a x1, \a y1) are the coordinates of the starting point. + * (\a x2, \a y2) are the coordinates of the ending point. + * + * The line has no anti-alias, and clipping is not performed. The line + * must be fully contained in the buffer (use clipLine() if you need + * to clip it). + */ +void line(unsigned char *buf, + unsigned long bufw, unsigned long bufh, unsigned long stride, + int x1, int y1, int x2, int y2, unsigned char color) +{ + int x, y, e, len, adx, ady, signx, signy; + + if (x2 > x1) + { + /* left to right */ + signx = +1; + adx = x2 - x1; + } + else + { + /* right to left */ + signx = -1; + adx = x1 - x2; + } + + if (y2 > y1) + { + /* top to bottom */ + signy = +1; + ady = y2 - y1; + } + else + { + /* bottom to top */ + signy = -1; + ady = y1 - y2; + } + + x = x1; + y = y1; + + if (adx > ady) + { + /* X-major line (octants 1/4/5/8) */ + len = adx; + e = -adx; + while (len--) + { + /* Sanity check */ + assert(y >= 0 && y < static_cast(bufh) && + x >= 0 && x < static_cast(bufw)); + buf[y * stride + x] = color; + x += signx; + e += ady; + if (e >= 0) + { + y += signy; + e -= adx; + } + } + } + else + { + /* Y-major line (octants 2/3/6/7) */ + len = ady; + e = -ady; + while (len--) + { + /* Sanity check */ + assert(y >= 0 && y < static_cast(bufh) && + x >= 0 && x < static_cast(bufw)); + buf[y * stride + x] = color; + y += signy; + e += adx; + if (e >= 0) + { + x += signx; + e -= ady; + } + } + } +} + +/// Helper routine for clipLine(). +static int region(int x, int y, int w, int h) +{ + int code = 0; + + if (y >= h) + code |= 1; // top + else if (y < 0) + code |= 2; // bottom + + if (x >= w) + code |= 4; // right + else if (x < 0) + code |= 8; // left + + return code; +} + +/** + * Low-level routine to draw a line, clipped to the buffer extents. + * + * This routine executes the clipping, and then invokes line(). + * Parameters are the same of line(). The clipping is performed + * using the Cohen-Sutherland algorithm, which is very fast. + */ +void clipLine(unsigned char *buf, + unsigned long w, unsigned long h, unsigned long stride, + int x1, int y1, int x2, int y2, unsigned char color) +{ + int code1 = region(x1, y1, w, h); + int code2 = region(x2, y2, w, h); + + // Loop while there is at least one point outside + while (code1 | code2) + { + // Check for line totally outside + if (code1 & code2) + return; + + int c = code1 ? code1 : code2; + int x, y; + + if (c & 1) // top + { + x = x1 + (x2 - x1) * (h - y1) / (y2 - y1); + y = h - 1; + } + else if (c & 2) //bottom + { + x = x1 + (x2 - x1) * -y1 / (y2 - y1); + y = 0; + } + else if (c & 4) //right + { + y = y1 + (y2 - y1) * (w - x1) / (x2 - x1); + x = w - 1; + } + else //left + { + y = y1 + (y2 - y1) * -x1 / (x2 - x1); + x = 0; + } + + if (c == code1) // first endpoint was clipped + { + x1 = x; y1 = y; + code1 = region(x1, y1, w, h); + } + else //second endpoint was clipped + { + x2 = x; y2 = y; + code2 = region(x2, y2, w, h); + } + } + + line(buf, w, h, stride, x1, y1, x2, y2, color); +} + + +/** + * Low-level routine to draw a filled rectangle. + * + * The triangle is filled with the given color. + * + * The \a stride represents the width of the image buffer. + * The points \a p1 and \a p2 are two opposite corners of the + * rectangle. + */ +void fillRectangle(unsigned char *buf, unsigned long stride, + QPoint p1, QPoint p2, unsigned char color) +{ + QPoint ul; // upper-left corner + QPoint lr; // lower-right corner + + if (p2.x() > p1.x()) + { + ul.setX(p1.x()); + lr.setX(p2.x()); + } + else + { + ul.setX(p2.x()); + lr.setX(p1.x()); + } + + if (p2.y() > p1.y()) + { + ul.setY(p1.y()); + lr.setY(p2.y()); + } + else + { + ul.setY(p2.y()); + lr.setY(p1.y()); + } + + int width = lr.x() - ul.x(); + unsigned long offset = ul.x() + ul.y()*stride; + + for (int h = ul.y(); h < lr.y(); h++) + { + memset(buf+offset, color, width); + offset += stride; + } +} + +/** + * Low-level routines to draw a filled triangle. + * + * The triangle is filled with the given \a color. + * The \a stride represents the width of the image buffer (\a buf). + * + * The routine use fixed-point arithmetic. + */ +void fillTriangle(unsigned char* buf, unsigned long stride, + QPoint v1, QPoint v2, QPoint v3, unsigned char color) +{ + int altezza[3]; + + // Sort by vertical coordinate + if (v1.y() > v2.y()) + std::swap(v1, v2); + if (v1.y() > v3.y()) + std::swap(v1, v3); + if (v2.y() > v3.y()) + std::swap(v2, v3); + + altezza[0] = v3.y() - v1.y(); + if (!altezza[0]) + return; + + int sezioni = 2; + int sezione = 1; + + buf += v1.y() * stride; + + altezza[1] = v2.y() - v1.y(); + altezza[2] = v3.y() - v2.y(); + + int sinistra = v1.x(); + int destra = sinistra; + + if (v1.y() == v2.y()) + { + if (v1.x() < v2.x()) + destra = v2.x(); + else + sinistra = v2.x(); + } + + sinistra <<= 16; + destra <<= 16; + + int stmp1, stmp2, stmp3; + + stmp1 = (altezza[1] << 16) / altezza[0]; + int lunghezza = stmp1 * (v3.x() - v1.x()) + ((v1.x() - v2.x()) << 16); + + if (!lunghezza ) + return; + + int delta_sinistra[2]; + int delta_destra[2]; + + stmp1 = ((v3.x() - v1.x()) << 16) / altezza[0]; + + if (altezza[1]) + stmp2 = ((v2.x() - v1.x()) << 16) / altezza[1]; + if (altezza[2]) + stmp3 = ((v3.x() - v2.x()) << 16) / altezza[2]; + + if (lunghezza < 0) // Il secondo vertice ~J a destra + { + delta_sinistra[0] = stmp1; + delta_sinistra[1] = stmp1; + delta_destra[0] = stmp2; + delta_destra[1] = stmp3; + } + else // Il secondo vertice ~J a sinistra + { + delta_sinistra[0] = stmp2; + delta_sinistra[1] = stmp3; + delta_destra[0] = stmp1; + delta_destra[1] = stmp1; + } + + int len2 = lunghezza; + + do + { + while (altezza [sezione]) + { + unsigned char* curpos = buf + ((sinistra )>> 16); + lunghezza = ((destra ) >> 16) - ((sinistra ) >> 16); + assert(lunghezza >= 0); + if (lunghezza) + memset(curpos, color, lunghezza); + buf += stride; + destra += delta_destra[sezione - 1]; + sinistra += delta_sinistra[sezione - 1]; + altezza[sezione]--; + } + if (len2 < 0) + destra = v2.x() << 16; + else + sinistra = v2.x() << 16; + sezione++; + } while (--sezioni); +} diff --git a/gui/levelbar.c b/gui/levelbar.c new file mode 100755 index 00000000..591d257b --- /dev/null +++ b/gui/levelbar.c @@ -0,0 +1,106 @@ +/** + * \file + * Copyright 2004, 2006 Develer S.r.l. (http://www.develer.com/) + * This file is part of DevLib - See README.devlib for information. + * + * \brief Graphics user interface element to display a level bar. + * + * \version $Id$ + * \author Stefano Fedrigo + */ + +#include "levelbar.h" + + +/** + * Initialize the LevelBar widget with the bitmap associated, + * the value range and the coordinates in the bitmap. + * \note The levelbar should be at least 5 pixels wide and high + * for correct borders drawing. No check is done on this. + */ +void lbar_init(struct LevelBar *lb, struct Bitmap *bmp, int type, int min, int max, int pos, + coord_t x1, coord_t y1, coord_t x2, coord_t y2) +{ + lb->bitmap = bmp; + lb->type = type; + lb->min = min; + lb->max = max; + lb->pos = pos; + lb->x1 = x1; + lb->y1 = y1; + lb->x2 = x2; + lb->y2 = y2; +} + + +/** + * Set the level. + */ +void lbar_setLevel(struct LevelBar *lb, int level) +{ + if (level < lb->min) + level = lb->min; + if (level > lb->max) + level = lb->max; + + lb->pos = level; +} + + +/** + * Get current level. + */ +int lbar_getLevel(struct LevelBar *lb) +{ + return lb->pos; +} + + +/** + * Change level with respect to previous value + * (delta can be negative). + */ +void lbar_changeLevel(struct LevelBar *lb, int delta) +{ + lbar_setLevel(lb, lb->pos + delta); +} + + +/** + * Change the top limit. + */ +void lbar_setMax(struct LevelBar *lb, int max) +{ + lb->max = max; +} + + +/** + * Render the LevelBar on the bitmap. + */ +void lbar_draw(struct LevelBar *lb) +{ +#define BORDERW 1 +#define BORDERH 1 + + /* Compute filled bar length in pixels */ + int totlen = (lb->type & LBAR_HORIZONTAL) ? lb->x2 - lb->x1 - BORDERW*4 : lb->y2 - lb->y1 - BORDERH*4; + int range = lb->max - lb->min; + int barlen = ((long)(lb->pos - lb->min) * (long)totlen + range - 1) / range; + + // Draw border + gfx_rectDraw(lb->bitmap, lb->x1, lb->y1, lb->x2, lb->y2); + + // Clear inside + gfx_rectClear(lb->bitmap, lb->x1 + BORDERW, lb->y1 + BORDERH, lb->x2 - BORDERW, lb->y2 - BORDERH); + + // Draw bar + if (lb->type & LBAR_HORIZONTAL) + gfx_rectFill(lb->bitmap, + lb->x1 + BORDERW*2, lb->y1 + BORDERH*2, + lb->x1 + BORDERW*2 + barlen, lb->y2 - BORDERH*2); + else + gfx_rectFill(lb->bitmap, + lb->x1 + BORDERW*2, lb->y2 - BORDERH*2 - barlen, + lb->x2 - BORDERW*2, lb->y2 - BORDERH*2); +} diff --git a/gui/levelbar.h b/gui/levelbar.h new file mode 100755 index 00000000..ad8e0cd7 --- /dev/null +++ b/gui/levelbar.h @@ -0,0 +1,41 @@ +/** + * \file + * Copyright 2004, 2006 Develer S.r.l. (http://www.develer.com/) + * This file is part of DevLib - See README.devlib for information. + * + * \version $Id$ + * + * \author Stefano Fedrigo + * + * \brief Graphics level bar widget + */ + +#ifndef GUI_LEVELBAR_H +#define GUI_LEVELBAR_H + +#include + + +/** Type of levelbar */ +#define LBAR_HORIZONTAL 1 +#define LBAR_VERTICAL 2 + +typedef struct LevelBar +{ + struct Bitmap *bitmap; + int type; + int pos; ///< Current level + int min; ///< Minimum level + int max; ///< Maximum level + coord_t x1, y1, x2, y2; ///< Position of widget in the bitmap +} LevelBar; + +void lbar_init(struct LevelBar *lb, struct Bitmap *bmp, int type, int min, int max, int pos, + coord_t x1, coord_t y1, coord_t x2, coord_t y2); +void lbar_setLevel(struct LevelBar *lb, int level); +int lbar_getLevel(struct LevelBar *lb); +void lbar_changeLevel(struct LevelBar *lb, int delta); +void lbar_setMax(struct LevelBar *lb, int max); +void lbar_draw(struct LevelBar *lb); + +#endif /* GUI_LEVELBAR_H */ diff --git a/gui/leveledit.c b/gui/leveledit.c new file mode 100755 index 00000000..e412223a --- /dev/null +++ b/gui/leveledit.c @@ -0,0 +1,305 @@ +/** + * \file + * + * + * \brief Generic editor for (volume/gain/contrast/...) setting. + * + * \version $Id$ + * \author Stefano Fedrigo + */ + +#include "leveledit.h" + +#include /* MAX() */ +#include +#include +#include +#include +#include +#include +#include + +#if CONFIG_MENU_MENUBAR +#include +#endif + +// BEGIN project_grl LOCAL +#include +#include +// END project_grl LOCAL + +#define LBAR_HEIGHT 16 + +/** + * Allow user to change level. + */ +void level_edit(struct LevelEdit *lev) +{ +#if CONFIG_MENU_MENUBAR + /* Labels for menubars */ + enum LabelId ch_labels[] = { LABEL_C1PLUS2, LABEL_CH_1, LABEL_CH_2 }; + const_iptr_t labels[] = + { + (const_iptr_t)LABEL_BACK, + (const_iptr_t)LABEL_MINUS, + (const_iptr_t)LABEL_PLUS, + (const_iptr_t)LABEL_EMPTY + }; + struct MenuBar mb; +#endif /* CONFIG_MENU_MENUBAR */ + + struct LevelBar bar1, bar2; + keymask_t keys, old_rpt_mask; + int step, rep_step; + + rep_step = MAX(lev->step, ((lev->max - lev->min) / 200)); + step = lev->step; + + // Allow keys repetition. + old_rpt_mask = kbd_setRepeatMask(K_UP | K_DOWN); + + text_clear(lev->bitmap); + //text_style(STYLEF_UNDERLINE, STYLEF_UNDERLINE); + text_puts(lev->title, lev->bitmap); + //text_style(0, STYLEF_UNDERLINE); + + if (lev->type & LEVELEDIT_DOUBLE) + { + int chn = 0; /* edit both channels */ + + /* Levelbars init */ + lbar_init(&bar1, lev->bitmap, LBAR_HORIZONTAL, + lev->min, lev->max, *lev->ch1_val, 0, 16, lev->bitmap->width / 2 - 1, 23); + lbar_init(&bar2, lev->bitmap, LBAR_HORIZONTAL, + lev->min, lev->max, *lev->ch2_val, lev->bitmap->width / 2 + 1, 16, lev->bitmap->width, 23); + + #if CONFIG_MENU_MENUBAR + labels[3] = (const_iptr_t)ch_labels[chn]; + mbar_init(&mb, lev->bitmap, labels, countof(labels)); + mbar_draw(&mb); + #endif /* CONFIG_MENU_MENUBAR */ + + /* Input loop for double level setting */ + for (;;) + { + #if CONFIG_LEVELEDIT_TIMEOUT != 0 + ticks_t idle_timeout = timer_clock(); + #endif + do + { + if (lev->display_hook) + lev->display_hook(lev); + else + { + text_xprintf(lev->bitmap, 1, 0, TEXT_CENTER | TEXT_FILL, lev->unit); + PGM_FUNC(text_xprintf)(lev->bitmap, 1, 3, 0, PGM_STR("%d"), *lev->ch1_val); + PGM_FUNC(text_xprintf)(lev->bitmap, 1, 14, 0, PGM_STR("%d"), *lev->ch2_val); + + lbar_setLevel(&bar1, *lev->ch1_val); + lbar_setLevel(&bar2, *lev->ch2_val); + lbar_draw(&bar1); + lbar_draw(&bar2); + } + + #if CONFIG_LEVELEDIT_TIMEOUT != 0 + if (timer_clock() - idle_timeout > ms_to_ticks(CONFIG_LEVELEDIT_TIMEOUT)) + { + /* Accept input implicitly */ + keys = K_OK; + break; + } + #endif + } + while (!(keys = kbd_peek())); + + if (keys & K_CANCEL) + break; + + if (keys & K_OK) + { + chn = (chn + 1) % 3; + + #if CONFIG_MENU_MENUBAR + labels[3] = (const_iptr_t)ch_labels[chn]; + mbar_draw(&mb); + #endif /* CONFIG_MENU_MENUBAR */ + } + + /* Increment step to achieve greater accelerations on larger values */ + if (keys & K_REPEAT) + step = MIN(rep_step, step + 1); + else + step = lev->step; + + if (keys & (K_UP | K_DOWN)) + { + if (keys & K_UP) + { + /* If changing both channels (chn == 0), don't change + * level if one of two is at min or max */ + if (chn != 0 || + (*lev->ch1_val + step <= lev->max + && *lev->ch2_val + step <= lev->max)) + { + /* If chn == 0 change both channels */ + if (chn != 2) + { + *lev->ch1_val += step; + if (*lev->ch1_val > lev->max) + *lev->ch1_val = lev->max; + } + if (chn != 1) + { + *lev->ch2_val += step; + if (*lev->ch2_val > lev->max) + *lev->ch2_val = lev->max; + } + } + } + else + { + if (chn != 0 || + (*lev->ch1_val - step >= lev->min + && *lev->ch2_val - step >= lev->min)) + { + if (chn != 2) + { + *lev->ch1_val -= step; + if (*lev->ch1_val < lev->min) + *lev->ch1_val = lev->min; + } + if (chn != 1) + { + *lev->ch2_val -= step; + if (*lev->ch2_val < lev->min) + *lev->ch2_val = lev->min; + } + } + } + + if (lev->set_hook) + lev->set_hook(); + } + } // end for(;;) + } + else + { + const PGM_ATTR char *fmt = lev->unit ? PGM_STR("%d %s") : PGM_STR("%d"); + +/* + const int textw = MAX(PGM_FUNC(text_widthf)(lev->bitmap, fmt, lev->max, lev->unit), + PGM_FUNC(text_widthf)(lev->bitmap, fmt, lev->min, lev->unit)); + + const coord_t barlen = lev->bitmap->width - 6 - textw; +*/ + const coord_t barlen = lev->bitmap->width; + const coord_t barvtop = lev->bitmap->height / 2 - LBAR_HEIGHT/2 + lev->bitmap->font->height; + lbar_init(&bar1, lev->bitmap, LBAR_HORIZONTAL, + lev->min, lev->max, *lev->ch1_val, + 0, barvtop, barlen, barvtop + LBAR_HEIGHT); + + #if CONFIG_MENU_MENUBAR + mbar_init(&mb, lev->bitmap, labels, countof(labels)); + mbar_draw(&mb); + #endif /* CONFIG_MENU_MENUBAR */ + + /* Input loop for single level setting */ + for (;;) + { + #if CONFIG_LEVELEDIT_TIMEOUT != 0 + ticks_t idle_timeout = timer_clock(); + #endif + do + { + if (lev->display_hook) + lev->display_hook(lev); + else + { + if (lev->type != LEVELEDIT_NOBAR) + { + lbar_setLevel(&bar1, *lev->ch1_val); + lbar_draw(&bar1); + } + PGM_FUNC(text_xyprintf)(lev->bitmap, 0, bar1.y1 - lev->bitmap->font->height, + TEXT_CENTER | TEXT_FILL, fmt, *lev->ch1_val, lev->unit); + } + + #if CONFIG_LEVELEDIT_TIMEOUT != 0 + if (timer_clock() - idle_timeout > CONFIG_LEVELEDIT_TIMEOUT) + { + /* Accept input implicitly */ + keys = K_CANCEL; + break; + } + #endif + +// BEGIN project_grl LOCAL +#if 1 + //FIXME: must to this only when needed + lcd_blitBitmap(&lcd_bitmap); + } + while (!(keys = GuiMan_KbdPeek())); +#else + } + while (!(keys = kbd_peek())); +#endif +// END project_grl LOCAL + + if (keys & K_CANCEL) + break; + + /* Increment step to achieve greater accelerations on larger values */ + if (keys & K_REPEAT) + step = MIN(rep_step, step + 1); + else + step = lev->step; + + if (keys & K_UP) + { + *lev->ch1_val += step; + if (*lev->ch1_val > lev->max) + *lev->ch1_val = lev->max; + } + + if (keys & K_DOWN) + { + *lev->ch1_val -= step; + if (*lev->ch1_val < lev->min) + *lev->ch1_val = lev->min; + } + + if (lev->set_hook) + lev->set_hook(); + } + } + + kbd_setRepeatMask(old_rpt_mask); +} + +/** + * LevelEdit structure initialization. + * Init data structure and init LevelEdit widgets. + */ +void level_init(struct LevelEdit *lev, + int type, + struct Bitmap *bmp, const char *title, const char *unit, + int min, int max, int step, + int *ch1_val, int *ch2_val, + level_set_callback *set_hook, display_callback *display_hook) +{ + lev->type = type; + lev->bitmap = bmp; + lev->title = title; + lev->unit = unit; + lev->min = min; + lev->max = max; + lev->step = step; + + lev->ch1_val = ch1_val; + lev->ch2_val = ch2_val; + lev->set_hook = set_hook; + lev->display_hook = display_hook; +} diff --git a/gui/leveledit.h b/gui/leveledit.h new file mode 100755 index 00000000..0b496661 --- /dev/null +++ b/gui/leveledit.h @@ -0,0 +1,64 @@ +/** + * \file + * + * + * \brief Generic editor for (volume/gain/contrast/...) setting. + * + * \version $Id$ + * + * \author Stefano Fedrigo + */ +#ifndef GUI_LEVELEDIT_H +#define GUI_LEVELEDIT_H + +//#include + +/* Type for level_init */ +#define LEVELEDIT_NOBAR 0 /**< Edit numeber only, without bar nor units */ +#define LEVELEDIT_SINGLE 1 /**< Single channel editing */ +#define LEVELEDIT_DOUBLE 2 /**< Double channel editing */ + + + +/* Fwd decl */ +struct Bitmap; +struct LevelEdit; + +/** Type for callback used to set meter levels */ +typedef void level_set_callback(void); + +/** Type for callback used to customize display of units */ +typedef void display_callback(struct LevelEdit *); + +/** + * State of a level meter + */ +typedef struct LevelEdit { + int type; /*