diff options
author | Linus Nielsen Feltzing <linus@haxx.se> | 2004-10-26 06:54:03 +0000 |
---|---|---|
committer | Linus Nielsen Feltzing <linus@haxx.se> | 2004-10-26 06:54:03 +0000 |
commit | 2a83ce5ddd7fbe3ff416549bff86e36f5f86ff1b (patch) | |
tree | c6285b48158b2a473055606d501c208a87267110 | |
parent | 5cf331717d5bb169f5404e7ef88a6920940f7332 (diff) | |
download | rockbox-2a83ce5ddd7fbe3ff416549bff86e36f5f86ff1b.tar.gz rockbox-2a83ce5ddd7fbe3ff416549bff86e36f5f86ff1b.zip |
New LCD driver for iRiver H100
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@5352 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | firmware/drivers/lcd-h100.c | 915 |
1 files changed, 915 insertions, 0 deletions
diff --git a/firmware/drivers/lcd-h100.c b/firmware/drivers/lcd-h100.c new file mode 100644 index 0000000000..df4ca42b10 --- /dev/null +++ b/firmware/drivers/lcd-h100.c | |||
@@ -0,0 +1,915 @@ | |||
1 | /*************************************************************************** | ||
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 | |||
21 | #ifdef HAVE_LCD_BITMAP | ||
22 | |||
23 | #include "cpu.h" | ||
24 | #include "lcd.h" | ||
25 | #include "kernel.h" | ||
26 | #include "thread.h" | ||
27 | #include <string.h> | ||
28 | #include <stdlib.h> | ||
29 | #include "file.h" | ||
30 | #include "debug.h" | ||
31 | #include "system.h" | ||
32 | #include "font.h" | ||
33 | |||
34 | /*** definitions ***/ | ||
35 | |||
36 | /* LCD command codes */ | ||
37 | #define LCD_CNTL_ON_OFF 0xae | ||
38 | #define LCD_CNTL_OFF_MODE 0xbe | ||
39 | #define LCD_CNTL_REVERSE 0xa6 | ||
40 | #define LCD_CNTL_ALL_LIGHTING 0xa4 | ||
41 | #define LCD_CNTL_COMMON_OUTPUT_STATUS 0xc4 | ||
42 | #define LCD_CNTL_COLUMN_ADDRESS_DIR 0xa0 | ||
43 | #define LCD_CNTL_NLINE_ON_OFF 0xe4 | ||
44 | #define LCD_CNTL_DISPLAY_MODE 0x66 | ||
45 | #define LCD_CNTL_ELECTRIC_VOLUME 0x81 | ||
46 | #define LCD_CNTL_DISPLAY_START_LINE 0x8a | ||
47 | |||
48 | #define LCD_CNTL_PAGE 0xb1 | ||
49 | #define LCD_CNTL_COLUMN 0x13 | ||
50 | #define LCD_CNTL_DATA_WRITE 0x1d | ||
51 | |||
52 | #define SCROLL_SPACING 3 | ||
53 | |||
54 | #define SCROLLABLE_LINES 13 | ||
55 | |||
56 | struct scrollinfo { | ||
57 | char line[MAX_PATH + LCD_WIDTH/2 + SCROLL_SPACING + 2]; | ||
58 | int len; /* length of line in chars */ | ||
59 | int width; /* length of line in pixels */ | ||
60 | int offset; | ||
61 | int startx; | ||
62 | bool backward; /* scroll presently forward or backward? */ | ||
63 | bool bidir; | ||
64 | bool invert; /* invert the scrolled text */ | ||
65 | long start_tick; | ||
66 | }; | ||
67 | |||
68 | static volatile int scrolling_lines=0; /* Bitpattern of which lines are scrolling */ | ||
69 | |||
70 | static void scroll_thread(void); | ||
71 | static char scroll_stack[DEFAULT_STACK_SIZE]; | ||
72 | static const char scroll_name[] = "scroll"; | ||
73 | static char scroll_ticks = 12; /* # of ticks between updates*/ | ||
74 | static int scroll_delay = HZ/2; /* ticks delay before start */ | ||
75 | static char scroll_step = 6; /* pixels per scroll step */ | ||
76 | static int bidir_limit = 50; /* percent */ | ||
77 | static struct scrollinfo scroll[SCROLLABLE_LINES]; | ||
78 | static int xmargin = 0; | ||
79 | static int ymargin = 0; | ||
80 | static int curfont = FONT_SYSFIXED; | ||
81 | #ifndef SIMULATOR | ||
82 | static int xoffset = 0; /* needed for flip */ | ||
83 | #endif | ||
84 | |||
85 | unsigned char lcd_framebuffer[LCD_HEIGHT/8][LCD_WIDTH]; | ||
86 | |||
87 | /* All zeros and ones bitmaps for area filling */ | ||
88 | static const unsigned char zeros[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; | ||
89 | static const unsigned char ones[8] = { 0xff, 0xff, 0xff, 0xff, | ||
90 | 0xff, 0xff, 0xff, 0xff}; | ||
91 | |||
92 | int lcd_default_contrast(void) | ||
93 | { | ||
94 | return 32; | ||
95 | } | ||
96 | |||
97 | #ifdef SIMULATOR | ||
98 | |||
99 | void lcd_init(void) | ||
100 | { | ||
101 | create_thread(scroll_thread, scroll_stack, | ||
102 | sizeof(scroll_stack), scroll_name); | ||
103 | } | ||
104 | |||
105 | #else | ||
106 | |||
107 | /* | ||
108 | * Initialize LCD | ||
109 | */ | ||
110 | void lcd_init (void) | ||
111 | { | ||
112 | /* GPO35 is the LCD A0 pin */ | ||
113 | GPIO1_FUNCTION |= 0x00000008; | ||
114 | GPIO1_ENABLE |= 0x00000008; | ||
115 | GPIO1_OUT |= 0x00000008; | ||
116 | |||
117 | lcd_write_command(LCD_CNTL_ON_OFF | 1); /* LCD ON */ | ||
118 | lcd_write_command(LCD_CNTL_COLUMN_ADDRESS_DIR | 0); /* Normal */ | ||
119 | lcd_write_command(LCD_CNTL_COMMON_OUTPUT_STATUS | 1); /* Reverse dir */ | ||
120 | lcd_write_command(LCD_CNTL_REVERSE | 0); /* Reverse OFF */ | ||
121 | lcd_write_command(LCD_CNTL_ALL_LIGHTING | 0); /* Normal */ | ||
122 | lcd_write_command(LCD_CNTL_OFF_MODE | 1); /* OFF -> VCC on drivers */ | ||
123 | lcd_write_command(LCD_CNTL_NLINE_ON_OFF | 1); /* N-line ON */ | ||
124 | |||
125 | lcd_write_command_ex(LCD_CNTL_DISPLAY_MODE, 1); /* Monochrome mode */ | ||
126 | |||
127 | lcd_clear_display(); | ||
128 | lcd_update(); | ||
129 | |||
130 | create_thread(scroll_thread, scroll_stack, | ||
131 | sizeof(scroll_stack), scroll_name); | ||
132 | } | ||
133 | |||
134 | |||
135 | /* Performance function that works with an external buffer | ||
136 | note that y and height are in 8-pixel units! */ | ||
137 | void lcd_blit (const unsigned char* p_data, int x, int y, int width, | ||
138 | int height, int stride) | ||
139 | { | ||
140 | /* Copy display bitmap to hardware */ | ||
141 | while (height--) | ||
142 | { | ||
143 | lcd_write_command_ex(LCD_CNTL_PAGE, y++ & 0xf); | ||
144 | lcd_write_command_ex(LCD_CNTL_COLUMN, x+xoffset); | ||
145 | |||
146 | lcd_write_command(LCD_CNTL_DATA_WRITE); | ||
147 | lcd_write_data(p_data, width); | ||
148 | p_data += stride; | ||
149 | } | ||
150 | } | ||
151 | |||
152 | |||
153 | /* | ||
154 | * Update the display. | ||
155 | * This must be called after all other LCD functions that change the display. | ||
156 | */ | ||
157 | void lcd_update (void) __attribute__ ((section (".icode"))); | ||
158 | void lcd_update (void) | ||
159 | { | ||
160 | int y; | ||
161 | |||
162 | /* Copy display bitmap to hardware */ | ||
163 | for (y = 0; y < LCD_HEIGHT/8; y++) | ||
164 | { | ||
165 | lcd_write_command_ex(LCD_CNTL_PAGE, y); | ||
166 | lcd_write_command_ex(LCD_CNTL_COLUMN, 0); | ||
167 | |||
168 | lcd_write_command(LCD_CNTL_DATA_WRITE); | ||
169 | lcd_write_data (lcd_framebuffer[y], LCD_WIDTH); | ||
170 | } | ||
171 | } | ||
172 | |||
173 | /* | ||
174 | * Update a fraction of the display. | ||
175 | */ | ||
176 | void lcd_update_rect (int, int, int, int) __attribute__ ((section (".icode"))); | ||
177 | void lcd_update_rect (int x_start, int y, | ||
178 | int width, int height) | ||
179 | { | ||
180 | int ymax; | ||
181 | |||
182 | /* The Y coordinates have to work on even 8 pixel rows */ | ||
183 | ymax = (y + height-1)/8; | ||
184 | y /= 8; | ||
185 | |||
186 | if(x_start + width > LCD_WIDTH) | ||
187 | width = LCD_WIDTH - x_start; | ||
188 | if (width <= 0) | ||
189 | return; /* nothing left to do, 0 is harmful to lcd_write_data() */ | ||
190 | if(ymax >= LCD_HEIGHT/8) | ||
191 | ymax = LCD_HEIGHT/8-1; | ||
192 | |||
193 | /* Copy specified rectange bitmap to hardware */ | ||
194 | for (; y <= ymax; y++) | ||
195 | { | ||
196 | lcd_write_command_ex(LCD_CNTL_PAGE, y); | ||
197 | lcd_write_command_ex(LCD_CNTL_COLUMN, x_start+xoffset); | ||
198 | |||
199 | lcd_write_command(LCD_CNTL_DATA_WRITE); | ||
200 | lcd_write_data (&lcd_framebuffer[y][x_start], width); | ||
201 | } | ||
202 | } | ||
203 | |||
204 | void lcd_set_contrast(int val) | ||
205 | { | ||
206 | lcd_write_command_ex(LCD_CNTL_ELECTRIC_VOLUME, val); | ||
207 | } | ||
208 | |||
209 | void lcd_set_invert_display(bool yesno) | ||
210 | { | ||
211 | lcd_write_command(LCD_CNTL_REVERSE | (yesno?1:0)); | ||
212 | } | ||
213 | |||
214 | /* turn the display upside down (call lcd_update() afterwards) */ | ||
215 | void lcd_set_flip(bool yesno) | ||
216 | { | ||
217 | if (yesno) | ||
218 | { | ||
219 | lcd_write_command(LCD_CNTL_COLUMN_ADDRESS_DIR | 1); | ||
220 | lcd_write_command(LCD_CNTL_COMMON_OUTPUT_STATUS | 0); | ||
221 | xoffset = 160 - LCD_WIDTH; /* 160 colums minus the 160 we have */ | ||
222 | } | ||
223 | else | ||
224 | { | ||
225 | lcd_write_command(LCD_CNTL_COLUMN_ADDRESS_DIR | 0); | ||
226 | lcd_write_command(LCD_CNTL_COMMON_OUTPUT_STATUS | 1); | ||
227 | xoffset = 0; | ||
228 | } | ||
229 | } | ||
230 | |||
231 | /** | ||
232 | * Rolls up the lcd display by the specified amount of lines. | ||
233 | * Lines that are rolled out over the top of the screen are | ||
234 | * rolled in from the bottom again. This is a hardware | ||
235 | * remapping only and all operations on the lcd are affected. | ||
236 | * -> | ||
237 | * @param int lines - The number of lines that are rolled. | ||
238 | * The value must be 0 <= pixels < LCD_HEIGHT. | ||
239 | */ | ||
240 | void lcd_roll(int lines) | ||
241 | { | ||
242 | char data[2]; | ||
243 | |||
244 | lines &= LCD_HEIGHT-1; | ||
245 | data[0] = lines & 0xff; | ||
246 | data[1] = lines >> 8; | ||
247 | |||
248 | lcd_write_command(LCD_CNTL_DISPLAY_START_LINE); | ||
249 | lcd_write_data(data, 2); | ||
250 | } | ||
251 | |||
252 | #endif /* SIMULATOR */ | ||
253 | |||
254 | void lcd_clear_display (void) | ||
255 | { | ||
256 | memset (lcd_framebuffer, 0, sizeof lcd_framebuffer); | ||
257 | scrolling_lines = 0; | ||
258 | } | ||
259 | |||
260 | void lcd_setmargins(int x, int y) | ||
261 | { | ||
262 | xmargin = x; | ||
263 | ymargin = y; | ||
264 | } | ||
265 | |||
266 | int lcd_getxmargin(void) | ||
267 | { | ||
268 | return xmargin; | ||
269 | } | ||
270 | |||
271 | int lcd_getymargin(void) | ||
272 | { | ||
273 | return ymargin; | ||
274 | } | ||
275 | |||
276 | void lcd_setfont(int newfont) | ||
277 | { | ||
278 | curfont = newfont; | ||
279 | } | ||
280 | |||
281 | int lcd_getstringsize(const unsigned char *str, int *w, int *h) | ||
282 | { | ||
283 | struct font* pf = font_get(curfont); | ||
284 | int ch; | ||
285 | int width = 0; | ||
286 | |||
287 | while((ch = *str++)) { | ||
288 | /* check input range*/ | ||
289 | if (ch < pf->firstchar || ch >= pf->firstchar+pf->size) | ||
290 | ch = pf->defaultchar; | ||
291 | ch -= pf->firstchar; | ||
292 | |||
293 | /* get proportional width and glyph bits*/ | ||
294 | width += pf->width? pf->width[ch]: pf->maxwidth; | ||
295 | } | ||
296 | if ( w ) | ||
297 | *w = width; | ||
298 | if ( h ) | ||
299 | *h = pf->height; | ||
300 | return width; | ||
301 | } | ||
302 | |||
303 | /* put a string at a given char position */ | ||
304 | void lcd_puts(int x, int y, const unsigned char *str) | ||
305 | { | ||
306 | lcd_puts_style(x, y, str, STYLE_DEFAULT); | ||
307 | } | ||
308 | |||
309 | void lcd_puts_style(int x, int y, const unsigned char *str, int style) | ||
310 | { | ||
311 | int xpos,ypos,w,h; | ||
312 | |||
313 | #if defined(SIMULATOR) && defined(HAVE_LCD_CHARCELLS) | ||
314 | /* We make the simulator truncate the string if it reaches the right edge, | ||
315 | as otherwise it'll wrap. The real target doesn't wrap. */ | ||
316 | |||
317 | char buffer[12]; | ||
318 | if(strlen(str)+x > 11 ) { | ||
319 | strncpy(buffer, str, sizeof buffer); | ||
320 | buffer[11-x]=0; | ||
321 | str = buffer; | ||
322 | } | ||
323 | xmargin = 0; | ||
324 | ymargin = 8; | ||
325 | #endif | ||
326 | |||
327 | /* make sure scrolling is turned off on the line we are updating */ | ||
328 | scrolling_lines &= ~(1 << y); | ||
329 | |||
330 | if(!str || !str[0]) | ||
331 | return; | ||
332 | |||
333 | lcd_getstringsize(str, &w, &h); | ||
334 | xpos = xmargin + x*w / strlen(str); | ||
335 | ypos = ymargin + y*h; | ||
336 | lcd_putsxy(xpos, ypos, str); | ||
337 | lcd_clearrect(xpos + w, ypos, LCD_WIDTH - (xpos + w), h); | ||
338 | if (style & STYLE_INVERT) | ||
339 | lcd_invertrect(xpos, ypos, LCD_WIDTH - xpos, h); | ||
340 | |||
341 | #if defined(SIMULATOR) && defined(HAVE_LCD_CHARCELLS) | ||
342 | lcd_update(); | ||
343 | #endif | ||
344 | } | ||
345 | |||
346 | /* put a string at a given pixel position, skipping first ofs pixel columns */ | ||
347 | static void lcd_putsxyofs(int x, int y, int ofs, const unsigned char *str) | ||
348 | { | ||
349 | int ch; | ||
350 | struct font* pf = font_get(curfont); | ||
351 | |||
352 | while ((ch = *str++) != '\0' && x < LCD_WIDTH) | ||
353 | { | ||
354 | int gwidth, width; | ||
355 | |||
356 | /* check input range */ | ||
357 | if (ch < pf->firstchar || ch >= pf->firstchar+pf->size) | ||
358 | ch = pf->defaultchar; | ||
359 | ch -= pf->firstchar; | ||
360 | |||
361 | /* no partial-height drawing for now... */ | ||
362 | if (y + pf->height > LCD_HEIGHT) | ||
363 | break; | ||
364 | |||
365 | /* get proportional width and glyph bits */ | ||
366 | gwidth = pf->width ? pf->width[ch] : pf->maxwidth; | ||
367 | width = MIN (gwidth, LCD_WIDTH - x); | ||
368 | |||
369 | if (ofs != 0) | ||
370 | { | ||
371 | if (ofs > width) | ||
372 | { | ||
373 | ofs -= width; | ||
374 | continue; | ||
375 | } | ||
376 | width -= ofs; | ||
377 | } | ||
378 | |||
379 | if (width > 0) | ||
380 | { | ||
381 | unsigned int i; | ||
382 | const unsigned char* bits = pf->bits + | ||
383 | (pf->offset ? pf->offset[ch] | ||
384 | : ((pf->height + 7) / 8 * pf->maxwidth * ch)); | ||
385 | |||
386 | if (ofs != 0) | ||
387 | { | ||
388 | for (i = 0; i < pf->height; i += 8) | ||
389 | { | ||
390 | lcd_bitmap (bits + ofs, x, y + i, width, | ||
391 | MIN(8, pf->height - i), true); | ||
392 | bits += gwidth; | ||
393 | } | ||
394 | } | ||
395 | else | ||
396 | lcd_bitmap ((unsigned char*) bits, x, y, gwidth, | ||
397 | pf->height, true); | ||
398 | x += width; | ||
399 | } | ||
400 | ofs = 0; | ||
401 | } | ||
402 | } | ||
403 | |||
404 | /* put a string at a given pixel position */ | ||
405 | void lcd_putsxy(int x, int y, const unsigned char *str) | ||
406 | { | ||
407 | lcd_putsxyofs(x, y, 0, str); | ||
408 | } | ||
409 | |||
410 | /* | ||
411 | * About Rockbox' internal bitmap format: | ||
412 | * | ||
413 | * A bitmap contains one bit for every pixel that defines if that pixel is | ||
414 | * black (1) or white (0). Bits within a byte are arranged vertically, LSB | ||
415 | * at top. | ||
416 | * The bytes are stored in row-major order, with byte 0 being top left, | ||
417 | * byte 1 2nd from left etc. The first row of bytes defines pixel rows | ||
418 | * 0..7, the second row defines pixel row 8..15 etc. | ||
419 | * | ||
420 | * This is the same as the internal lcd hw format. | ||
421 | */ | ||
422 | |||
423 | /* | ||
424 | * Draw a bitmap at (x, y), size (nx, ny) | ||
425 | * if 'clear' is true, clear destination area first | ||
426 | */ | ||
427 | void lcd_bitmap (const unsigned char *src, int x, int y, int nx, int ny, | ||
428 | bool clear) __attribute__ ((section (".icode"))); | ||
429 | void lcd_bitmap (const unsigned char *src, int x, int y, int nx, int ny, | ||
430 | bool clear) | ||
431 | { | ||
432 | const unsigned char *src_col; | ||
433 | unsigned char *dst, *dst_col; | ||
434 | unsigned int data, mask1, mask2, mask3, mask4; | ||
435 | int stride, shift; | ||
436 | |||
437 | if (((unsigned) x >= LCD_WIDTH) || ((unsigned) y >= LCD_HEIGHT)) | ||
438 | return; | ||
439 | |||
440 | stride = nx; /* otherwise right-clipping will destroy the image */ | ||
441 | |||
442 | if (((unsigned) (x + nx)) >= LCD_WIDTH) | ||
443 | nx = LCD_WIDTH - x; | ||
444 | if (((unsigned) (y + ny)) >= LCD_HEIGHT) | ||
445 | ny = LCD_HEIGHT - y; | ||
446 | |||
447 | dst = &lcd_framebuffer[y >> 3][x]; | ||
448 | shift = y & 7; | ||
449 | |||
450 | if (!shift && clear) /* shortcut for byte aligned match with clear */ | ||
451 | { | ||
452 | while (ny >= 8) /* all full rows */ | ||
453 | { | ||
454 | memcpy(dst, src, nx); | ||
455 | src += stride; | ||
456 | dst += LCD_WIDTH; | ||
457 | ny -= 8; | ||
458 | } | ||
459 | if (ny == 0) /* nothing left to do? */ | ||
460 | return; | ||
461 | /* last partial row to do by default routine */ | ||
462 | } | ||
463 | |||
464 | ny += shift; | ||
465 | |||
466 | /* Calculate bit masks */ | ||
467 | mask4 = ~(0xfe << ((ny-1) & 7)); /* data mask for last partial row */ | ||
468 | if (clear) | ||
469 | { | ||
470 | mask1 = ~(0xff << shift); /* clearing of first partial row */ | ||
471 | mask2 = 0; /* clearing of intermediate (full) rows */ | ||
472 | mask3 = ~mask4; /* clearing of last partial row */ | ||
473 | if (ny <= 8) | ||
474 | mask3 |= mask1; | ||
475 | } | ||
476 | else | ||
477 | mask1 = mask2 = mask3 = 0xff; | ||
478 | |||
479 | /* Loop for each column */ | ||
480 | for (x = 0; x < nx; x++) | ||
481 | { | ||
482 | src_col = src++; | ||
483 | dst_col = dst++; | ||
484 | data = 0; | ||
485 | y = 0; | ||
486 | |||
487 | if (ny > 8) | ||
488 | { | ||
489 | /* First partial row */ | ||
490 | data = *src_col << shift; | ||
491 | *dst_col = (*dst_col & mask1) | data; | ||
492 | src_col += stride; | ||
493 | dst_col += LCD_WIDTH; | ||
494 | data >>= 8; | ||
495 | |||
496 | /* Intermediate rows */ | ||
497 | for (y = 8; y < ny-8; y += 8) | ||
498 | { | ||
499 | data |= *src_col << shift; | ||
500 | *dst_col = (*dst_col & mask2) | data; | ||
501 | src_col += stride; | ||
502 | dst_col += LCD_WIDTH; | ||
503 | data >>= 8; | ||
504 | } | ||
505 | } | ||
506 | |||
507 | /* Last partial row */ | ||
508 | if (y + shift < ny) | ||
509 | data |= *src_col << shift; | ||
510 | *dst_col = (*dst_col & mask3) | (data & mask4); | ||
511 | } | ||
512 | } | ||
513 | |||
514 | /* | ||
515 | * Draw a rectangle with upper left corner at (x, y) | ||
516 | * and size (nx, ny) | ||
517 | */ | ||
518 | void lcd_drawrect (int x, int y, int nx, int ny) | ||
519 | { | ||
520 | int i; | ||
521 | |||
522 | if (x > LCD_WIDTH) | ||
523 | return; | ||
524 | if (y > LCD_HEIGHT) | ||
525 | return; | ||
526 | |||
527 | if (x + nx > LCD_WIDTH) | ||
528 | nx = LCD_WIDTH - x; | ||
529 | if (y + ny > LCD_HEIGHT) | ||
530 | ny = LCD_HEIGHT - y; | ||
531 | |||
532 | /* vertical lines */ | ||
533 | for (i = 0; i < ny; i++) { | ||
534 | DRAW_PIXEL(x, (y + i)); | ||
535 | DRAW_PIXEL((x + nx - 1), (y + i)); | ||
536 | } | ||
537 | |||
538 | /* horizontal lines */ | ||
539 | for (i = 0; i < nx; i++) { | ||
540 | DRAW_PIXEL((x + i),y); | ||
541 | DRAW_PIXEL((x + i),(y + ny - 1)); | ||
542 | } | ||
543 | } | ||
544 | |||
545 | /* | ||
546 | * Clear a rectangular area at (x, y), size (nx, ny) | ||
547 | */ | ||
548 | void lcd_clearrect (int x, int y, int nx, int ny) | ||
549 | { | ||
550 | int i; | ||
551 | for (i = 0; i < nx; i++) | ||
552 | lcd_bitmap (zeros, x+i, y, 1, ny, true); | ||
553 | } | ||
554 | |||
555 | /* | ||
556 | * Fill a rectangular area at (x, y), size (nx, ny) | ||
557 | */ | ||
558 | void lcd_fillrect (int x, int y, int nx, int ny) | ||
559 | { | ||
560 | int i; | ||
561 | for (i = 0; i < nx; i++) | ||
562 | lcd_bitmap (ones, x+i, y, 1, ny, true); | ||
563 | } | ||
564 | |||
565 | /* Invert a rectangular area at (x, y), size (nx, ny) */ | ||
566 | void lcd_invertrect (int x, int y, int nx, int ny) | ||
567 | { | ||
568 | int i, j; | ||
569 | |||
570 | if (x > LCD_WIDTH) | ||
571 | return; | ||
572 | if (y > LCD_HEIGHT) | ||
573 | return; | ||
574 | |||
575 | if (x + nx > LCD_WIDTH) | ||
576 | nx = LCD_WIDTH - x; | ||
577 | if (y + ny > LCD_HEIGHT) | ||
578 | ny = LCD_HEIGHT - y; | ||
579 | |||
580 | for (i = 0; i < nx; i++) | ||
581 | for (j = 0; j < ny; j++) | ||
582 | INVERT_PIXEL((x + i), (y + j)); | ||
583 | } | ||
584 | |||
585 | /* Reverse the invert setting of the scrolling line (if any) at given char | ||
586 | position. Setting will go into affect next time line scrolls. */ | ||
587 | void lcd_invertscroll(int x, int y) | ||
588 | { | ||
589 | struct scrollinfo* s; | ||
590 | |||
591 | (void)x; | ||
592 | |||
593 | s = &scroll[y]; | ||
594 | s->invert = !s->invert; | ||
595 | } | ||
596 | |||
597 | void lcd_drawline( int x1, int y1, int x2, int y2 ) | ||
598 | { | ||
599 | int numpixels; | ||
600 | int i; | ||
601 | int deltax, deltay; | ||
602 | int d, dinc1, dinc2; | ||
603 | int x, xinc1, xinc2; | ||
604 | int y, yinc1, yinc2; | ||
605 | |||
606 | deltax = abs(x2 - x1); | ||
607 | deltay = abs(y2 - y1); | ||
608 | |||
609 | if(deltax >= deltay) | ||
610 | { | ||
611 | numpixels = deltax; | ||
612 | d = 2 * deltay - deltax; | ||
613 | dinc1 = deltay * 2; | ||
614 | dinc2 = (deltay - deltax) * 2; | ||
615 | xinc1 = 1; | ||
616 | xinc2 = 1; | ||
617 | yinc1 = 0; | ||
618 | yinc2 = 1; | ||
619 | } | ||
620 | else | ||
621 | { | ||
622 | numpixels = deltay; | ||
623 | d = 2 * deltax - deltay; | ||
624 | dinc1 = deltax * 2; | ||
625 | dinc2 = (deltax - deltay) * 2; | ||
626 | xinc1 = 0; | ||
627 | xinc2 = 1; | ||
628 | yinc1 = 1; | ||
629 | yinc2 = 1; | ||
630 | } | ||
631 | numpixels++; /* include endpoints */ | ||
632 | |||
633 | if(x1 > x2) | ||
634 | { | ||
635 | xinc1 = -xinc1; | ||
636 | xinc2 = -xinc2; | ||
637 | } | ||
638 | |||
639 | if(y1 > y2) | ||
640 | { | ||
641 | yinc1 = -yinc1; | ||
642 | yinc2 = -yinc2; | ||
643 | } | ||
644 | |||
645 | x = x1; | ||
646 | y = y1; | ||
647 | |||
648 | for(i=0; i<numpixels; i++) | ||
649 | { | ||
650 | DRAW_PIXEL(x,y); | ||
651 | |||
652 | if(d < 0) | ||
653 | { | ||
654 | d += dinc1; | ||
655 | x += xinc1; | ||
656 | y += yinc1; | ||
657 | } | ||
658 | else | ||
659 | { | ||
660 | d += dinc2; | ||
661 | x += xinc2; | ||
662 | y += yinc2; | ||
663 | } | ||
664 | } | ||
665 | } | ||
666 | |||
667 | void lcd_clearline( int x1, int y1, int x2, int y2 ) | ||
668 | { | ||
669 | int numpixels; | ||
670 | int i; | ||
671 | int deltax, deltay; | ||
672 | int d, dinc1, dinc2; | ||
673 | int x, xinc1, xinc2; | ||
674 | int y, yinc1, yinc2; | ||
675 | |||
676 | deltax = abs(x2 - x1); | ||
677 | deltay = abs(y2 - y1); | ||
678 | |||
679 | if(deltax >= deltay) | ||
680 | { | ||
681 | numpixels = deltax; | ||
682 | d = 2 * deltay - deltax; | ||
683 | dinc1 = deltay * 2; | ||
684 | dinc2 = (deltay - deltax) * 2; | ||
685 | xinc1 = 1; | ||
686 | xinc2 = 1; | ||
687 | yinc1 = 0; | ||
688 | yinc2 = 1; | ||
689 | } | ||
690 | else | ||
691 | { | ||
692 | numpixels = deltay; | ||
693 | d = 2 * deltax - deltay; | ||
694 | dinc1 = deltax * 2; | ||
695 | dinc2 = (deltax - deltay) * 2; | ||
696 | xinc1 = 0; | ||
697 | xinc2 = 1; | ||
698 | yinc1 = 1; | ||
699 | yinc2 = 1; | ||
700 | } | ||
701 | numpixels++; /* include endpoints */ | ||
702 | |||
703 | if(x1 > x2) | ||
704 | { | ||
705 | xinc1 = -xinc1; | ||
706 | xinc2 = -xinc2; | ||
707 | } | ||
708 | |||
709 | if(y1 > y2) | ||
710 | { | ||
711 | yinc1 = -yinc1; | ||
712 | yinc2 = -yinc2; | ||
713 | } | ||
714 | |||
715 | x = x1; | ||
716 | y = y1; | ||
717 | |||
718 | for(i=0; i<numpixels; i++) | ||
719 | { | ||
720 | CLEAR_PIXEL(x,y); | ||
721 | |||
722 | if(d < 0) | ||
723 | { | ||
724 | d += dinc1; | ||
725 | x += xinc1; | ||
726 | y += yinc1; | ||
727 | } | ||
728 | else | ||
729 | { | ||
730 | d += dinc2; | ||
731 | x += xinc2; | ||
732 | y += yinc2; | ||
733 | } | ||
734 | } | ||
735 | } | ||
736 | |||
737 | /* | ||
738 | * Set a single pixel | ||
739 | */ | ||
740 | void lcd_drawpixel(int x, int y) | ||
741 | { | ||
742 | DRAW_PIXEL(x,y); | ||
743 | } | ||
744 | |||
745 | /* | ||
746 | * Clear a single pixel | ||
747 | */ | ||
748 | void lcd_clearpixel(int x, int y) | ||
749 | { | ||
750 | CLEAR_PIXEL(x,y); | ||
751 | } | ||
752 | |||
753 | /* | ||
754 | * Invert a single pixel | ||
755 | */ | ||
756 | void lcd_invertpixel(int x, int y) | ||
757 | { | ||
758 | INVERT_PIXEL(x,y); | ||
759 | } | ||
760 | |||
761 | void lcd_puts_scroll(int x, int y, const unsigned char *string) | ||
762 | { | ||
763 | lcd_puts_scroll_style(x, y, string, STYLE_DEFAULT); | ||
764 | } | ||
765 | |||
766 | void lcd_puts_scroll_style(int x, int y, const unsigned char *string, int style) | ||
767 | { | ||
768 | struct scrollinfo* s; | ||
769 | int w, h; | ||
770 | |||
771 | s = &scroll[y]; | ||
772 | |||
773 | s->start_tick = current_tick + scroll_delay; | ||
774 | s->invert = false; | ||
775 | if (style & STYLE_INVERT) { | ||
776 | s->invert = true; | ||
777 | lcd_puts_style(x,y,string,STYLE_INVERT); | ||
778 | } | ||
779 | else | ||
780 | lcd_puts(x,y,string); | ||
781 | |||
782 | lcd_getstringsize(string, &w, &h); | ||
783 | |||
784 | if (LCD_WIDTH - x * 8 - xmargin < w) { | ||
785 | /* prepare scroll line */ | ||
786 | char *end; | ||
787 | |||
788 | memset(s->line, 0, sizeof s->line); | ||
789 | strcpy(s->line, string); | ||
790 | |||
791 | /* get width */ | ||
792 | s->width = lcd_getstringsize(s->line, &w, &h); | ||
793 | |||
794 | /* scroll bidirectional or forward only depending on the string | ||
795 | width */ | ||
796 | if ( bidir_limit ) { | ||
797 | s->bidir = s->width < (LCD_WIDTH - xmargin) * | ||
798 | (100 + bidir_limit) / 100; | ||
799 | } | ||
800 | else | ||
801 | s->bidir = false; | ||
802 | |||
803 | if (!s->bidir) { /* add spaces if scrolling in the round */ | ||
804 | strcat(s->line, " "); | ||
805 | /* get new width incl. spaces */ | ||
806 | s->width = lcd_getstringsize(s->line, &w, &h); | ||
807 | } | ||
808 | |||
809 | end = strchr(s->line, '\0'); | ||
810 | strncpy(end, string, LCD_WIDTH/2); | ||
811 | |||
812 | s->len = strlen(string); | ||
813 | s->offset = 0; | ||
814 | s->startx = x; | ||
815 | s->backward = false; | ||
816 | scrolling_lines |= (1<<y); | ||
817 | } | ||
818 | else | ||
819 | /* force a bit switch-off since it doesn't scroll */ | ||
820 | scrolling_lines &= ~(1<<y); | ||
821 | } | ||
822 | |||
823 | void lcd_stop_scroll(void) | ||
824 | { | ||
825 | scrolling_lines=0; | ||
826 | } | ||
827 | |||
828 | static const char scroll_tick_table[16] = { | ||
829 | /* Hz values: | ||
830 | 1, 1.25, 1.55, 2, 2.5, 3.12, 4, 5, 6.25, 8.33, 10, 12.5, 16.7, 20, 25, 33 */ | ||
831 | 100, 80, 64, 50, 40, 32, 25, 20, 16, 12, 10, 8, 6, 5, 4, 3 | ||
832 | }; | ||
833 | |||
834 | void lcd_scroll_speed(int speed) | ||
835 | { | ||
836 | scroll_ticks = scroll_tick_table[speed]; | ||
837 | } | ||
838 | |||
839 | void lcd_scroll_step(int step) | ||
840 | { | ||
841 | scroll_step = step; | ||
842 | } | ||
843 | |||
844 | void lcd_scroll_delay(int ms) | ||
845 | { | ||
846 | scroll_delay = ms / (HZ / 10); | ||
847 | } | ||
848 | |||
849 | void lcd_bidir_scroll(int percent) | ||
850 | { | ||
851 | bidir_limit = percent; | ||
852 | } | ||
853 | static void scroll_thread(void) | ||
854 | { | ||
855 | struct font* pf; | ||
856 | struct scrollinfo* s; | ||
857 | int index; | ||
858 | int xpos, ypos; | ||
859 | |||
860 | /* initialize scroll struct array */ | ||
861 | scrolling_lines = 0; | ||
862 | |||
863 | while ( 1 ) { | ||
864 | for ( index = 0; index < SCROLLABLE_LINES; index++ ) { | ||
865 | /* really scroll? */ | ||
866 | if ( !(scrolling_lines&(1<<index)) ) | ||
867 | continue; | ||
868 | |||
869 | s = &scroll[index]; | ||
870 | |||
871 | /* check pause */ | ||
872 | if (TIME_BEFORE(current_tick, s->start_tick)) | ||
873 | continue; | ||
874 | |||
875 | if (s->backward) | ||
876 | s->offset -= scroll_step; | ||
877 | else | ||
878 | s->offset += scroll_step; | ||
879 | |||
880 | pf = font_get(curfont); | ||
881 | xpos = xmargin + s->startx * s->width / s->len; | ||
882 | ypos = ymargin + index * pf->height; | ||
883 | |||
884 | if (s->bidir) { /* scroll bidirectional */ | ||
885 | if (s->offset <= 0) { | ||
886 | /* at beginning of line */ | ||
887 | s->offset = 0; | ||
888 | s->backward = false; | ||
889 | s->start_tick = current_tick + scroll_delay * 2; | ||
890 | } | ||
891 | if (s->offset >= s->width - (LCD_WIDTH - xpos)) { | ||
892 | /* at end of line */ | ||
893 | s->offset = s->width - (LCD_WIDTH - xpos); | ||
894 | s->backward = true; | ||
895 | s->start_tick = current_tick + scroll_delay * 2; | ||
896 | } | ||
897 | } | ||
898 | else { | ||
899 | /* scroll forward the whole time */ | ||
900 | if (s->offset >= s->width) | ||
901 | s->offset %= s->width; | ||
902 | } | ||
903 | |||
904 | lcd_clearrect(xpos, ypos, LCD_WIDTH - xpos, pf->height); | ||
905 | lcd_putsxyofs(xpos, ypos, s->offset, s->line); | ||
906 | if (s->invert) | ||
907 | lcd_invertrect(xpos, ypos, LCD_WIDTH - xpos, pf->height); | ||
908 | lcd_update_rect(xpos, ypos, LCD_WIDTH - xpos, pf->height); | ||
909 | } | ||
910 | |||
911 | sleep(scroll_ticks); | ||
912 | } | ||
913 | } | ||
914 | |||
915 | #endif | ||