blob: 081a7a5267eef81647a1f6d705fffa03891c5c78 [file] [log] [blame]
Björn Stenberg86f9a842002-09-23 11:17:52 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Alan Korr
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#include "config.h"
20
Björn Stenberg86f9a842002-09-23 11:17:52 +000021#include "lcd.h"
22#include "kernel.h"
23#include "thread.h"
24#include <string.h>
25#include <stdlib.h>
26#include "file.h"
27#include "debug.h"
28#include "system.h"
29#include "font.h"
Björn Stenberg067262d2003-07-09 23:07:49 +000030#include "hwcompat.h"
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +000031#include "rbunicode.h"
Linus Nielsen Feltzing41a53d22005-08-08 19:23:28 +000032#include "bidi.h"
Björn Stenberg86f9a842002-09-23 11:17:52 +000033
34/*** definitions ***/
35
36#define LCD_SET_LOWER_COLUMN_ADDRESS ((char)0x00)
37#define LCD_SET_HIGHER_COLUMN_ADDRESS ((char)0x10)
38#define LCD_SET_INTERNAL_REGULATOR_RESISTOR_RATIO ((char)0x20)
39#define LCD_SET_POWER_CONTROL_REGISTER ((char)0x28)
40#define LCD_SET_DISPLAY_START_LINE ((char)0x40)
41#define LCD_SET_CONTRAST_CONTROL_REGISTER ((char)0x81)
42#define LCD_SET_SEGMENT_REMAP ((char)0xA0)
43#define LCD_SET_LCD_BIAS ((char)0xA2)
44#define LCD_SET_ENTIRE_DISPLAY_OFF ((char)0xA4)
45#define LCD_SET_ENTIRE_DISPLAY_ON ((char)0xA5)
46#define LCD_SET_NORMAL_DISPLAY ((char)0xA6)
47#define LCD_SET_REVERSE_DISPLAY ((char)0xA7)
Jörg Hohensohnd3fba462003-06-26 06:49:34 +000048#define LCD_SET_MULTIPLEX_RATIO ((char)0xA8)
49#define LCD_SET_BIAS_TC_OSC ((char)0xA9)
50#define LCD_SET_1OVER4_BIAS_RATIO ((char)0xAA)
Björn Stenberg86f9a842002-09-23 11:17:52 +000051#define LCD_SET_INDICATOR_OFF ((char)0xAC)
52#define LCD_SET_INDICATOR_ON ((char)0xAD)
53#define LCD_SET_DISPLAY_OFF ((char)0xAE)
54#define LCD_SET_DISPLAY_ON ((char)0xAF)
55#define LCD_SET_PAGE_ADDRESS ((char)0xB0)
56#define LCD_SET_COM_OUTPUT_SCAN_DIRECTION ((char)0xC0)
Jörg Hohensohnd3fba462003-06-26 06:49:34 +000057#define LCD_SET_TOTAL_FRAME_PHASES ((char)0xD2)
Björn Stenberg86f9a842002-09-23 11:17:52 +000058#define LCD_SET_DISPLAY_OFFSET ((char)0xD3)
59#define LCD_SET_READ_MODIFY_WRITE_MODE ((char)0xE0)
60#define LCD_SOFTWARE_RESET ((char)0xE2)
61#define LCD_NOP ((char)0xE3)
62#define LCD_SET_END_OF_READ_MODIFY_WRITE_MODE ((char)0xEE)
63
64/* LCD command codes */
Björn Stenbergdf194b02003-02-23 19:02:31 +000065#define LCD_CNTL_RESET 0xe2 /* Software reset */
66#define LCD_CNTL_POWER 0x2f /* Power control */
67#define LCD_CNTL_CONTRAST 0x81 /* Contrast */
68#define LCD_CNTL_OUTSCAN 0xc8 /* Output scan direction */
69#define LCD_CNTL_SEGREMAP 0xa1 /* Segment remap */
70#define LCD_CNTL_DISPON 0xaf /* Display on */
Björn Stenberg86f9a842002-09-23 11:17:52 +000071
Björn Stenbergdf194b02003-02-23 19:02:31 +000072#define LCD_CNTL_PAGE 0xb0 /* Page address */
73#define LCD_CNTL_HIGHCOL 0x10 /* Upper column address */
74#define LCD_CNTL_LOWCOL 0x00 /* Lower column address */
Björn Stenberg86f9a842002-09-23 11:17:52 +000075
Linus Nielsen Feltzing7c931a12004-07-09 11:21:13 +000076#define SCROLLABLE_LINES 13
Markus Braunbc254fe2002-10-21 13:14:25 +000077
Jens Arnold6a556c12005-06-23 16:53:54 +000078/*** globals ***/
Björn Stenberg86f9a842002-09-23 11:17:52 +000079
Jens Arnold6a556c12005-06-23 16:53:54 +000080unsigned char lcd_framebuffer[LCD_HEIGHT/8][LCD_WIDTH];
81
Jens Arnold04daef12005-06-24 22:33:21 +000082static int drawmode = DRMODE_SOLID;
Jens Arnold6a556c12005-06-23 16:53:54 +000083static int xmargin = 0;
84static int ymargin = 0;
85static int curfont = FONT_SYSFIXED;
86#ifndef SIMULATOR
87static int xoffset; /* needed for flip */
88#endif
89
Jens Arnold6a556c12005-06-23 16:53:54 +000090/* scrolling */
Daniel Stenbergedf4d8c2003-01-28 15:23:48 +000091static volatile int scrolling_lines=0; /* Bitpattern of which lines are scrolling */
Björn Stenberg86f9a842002-09-23 11:17:52 +000092static void scroll_thread(void);
93static char scroll_stack[DEFAULT_STACK_SIZE];
Jens Arnold2b0694c2004-08-03 05:58:46 +000094static const char scroll_name[] = "scroll";
Jens Arnold05af2802005-07-28 08:36:24 +000095static int scroll_ticks = 12; /* # of ticks between updates*/
Björn Stenberga2e98c12002-12-12 15:20:37 +000096static int scroll_delay = HZ/2; /* ticks delay before start */
Jens Arnold05af2802005-07-28 08:36:24 +000097static int scroll_step = 6; /* pixels per scroll step */
Björn Stenberga2e98c12002-12-12 15:20:37 +000098static int bidir_limit = 50; /* percent */
Markus Braunbc254fe2002-10-21 13:14:25 +000099static struct scrollinfo scroll[SCROLLABLE_LINES];
Björn Stenberg86f9a842002-09-23 11:17:52 +0000100
Jens Arnold6a556c12005-06-23 16:53:54 +0000101static const char scroll_tick_table[16] = {
102 /* Hz values:
103 1, 1.25, 1.55, 2, 2.5, 3.12, 4, 5, 6.25, 8.33, 10, 12.5, 16.7, 20, 25, 33 */
104 100, 80, 64, 50, 40, 32, 25, 20, 16, 12, 10, 8, 6, 5, 4, 3
105};
Björn Stenberg86f9a842002-09-23 11:17:52 +0000106
Jens Arnold6a556c12005-06-23 16:53:54 +0000107/*** driver routines ***/
Björn Stenberg86f9a842002-09-23 11:17:52 +0000108
Jens Arnold6a556c12005-06-23 16:53:54 +0000109/* optimised archos recorder code is in lcd.S */
Jean-Philippe Bernardycacc37d2005-01-20 22:13:41 +0000110
111#if CONFIG_CPU == TCC730
112/* Optimization opportunity:
113 In the following I do exactly as in the archos firmware.
Jens Arnold6a556c12005-06-23 16:53:54 +0000114 There is probably a better way (ie. do only one mask operation)
Jean-Philippe Bernardycacc37d2005-01-20 22:13:41 +0000115*/
116void lcd_write_command(int cmd) {
Jean-Philippe Bernardye386d942005-01-20 22:44:35 +0000117 P2 &= 0xF7;
Jean-Philippe Bernardycacc37d2005-01-20 22:13:41 +0000118 P2 &= 0xDF;
119 P2 &= 0xFB;
Jean-Philippe Bernardye386d942005-01-20 22:44:35 +0000120 P0 = cmd;
Jean-Philippe Bernardycacc37d2005-01-20 22:13:41 +0000121 P2 |= 0x04;
122 P2 |= 0x08;
123 P2 |= 0x20;
Jean-Philippe Bernardye386d942005-01-20 22:44:35 +0000124}
125
126void lcd_write_data( const unsigned char* data, int count ) {
127 int i;
128 for (i=0; i < count; i++) {
129 P2 |= 0x08;
130 P2 &= 0xDF;
131 P2 &= 0xFB;
132 P0 = data[i];
133 P2 |= 0x04;
134 P2 |= 0x08;
135 P2 |= 0x20;
136 }
Jean-Philippe Bernardycacc37d2005-01-20 22:13:41 +0000137}
138#endif
139
Jens Arnold6a556c12005-06-23 16:53:54 +0000140/*** hardware configuration ***/
Jean-Philippe Bernardycacc37d2005-01-20 22:13:41 +0000141
Björn Stenberg067262d2003-07-09 23:07:49 +0000142int lcd_default_contrast(void)
143{
144#ifdef SIMULATOR
145 return 30;
Daniel Stenberg9c394a32005-01-27 15:33:49 +0000146#elif CONFIG_LCD == LCD_GMINI100
Jean-Philippe Bernardy0d72de92005-01-27 14:14:21 +0000147 return 31;
Björn Stenberg067262d2003-07-09 23:07:49 +0000148#else
149 return (read_hw_mask() & LCD_CONTRAST_BIAS) ? 31 : 49;
150#endif
151}
152
Jens Arnold6a556c12005-06-23 16:53:54 +0000153#ifndef SIMULATOR
Björn Stenberg86f9a842002-09-23 11:17:52 +0000154
155void lcd_set_contrast(int val)
156{
Jens Arnold38e8a112004-04-21 09:39:29 +0000157 lcd_write_command(LCD_CNTL_CONTRAST);
158 lcd_write_command(val);
Björn Stenberg86f9a842002-09-23 11:17:52 +0000159}
160
Björn Stenbergb1079202003-02-27 14:22:30 +0000161void lcd_set_invert_display(bool yesno)
162{
163 if (yesno)
Jens Arnold38e8a112004-04-21 09:39:29 +0000164 lcd_write_command(LCD_SET_REVERSE_DISPLAY);
Björn Stenbergb1079202003-02-27 14:22:30 +0000165 else
Jens Arnold38e8a112004-04-21 09:39:29 +0000166 lcd_write_command(LCD_SET_NORMAL_DISPLAY);
Björn Stenbergb1079202003-02-27 14:22:30 +0000167}
168
Jörg Hohensohn56271b52003-12-12 22:11:08 +0000169/* turn the display upside down (call lcd_update() afterwards) */
170void lcd_set_flip(bool yesno)
171{
Jörg Hohensohnd5c293a2004-09-09 06:12:40 +0000172#ifdef HAVE_DISPLAY_FLIPPED
173 if (!yesno)
174#else
Jörg Hohensohn56271b52003-12-12 22:11:08 +0000175 if (yesno)
Jörg Hohensohnd5c293a2004-09-09 06:12:40 +0000176#endif
Jean-Philippe Bernardycacc37d2005-01-20 22:13:41 +0000177#if CONFIG_LCD == LCD_GMINI100
Jean-Philippe Bernardye386d942005-01-20 22:44:35 +0000178 {
179 lcd_write_command(LCD_SET_SEGMENT_REMAP | 0x01);
180 lcd_write_command(LCD_SET_COM_OUTPUT_SCAN_DIRECTION | 0x08);
181 xoffset = 132 - LCD_WIDTH;
Jens Arnold6a556c12005-06-23 16:53:54 +0000182 }
183 else
184 {
Jean-Philippe Bernardye386d942005-01-20 22:44:35 +0000185 lcd_write_command(LCD_SET_SEGMENT_REMAP);
186 lcd_write_command(LCD_SET_COM_OUTPUT_SCAN_DIRECTION | 0x08);
187 xoffset = 0;
188 }
Jean-Philippe Bernardycacc37d2005-01-20 22:13:41 +0000189#else
190
Jörg Hohensohn56271b52003-12-12 22:11:08 +0000191 {
Jens Arnold38e8a112004-04-21 09:39:29 +0000192 lcd_write_command(LCD_SET_SEGMENT_REMAP);
193 lcd_write_command(LCD_SET_COM_OUTPUT_SCAN_DIRECTION);
Jörg Hohensohn56271b52003-12-12 22:11:08 +0000194 xoffset = 132 - LCD_WIDTH; /* 132 colums minus the 112 we have */
195 }
196 else
197 {
Jens Arnold38e8a112004-04-21 09:39:29 +0000198 lcd_write_command(LCD_SET_SEGMENT_REMAP | 0x01);
199 lcd_write_command(LCD_SET_COM_OUTPUT_SCAN_DIRECTION | 0x08);
Jörg Hohensohn56271b52003-12-12 22:11:08 +0000200 xoffset = 0;
201 }
Jean-Philippe Bernardycacc37d2005-01-20 22:13:41 +0000202#endif
Jörg Hohensohn56271b52003-12-12 22:11:08 +0000203}
204
Jens Arnold6a556c12005-06-23 16:53:54 +0000205/* Rolls up the lcd display by the specified amount of lines.
Björn Stenberg86f9a842002-09-23 11:17:52 +0000206 * Lines that are rolled out over the top of the screen are
207 * rolled in from the bottom again. This is a hardware
208 * remapping only and all operations on the lcd are affected.
209 * ->
210 * @param int lines - The number of lines that are rolled.
Jens Arnold6a556c12005-06-23 16:53:54 +0000211 * The value must be 0 <= pixels < LCD_HEIGHT. */
Björn Stenberg86f9a842002-09-23 11:17:52 +0000212void lcd_roll(int lines)
213{
Jens Arnold38e8a112004-04-21 09:39:29 +0000214 lcd_write_command(LCD_SET_DISPLAY_START_LINE | (lines & (LCD_HEIGHT-1)));
Björn Stenberg86f9a842002-09-23 11:17:52 +0000215}
216
Jens Arnold6a556c12005-06-23 16:53:54 +0000217#endif /* !SIMULATOR */
Björn Stenberg86f9a842002-09-23 11:17:52 +0000218
Jens Arnold6a556c12005-06-23 16:53:54 +0000219/* LCD init */
220#ifdef SIMULATOR
221
222void lcd_init(void)
Björn Stenberg86f9a842002-09-23 11:17:52 +0000223{
Jens Arnold6a556c12005-06-23 16:53:54 +0000224 create_thread(scroll_thread, scroll_stack,
225 sizeof(scroll_stack), scroll_name);
Björn Stenberg86f9a842002-09-23 11:17:52 +0000226}
Jens Arnold6a556c12005-06-23 16:53:54 +0000227#else
228
229void lcd_init(void)
230{
231#if CONFIG_CPU == TCC730
232 /* Initialise P0 & some P2 output pins:
233 P0 -> all pins normal cmos output
234 P2 -> pins 1 to 5 normal cmos output. */
235 P0CON = 0xff;
236 P2CONL |= 0x5a;
237 P2CONL &= 0x5b;
238 P2CONH |= 1;
239#else
240 /* Initialize PB0-3 as output pins */
241 PBCR2 &= 0xff00; /* MD = 00 */
242 PBIOR |= 0x000f; /* IOR = 1 */
243#endif
244
245 /* inits like the original firmware */
246 lcd_write_command(LCD_SOFTWARE_RESET);
247 lcd_write_command(LCD_SET_INTERNAL_REGULATOR_RESISTOR_RATIO + 4);
248 lcd_write_command(LCD_SET_1OVER4_BIAS_RATIO + 0); /* force 1/4 bias: 0 */
249 lcd_write_command(LCD_SET_POWER_CONTROL_REGISTER + 7);
250 /* power control register: op-amp=1, regulator=1, booster=1 */
251 lcd_write_command(LCD_SET_DISPLAY_ON);
252 lcd_write_command(LCD_SET_NORMAL_DISPLAY);
253 lcd_set_flip(false);
254 lcd_write_command(LCD_SET_DISPLAY_START_LINE + 0);
255 lcd_set_contrast(lcd_default_contrast());
256 lcd_write_command(LCD_SET_PAGE_ADDRESS);
257 lcd_write_command(LCD_SET_LOWER_COLUMN_ADDRESS + 0);
258 lcd_write_command(LCD_SET_HIGHER_COLUMN_ADDRESS + 0);
259
260 lcd_clear_display();
261 lcd_update();
262
263 create_thread(scroll_thread, scroll_stack,
264 sizeof(scroll_stack), scroll_name);
265}
266
267/*** Update functions ***/
268
269/* Performance function that works with an external buffer
Jens Arnold7e11acb2005-06-28 23:15:47 +0000270 note that by and bheight are in 8-pixel units! */
271void lcd_blit(const unsigned char* data, int x, int by, int width,
272 int bheight, int stride)
Jens Arnold6a556c12005-06-23 16:53:54 +0000273{
274 /* Copy display bitmap to hardware */
Jens Arnold7e11acb2005-06-28 23:15:47 +0000275 while (bheight--)
Jens Arnold6a556c12005-06-23 16:53:54 +0000276 {
Jens Arnold7e11acb2005-06-28 23:15:47 +0000277 lcd_write_command (LCD_CNTL_PAGE | (by++ & 0xf));
Jens Arnold6a556c12005-06-23 16:53:54 +0000278 lcd_write_command (LCD_CNTL_HIGHCOL | (((x+xoffset)>>4) & 0xf));
279 lcd_write_command (LCD_CNTL_LOWCOL | ((x+xoffset) & 0xf));
280
Jens Arnold7e11acb2005-06-28 23:15:47 +0000281 lcd_write_data(data, width);
282 data += stride;
283 }
Jens Arnold6a556c12005-06-23 16:53:54 +0000284}
285
286
287/* Update the display.
288 This must be called after all other LCD functions that change the display. */
Jens Arnoldf89f0112005-07-04 06:20:50 +0000289void lcd_update(void) ICODE_ATTR;
Jens Arnold6a556c12005-06-23 16:53:54 +0000290void lcd_update(void)
291{
292 int y;
293
294 /* Copy display bitmap to hardware */
295 for (y = 0; y < LCD_HEIGHT/8; y++)
296 {
297 lcd_write_command (LCD_CNTL_PAGE | (y & 0xf));
Jens Arnoldce19ce02005-06-29 01:54:21 +0000298 lcd_write_command (LCD_CNTL_HIGHCOL | ((xoffset >> 4) & 0xf));
Jens Arnold6a556c12005-06-23 16:53:54 +0000299 lcd_write_command (LCD_CNTL_LOWCOL | (xoffset & 0xf));
300
301 lcd_write_data (lcd_framebuffer[y], LCD_WIDTH);
302 }
303}
304
305/* Update a fraction of the display. */
Jens Arnoldf89f0112005-07-04 06:20:50 +0000306void lcd_update_rect(int, int, int, int) ICODE_ATTR;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000307void lcd_update_rect(int x, int y, int width, int height)
Jens Arnold6a556c12005-06-23 16:53:54 +0000308{
309 int ymax;
310
311 /* The Y coordinates have to work on even 8 pixel rows */
Jens Arnold7e11acb2005-06-28 23:15:47 +0000312 ymax = (y + height-1) >> 3;
313 y >>= 3;
Jens Arnold6a556c12005-06-23 16:53:54 +0000314
Jens Arnold7e11acb2005-06-28 23:15:47 +0000315 if(x + width > LCD_WIDTH)
316 width = LCD_WIDTH - x;
Jens Arnold6a556c12005-06-23 16:53:54 +0000317 if (width <= 0)
318 return; /* nothing left to do, 0 is harmful to lcd_write_data() */
319 if(ymax >= LCD_HEIGHT/8)
320 ymax = LCD_HEIGHT/8-1;
321
322 /* Copy specified rectange bitmap to hardware */
323 for (; y <= ymax; y++)
324 {
325 lcd_write_command (LCD_CNTL_PAGE | (y & 0xf));
Jens Arnoldce19ce02005-06-29 01:54:21 +0000326 lcd_write_command (LCD_CNTL_HIGHCOL | (((x+xoffset) >> 4) & 0xf));
327 lcd_write_command (LCD_CNTL_LOWCOL | ((x+xoffset) & 0xf));
Jens Arnold6a556c12005-06-23 16:53:54 +0000328
Jens Arnold7e11acb2005-06-28 23:15:47 +0000329 lcd_write_data (&lcd_framebuffer[y][x], width);
Jens Arnold6a556c12005-06-23 16:53:54 +0000330 }
331}
332#endif /* !SIMULATOR */
333
334/*** parameter handling ***/
Björn Stenberg86f9a842002-09-23 11:17:52 +0000335
Jens Arnold04daef12005-06-24 22:33:21 +0000336void lcd_set_drawmode(int mode)
337{
338 drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
339}
340
341int lcd_get_drawmode(void)
342{
343 return drawmode;
344}
345
Björn Stenberg86f9a842002-09-23 11:17:52 +0000346void lcd_setmargins(int x, int y)
347{
348 xmargin = x;
349 ymargin = y;
350}
351
352int lcd_getxmargin(void)
353{
354 return xmargin;
355}
356
357int lcd_getymargin(void)
358{
359 return ymargin;
360}
361
362void lcd_setfont(int newfont)
363{
364 curfont = newfont;
365}
366
Jens Arnoldabe77a12004-08-01 21:50:57 +0000367int lcd_getstringsize(const unsigned char *str, int *w, int *h)
Björn Stenberg86f9a842002-09-23 11:17:52 +0000368{
Christian Gmeinerc3d0a222005-04-19 12:50:02 +0000369 return font_getstringsize(str, w, h, curfont);
Björn Stenberg86f9a842002-09-23 11:17:52 +0000370}
371
Jens Arnold04daef12005-06-24 22:33:21 +0000372/*** low-level drawing functions ***/
373
374static void setpixel(int x, int y)
375{
Jens Arnoldb5fc2532005-07-08 19:09:17 +0000376 lcd_framebuffer[y>>3][x] |= 1 << (y & 7);
Jens Arnold04daef12005-06-24 22:33:21 +0000377}
378
379static void clearpixel(int x, int y)
380{
Jens Arnoldb5fc2532005-07-08 19:09:17 +0000381 lcd_framebuffer[y>>3][x] &= ~(1 << (y & 7));
Jens Arnold04daef12005-06-24 22:33:21 +0000382}
383
384static void flippixel(int x, int y)
385{
Jens Arnoldb5fc2532005-07-08 19:09:17 +0000386 lcd_framebuffer[y>>3][x] ^= 1 << (y & 7);
Jens Arnold04daef12005-06-24 22:33:21 +0000387}
388
389static void nopixel(int x, int y)
390{
391 (void)x;
392 (void)y;
393}
394
Jens Arnold71bc5042005-11-06 23:49:29 +0000395lcd_pixelfunc_type* const lcd_pixelfuncs[8] = {
Jens Arnold7e11acb2005-06-28 23:15:47 +0000396 flippixel, nopixel, setpixel, setpixel,
397 nopixel, clearpixel, nopixel, clearpixel
398};
Jens Arnold04daef12005-06-24 22:33:21 +0000399
400static void flipblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnoldf89f0112005-07-04 06:20:50 +0000401 ICODE_ATTR;
Jens Arnolda142d4d2005-06-30 18:42:24 +0000402static void flipblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnold04daef12005-06-24 22:33:21 +0000403{
Jens Arnoldf89f0112005-07-04 06:20:50 +0000404 *address ^= bits & mask;
Jens Arnold04daef12005-06-24 22:33:21 +0000405}
406
407static void bgblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnoldf89f0112005-07-04 06:20:50 +0000408 ICODE_ATTR;
Jens Arnolda142d4d2005-06-30 18:42:24 +0000409static void bgblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnold04daef12005-06-24 22:33:21 +0000410{
Jens Arnoldf89f0112005-07-04 06:20:50 +0000411 *address &= bits | ~mask;
Jens Arnold04daef12005-06-24 22:33:21 +0000412}
413
414static void fgblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnoldf89f0112005-07-04 06:20:50 +0000415 ICODE_ATTR;
Jens Arnolda142d4d2005-06-30 18:42:24 +0000416static void fgblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnold04daef12005-06-24 22:33:21 +0000417{
Jens Arnoldf89f0112005-07-04 06:20:50 +0000418 *address |= bits & mask;
Jens Arnold04daef12005-06-24 22:33:21 +0000419}
420
421static void solidblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnoldf89f0112005-07-04 06:20:50 +0000422 ICODE_ATTR;
Jens Arnolda142d4d2005-06-30 18:42:24 +0000423static void solidblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnold04daef12005-06-24 22:33:21 +0000424{
425 *address = (*address & ~mask) | (bits & mask);
426}
427
Jens Arnold7e11acb2005-06-28 23:15:47 +0000428static void flipinvblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnoldf89f0112005-07-04 06:20:50 +0000429 ICODE_ATTR;
Jens Arnolda142d4d2005-06-30 18:42:24 +0000430static void flipinvblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnold7e11acb2005-06-28 23:15:47 +0000431{
Jens Arnoldf89f0112005-07-04 06:20:50 +0000432 *address ^= ~bits & mask;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000433}
434
435static void bginvblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnoldf89f0112005-07-04 06:20:50 +0000436 ICODE_ATTR;
Jens Arnolda142d4d2005-06-30 18:42:24 +0000437static void bginvblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnold7e11acb2005-06-28 23:15:47 +0000438{
439 *address &= ~(bits & mask);
440}
441
442static void fginvblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnoldf89f0112005-07-04 06:20:50 +0000443 ICODE_ATTR;
Jens Arnolda142d4d2005-06-30 18:42:24 +0000444static void fginvblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnold7e11acb2005-06-28 23:15:47 +0000445{
Jens Arnoldf89f0112005-07-04 06:20:50 +0000446 *address |= ~bits & mask;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000447}
448
449static void solidinvblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnoldf89f0112005-07-04 06:20:50 +0000450 ICODE_ATTR;
Jens Arnolda142d4d2005-06-30 18:42:24 +0000451static void solidinvblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnold7e11acb2005-06-28 23:15:47 +0000452{
453 *address = (*address & ~mask) | (~bits & mask);
454}
455
Jens Arnold71bc5042005-11-06 23:49:29 +0000456lcd_blockfunc_type* const lcd_blockfuncs[8] = {
Jens Arnold7e11acb2005-06-28 23:15:47 +0000457 flipblock, bgblock, fgblock, solidblock,
458 flipinvblock, bginvblock, fginvblock, solidinvblock
459};
Jens Arnold04daef12005-06-24 22:33:21 +0000460
Jens Arnold6a556c12005-06-23 16:53:54 +0000461/*** drawing functions ***/
462
Jens Arnold04daef12005-06-24 22:33:21 +0000463/* Clear the whole display */
Jens Arnold6a556c12005-06-23 16:53:54 +0000464void lcd_clear_display(void)
Björn Stenberg86f9a842002-09-23 11:17:52 +0000465{
Jens Arnold93494122005-06-25 00:28:09 +0000466 unsigned bits = (drawmode & DRMODE_INVERSEVID) ? 0xFFu : 0;
467
468 memset(lcd_framebuffer, bits, sizeof lcd_framebuffer);
Jens Arnold6a556c12005-06-23 16:53:54 +0000469 scrolling_lines = 0;
Linus Nielsen Feltzinge43b78a2003-04-16 00:12:31 +0000470}
471
Jens Arnold6a556c12005-06-23 16:53:54 +0000472/* Set a single pixel */
473void lcd_drawpixel(int x, int y)
Linus Nielsen Feltzinge43b78a2003-04-16 00:12:31 +0000474{
Jens Arnold7f8cc3f2005-06-29 21:33:36 +0000475 if (((unsigned)x < LCD_WIDTH) && ((unsigned)y < LCD_HEIGHT))
Jens Arnold576908d2005-06-29 01:39:50 +0000476 lcd_pixelfuncs[drawmode](x, y);
Jens Arnold6a556c12005-06-23 16:53:54 +0000477}
Björn Stenberg86f9a842002-09-23 11:17:52 +0000478
Jens Arnold04daef12005-06-24 22:33:21 +0000479/* Draw a line */
Jens Arnold6a556c12005-06-23 16:53:54 +0000480void lcd_drawline(int x1, int y1, int x2, int y2)
481{
482 int numpixels;
483 int i;
484 int deltax, deltay;
485 int d, dinc1, dinc2;
486 int x, xinc1, xinc2;
487 int y, yinc1, yinc2;
Jens Arnold576908d2005-06-29 01:39:50 +0000488 lcd_pixelfunc_type *pfunc = lcd_pixelfuncs[drawmode];
Jens Arnold6a556c12005-06-23 16:53:54 +0000489
490 deltax = abs(x2 - x1);
491 deltay = abs(y2 - y1);
Jens Arnold04daef12005-06-24 22:33:21 +0000492 xinc2 = 1;
493 yinc2 = 1;
Jens Arnold6a556c12005-06-23 16:53:54 +0000494
Jens Arnold04daef12005-06-24 22:33:21 +0000495 if (deltax >= deltay)
Jens Arnold6a556c12005-06-23 16:53:54 +0000496 {
497 numpixels = deltax;
498 d = 2 * deltay - deltax;
499 dinc1 = deltay * 2;
500 dinc2 = (deltay - deltax) * 2;
501 xinc1 = 1;
Jens Arnold6a556c12005-06-23 16:53:54 +0000502 yinc1 = 0;
Björn Stenberg86f9a842002-09-23 11:17:52 +0000503 }
Jens Arnold6a556c12005-06-23 16:53:54 +0000504 else
505 {
506 numpixels = deltay;
507 d = 2 * deltax - deltay;
508 dinc1 = deltax * 2;
509 dinc2 = (deltax - deltay) * 2;
510 xinc1 = 0;
Jens Arnold6a556c12005-06-23 16:53:54 +0000511 yinc1 = 1;
Jens Arnold6a556c12005-06-23 16:53:54 +0000512 }
513 numpixels++; /* include endpoints */
Björn Stenberg86f9a842002-09-23 11:17:52 +0000514
Jens Arnold04daef12005-06-24 22:33:21 +0000515 if (x1 > x2)
Jens Arnold6a556c12005-06-23 16:53:54 +0000516 {
517 xinc1 = -xinc1;
518 xinc2 = -xinc2;
519 }
Daniel Stenberg50b63582004-01-13 14:59:51 +0000520
Jens Arnold04daef12005-06-24 22:33:21 +0000521 if (y1 > y2)
Jens Arnold6a556c12005-06-23 16:53:54 +0000522 {
523 yinc1 = -yinc1;
524 yinc2 = -yinc2;
525 }
526
527 x = x1;
528 y = y1;
529
Jens Arnold04daef12005-06-24 22:33:21 +0000530 for (i = 0; i < numpixels; i++)
Jens Arnold6a556c12005-06-23 16:53:54 +0000531 {
Jens Arnold04daef12005-06-24 22:33:21 +0000532 if (((unsigned)x < LCD_WIDTH) && ((unsigned)y < LCD_HEIGHT))
533 pfunc(x, y);
Jens Arnold6a556c12005-06-23 16:53:54 +0000534
Jens Arnold04daef12005-06-24 22:33:21 +0000535 if (d < 0)
Jens Arnold6a556c12005-06-23 16:53:54 +0000536 {
537 d += dinc1;
538 x += xinc1;
539 y += yinc1;
540 }
541 else
542 {
543 d += dinc2;
544 x += xinc2;
545 y += yinc2;
546 }
547 }
548}
549
Jens Arnold04daef12005-06-24 22:33:21 +0000550/* Draw a horizontal line (optimised) */
551void lcd_hline(int x1, int x2, int y)
Jens Arnold6a556c12005-06-23 16:53:54 +0000552{
Jens Arnold04daef12005-06-24 22:33:21 +0000553 int x;
Jens Arnold3291ae62005-07-02 07:21:21 +0000554 unsigned char *dst, *dst_end;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000555 unsigned mask;
Jens Arnold24a1f942005-06-24 23:06:06 +0000556 lcd_blockfunc_type *bfunc;
Jens Arnold6a556c12005-06-23 16:53:54 +0000557
Jens Arnold04daef12005-06-24 22:33:21 +0000558 /* direction flip */
559 if (x2 < x1)
Jens Arnold6a556c12005-06-23 16:53:54 +0000560 {
Jens Arnold04daef12005-06-24 22:33:21 +0000561 x = x1;
562 x1 = x2;
563 x2 = x;
564 }
565
566 /* nothing to draw? */
567 if (((unsigned)y >= LCD_HEIGHT) || (x1 >= LCD_WIDTH) || (x2 < 0))
568 return;
569
570 /* clipping */
571 if (x1 < 0)
572 x1 = 0;
573 if (x2 >= LCD_WIDTH)
574 x2 = LCD_WIDTH-1;
575
Jens Arnold576908d2005-06-29 01:39:50 +0000576 bfunc = lcd_blockfuncs[drawmode];
Jens Arnold7e11acb2005-06-28 23:15:47 +0000577 dst = &lcd_framebuffer[y>>3][x1];
Jens Arnold04daef12005-06-24 22:33:21 +0000578 mask = 1 << (y & 7);
579
Jens Arnold3291ae62005-07-02 07:21:21 +0000580 dst_end = dst + x2 - x1;
581 do
Jens Arnold7e11acb2005-06-28 23:15:47 +0000582 bfunc(dst++, mask, 0xFFu);
Jens Arnold3291ae62005-07-02 07:21:21 +0000583 while (dst <= dst_end);
Jens Arnold04daef12005-06-24 22:33:21 +0000584}
585
586/* Draw a vertical line (optimised) */
587void lcd_vline(int x, int y1, int y2)
588{
589 int ny;
590 unsigned char *dst;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000591 unsigned mask, mask_bottom;
Jens Arnold24a1f942005-06-24 23:06:06 +0000592 lcd_blockfunc_type *bfunc;
Jens Arnold04daef12005-06-24 22:33:21 +0000593
594 /* direction flip */
595 if (y2 < y1)
596 {
597 ny = y1;
598 y1 = y2;
599 y2 = ny;
600 }
601
602 /* nothing to draw? */
603 if (((unsigned)x >= LCD_WIDTH) || (y1 >= LCD_HEIGHT) || (y2 < 0))
604 return;
605
606 /* clipping */
607 if (y1 < 0)
608 y1 = 0;
609 if (y2 >= LCD_HEIGHT)
610 y2 = LCD_HEIGHT-1;
611
Jens Arnold576908d2005-06-29 01:39:50 +0000612 bfunc = lcd_blockfuncs[drawmode];
Jens Arnold7e11acb2005-06-28 23:15:47 +0000613 dst = &lcd_framebuffer[y1>>3][x];
Jens Arnold04daef12005-06-24 22:33:21 +0000614 ny = y2 - (y1 & ~7);
Jens Arnold7e11acb2005-06-28 23:15:47 +0000615 mask = 0xFFu << (y1 & 7);
Jens Arnoldf89f0112005-07-04 06:20:50 +0000616 mask_bottom = 0xFFu >> (~ny & 7);
Jens Arnold7e11acb2005-06-28 23:15:47 +0000617
618 for (; ny >= 8; ny -= 8)
Jens Arnold04daef12005-06-24 22:33:21 +0000619 {
Jens Arnold7e11acb2005-06-28 23:15:47 +0000620 bfunc(dst, mask, 0xFFu);
Jens Arnold04daef12005-06-24 22:33:21 +0000621 dst += LCD_WIDTH;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000622 mask = 0xFFu;
Jens Arnold6a556c12005-06-23 16:53:54 +0000623 }
Jens Arnolda142d4d2005-06-30 18:42:24 +0000624 mask &= mask_bottom;
625 bfunc(dst, mask, 0xFFu);
Jens Arnold04daef12005-06-24 22:33:21 +0000626}
627
628/* Draw a rectangular box */
629void lcd_drawrect(int x, int y, int width, int height)
630{
631 if ((width <= 0) || (height <= 0))
632 return;
633
634 int x2 = x + width - 1;
635 int y2 = y + height - 1;
636
637 lcd_vline(x, y, y2);
638 lcd_vline(x2, y, y2);
639 lcd_hline(x, x2, y);
640 lcd_hline(x, x2, y2);
641}
642
Jens Arnold04daef12005-06-24 22:33:21 +0000643/* Fill a rectangular area */
644void lcd_fillrect(int x, int y, int width, int height)
645{
Jens Arnold3291ae62005-07-02 07:21:21 +0000646 int ny;
647 unsigned char *dst, *dst_end;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000648 unsigned mask, mask_bottom;
649 unsigned bits = 0xFFu;
Jens Arnold24a1f942005-06-24 23:06:06 +0000650 lcd_blockfunc_type *bfunc;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000651 bool fillopt;
Jens Arnold04daef12005-06-24 22:33:21 +0000652
653 /* nothing to draw? */
654 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
Jens Arnold7e11acb2005-06-28 23:15:47 +0000655 || (x + width <= 0) || (y + height <= 0))
Jens Arnold04daef12005-06-24 22:33:21 +0000656 return;
657
658 /* clipping */
659 if (x < 0)
Jens Arnold6a556c12005-06-23 16:53:54 +0000660 {
Jens Arnold04daef12005-06-24 22:33:21 +0000661 width += x;
662 x = 0;
Jens Arnold6a556c12005-06-23 16:53:54 +0000663 }
Jens Arnold04daef12005-06-24 22:33:21 +0000664 if (y < 0)
Jens Arnold6a556c12005-06-23 16:53:54 +0000665 {
Jens Arnold04daef12005-06-24 22:33:21 +0000666 height += y;
667 y = 0;
Jens Arnold6a556c12005-06-23 16:53:54 +0000668 }
Jens Arnold04daef12005-06-24 22:33:21 +0000669 if (x + width > LCD_WIDTH)
670 width = LCD_WIDTH - x;
671 if (y + height > LCD_HEIGHT)
672 height = LCD_HEIGHT - y;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000673
674 fillopt = (drawmode & DRMODE_INVERSEVID) ?
675 (drawmode & DRMODE_BG) : (drawmode & DRMODE_FG);
676 if (fillopt &&(drawmode & DRMODE_INVERSEVID))
677 bits = 0;
Jens Arnold576908d2005-06-29 01:39:50 +0000678 bfunc = lcd_blockfuncs[drawmode];
Jens Arnold7e11acb2005-06-28 23:15:47 +0000679 dst = &lcd_framebuffer[y>>3][x];
Jens Arnold04daef12005-06-24 22:33:21 +0000680 ny = height - 1 + (y & 7);
Jens Arnold7e11acb2005-06-28 23:15:47 +0000681 mask = 0xFFu << (y & 7);
Jens Arnoldf89f0112005-07-04 06:20:50 +0000682 mask_bottom = 0xFFu >> (~ny & 7);
Jens Arnold6a556c12005-06-23 16:53:54 +0000683
Jens Arnold7e11acb2005-06-28 23:15:47 +0000684 for (; ny >= 8; ny -= 8)
Jens Arnold6a556c12005-06-23 16:53:54 +0000685 {
Jens Arnold7e11acb2005-06-28 23:15:47 +0000686 if (fillopt && (mask == 0xFFu))
Jens Arnold04daef12005-06-24 22:33:21 +0000687 memset(dst, bits, width);
Jens Arnold6a556c12005-06-23 16:53:54 +0000688 else
689 {
Jens Arnold7e11acb2005-06-28 23:15:47 +0000690 unsigned char *dst_row = dst;
691
Jens Arnold3291ae62005-07-02 07:21:21 +0000692 dst_end = dst_row + width;
693 do
Jens Arnold7e11acb2005-06-28 23:15:47 +0000694 bfunc(dst_row++, mask, 0xFFu);
Jens Arnold3291ae62005-07-02 07:21:21 +0000695 while (dst_row < dst_end);
Jens Arnold6a556c12005-06-23 16:53:54 +0000696 }
Jens Arnold7e11acb2005-06-28 23:15:47 +0000697
698 dst += LCD_WIDTH;
699 mask = 0xFFu;
Jens Arnold6a556c12005-06-23 16:53:54 +0000700 }
Jens Arnolda142d4d2005-06-30 18:42:24 +0000701 mask &= mask_bottom;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000702
Jens Arnolda142d4d2005-06-30 18:42:24 +0000703 if (fillopt && (mask == 0xFFu))
Jens Arnold04daef12005-06-24 22:33:21 +0000704 memset(dst, bits, width);
705 else
Jens Arnold7e11acb2005-06-28 23:15:47 +0000706 {
Jens Arnold3291ae62005-07-02 07:21:21 +0000707 dst_end = dst + width;
708 do
Jens Arnolda142d4d2005-06-30 18:42:24 +0000709 bfunc(dst++, mask, 0xFFu);
Jens Arnold3291ae62005-07-02 07:21:21 +0000710 while (dst < dst_end);
Jens Arnold7e11acb2005-06-28 23:15:47 +0000711 }
Jens Arnold6a556c12005-06-23 16:53:54 +0000712}
713
714/* About Rockbox' internal bitmap format:
Björn Stenberg86f9a842002-09-23 11:17:52 +0000715 *
Jens Arnold2d446fe2004-05-14 22:55:05 +0000716 * A bitmap contains one bit for every pixel that defines if that pixel is
717 * black (1) or white (0). Bits within a byte are arranged vertically, LSB
718 * at top.
719 * The bytes are stored in row-major order, with byte 0 being top left,
720 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
721 * 0..7, the second row defines pixel row 8..15 etc.
Björn Stenberg86f9a842002-09-23 11:17:52 +0000722 *
Jens Arnold6a556c12005-06-23 16:53:54 +0000723 * This is the same as the internal lcd hw format. */
Björn Stenberg86f9a842002-09-23 11:17:52 +0000724
Jens Arnold7e11acb2005-06-28 23:15:47 +0000725/* Draw a partial bitmap */
726void lcd_bitmap_part(const unsigned char *src, int src_x, int src_y,
727 int stride, int x, int y, int width, int height)
Jens Arnoldf89f0112005-07-04 06:20:50 +0000728 ICODE_ATTR;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000729void lcd_bitmap_part(const unsigned char *src, int src_x, int src_y,
730 int stride, int x, int y, int width, int height)
Björn Stenberg86f9a842002-09-23 11:17:52 +0000731{
Jens Arnold3291ae62005-07-02 07:21:21 +0000732 int shift, ny;
733 unsigned char *dst, *dst_end;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000734 unsigned mask, mask_bottom;
735 lcd_blockfunc_type *bfunc;
Björn Stenberg86f9a842002-09-23 11:17:52 +0000736
Jens Arnold7e11acb2005-06-28 23:15:47 +0000737 /* nothing to draw? */
738 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
739 || (x + width <= 0) || (y + height <= 0))
Björn Stenberg86f9a842002-09-23 11:17:52 +0000740 return;
Jens Arnold2d446fe2004-05-14 22:55:05 +0000741
Jens Arnold7e11acb2005-06-28 23:15:47 +0000742 /* clipping */
743 if (x < 0)
Jörg Hohensohn5040cc52003-12-23 23:41:45 +0000744 {
Jens Arnold7e11acb2005-06-28 23:15:47 +0000745 width += x;
746 src_x -= x;
747 x = 0;
748 }
749 if (y < 0)
750 {
751 height += y;
752 src_y -= y;
753 y = 0;
754 }
755 if (x + width > LCD_WIDTH)
756 width = LCD_WIDTH - x;
757 if (y + height > LCD_HEIGHT)
758 height = LCD_HEIGHT - y;
759
760 src += stride * (src_y >> 3) + src_x; /* move starting point */
761 src_y &= 7;
762 y -= src_y;
763 dst = &lcd_framebuffer[y>>3][x];
764 shift = y & 7;
765 ny = height - 1 + shift + src_y;
766
Jens Arnold576908d2005-06-29 01:39:50 +0000767 bfunc = lcd_blockfuncs[drawmode];
Jens Arnold7e11acb2005-06-28 23:15:47 +0000768 mask = 0xFFu << (shift + src_y);
Jens Arnoldf89f0112005-07-04 06:20:50 +0000769 mask_bottom = 0xFFu >> (~ny & 7);
Jens Arnold7e11acb2005-06-28 23:15:47 +0000770
771 if (shift == 0)
772 {
773 bool copyopt = (drawmode == DRMODE_SOLID);
774
775 for (; ny >= 8; ny -= 8)
Jens Arnold6dc88dc2004-05-14 23:08:08 +0000776 {
Jens Arnold7e11acb2005-06-28 23:15:47 +0000777 if (copyopt && (mask == 0xFFu))
778 memcpy(dst, src, width);
779 else
780 {
781 const unsigned char *src_row = src;
782 unsigned char *dst_row = dst;
Jens Arnold3291ae62005-07-02 07:21:21 +0000783
784 dst_end = dst_row + width;
785 do
Jens Arnold7e11acb2005-06-28 23:15:47 +0000786 bfunc(dst_row++, mask, *src_row++);
Jens Arnold3291ae62005-07-02 07:21:21 +0000787 while (dst_row < dst_end);
Jens Arnold7e11acb2005-06-28 23:15:47 +0000788 }
789
Jens Arnold6dc88dc2004-05-14 23:08:08 +0000790 src += stride;
791 dst += LCD_WIDTH;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000792 mask = 0xFFu;
Jens Arnold6dc88dc2004-05-14 23:08:08 +0000793 }
Jens Arnolda142d4d2005-06-30 18:42:24 +0000794 mask &= mask_bottom;
Jörg Hohensohn5040cc52003-12-23 23:41:45 +0000795
Jens Arnolda142d4d2005-06-30 18:42:24 +0000796 if (copyopt && (mask == 0xFFu))
Jens Arnold7e11acb2005-06-28 23:15:47 +0000797 memcpy(dst, src, width);
798 else
799 {
Jens Arnold3291ae62005-07-02 07:21:21 +0000800 dst_end = dst + width;
801 do
Jens Arnolda142d4d2005-06-30 18:42:24 +0000802 bfunc(dst++, mask, *src++);
Jens Arnold3291ae62005-07-02 07:21:21 +0000803 while (dst < dst_end);
Jens Arnold7e11acb2005-06-28 23:15:47 +0000804 }
Björn Stenberg86f9a842002-09-23 11:17:52 +0000805 }
806 else
Björn Stenberg86f9a842002-09-23 11:17:52 +0000807 {
Jens Arnold3291ae62005-07-02 07:21:21 +0000808 dst_end = dst + width;
809 do
Björn Stenberg86f9a842002-09-23 11:17:52 +0000810 {
Jens Arnold7e11acb2005-06-28 23:15:47 +0000811 const unsigned char *src_col = src++;
812 unsigned char *dst_col = dst++;
813 unsigned mask_col = mask;
814 unsigned data = 0;
815
816 for (y = ny; y >= 8; y -= 8)
Björn Stenberg86f9a842002-09-23 11:17:52 +0000817 {
Jens Arnold2d446fe2004-05-14 22:55:05 +0000818 data |= *src_col << shift;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000819
820 if (mask_col & 0xFFu)
821 {
822 bfunc(dst_col, mask_col, data);
823 mask_col = 0xFFu;
824 }
825 else
826 mask_col >>= 8;
827
Jens Arnold2d446fe2004-05-14 22:55:05 +0000828 src_col += stride;
829 dst_col += LCD_WIDTH;
Björn Stenberg86f9a842002-09-23 11:17:52 +0000830 data >>= 8;
Björn Stenberg86f9a842002-09-23 11:17:52 +0000831 }
Jens Arnold2d446fe2004-05-14 22:55:05 +0000832 data |= *src_col << shift;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000833 bfunc(dst_col, mask_col & mask_bottom, data);
834 }
Jens Arnold3291ae62005-07-02 07:21:21 +0000835 while (dst < dst_end);
Björn Stenberg86f9a842002-09-23 11:17:52 +0000836 }
837}
838
Jens Arnold7e11acb2005-06-28 23:15:47 +0000839/* Draw a full bitmap */
840void lcd_bitmap(const unsigned char *src, int x, int y, int width, int height)
841{
842 lcd_bitmap_part(src, 0, 0, width, x, y, width, height);
843}
844
Jens Arnold6a556c12005-06-23 16:53:54 +0000845/* put a string at a given pixel position, skipping first ofs pixel columns */
846static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
Björn Stenberg86f9a842002-09-23 11:17:52 +0000847{
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +0000848 unsigned short ch;
849 unsigned short *ucs;
Jens Arnold6a556c12005-06-23 16:53:54 +0000850 struct font* pf = font_get(curfont);
Björn Stenberg86f9a842002-09-23 11:17:52 +0000851
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +0000852 ucs = bidi_l2v(str, 1);
Linus Nielsen Feltzing41a53d22005-08-08 19:23:28 +0000853
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +0000854 while ((ch = *ucs++) != 0 && x < LCD_WIDTH)
Jens Arnold6a556c12005-06-23 16:53:54 +0000855 {
Jens Arnold7e11acb2005-06-28 23:15:47 +0000856 int width;
857 const unsigned char *bits;
Björn Stenberg86f9a842002-09-23 11:17:52 +0000858
Jens Arnold6a556c12005-06-23 16:53:54 +0000859 /* check input range */
860 if (ch < pf->firstchar || ch >= pf->firstchar+pf->size)
861 ch = pf->defaultchar;
862 ch -= pf->firstchar;
Björn Stenberg86f9a842002-09-23 11:17:52 +0000863
Jens Arnold6a556c12005-06-23 16:53:54 +0000864 /* get proportional width and glyph bits */
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +0000865 width = font_get_width(pf,ch);
Björn Stenberg86f9a842002-09-23 11:17:52 +0000866
Jens Arnold7e11acb2005-06-28 23:15:47 +0000867 if (ofs > width)
Jens Arnold6a556c12005-06-23 16:53:54 +0000868 {
Jens Arnold7e11acb2005-06-28 23:15:47 +0000869 ofs -= width;
870 continue;
Jens Arnold6a556c12005-06-23 16:53:54 +0000871 }
872
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +0000873 bits = font_get_bits(pf, ch);
Jens Arnold6a556c12005-06-23 16:53:54 +0000874
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000875 lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs, pf->height);
Jens Arnold7e11acb2005-06-28 23:15:47 +0000876
877 x += width - ofs;
Jens Arnold6a556c12005-06-23 16:53:54 +0000878 ofs = 0;
Björn Stenberg86f9a842002-09-23 11:17:52 +0000879 }
880}
Jens Arnold6a556c12005-06-23 16:53:54 +0000881/* put a string at a given pixel position */
882void lcd_putsxy(int x, int y, const unsigned char *str)
Björn Stenberg86f9a842002-09-23 11:17:52 +0000883{
Jens Arnold6a556c12005-06-23 16:53:54 +0000884 lcd_putsxyofs(x, y, 0, str);
Björn Stenberg86f9a842002-09-23 11:17:52 +0000885}
886
Jens Arnold6a556c12005-06-23 16:53:54 +0000887/*** Line oriented text output ***/
Björn Stenberg86f9a842002-09-23 11:17:52 +0000888
Jens Arnold6a556c12005-06-23 16:53:54 +0000889void lcd_puts_style(int x, int y, const unsigned char *str, int style)
Björn Stenberg86f9a842002-09-23 11:17:52 +0000890{
Jens Arnold6a556c12005-06-23 16:53:54 +0000891 int xpos,ypos,w,h;
Jens Arnold3a5bd7a2005-07-07 21:03:58 +0000892 int lastmode = drawmode;
Björn Stenberg86f9a842002-09-23 11:17:52 +0000893
Jens Arnold6a556c12005-06-23 16:53:54 +0000894 /* make sure scrolling is turned off on the line we are updating */
895 scrolling_lines &= ~(1 << y);
896
897 if(!str || !str[0])
Björn Stenberg86f9a842002-09-23 11:17:52 +0000898 return;
899
Jens Arnold6a556c12005-06-23 16:53:54 +0000900 lcd_getstringsize(str, &w, &h);
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +0000901 xpos = xmargin + x*w / utf8length(str);
Jens Arnold6a556c12005-06-23 16:53:54 +0000902 ypos = ymargin + y*h;
903 lcd_putsxy(xpos, ypos, str);
Jens Arnold3a5bd7a2005-07-07 21:03:58 +0000904 drawmode = (DRMODE_SOLID|DRMODE_INVERSEVID);
Jens Arnold04daef12005-06-24 22:33:21 +0000905 lcd_fillrect(xpos + w, ypos, LCD_WIDTH - (xpos + w), h);
Jens Arnold6a556c12005-06-23 16:53:54 +0000906 if (style & STYLE_INVERT)
Jens Arnold04daef12005-06-24 22:33:21 +0000907 {
Jens Arnold3a5bd7a2005-07-07 21:03:58 +0000908 drawmode = DRMODE_COMPLEMENT;
Jens Arnold04daef12005-06-24 22:33:21 +0000909 lcd_fillrect(xpos, ypos, LCD_WIDTH - xpos, h);
910 }
Jens Arnold3a5bd7a2005-07-07 21:03:58 +0000911 drawmode = lastmode;
Björn Stenberg86f9a842002-09-23 11:17:52 +0000912}
913
Jens Arnold6a556c12005-06-23 16:53:54 +0000914/* put a string at a given char position */
915void lcd_puts(int x, int y, const unsigned char *str)
916{
917 lcd_puts_style(x, y, str, STYLE_DEFAULT);
918}
919
920/*** scrolling ***/
921
Hardeep Sidhu00acdfa2003-12-10 00:11:25 +0000922/* Reverse the invert setting of the scrolling line (if any) at given char
923 position. Setting will go into affect next time line scrolls. */
924void lcd_invertscroll(int x, int y)
925{
926 struct scrollinfo* s;
Hardeep Sidhu00acdfa2003-12-10 00:11:25 +0000927
Linus Nielsen Feltzing7c931a12004-07-09 11:21:13 +0000928 (void)x;
Hardeep Sidhu00acdfa2003-12-10 00:11:25 +0000929
Linus Nielsen Feltzing7c931a12004-07-09 11:21:13 +0000930 s = &scroll[y];
931 s->invert = !s->invert;
Hardeep Sidhu00acdfa2003-12-10 00:11:25 +0000932}
933
Jens Arnold6a556c12005-06-23 16:53:54 +0000934void lcd_stop_scroll(void)
Björn Stenberg86f9a842002-09-23 11:17:52 +0000935{
Jens Arnold6a556c12005-06-23 16:53:54 +0000936 scrolling_lines=0;
Björn Stenberg86f9a842002-09-23 11:17:52 +0000937}
938
Jens Arnold6a556c12005-06-23 16:53:54 +0000939void lcd_scroll_speed(int speed)
Björn Stenberg86f9a842002-09-23 11:17:52 +0000940{
Jens Arnold6a556c12005-06-23 16:53:54 +0000941 scroll_ticks = scroll_tick_table[speed];
Björn Stenberg86f9a842002-09-23 11:17:52 +0000942}
943
Jens Arnold6a556c12005-06-23 16:53:54 +0000944void lcd_scroll_step(int step)
Björn Stenberg86f9a842002-09-23 11:17:52 +0000945{
Jens Arnold6a556c12005-06-23 16:53:54 +0000946 scroll_step = step;
Björn Stenberg86f9a842002-09-23 11:17:52 +0000947}
948
Jens Arnold6a556c12005-06-23 16:53:54 +0000949void lcd_scroll_delay(int ms)
Björn Stenberg86f9a842002-09-23 11:17:52 +0000950{
Jens Arnold6a556c12005-06-23 16:53:54 +0000951 scroll_delay = ms / (HZ / 10);
Björn Stenberg86f9a842002-09-23 11:17:52 +0000952}
953
Jens Arnold6a556c12005-06-23 16:53:54 +0000954void lcd_bidir_scroll(int percent)
Björn Stenberg86f9a842002-09-23 11:17:52 +0000955{
Jens Arnold6a556c12005-06-23 16:53:54 +0000956 bidir_limit = percent;
Björn Stenberg86f9a842002-09-23 11:17:52 +0000957}
958
Jens Arnoldc76c5682004-08-16 23:37:23 +0000959void lcd_puts_scroll(int x, int y, const unsigned char *string)
Linus Nielsen Feltzinge43b78a2003-04-16 00:12:31 +0000960{
961 lcd_puts_scroll_style(x, y, string, STYLE_DEFAULT);
962}
963
Jens Arnoldc76c5682004-08-16 23:37:23 +0000964void lcd_puts_scroll_style(int x, int y, const unsigned char *string, int style)
Björn Stenberg86f9a842002-09-23 11:17:52 +0000965{
Markus Braunbc254fe2002-10-21 13:14:25 +0000966 struct scrollinfo* s;
Björn Stenberg86f9a842002-09-23 11:17:52 +0000967 int w, h;
Markus Braunbc254fe2002-10-21 13:14:25 +0000968
Linus Nielsen Feltzing7c931a12004-07-09 11:21:13 +0000969 s = &scroll[y];
Daniel Stenbergedf4d8c2003-01-28 15:23:48 +0000970
Björn Stenberga2e98c12002-12-12 15:20:37 +0000971 s->start_tick = current_tick + scroll_delay;
Linus Nielsen Feltzinge43b78a2003-04-16 00:12:31 +0000972 s->invert = false;
973 if (style & STYLE_INVERT) {
974 s->invert = true;
975 lcd_puts_style(x,y,string,STYLE_INVERT);
976 }
977 else
978 lcd_puts(x,y,string);
Björn Stenberg86f9a842002-09-23 11:17:52 +0000979
Björn Stenbergd34fbd02002-09-24 22:44:59 +0000980 lcd_getstringsize(string, &w, &h);
Björn Stenberg86f9a842002-09-23 11:17:52 +0000981
Markus Braunbc254fe2002-10-21 13:14:25 +0000982 if (LCD_WIDTH - x * 8 - xmargin < w) {
Björn Stenbergd34fbd02002-09-24 22:44:59 +0000983 /* prepare scroll line */
984 char *end;
985
Björn Stenberg86f9a842002-09-23 11:17:52 +0000986 memset(s->line, 0, sizeof s->line);
Björn Stenbergd34fbd02002-09-24 22:44:59 +0000987 strcpy(s->line, string);
Björn Stenberga2e98c12002-12-12 15:20:37 +0000988
989 /* get width */
Björn Stenbergd34fbd02002-09-24 22:44:59 +0000990 s->width = lcd_getstringsize(s->line, &w, &h);
991
Daniel Stenbergedf4d8c2003-01-28 15:23:48 +0000992 /* scroll bidirectional or forward only depending on the string
993 width */
Björn Stenberga2e98c12002-12-12 15:20:37 +0000994 if ( bidir_limit ) {
995 s->bidir = s->width < (LCD_WIDTH - xmargin) *
996 (100 + bidir_limit) / 100;
997 }
998 else
999 s->bidir = false;
1000
1001 if (!s->bidir) { /* add spaces if scrolling in the round */
1002 strcat(s->line, " ");
1003 /* get new width incl. spaces */
1004 s->width = lcd_getstringsize(s->line, &w, &h);
1005 }
1006
Daniel Stenbergedf4d8c2003-01-28 15:23:48 +00001007 end = strchr(s->line, '\0');
Björn Stenbergd34fbd02002-09-24 22:44:59 +00001008 strncpy(end, string, LCD_WIDTH/2);
1009
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +00001010 s->len = utf8length(string);
Björn Stenbergd34fbd02002-09-24 22:44:59 +00001011 s->offset = 0;
1012 s->startx = x;
Björn Stenberga2e98c12002-12-12 15:20:37 +00001013 s->backward = false;
Linus Nielsen Feltzing7c931a12004-07-09 11:21:13 +00001014 scrolling_lines |= (1<<y);
Björn Stenberg86f9a842002-09-23 11:17:52 +00001015 }
Daniel Stenbergedf4d8c2003-01-28 15:23:48 +00001016 else
1017 /* force a bit switch-off since it doesn't scroll */
Linus Nielsen Feltzing7c931a12004-07-09 11:21:13 +00001018 scrolling_lines &= ~(1<<y);
Björn Stenberg86f9a842002-09-23 11:17:52 +00001019}
1020
Björn Stenberg86f9a842002-09-23 11:17:52 +00001021static void scroll_thread(void)
1022{
Daniel Stenberg50b63582004-01-13 14:59:51 +00001023 struct font* pf;
Markus Braunbc254fe2002-10-21 13:14:25 +00001024 struct scrollinfo* s;
1025 int index;
Markus Braunbc254fe2002-10-21 13:14:25 +00001026 int xpos, ypos;
Jens Arnold04daef12005-06-24 22:33:21 +00001027 int lastmode;
Markus Braunbc254fe2002-10-21 13:14:25 +00001028
1029 /* initialize scroll struct array */
Kjell Ericsonf5d95842003-01-23 14:24:52 +00001030 scrolling_lines = 0;
Markus Braunbc254fe2002-10-21 13:14:25 +00001031
Björn Stenberg86f9a842002-09-23 11:17:52 +00001032 while ( 1 ) {
Björn Stenberga2e98c12002-12-12 15:20:37 +00001033 for ( index = 0; index < SCROLLABLE_LINES; index++ ) {
Björn Stenberga2e98c12002-12-12 15:20:37 +00001034 /* really scroll? */
Kjell Ericsonf5d95842003-01-23 14:24:52 +00001035 if ( !(scrolling_lines&(1<<index)) )
Björn Stenberga2e98c12002-12-12 15:20:37 +00001036 continue;
Björn Stenberg86f9a842002-09-23 11:17:52 +00001037
Daniel Stenbergedf4d8c2003-01-28 15:23:48 +00001038 s = &scroll[index];
1039
Björn Stenberga2e98c12002-12-12 15:20:37 +00001040 /* check pause */
1041 if (TIME_BEFORE(current_tick, s->start_tick))
1042 continue;
Björn Stenberg86f9a842002-09-23 11:17:52 +00001043
Björn Stenberga2e98c12002-12-12 15:20:37 +00001044 if (s->backward)
1045 s->offset -= scroll_step;
1046 else
1047 s->offset += scroll_step;
Björn Stenberg86f9a842002-09-23 11:17:52 +00001048
Daniel Stenberg50b63582004-01-13 14:59:51 +00001049 pf = font_get(curfont);
1050 xpos = xmargin + s->startx * s->width / s->len;
Linus Nielsen Feltzing7c931a12004-07-09 11:21:13 +00001051 ypos = ymargin + index * pf->height;
Daniel Stenberg50b63582004-01-13 14:59:51 +00001052
Björn Stenberga2e98c12002-12-12 15:20:37 +00001053 if (s->bidir) { /* scroll bidirectional */
1054 if (s->offset <= 0) {
1055 /* at beginning of line */
1056 s->offset = 0;
1057 s->backward = false;
1058 s->start_tick = current_tick + scroll_delay * 2;
1059 }
Daniel Stenberg50b63582004-01-13 14:59:51 +00001060 if (s->offset >= s->width - (LCD_WIDTH - xpos)) {
Björn Stenberga2e98c12002-12-12 15:20:37 +00001061 /* at end of line */
Daniel Stenberg50b63582004-01-13 14:59:51 +00001062 s->offset = s->width - (LCD_WIDTH - xpos);
Björn Stenberga2e98c12002-12-12 15:20:37 +00001063 s->backward = true;
1064 s->start_tick = current_tick + scroll_delay * 2;
Markus Braunbc254fe2002-10-21 13:14:25 +00001065 }
1066 }
Björn Stenberga2e98c12002-12-12 15:20:37 +00001067 else {
Marcoen Hirschberg40d22092005-11-30 15:37:48 +00001068 /* scroll forward the whole time */
1069 if (s->offset >= s->width)
1070 s->offset %= s->width;
Björn Stenberga2e98c12002-12-12 15:20:37 +00001071 }
1072
Jens Arnold3a5bd7a2005-07-07 21:03:58 +00001073 lastmode = drawmode;
1074 drawmode = (DRMODE_SOLID|DRMODE_INVERSEVID);
Jens Arnold04daef12005-06-24 22:33:21 +00001075 lcd_fillrect(xpos, ypos, LCD_WIDTH - xpos, pf->height);
Jens Arnold3a5bd7a2005-07-07 21:03:58 +00001076 drawmode = DRMODE_SOLID;
Björn Stenberga2e98c12002-12-12 15:20:37 +00001077 lcd_putsxyofs(xpos, ypos, s->offset, s->line);
Linus Nielsen Feltzinge43b78a2003-04-16 00:12:31 +00001078 if (s->invert)
Jens Arnold04daef12005-06-24 22:33:21 +00001079 {
Jens Arnold3a5bd7a2005-07-07 21:03:58 +00001080 drawmode = DRMODE_COMPLEMENT;
Jens Arnold04daef12005-06-24 22:33:21 +00001081 lcd_fillrect(xpos, ypos, LCD_WIDTH - xpos, pf->height);
1082 }
Jens Arnold3a5bd7a2005-07-07 21:03:58 +00001083 drawmode = lastmode;
Daniel Stenberg50b63582004-01-13 14:59:51 +00001084 lcd_update_rect(xpos, ypos, LCD_WIDTH - xpos, pf->height);
Björn Stenberg86f9a842002-09-23 11:17:52 +00001085 }
Markus Braunbc254fe2002-10-21 13:14:25 +00001086
Jens Arnold566eae22004-10-15 20:30:29 +00001087 sleep(scroll_ticks);
Björn Stenberg86f9a842002-09-23 11:17:52 +00001088 }
1089}
1090