blob: 1199bf04a10f481bf36983c3c0fecfe3095961c4 [file] [log] [blame]
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2004 by Linus Nielsen Feltzing
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
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +000021#include "cpu.h"
22#include "lcd.h"
23#include "kernel.h"
24#include "thread.h"
25#include <string.h>
26#include <stdlib.h>
27#include "file.h"
28#include "debug.h"
29#include "system.h"
30#include "font.h"
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +000031#include "rbunicode.h"
Linus Nielsen Feltzing41a53d22005-08-08 19:23:28 +000032#include "bidi.h"
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +000033
34/*** definitions ***/
35
36/* LCD command codes */
Jens Arnold6a556c12005-06-23 16:53:54 +000037#define LCD_CNTL_POWER_CONTROL 0x25
38#define LCD_CNTL_VOLTAGE_SELECT 0x2b
39#define LCD_CNTL_LINE_INVERT_DRIVE 0x36
40#define LCD_CNTL_GRAY_SCALE_PATTERN 0x39
41#define LCD_CNTL_TEMP_GRADIENT_SELECT 0x4e
42#define LCD_CNTL_OSC_FREQUENCY 0x5f
43#define LCD_CNTL_ON_OFF 0xae
Jens Arnold492424b2005-06-27 19:28:55 +000044#define LCD_CNTL_OSC_ON_OFF 0xaa
Jens Arnold6a556c12005-06-23 16:53:54 +000045#define LCD_CNTL_OFF_MODE 0xbe
46#define LCD_CNTL_REVERSE 0xa6
47#define LCD_CNTL_ALL_LIGHTING 0xa4
48#define LCD_CNTL_COMMON_OUTPUT_STATUS 0xc4
49#define LCD_CNTL_COLUMN_ADDRESS_DIR 0xa0
50#define LCD_CNTL_NLINE_ON_OFF 0xe4
51#define LCD_CNTL_DISPLAY_MODE 0x66
52#define LCD_CNTL_DUTY_SET 0x6d
53#define LCD_CNTL_ELECTRONIC_VOLUME 0x81
54#define LCD_CNTL_DATA_INPUT_DIR 0x84
55#define LCD_CNTL_DISPLAY_START_LINE 0x8a
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +000056
Jens Arnold6a556c12005-06-23 16:53:54 +000057#define LCD_CNTL_PAGE 0xb1
58#define LCD_CNTL_COLUMN 0x13
59#define LCD_CNTL_DATA_WRITE 0x1d
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +000060
Jens Arnolda048ff42005-02-07 11:15:56 +000061#define SCROLLABLE_LINES 26
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +000062
Jens Arnold6a556c12005-06-23 16:53:54 +000063/*** globals ***/
64
Jens Arnoldabd9f832005-10-19 19:35:24 +000065unsigned char lcd_framebuffer[LCD_HEIGHT/4][LCD_WIDTH] IBSS_ATTR;
Jens Arnold6a556c12005-06-23 16:53:54 +000066
Jens Arnoldabd9f832005-10-19 19:35:24 +000067static const unsigned char dibits[16] ICONST_ATTR = {
Jens Arnoldf894a4c2005-07-06 22:58:02 +000068 0x00, 0x03, 0x0C, 0x0F, 0x30, 0x33, 0x3C, 0x3F,
69 0xC0, 0xC3, 0xCC, 0xCF, 0xF0, 0xF3, 0xFC, 0xFF
70};
71
72static unsigned fg_pattern IDATA_ATTR = 0xFF; /* initially black */
73static unsigned bg_pattern IDATA_ATTR = 0x00; /* initially white */
Jens Arnold04daef12005-06-24 22:33:21 +000074static int drawmode = DRMODE_SOLID;
Jens Arnold6a556c12005-06-23 16:53:54 +000075static int xmargin = 0;
76static int ymargin = 0;
77static int curfont = FONT_SYSFIXED;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +000078
Jens Arnold6a556c12005-06-23 16:53:54 +000079/* scrolling */
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +000080static volatile int scrolling_lines=0; /* Bitpattern of which lines are scrolling */
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +000081static void scroll_thread(void);
Linus Nielsen Feltzing23756ed2005-02-06 02:31:46 +000082static long scroll_stack[DEFAULT_STACK_SIZE/sizeof(long)];
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +000083static const char scroll_name[] = "scroll";
Jens Arnold05af2802005-07-28 08:36:24 +000084static int scroll_ticks = 12; /* # of ticks between updates*/
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +000085static int scroll_delay = HZ/2; /* ticks delay before start */
Jens Arnold05af2802005-07-28 08:36:24 +000086static int scroll_step = 6; /* pixels per scroll step */
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +000087static int bidir_limit = 50; /* percent */
88static struct scrollinfo scroll[SCROLLABLE_LINES];
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +000089
Jens Arnold6a556c12005-06-23 16:53:54 +000090static const char scroll_tick_table[16] = {
91 /* Hz values:
92 1, 1.25, 1.55, 2, 2.5, 3.12, 4, 5, 6.25, 8.33, 10, 12.5, 16.7, 20, 25, 33 */
93 100, 80, 64, 50, 40, 32, 25, 20, 16, 12, 10, 8, 6, 5, 4, 3
94};
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +000095
Jens Arnold6a556c12005-06-23 16:53:54 +000096/*** driver code is in lcd.S ***/
97
98/*** hardware configuration ***/
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +000099
100int lcd_default_contrast(void)
101{
Linus Nielsen Feltzing23756ed2005-02-06 02:31:46 +0000102 return 28;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000103}
104
Jens Arnold6a556c12005-06-23 16:53:54 +0000105#ifndef SIMULATOR
106
107void lcd_set_contrast(int val)
108{
109 lcd_write_command_ex(LCD_CNTL_ELECTRONIC_VOLUME, val, -1);
110}
111
112void lcd_set_invert_display(bool yesno)
113{
114 lcd_write_command(LCD_CNTL_REVERSE | (yesno?1:0));
115}
116
117/* turn the display upside down (call lcd_update() afterwards) */
118void lcd_set_flip(bool yesno)
119{
120 if (yesno)
121 {
122 lcd_write_command(LCD_CNTL_COLUMN_ADDRESS_DIR | 1);
123 lcd_write_command(LCD_CNTL_COMMON_OUTPUT_STATUS | 0);
124 lcd_write_command_ex(LCD_CNTL_DUTY_SET, 0x20, 0);
Jens Arnold6a556c12005-06-23 16:53:54 +0000125 }
Jens Arnold04daef12005-06-24 22:33:21 +0000126 else
Jens Arnold6a556c12005-06-23 16:53:54 +0000127 {
128 lcd_write_command(LCD_CNTL_COLUMN_ADDRESS_DIR | 0);
129 lcd_write_command(LCD_CNTL_COMMON_OUTPUT_STATUS | 1);
130 lcd_write_command_ex(LCD_CNTL_DUTY_SET, 0x20, 1);
Jens Arnold6a556c12005-06-23 16:53:54 +0000131 }
132}
133
134/* Rolls up the lcd display by the specified amount of lines.
135 * Lines that are rolled out over the top of the screen are
136 * rolled in from the bottom again. This is a hardware
137 * remapping only and all operations on the lcd are affected.
138 * ->
139 * @param int lines - The number of lines that are rolled.
140 * The value must be 0 <= pixels < LCD_HEIGHT. */
141void lcd_roll(int lines)
142{
Jens Arnold6a556c12005-06-23 16:53:54 +0000143 lines &= LCD_HEIGHT-1;
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000144 lcd_write_command_ex(LCD_CNTL_DISPLAY_START_LINE, lines, -1);
Jens Arnold6a556c12005-06-23 16:53:54 +0000145}
146
147#endif /* !SIMULATOR */
148
149/* LCD init */
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000150#ifdef SIMULATOR
151
152void lcd_init(void)
153{
154 create_thread(scroll_thread, scroll_stack,
155 sizeof(scroll_stack), scroll_name);
156}
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000157#else
158
Jens Arnold6a556c12005-06-23 16:53:54 +0000159void lcd_init(void)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000160{
Linus Nielsen Feltzingf4702042005-01-27 00:00:55 +0000161 /* GPO35 is the LCD A0 pin
162 GPO46 is LCD RESET */
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000163 or_l(0x00004008, &GPIO1_OUT);
164 or_l(0x00004008, &GPIO1_ENABLE);
165 or_l(0x00004008, &GPIO1_FUNCTION);
Linus Nielsen Feltzingf4702042005-01-27 00:00:55 +0000166
167 /* Reset LCD */
168 sleep(1);
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000169 and_l(~0x00004000, &GPIO1_OUT);
Linus Nielsen Feltzingf4702042005-01-27 00:00:55 +0000170 sleep(1);
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000171 or_l(0x00004000, &GPIO1_OUT);
Linus Nielsen Feltzingf4702042005-01-27 00:00:55 +0000172 sleep(1);
Jens Arnold492424b2005-06-27 19:28:55 +0000173
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000174 lcd_write_command(LCD_CNTL_COLUMN_ADDRESS_DIR | 0); /* Normal */
175 lcd_write_command(LCD_CNTL_COMMON_OUTPUT_STATUS | 1); /* Reverse dir */
176 lcd_write_command(LCD_CNTL_REVERSE | 0); /* Reverse OFF */
177 lcd_write_command(LCD_CNTL_ALL_LIGHTING | 0); /* Normal */
Linus Nielsen Feltzingf4702042005-01-27 00:00:55 +0000178 lcd_write_command_ex(LCD_CNTL_DUTY_SET, 0x20, 1);
Jens Arnold492424b2005-06-27 19:28:55 +0000179 lcd_write_command(LCD_CNTL_OFF_MODE | 1); /* OFF -> VCC on drivers */
Linus Nielsen Feltzingf4702042005-01-27 00:00:55 +0000180 lcd_write_command_ex(LCD_CNTL_VOLTAGE_SELECT, 3, -1);
181 lcd_write_command_ex(LCD_CNTL_ELECTRONIC_VOLUME, 0x1c, -1);
182 lcd_write_command_ex(LCD_CNTL_TEMP_GRADIENT_SELECT, 0, -1);
Jens Arnold492424b2005-06-27 19:28:55 +0000183
Linus Nielsen Feltzingf4702042005-01-27 00:00:55 +0000184 lcd_write_command_ex(LCD_CNTL_LINE_INVERT_DRIVE, 0x10, -1);
Jens Arnold492424b2005-06-27 19:28:55 +0000185 lcd_write_command(LCD_CNTL_NLINE_ON_OFF | 1); /* N-line ON */
186
Linus Nielsen Feltzingf4702042005-01-27 00:00:55 +0000187 lcd_write_command_ex(LCD_CNTL_OSC_FREQUENCY, 3, -1);
188 lcd_write_command(LCD_CNTL_OSC_ON_OFF | 1); /* Oscillator ON */
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000189
Jens Arnold492424b2005-06-27 19:28:55 +0000190 lcd_write_command_ex(LCD_CNTL_POWER_CONTROL, 0x16, -1);
191 sleep(HZ/10); /* 100 ms pause */
192 lcd_write_command_ex(LCD_CNTL_POWER_CONTROL, 0x17, -1);
Linus Nielsen Feltzingf4702042005-01-27 00:00:55 +0000193
194 lcd_write_command_ex(LCD_CNTL_DISPLAY_START_LINE, 0, -1);
195 lcd_write_command_ex(LCD_CNTL_GRAY_SCALE_PATTERN, 0x42, -1);
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000196 lcd_write_command_ex(LCD_CNTL_DISPLAY_MODE, 0, -1); /* Greyscale mode */
Linus Nielsen Feltzingf4702042005-01-27 00:00:55 +0000197 lcd_write_command(LCD_CNTL_DATA_INPUT_DIR | 0); /* Column mode */
198
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000199 lcd_clear_display();
200 lcd_update();
Jens Arnold492424b2005-06-27 19:28:55 +0000201 lcd_write_command(LCD_CNTL_ON_OFF | 1); /* LCD ON */
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000202
203 create_thread(scroll_thread, scroll_stack,
204 sizeof(scroll_stack), scroll_name);
205}
206
Jens Arnold6a556c12005-06-23 16:53:54 +0000207/*** update functions ***/
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000208
209/* Performance function that works with an external buffer
Jens Arnolda1de0812005-09-30 20:11:31 +0000210 note that by and bheight are in 8-pixel units! */
Jens Arnold7e11acb2005-06-28 23:15:47 +0000211void lcd_blit(const unsigned char* data, int x, int by, int width,
212 int bheight, int stride)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000213{
Jens Arnold1a40e102005-09-30 20:10:27 +0000214 const unsigned char *src, *src_end;
215 unsigned char *dst_u, *dst_l;
Jens Arnoldbfe28d92005-11-09 23:24:33 +0000216 static unsigned char upper[LCD_WIDTH] IBSS_ATTR;
217 static unsigned char lower[LCD_WIDTH] IBSS_ATTR;
Jens Arnold1a40e102005-09-30 20:10:27 +0000218 unsigned int byte;
219
220 by *= 2;
221
Jens Arnold7e11acb2005-06-28 23:15:47 +0000222 while (bheight--)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000223 {
Jens Arnold1a40e102005-09-30 20:10:27 +0000224 src = data;
225 src_end = data + width;
226 dst_u = upper;
227 dst_l = lower;
228 do
229 {
230 byte = *src++;
231 *dst_u++ = dibits[byte & 0x0F];
232 byte >>= 4;
233 *dst_l++ = dibits[byte & 0x0F];
234 }
235 while (src < src_end);
236
Jens Arnold2ef8a872005-07-16 02:09:22 +0000237 lcd_write_command_ex(LCD_CNTL_PAGE, by++, -1);
Jens Arnold04daef12005-06-24 22:33:21 +0000238 lcd_write_command_ex(LCD_CNTL_COLUMN, x, -1);
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000239 lcd_write_command(LCD_CNTL_DATA_WRITE);
Jens Arnold1a40e102005-09-30 20:10:27 +0000240 lcd_write_data(upper, width);
241
242 lcd_write_command_ex(LCD_CNTL_PAGE, by++, -1);
243 lcd_write_command_ex(LCD_CNTL_COLUMN, x, -1);
244 lcd_write_command(LCD_CNTL_DATA_WRITE);
245 lcd_write_data(lower, width);
246
Jens Arnold7e11acb2005-06-28 23:15:47 +0000247 data += stride;
Jens Arnold1a40e102005-09-30 20:10:27 +0000248 }
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000249}
250
251
Jens Arnold6a556c12005-06-23 16:53:54 +0000252/* Update the display.
253 This must be called after all other LCD functions that change the display. */
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000254void lcd_update(void) ICODE_ATTR;
Jens Arnold6a556c12005-06-23 16:53:54 +0000255void lcd_update(void)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000256{
257 int y;
258
259 /* Copy display bitmap to hardware */
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000260 for (y = 0; y < LCD_HEIGHT/4; y++)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000261 {
Linus Nielsen Feltzingf4702042005-01-27 00:00:55 +0000262 lcd_write_command_ex(LCD_CNTL_PAGE, y, -1);
263 lcd_write_command_ex(LCD_CNTL_COLUMN, 0, -1);
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000264
265 lcd_write_command(LCD_CNTL_DATA_WRITE);
266 lcd_write_data (lcd_framebuffer[y], LCD_WIDTH);
267 }
268}
269
Jens Arnold6a556c12005-06-23 16:53:54 +0000270/* Update a fraction of the display. */
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000271void lcd_update_rect(int, int, int, int) ICODE_ATTR;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000272void lcd_update_rect(int x, int y, int width, int height)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000273{
274 int ymax;
275
276 /* The Y coordinates have to work on even 8 pixel rows */
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000277 ymax = (y + height-1) >> 2;
278 y >>= 2;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000279
Jens Arnold7e11acb2005-06-28 23:15:47 +0000280 if(x + width > LCD_WIDTH)
281 width = LCD_WIDTH - x;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000282 if (width <= 0)
283 return; /* nothing left to do, 0 is harmful to lcd_write_data() */
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000284 if(ymax >= LCD_HEIGHT/4)
285 ymax = LCD_HEIGHT/4-1;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000286
287 /* Copy specified rectange bitmap to hardware */
288 for (; y <= ymax; y++)
289 {
Linus Nielsen Feltzingf4702042005-01-27 00:00:55 +0000290 lcd_write_command_ex(LCD_CNTL_PAGE, y, -1);
Jens Arnold7e11acb2005-06-28 23:15:47 +0000291 lcd_write_command_ex(LCD_CNTL_COLUMN, x, -1);
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000292
293 lcd_write_command(LCD_CNTL_DATA_WRITE);
Jens Arnold7e11acb2005-06-28 23:15:47 +0000294 lcd_write_data (&lcd_framebuffer[y][x], width);
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000295 }
296}
Jens Arnold6a556c12005-06-23 16:53:54 +0000297#endif /* !SIMULATOR */
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000298
Jens Arnold6a556c12005-06-23 16:53:54 +0000299/*** parameter handling ***/
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000300
Jens Arnold04daef12005-06-24 22:33:21 +0000301void lcd_set_drawmode(int mode)
302{
303 drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
304}
305
306int lcd_get_drawmode(void)
307{
308 return drawmode;
309}
310
Jens Arnold5b2cba12005-11-16 21:09:23 +0000311void lcd_set_foreground(unsigned brightness)
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000312{
313 fg_pattern = 0x55 * (~brightness & 3);
314}
315
Jens Arnold5b2cba12005-11-16 21:09:23 +0000316unsigned lcd_get_foreground(void)
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000317{
318 return ~fg_pattern & 3;
319}
320
Jens Arnold5b2cba12005-11-16 21:09:23 +0000321void lcd_set_background(unsigned brightness)
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000322{
323 bg_pattern = 0x55 * (~brightness & 3);
324}
325
Jens Arnold5b2cba12005-11-16 21:09:23 +0000326unsigned lcd_get_background(void)
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000327{
328 return ~bg_pattern & 3;
329}
330
Jens Arnold5b2cba12005-11-16 21:09:23 +0000331void lcd_set_drawinfo(int mode, unsigned fg_brightness, unsigned bg_brightness)
Jens Arnoldbf9f28b2005-07-14 18:11:36 +0000332{
333 lcd_set_drawmode(mode);
334 lcd_set_foreground(fg_brightness);
335 lcd_set_background(bg_brightness);
336}
337
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000338void lcd_setmargins(int x, int y)
339{
340 xmargin = x;
341 ymargin = y;
342}
343
344int lcd_getxmargin(void)
345{
346 return xmargin;
347}
348
349int lcd_getymargin(void)
350{
351 return ymargin;
352}
353
354void lcd_setfont(int newfont)
355{
356 curfont = newfont;
357}
358
359int lcd_getstringsize(const unsigned char *str, int *w, int *h)
360{
Christian Gmeinerc6ec0f42005-04-19 12:47:16 +0000361 return font_getstringsize(str, w, h, curfont);
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000362}
363
Jens Arnold04daef12005-06-24 22:33:21 +0000364/*** low-level drawing functions ***/
365
366static void setpixel(int x, int y)
367{
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000368 unsigned char *data = &lcd_framebuffer[y>>2][x];
369 unsigned mask = 3 << (2 * (y & 3));
370 *data = (*data & ~mask) | (fg_pattern & mask);
Jens Arnold04daef12005-06-24 22:33:21 +0000371}
372
373static void clearpixel(int x, int y)
374{
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000375 unsigned char *data = &lcd_framebuffer[y>>2][x];
376 unsigned mask = 3 << (2 * (y & 3));
377 *data = (*data & ~mask) | (bg_pattern & mask);
Jens Arnold04daef12005-06-24 22:33:21 +0000378}
379
380static void flippixel(int x, int y)
381{
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000382 lcd_framebuffer[y>>2][x] ^= 3 << (2 * (y & 3));
Jens Arnold04daef12005-06-24 22:33:21 +0000383}
384
385static void nopixel(int x, int y)
386{
387 (void)x;
388 (void)y;
389}
390
Jens Arnold71bc5042005-11-06 23:49:29 +0000391lcd_pixelfunc_type* const lcd_pixelfuncs[8] = {
Jens Arnold7e11acb2005-06-28 23:15:47 +0000392 flippixel, nopixel, setpixel, setpixel,
393 nopixel, clearpixel, nopixel, clearpixel
394};
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000395
396/* 'mask' and 'bits' contain 2 bits per pixel */
Jens Arnold04daef12005-06-24 22:33:21 +0000397static void flipblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000398 ICODE_ATTR;
Jens Arnolda142d4d2005-06-30 18:42:24 +0000399static void flipblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnold04daef12005-06-24 22:33:21 +0000400{
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000401 *address ^= bits & mask;
Jens Arnold04daef12005-06-24 22:33:21 +0000402}
403
404static void bgblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000405 ICODE_ATTR;
Jens Arnolda142d4d2005-06-30 18:42:24 +0000406static void bgblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnold04daef12005-06-24 22:33:21 +0000407{
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000408 mask &= ~bits;
409 *address = (*address & ~mask) | (bg_pattern & mask);
Jens Arnold04daef12005-06-24 22:33:21 +0000410}
411
412static void fgblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000413 ICODE_ATTR;
Jens Arnolda142d4d2005-06-30 18:42:24 +0000414static void fgblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnold04daef12005-06-24 22:33:21 +0000415{
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000416 mask &= bits;
417 *address = (*address & ~mask) | (fg_pattern & mask);
Jens Arnold04daef12005-06-24 22:33:21 +0000418}
419
420static void solidblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000421 ICODE_ATTR;
Jens Arnolda142d4d2005-06-30 18:42:24 +0000422static void solidblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnold04daef12005-06-24 22:33:21 +0000423{
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000424 *address = (*address & ~mask) | (bits & mask & fg_pattern)
425 | (~bits & mask & bg_pattern);
Jens Arnold04daef12005-06-24 22:33:21 +0000426}
427
Jens Arnold7e11acb2005-06-28 23:15:47 +0000428static void flipinvblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnoldf894a4c2005-07-06 22:58:02 +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 Arnoldf894a4c2005-07-06 22:58:02 +0000432 *address ^= ~bits & mask;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000433}
434
435static void bginvblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnoldf894a4c2005-07-06 22:58:02 +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{
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000439 mask &= bits;
440 *address = (*address & ~mask) | (bg_pattern & mask);
Jens Arnold7e11acb2005-06-28 23:15:47 +0000441}
442
443static void fginvblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000444 ICODE_ATTR;
Jens Arnolda142d4d2005-06-30 18:42:24 +0000445static void fginvblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnold7e11acb2005-06-28 23:15:47 +0000446{
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000447 mask &= ~bits;
448 *address = (*address & ~mask) | (fg_pattern & mask);
Jens Arnold7e11acb2005-06-28 23:15:47 +0000449}
450
451static void solidinvblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000452 ICODE_ATTR;
Jens Arnolda142d4d2005-06-30 18:42:24 +0000453static void solidinvblock(unsigned char *address, unsigned mask, unsigned bits)
Jens Arnold7e11acb2005-06-28 23:15:47 +0000454{
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000455 *address = (*address & ~mask) | (~bits & mask & fg_pattern)
456 | (bits & mask & bg_pattern);
Jens Arnold7e11acb2005-06-28 23:15:47 +0000457}
458
Jens Arnold71bc5042005-11-06 23:49:29 +0000459lcd_blockfunc_type* const lcd_blockfuncs[8] = {
Jens Arnold7e11acb2005-06-28 23:15:47 +0000460 flipblock, bgblock, fgblock, solidblock,
461 flipinvblock, bginvblock, fginvblock, solidinvblock
462};
Jens Arnold04daef12005-06-24 22:33:21 +0000463
Jens Arnold6a556c12005-06-23 16:53:54 +0000464/*** drawing functions ***/
465
Jens Arnold04daef12005-06-24 22:33:21 +0000466/* Clear the whole display */
Jens Arnold6a556c12005-06-23 16:53:54 +0000467void lcd_clear_display(void)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000468{
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000469 unsigned bits = (drawmode & DRMODE_INVERSEVID) ? fg_pattern : bg_pattern;
Jens Arnold93494122005-06-25 00:28:09 +0000470
471 memset(lcd_framebuffer, bits, sizeof lcd_framebuffer);
Jens Arnold6a556c12005-06-23 16:53:54 +0000472 scrolling_lines = 0;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000473}
474
Jens Arnold6a556c12005-06-23 16:53:54 +0000475/* Set a single pixel */
476void lcd_drawpixel(int x, int y)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000477{
Jens Arnold7f8cc3f2005-06-29 21:33:36 +0000478 if (((unsigned)x < LCD_WIDTH) && ((unsigned)y < LCD_HEIGHT))
Jens Arnold576908d2005-06-29 01:39:50 +0000479 lcd_pixelfuncs[drawmode](x, y);
Jens Arnold6a556c12005-06-23 16:53:54 +0000480}
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000481
Jens Arnold04daef12005-06-24 22:33:21 +0000482/* Draw a line */
Jens Arnold6a556c12005-06-23 16:53:54 +0000483void lcd_drawline(int x1, int y1, int x2, int y2)
484{
485 int numpixels;
486 int i;
487 int deltax, deltay;
488 int d, dinc1, dinc2;
489 int x, xinc1, xinc2;
490 int y, yinc1, yinc2;
Jens Arnold576908d2005-06-29 01:39:50 +0000491 lcd_pixelfunc_type *pfunc = lcd_pixelfuncs[drawmode];
Jens Arnold6a556c12005-06-23 16:53:54 +0000492
493 deltax = abs(x2 - x1);
494 deltay = abs(y2 - y1);
Jens Arnold04daef12005-06-24 22:33:21 +0000495 xinc2 = 1;
496 yinc2 = 1;
Jens Arnold6a556c12005-06-23 16:53:54 +0000497
Jens Arnold04daef12005-06-24 22:33:21 +0000498 if (deltax >= deltay)
Jens Arnold6a556c12005-06-23 16:53:54 +0000499 {
500 numpixels = deltax;
501 d = 2 * deltay - deltax;
502 dinc1 = deltay * 2;
503 dinc2 = (deltay - deltax) * 2;
504 xinc1 = 1;
Jens Arnold6a556c12005-06-23 16:53:54 +0000505 yinc1 = 0;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000506 }
Jens Arnold6a556c12005-06-23 16:53:54 +0000507 else
508 {
509 numpixels = deltay;
510 d = 2 * deltax - deltay;
511 dinc1 = deltax * 2;
512 dinc2 = (deltax - deltay) * 2;
513 xinc1 = 0;
Jens Arnold6a556c12005-06-23 16:53:54 +0000514 yinc1 = 1;
Jens Arnold6a556c12005-06-23 16:53:54 +0000515 }
516 numpixels++; /* include endpoints */
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000517
Jens Arnold04daef12005-06-24 22:33:21 +0000518 if (x1 > x2)
Jens Arnold6a556c12005-06-23 16:53:54 +0000519 {
520 xinc1 = -xinc1;
521 xinc2 = -xinc2;
522 }
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000523
Jens Arnold04daef12005-06-24 22:33:21 +0000524 if (y1 > y2)
Jens Arnold6a556c12005-06-23 16:53:54 +0000525 {
526 yinc1 = -yinc1;
527 yinc2 = -yinc2;
528 }
529
530 x = x1;
531 y = y1;
532
Jens Arnold04daef12005-06-24 22:33:21 +0000533 for (i = 0; i < numpixels; i++)
Jens Arnold6a556c12005-06-23 16:53:54 +0000534 {
Jens Arnold04daef12005-06-24 22:33:21 +0000535 if (((unsigned)x < LCD_WIDTH) && ((unsigned)y < LCD_HEIGHT))
536 pfunc(x, y);
Jens Arnold6a556c12005-06-23 16:53:54 +0000537
Jens Arnold04daef12005-06-24 22:33:21 +0000538 if (d < 0)
Jens Arnold6a556c12005-06-23 16:53:54 +0000539 {
540 d += dinc1;
541 x += xinc1;
542 y += yinc1;
543 }
544 else
545 {
546 d += dinc2;
547 x += xinc2;
548 y += yinc2;
549 }
550 }
551}
552
Jens Arnold04daef12005-06-24 22:33:21 +0000553/* Draw a horizontal line (optimised) */
554void lcd_hline(int x1, int x2, int y)
Jens Arnold6a556c12005-06-23 16:53:54 +0000555{
Jens Arnold04daef12005-06-24 22:33:21 +0000556 int x;
Jens Arnold3291ae62005-07-02 07:21:21 +0000557 unsigned char *dst, *dst_end;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000558 unsigned mask;
Jens Arnold24a1f942005-06-24 23:06:06 +0000559 lcd_blockfunc_type *bfunc;
Jens Arnold6a556c12005-06-23 16:53:54 +0000560
Jens Arnold04daef12005-06-24 22:33:21 +0000561 /* direction flip */
562 if (x2 < x1)
Jens Arnold6a556c12005-06-23 16:53:54 +0000563 {
Jens Arnold04daef12005-06-24 22:33:21 +0000564 x = x1;
565 x1 = x2;
566 x2 = x;
567 }
568
569 /* nothing to draw? */
570 if (((unsigned)y >= LCD_HEIGHT) || (x1 >= LCD_WIDTH) || (x2 < 0))
571 return;
572
573 /* clipping */
574 if (x1 < 0)
575 x1 = 0;
576 if (x2 >= LCD_WIDTH)
577 x2 = LCD_WIDTH-1;
578
Jens Arnold576908d2005-06-29 01:39:50 +0000579 bfunc = lcd_blockfuncs[drawmode];
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000580 dst = &lcd_framebuffer[y>>2][x1];
581 mask = 3 << (2 * (y & 3));
Jens Arnold04daef12005-06-24 22:33:21 +0000582
Jens Arnold3291ae62005-07-02 07:21:21 +0000583 dst_end = dst + x2 - x1;
584 do
Jens Arnold7e11acb2005-06-28 23:15:47 +0000585 bfunc(dst++, mask, 0xFFu);
Jens Arnold3291ae62005-07-02 07:21:21 +0000586 while (dst <= dst_end);
Jens Arnold04daef12005-06-24 22:33:21 +0000587}
588
589/* Draw a vertical line (optimised) */
590void lcd_vline(int x, int y1, int y2)
591{
592 int ny;
593 unsigned char *dst;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000594 unsigned mask, mask_bottom;
Jens Arnold24a1f942005-06-24 23:06:06 +0000595 lcd_blockfunc_type *bfunc;
Jens Arnold04daef12005-06-24 22:33:21 +0000596
597 /* direction flip */
598 if (y2 < y1)
599 {
600 ny = y1;
601 y1 = y2;
602 y2 = ny;
603 }
604
605 /* nothing to draw? */
606 if (((unsigned)x >= LCD_WIDTH) || (y1 >= LCD_HEIGHT) || (y2 < 0))
607 return;
608
609 /* clipping */
610 if (y1 < 0)
611 y1 = 0;
612 if (y2 >= LCD_HEIGHT)
613 y2 = LCD_HEIGHT-1;
614
Jens Arnold576908d2005-06-29 01:39:50 +0000615 bfunc = lcd_blockfuncs[drawmode];
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000616 dst = &lcd_framebuffer[y1>>2][x];
617 ny = y2 - (y1 & ~3);
618 mask = 0xFFu << (2 * (y1 & 3));
619 mask_bottom = 0xFFu >> (2 * (~ny & 3));
Jens Arnold7e11acb2005-06-28 23:15:47 +0000620
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000621 for (; ny >= 4; ny -= 4)
Jens Arnold04daef12005-06-24 22:33:21 +0000622 {
Jens Arnold7e11acb2005-06-28 23:15:47 +0000623 bfunc(dst, mask, 0xFFu);
Jens Arnold04daef12005-06-24 22:33:21 +0000624 dst += LCD_WIDTH;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000625 mask = 0xFFu;
Jens Arnold6a556c12005-06-23 16:53:54 +0000626 }
Jens Arnolda142d4d2005-06-30 18:42:24 +0000627 mask &= mask_bottom;
628 bfunc(dst, mask, 0xFFu);
Jens Arnold04daef12005-06-24 22:33:21 +0000629}
630
631/* Draw a rectangular box */
632void lcd_drawrect(int x, int y, int width, int height)
633{
634 if ((width <= 0) || (height <= 0))
635 return;
636
637 int x2 = x + width - 1;
638 int y2 = y + height - 1;
639
640 lcd_vline(x, y, y2);
641 lcd_vline(x2, y, y2);
642 lcd_hline(x, x2, y);
643 lcd_hline(x, x2, y2);
644}
645
Jens Arnold04daef12005-06-24 22:33:21 +0000646/* Fill a rectangular area */
647void lcd_fillrect(int x, int y, int width, int height)
648{
Jens Arnold3291ae62005-07-02 07:21:21 +0000649 int ny;
650 unsigned char *dst, *dst_end;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000651 unsigned mask, mask_bottom;
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000652 unsigned bits = fg_pattern;
Jens Arnold24a1f942005-06-24 23:06:06 +0000653 lcd_blockfunc_type *bfunc;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000654 bool fillopt;
Jens Arnold04daef12005-06-24 22:33:21 +0000655
656 /* nothing to draw? */
657 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
Jens Arnold7e11acb2005-06-28 23:15:47 +0000658 || (x + width <= 0) || (y + height <= 0))
Jens Arnold04daef12005-06-24 22:33:21 +0000659 return;
660
661 /* clipping */
662 if (x < 0)
Jens Arnold6a556c12005-06-23 16:53:54 +0000663 {
Jens Arnold04daef12005-06-24 22:33:21 +0000664 width += x;
665 x = 0;
Jens Arnold6a556c12005-06-23 16:53:54 +0000666 }
Jens Arnold04daef12005-06-24 22:33:21 +0000667 if (y < 0)
Jens Arnold6a556c12005-06-23 16:53:54 +0000668 {
Jens Arnold04daef12005-06-24 22:33:21 +0000669 height += y;
670 y = 0;
Jens Arnold6a556c12005-06-23 16:53:54 +0000671 }
Jens Arnold04daef12005-06-24 22:33:21 +0000672 if (x + width > LCD_WIDTH)
673 width = LCD_WIDTH - x;
674 if (y + height > LCD_HEIGHT)
675 height = LCD_HEIGHT - y;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000676
677 fillopt = (drawmode & DRMODE_INVERSEVID) ?
678 (drawmode & DRMODE_BG) : (drawmode & DRMODE_FG);
679 if (fillopt &&(drawmode & DRMODE_INVERSEVID))
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000680 bits = bg_pattern;
Jens Arnold576908d2005-06-29 01:39:50 +0000681 bfunc = lcd_blockfuncs[drawmode];
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000682 dst = &lcd_framebuffer[y>>2][x];
683 ny = height - 1 + (y & 3);
684 mask = 0xFFu << (2 * (y & 3));
685 mask_bottom = 0xFFu >> (2 * (~ny & 3));
Jens Arnold6a556c12005-06-23 16:53:54 +0000686
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000687 for (; ny >= 4; ny -= 4)
Jens Arnold6a556c12005-06-23 16:53:54 +0000688 {
Jens Arnold7e11acb2005-06-28 23:15:47 +0000689 if (fillopt && (mask == 0xFFu))
Jens Arnold04daef12005-06-24 22:33:21 +0000690 memset(dst, bits, width);
Jens Arnold6a556c12005-06-23 16:53:54 +0000691 else
692 {
Jens Arnold7e11acb2005-06-28 23:15:47 +0000693 unsigned char *dst_row = dst;
694
Jens Arnold3291ae62005-07-02 07:21:21 +0000695 dst_end = dst_row + width;
696 do
Jens Arnold7e11acb2005-06-28 23:15:47 +0000697 bfunc(dst_row++, mask, 0xFFu);
Jens Arnold3291ae62005-07-02 07:21:21 +0000698 while (dst_row < dst_end);
Jens Arnold6a556c12005-06-23 16:53:54 +0000699 }
Jens Arnold7e11acb2005-06-28 23:15:47 +0000700
701 dst += LCD_WIDTH;
702 mask = 0xFFu;
Jens Arnold6a556c12005-06-23 16:53:54 +0000703 }
Jens Arnolda142d4d2005-06-30 18:42:24 +0000704 mask &= mask_bottom;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000705
Jens Arnolda142d4d2005-06-30 18:42:24 +0000706 if (fillopt && (mask == 0xFFu))
Jens Arnold04daef12005-06-24 22:33:21 +0000707 memset(dst, bits, width);
708 else
Jens Arnold7e11acb2005-06-28 23:15:47 +0000709 {
Jens Arnold3291ae62005-07-02 07:21:21 +0000710 dst_end = dst + width;
711 do
Jens Arnolda142d4d2005-06-30 18:42:24 +0000712 bfunc(dst++, mask, 0xFFu);
Jens Arnold3291ae62005-07-02 07:21:21 +0000713 while (dst < dst_end);
Jens Arnold7e11acb2005-06-28 23:15:47 +0000714 }
Jens Arnold6a556c12005-06-23 16:53:54 +0000715}
716
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000717/* About Rockbox' internal monochrome bitmap format:
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000718 *
719 * A bitmap contains one bit for every pixel that defines if that pixel is
720 * black (1) or white (0). Bits within a byte are arranged vertically, LSB
721 * at top.
722 * The bytes are stored in row-major order, with byte 0 being top left,
723 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
724 * 0..7, the second row defines pixel row 8..15 etc.
725 *
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000726 * This is similar to the internal lcd hw format. */
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000727
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000728/* Draw a partial monochrome bitmap */
729void lcd_mono_bitmap_part(const unsigned char *src, int src_x, int src_y,
730 int stride, int x, int y, int width, int height)
731 ICODE_ATTR;
732void lcd_mono_bitmap_part(const unsigned char *src, int src_x, int src_y,
733 int stride, int x, int y, int width, int height)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000734{
Jens Arnold3291ae62005-07-02 07:21:21 +0000735 int shift, ny;
736 unsigned char *dst, *dst_end;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000737 unsigned mask, mask_bottom;
738 lcd_blockfunc_type *bfunc;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000739
Jens Arnold7e11acb2005-06-28 23:15:47 +0000740 /* nothing to draw? */
741 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
742 || (x + width <= 0) || (y + height <= 0))
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000743 return;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000744
Jens Arnold7e11acb2005-06-28 23:15:47 +0000745 /* clipping */
746 if (x < 0)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000747 {
Jens Arnold7e11acb2005-06-28 23:15:47 +0000748 width += x;
749 src_x -= x;
750 x = 0;
751 }
752 if (y < 0)
753 {
754 height += y;
755 src_y -= y;
756 y = 0;
757 }
758 if (x + width > LCD_WIDTH)
759 width = LCD_WIDTH - x;
760 if (y + height > LCD_HEIGHT)
761 height = LCD_HEIGHT - y;
762
763 src += stride * (src_y >> 3) + src_x; /* move starting point */
764 src_y &= 7;
765 y -= src_y;
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000766 dst = &lcd_framebuffer[y>>2][x];
767 shift = y & 3;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000768 ny = height - 1 + shift + src_y;
769
Jens Arnold576908d2005-06-29 01:39:50 +0000770 bfunc = lcd_blockfuncs[drawmode];
Jens Arnold7e11acb2005-06-28 23:15:47 +0000771 mask = 0xFFu << (shift + src_y);
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000772 mask_bottom = 0xFFu >> (~ny & 7);
Jens Arnold7e11acb2005-06-28 23:15:47 +0000773
774 if (shift == 0)
775 {
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000776 unsigned dmask1, dmask2, data;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000777
778 for (; ny >= 8; ny -= 8)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000779 {
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000780 const unsigned char *src_row = src;
781 unsigned char *dst_row = dst + LCD_WIDTH;
782
783 dmask1 = dibits[mask&0x0F];
784 dmask2 = dibits[(mask>>4)&0x0F];
785 dst_end = dst_row + width;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000786
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000787 if (dmask1 != 0)
788 {
Jens Arnold3291ae62005-07-02 07:21:21 +0000789 do
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000790 {
791 data = *src_row++;
792 bfunc(dst_row - LCD_WIDTH, dmask1, dibits[data&0x0F]);
793 bfunc(dst_row++, dmask2, dibits[(data>>4)&0x0F]);
794 }
Jens Arnold3291ae62005-07-02 07:21:21 +0000795 while (dst_row < dst_end);
Jens Arnold7e11acb2005-06-28 23:15:47 +0000796 }
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000797 else
798 {
799 do
800 bfunc(dst_row++, dmask2, dibits[((*src_row++)>>4)&0x0F]);
801 while (dst_row < dst_end);
802 }
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000803 src += stride;
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000804 dst += 2*LCD_WIDTH;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000805 mask = 0xFFu;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000806 }
Jens Arnolda142d4d2005-06-30 18:42:24 +0000807 mask &= mask_bottom;
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000808 dmask1 = dibits[mask&0x0F];
809 dmask2 = dibits[(mask>>4)&0x0F];
810 dst_end = dst + width;
811
812 if (dmask1 != 0)
813 {
814 if (dmask2 != 0)
815 {
816 do
817 {
818 data = *src++;
819 bfunc(dst, dmask1, dibits[data&0x0F]);
820 bfunc((dst++) + LCD_WIDTH, dmask2, dibits[(data>>4)&0x0F]);
821 }
822 while (dst < dst_end);
823 }
824 else
825 {
826 do
827 bfunc(dst++, dmask1, dibits[(*src++)&0x0F]);
828 while (dst < dst_end);
829 }
830 }
Jens Arnold7e11acb2005-06-28 23:15:47 +0000831 else
832 {
Jens Arnold3291ae62005-07-02 07:21:21 +0000833 do
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000834 bfunc((dst++) + LCD_WIDTH, dmask2, dibits[((*src++)>>4)&0x0F]);
Jens Arnold3291ae62005-07-02 07:21:21 +0000835 while (dst < dst_end);
Jens Arnold7e11acb2005-06-28 23:15:47 +0000836 }
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000837 }
838 else
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000839 {
Jens Arnold3291ae62005-07-02 07:21:21 +0000840 dst_end = dst + width;
841 do
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000842 {
Jens Arnold7e11acb2005-06-28 23:15:47 +0000843 const unsigned char *src_col = src++;
844 unsigned char *dst_col = dst++;
845 unsigned mask_col = mask;
846 unsigned data = 0;
847
848 for (y = ny; y >= 8; y -= 8)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000849 {
850 data |= *src_col << shift;
Jens Arnold7e11acb2005-06-28 23:15:47 +0000851
852 if (mask_col & 0xFFu)
853 {
Jens Arnoldf894a4c2005-07-06 22:58:02 +0000854 if (mask_col & 0x0F)
855 bfunc(dst_col, dibits[mask_col&0x0F], dibits[data&0x0F]);
856 bfunc(dst_col + LCD_WIDTH, dibits[(mask_col>>4)&0x0F],
857 dibits[(data>>4)&0x0F]);
858 mask_col = 0xFFu;
859 }
860 else
861 mask_col >>= 8;
862
863 src_col += stride;
864 dst_col += 2*LCD_WIDTH;
865 data >>= 8;
866 }
867 data |= *src_col << shift;
868 mask_bottom &= mask_col;
869 if (mask_bottom & 0x0F)
870 bfunc(dst_col, dibits[mask_bottom&0x0F], dibits[data&0x0F]);
871 if (mask_bottom & 0xF0)
872 bfunc(dst_col + LCD_WIDTH, dibits[(mask_bottom&0xF0)>>4],
873 dibits[(data>>4)&0x0F]);
874 }
875 while (dst < dst_end);
876 }
877}
878
879/* Draw a full monochrome bitmap */
880void lcd_mono_bitmap(const unsigned char *src, int x, int y, int width, int height)
881{
882 lcd_mono_bitmap_part(src, 0, 0, width, x, y, width, height);
883}
884
885/* About Rockbox' internal native bitmap format:
886 *
887 * A bitmap contains two bits for every pixel. 00 = white, 01 = light grey,
888 * 10 = dark grey, 11 = black. Bits within a byte are arranged vertically, LSB
889 * at top.
890 * The bytes are stored in row-major order, with byte 0 being top left,
891 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
892 * 0..3, the second row defines pixel row 4..7 etc.
893 *
894 * This is the same as the internal lcd hw format. */
895
896/* Draw a partial native bitmap */
897void lcd_bitmap_part(const unsigned char *src, int src_x, int src_y,
898 int stride, int x, int y, int width, int height)
899 ICODE_ATTR;
900void lcd_bitmap_part(const unsigned char *src, int src_x, int src_y,
901 int stride, int x, int y, int width, int height)
902{
903 int shift, ny;
904 unsigned char *dst, *dst_end;
905 unsigned mask, mask_bottom;
906 lcd_blockfunc_type *bfunc;
907
908 /* nothing to draw? */
909 if ((width <= 0) || (height <= 0) || (x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
910 || (x + width <= 0) || (y + height <= 0))
911 return;
912
913 /* clipping */
914 if (x < 0)
915 {
916 width += x;
917 src_x -= x;
918 x = 0;
919 }
920 if (y < 0)
921 {
922 height += y;
923 src_y -= y;
924 y = 0;
925 }
926 if (x + width > LCD_WIDTH)
927 width = LCD_WIDTH - x;
928 if (y + height > LCD_HEIGHT)
929 height = LCD_HEIGHT - y;
930
931 src += stride * (src_y >> 2) + src_x; /* move starting point */
932 src_y &= 3;
933 y -= src_y;
934 dst = &lcd_framebuffer[y>>2][x];
935 shift = y & 3;
936 ny = height - 1 + shift + src_y;
937
938 bfunc = lcd_blockfuncs[drawmode];
939 mask = 0xFFu << (2 * (shift + src_y));
940 mask_bottom = 0xFFu >> (2 * (~ny & 3));
941
942 if (shift == 0)
943 {
944 for (; ny >= 4; ny -= 4)
945 {
946 if (mask == 0xFFu)
947 memcpy(dst, src, width);
948 else
949 {
950 const unsigned char *src_row = src;
951 unsigned char *dst_row = dst;
952
953 dst_end = dst_row + width;
954 do
955 bfunc(dst_row++, mask, *src_row++);
956 while (dst_row < dst_end);
957 }
958
959 src += stride;
960 dst += LCD_WIDTH;
961 mask = 0xFFu;
962 }
963 mask &= mask_bottom;
964
965 if (mask == 0xFFu)
966 memcpy(dst, src, width);
967 else
968 {
969 dst_end = dst + width;
970 do
971 bfunc(dst++, mask, *src++);
972 while (dst < dst_end);
973 }
974 }
975 else
976 {
977 shift *= 2;
978 dst_end = dst + width;
979 do
980 {
981 const unsigned char *src_col = src++;
982 unsigned char *dst_col = dst++;
983 unsigned mask_col = mask;
984 unsigned data = 0;
985
986 for (y = ny; y >= 4; y -= 4)
987 {
988 data |= *src_col << shift;
989
990 if (mask_col & 0xFFu)
991 {
Jens Arnold7e11acb2005-06-28 23:15:47 +0000992 bfunc(dst_col, mask_col, data);
993 mask_col = 0xFFu;
994 }
995 else
996 mask_col >>= 8;
997
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +0000998 src_col += stride;
999 dst_col += LCD_WIDTH;
1000 data >>= 8;
1001 }
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001002 data |= *src_col << shift;
Jens Arnold7e11acb2005-06-28 23:15:47 +00001003 bfunc(dst_col, mask_col & mask_bottom, data);
1004 }
Jens Arnold3291ae62005-07-02 07:21:21 +00001005 while (dst < dst_end);
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001006 }
1007}
1008
Jens Arnoldf894a4c2005-07-06 22:58:02 +00001009/* Draw a full native bitmap */
Jens Arnold7e11acb2005-06-28 23:15:47 +00001010void lcd_bitmap(const unsigned char *src, int x, int y, int width, int height)
1011{
1012 lcd_bitmap_part(src, 0, 0, width, x, y, width, height);
1013}
1014
Jens Arnold6a556c12005-06-23 16:53:54 +00001015/* put a string at a given pixel position, skipping first ofs pixel columns */
1016static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001017{
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +00001018 unsigned short ch;
1019 unsigned short *ucs;
Jens Arnold6a556c12005-06-23 16:53:54 +00001020 struct font* pf = font_get(curfont);
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001021
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +00001022 ucs = bidi_l2v(str, 1);
Linus Nielsen Feltzing41a53d22005-08-08 19:23:28 +00001023
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +00001024 while ((ch = *ucs++) != 0 && x < LCD_WIDTH)
Jens Arnold6a556c12005-06-23 16:53:54 +00001025 {
Jens Arnold7e11acb2005-06-28 23:15:47 +00001026 int width;
1027 const unsigned char *bits;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001028
Jens Arnold6a556c12005-06-23 16:53:54 +00001029 /* check input range */
1030 if (ch < pf->firstchar || ch >= pf->firstchar+pf->size)
1031 ch = pf->defaultchar;
1032 ch -= pf->firstchar;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001033
Jens Arnold6a556c12005-06-23 16:53:54 +00001034 /* get proportional width and glyph bits */
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +00001035 width = font_get_width(pf,ch);
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001036
Jens Arnold7e11acb2005-06-28 23:15:47 +00001037 if (ofs > width)
Jens Arnold6a556c12005-06-23 16:53:54 +00001038 {
Jens Arnold7e11acb2005-06-28 23:15:47 +00001039 ofs -= width;
1040 continue;
Jens Arnold6a556c12005-06-23 16:53:54 +00001041 }
Jens Arnold576908d2005-06-29 01:39:50 +00001042
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +00001043 bits = font_get_bits(pf, ch);
Jens Arnold6a556c12005-06-23 16:53:54 +00001044
Jens Arnoldf894a4c2005-07-06 22:58:02 +00001045 lcd_mono_bitmap_part(bits, ofs, 0, width, x, y, width - ofs, pf->height);
Jens Arnold7e11acb2005-06-28 23:15:47 +00001046
1047 x += width - ofs;
Jens Arnold6a556c12005-06-23 16:53:54 +00001048 ofs = 0;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001049 }
1050}
1051
Jens Arnold6a556c12005-06-23 16:53:54 +00001052/* put a string at a given pixel position */
1053void lcd_putsxy(int x, int y, const unsigned char *str)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001054{
Jens Arnold6a556c12005-06-23 16:53:54 +00001055 lcd_putsxyofs(x, y, 0, str);
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001056}
1057
Jens Arnold6a556c12005-06-23 16:53:54 +00001058/*** line oriented text output ***/
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001059
Jens Arnold6a556c12005-06-23 16:53:54 +00001060void lcd_puts_style(int x, int y, const unsigned char *str, int style)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001061{
Jens Arnold6a556c12005-06-23 16:53:54 +00001062 int xpos,ypos,w,h;
Jens Arnoldf894a4c2005-07-06 22:58:02 +00001063 int lastmode = drawmode;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001064
Jens Arnold6a556c12005-06-23 16:53:54 +00001065 /* make sure scrolling is turned off on the line we are updating */
1066 scrolling_lines &= ~(1 << y);
1067
1068 if(!str || !str[0])
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001069 return;
1070
Jens Arnold6a556c12005-06-23 16:53:54 +00001071 lcd_getstringsize(str, &w, &h);
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +00001072 xpos = xmargin + x*w / utf8length((char *)str);
Jens Arnold6a556c12005-06-23 16:53:54 +00001073 ypos = ymargin + y*h;
1074 lcd_putsxy(xpos, ypos, str);
Jens Arnoldf894a4c2005-07-06 22:58:02 +00001075 drawmode = (DRMODE_SOLID|DRMODE_INVERSEVID);
Jens Arnold04daef12005-06-24 22:33:21 +00001076 lcd_fillrect(xpos + w, ypos, LCD_WIDTH - (xpos + w), h);
Jens Arnold6a556c12005-06-23 16:53:54 +00001077 if (style & STYLE_INVERT)
Jens Arnold04daef12005-06-24 22:33:21 +00001078 {
Jens Arnoldf894a4c2005-07-06 22:58:02 +00001079 drawmode = DRMODE_COMPLEMENT;
Jens Arnold04daef12005-06-24 22:33:21 +00001080 lcd_fillrect(xpos, ypos, LCD_WIDTH - xpos, h);
1081 }
Jens Arnoldf894a4c2005-07-06 22:58:02 +00001082 drawmode = lastmode;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001083}
1084
Jens Arnold6a556c12005-06-23 16:53:54 +00001085/* put a string at a given char position */
1086void lcd_puts(int x, int y, const unsigned char *str)
1087{
1088 lcd_puts_style(x, y, str, STYLE_DEFAULT);
1089}
1090
1091/*** scrolling ***/
1092
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001093/* Reverse the invert setting of the scrolling line (if any) at given char
1094 position. Setting will go into affect next time line scrolls. */
1095void lcd_invertscroll(int x, int y)
1096{
1097 struct scrollinfo* s;
1098
1099 (void)x;
1100
1101 s = &scroll[y];
1102 s->invert = !s->invert;
1103}
1104
Jens Arnold6a556c12005-06-23 16:53:54 +00001105void lcd_stop_scroll(void)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001106{
Jens Arnold6a556c12005-06-23 16:53:54 +00001107 scrolling_lines=0;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001108}
1109
Jens Arnold6a556c12005-06-23 16:53:54 +00001110void lcd_scroll_speed(int speed)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001111{
Jens Arnold6a556c12005-06-23 16:53:54 +00001112 scroll_ticks = scroll_tick_table[speed];
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001113}
1114
Jens Arnold6a556c12005-06-23 16:53:54 +00001115void lcd_scroll_step(int step)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001116{
Jens Arnold6a556c12005-06-23 16:53:54 +00001117 scroll_step = step;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001118}
1119
Jens Arnold6a556c12005-06-23 16:53:54 +00001120void lcd_scroll_delay(int ms)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001121{
Jens Arnold6a556c12005-06-23 16:53:54 +00001122 scroll_delay = ms / (HZ / 10);
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001123}
1124
Jens Arnold6a556c12005-06-23 16:53:54 +00001125void lcd_bidir_scroll(int percent)
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001126{
Jens Arnold6a556c12005-06-23 16:53:54 +00001127 bidir_limit = percent;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001128}
1129
1130void lcd_puts_scroll(int x, int y, const unsigned char *string)
1131{
1132 lcd_puts_scroll_style(x, y, string, STYLE_DEFAULT);
1133}
1134
1135void lcd_puts_scroll_style(int x, int y, const unsigned char *string, int style)
1136{
1137 struct scrollinfo* s;
1138 int w, h;
1139
1140 s = &scroll[y];
1141
1142 s->start_tick = current_tick + scroll_delay;
1143 s->invert = false;
1144 if (style & STYLE_INVERT) {
1145 s->invert = true;
1146 lcd_puts_style(x,y,string,STYLE_INVERT);
1147 }
1148 else
1149 lcd_puts(x,y,string);
1150
1151 lcd_getstringsize(string, &w, &h);
1152
1153 if (LCD_WIDTH - x * 8 - xmargin < w) {
1154 /* prepare scroll line */
1155 char *end;
1156
1157 memset(s->line, 0, sizeof s->line);
Daniel Stenberg1a29cfb2005-11-25 00:10:12 +00001158 strcpy(s->line, (char *)string);
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001159
1160 /* get width */
Daniel Stenberg1a29cfb2005-11-25 00:10:12 +00001161 s->width = lcd_getstringsize((unsigned char *)s->line, &w, &h);
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001162
1163 /* scroll bidirectional or forward only depending on the string
1164 width */
1165 if ( bidir_limit ) {
1166 s->bidir = s->width < (LCD_WIDTH - xmargin) *
1167 (100 + bidir_limit) / 100;
1168 }
1169 else
1170 s->bidir = false;
1171
1172 if (!s->bidir) { /* add spaces if scrolling in the round */
1173 strcat(s->line, " ");
1174 /* get new width incl. spaces */
Daniel Stenberg1a29cfb2005-11-25 00:10:12 +00001175 s->width = lcd_getstringsize((unsigned char *)s->line, &w, &h);
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001176 }
1177
1178 end = strchr(s->line, '\0');
Daniel Stenberg1a29cfb2005-11-25 00:10:12 +00001179 strncpy(end, (char *)string, LCD_WIDTH/2);
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001180
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +00001181 s->len = utf8length((char *)string);
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001182 s->offset = 0;
1183 s->startx = x;
1184 s->backward = false;
1185 scrolling_lines |= (1<<y);
1186 }
1187 else
1188 /* force a bit switch-off since it doesn't scroll */
1189 scrolling_lines &= ~(1<<y);
1190}
1191
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001192static void scroll_thread(void)
1193{
1194 struct font* pf;
1195 struct scrollinfo* s;
1196 int index;
1197 int xpos, ypos;
Jens Arnold04daef12005-06-24 22:33:21 +00001198 int lastmode;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001199
1200 /* initialize scroll struct array */
1201 scrolling_lines = 0;
1202
1203 while ( 1 ) {
1204 for ( index = 0; index < SCROLLABLE_LINES; index++ ) {
1205 /* really scroll? */
1206 if ( !(scrolling_lines&(1<<index)) )
1207 continue;
1208
1209 s = &scroll[index];
1210
1211 /* check pause */
1212 if (TIME_BEFORE(current_tick, s->start_tick))
1213 continue;
1214
1215 if (s->backward)
1216 s->offset -= scroll_step;
1217 else
1218 s->offset += scroll_step;
1219
1220 pf = font_get(curfont);
1221 xpos = xmargin + s->startx * s->width / s->len;
1222 ypos = ymargin + index * pf->height;
1223
1224 if (s->bidir) { /* scroll bidirectional */
1225 if (s->offset <= 0) {
1226 /* at beginning of line */
1227 s->offset = 0;
1228 s->backward = false;
1229 s->start_tick = current_tick + scroll_delay * 2;
1230 }
1231 if (s->offset >= s->width - (LCD_WIDTH - xpos)) {
1232 /* at end of line */
1233 s->offset = s->width - (LCD_WIDTH - xpos);
1234 s->backward = true;
1235 s->start_tick = current_tick + scroll_delay * 2;
1236 }
1237 }
1238 else {
Marcoen Hirschberg40d22092005-11-30 15:37:48 +00001239 /* scroll forward the whole time */
1240 if (s->offset >= s->width)
1241 s->offset %= s->width;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001242 }
1243
Jens Arnoldf894a4c2005-07-06 22:58:02 +00001244 lastmode = drawmode;
1245 drawmode = (DRMODE_SOLID|DRMODE_INVERSEVID);
Jens Arnold04daef12005-06-24 22:33:21 +00001246 lcd_fillrect(xpos, ypos, LCD_WIDTH - xpos, pf->height);
Jens Arnoldf894a4c2005-07-06 22:58:02 +00001247 drawmode = DRMODE_SOLID;
Daniel Stenberg1a29cfb2005-11-25 00:10:12 +00001248 lcd_putsxyofs(xpos, ypos, s->offset, (unsigned char *)s->line);
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001249 if (s->invert)
Jens Arnold04daef12005-06-24 22:33:21 +00001250 {
Jens Arnoldf894a4c2005-07-06 22:58:02 +00001251 drawmode = DRMODE_COMPLEMENT;
Jens Arnold04daef12005-06-24 22:33:21 +00001252 lcd_fillrect(xpos, ypos, LCD_WIDTH - xpos, pf->height);
1253 }
Jens Arnoldf894a4c2005-07-06 22:58:02 +00001254 drawmode = lastmode;
Linus Nielsen Feltzing2a83ce52004-10-26 06:54:03 +00001255 lcd_update_rect(xpos, ypos, LCD_WIDTH - xpos, pf->height);
1256 }
1257
1258 sleep(scroll_ticks);
1259 }
1260}
1261