rockbox/firmware/drivers/lcd-1bit-vert.c
Daniel Stenberg 2acc0ac542 Updated our source code header to explicitly mention that we are GPL v2 or
later. We still need to hunt down snippets used that are not. 1324 modified
files...
http://www.rockbox.org/mail/archive/rockbox-dev-archive-2008-06/0060.shtml


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17847 a1c6a512-1295-4272-9138-f99709370657
2008-06-28 18:10:04 +00:00

898 lines
22 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2002 by Alan Korr
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
#include "lcd.h"
#include "kernel.h"
#include "thread.h"
#include <string.h>
#include <stdlib.h>
#include "file.h"
#include "debug.h"
#include "system.h"
#include "font.h"
#include "rbunicode.h"
#include "bidi.h"
#include "scroll_engine.h"
#ifndef LCDFN /* Not compiling for remote - define macros for main LCD. */
#define LCDFN(fn) lcd_ ## fn
#define FBFN(fn) fb_ ## fn
#define LCDM(ma) LCD_ ## ma
#define LCDNAME "lcd_"
#define MAIN_LCD
#endif
/*** globals ***/
FBFN(data) LCDFN(framebuffer)[LCDM(FBHEIGHT)][LCDM(FBWIDTH)]
#if CONFIG_CPU != SH7034
IBSS_ATTR
#endif
;
static struct viewport default_vp =
{
.x = 0,
.y = 0,
.width = LCDM(WIDTH),
.height = LCDM(HEIGHT),
.font = FONT_SYSFIXED,
.drawmode = DRMODE_SOLID,
};
static struct viewport* current_vp = &default_vp;
/*** Viewports ***/
void LCDFN(set_viewport)(struct viewport* vp)
{
if (vp == NULL)
current_vp = &default_vp;
else
current_vp = vp;
}
void LCDFN(update_viewport)(void)
{
LCDFN(update_rect)(current_vp->x, current_vp->y,
current_vp->width, current_vp->height);
}
void LCDFN(update_viewport_rect)(int x, int y, int width, int height)
{
LCDFN(update_rect)(current_vp->x + x, current_vp->y + y, width, height);
}
/* LCD init */
void LCDFN(init)(void)
{
LCDFN(clear_display)();
#ifndef SIMULATOR
LCDFN(init_device)();
#endif
#ifdef MAIN_LCD
scroll_init();
#endif
}
/*** parameter handling ***/
void LCDFN(set_drawmode)(int mode)
{
current_vp->drawmode = mode & (DRMODE_SOLID|DRMODE_INVERSEVID);
}
int LCDFN(get_drawmode)(void)
{
return current_vp->drawmode;
}
int LCDFN(getwidth)(void)
{
return current_vp->width;
}
int LCDFN(getheight)(void)
{
return current_vp->height;
}
void LCDFN(setfont)(int newfont)
{
current_vp->font = newfont;
}
int LCDFN(getfont)(void)
{
return current_vp->font;
}
int LCDFN(getstringsize)(const unsigned char *str, int *w, int *h)
{
return font_getstringsize(str, w, h, current_vp->font);
}
/*** low-level drawing functions ***/
static void setpixel(int x, int y)
{
LCDFN(framebuffer)[y>>3][x] |= 1 << (y & 7);
}
static void clearpixel(int x, int y)
{
LCDFN(framebuffer)[y>>3][x] &= ~(1 << (y & 7));
}
static void flippixel(int x, int y)
{
LCDFN(framebuffer)[y>>3][x] ^= 1 << (y & 7);
}
static void nopixel(int x, int y)
{
(void)x;
(void)y;
}
LCDFN(pixelfunc_type)* const LCDFN(pixelfuncs)[8] = {
flippixel, nopixel, setpixel, setpixel,
nopixel, clearpixel, nopixel, clearpixel
};
static void ICODE_ATTR flipblock(FBFN(data) *address, unsigned mask,
unsigned bits)
{
*address ^= bits & mask;
}
static void ICODE_ATTR bgblock(FBFN(data) *address, unsigned mask,
unsigned bits)
{
*address &= bits | ~mask;
}
static void ICODE_ATTR fgblock(FBFN(data) *address, unsigned mask,
unsigned bits)
{
*address |= bits & mask;
}
static void ICODE_ATTR solidblock(FBFN(data) *address, unsigned mask,
unsigned bits)
{
unsigned data = *(char*)address;
bits ^= data;
*address = data ^ (bits & mask);
}
static void ICODE_ATTR flipinvblock(FBFN(data) *address, unsigned mask,
unsigned bits)
{
*address ^= ~bits & mask;
}
static void ICODE_ATTR bginvblock(FBFN(data) *address, unsigned mask,
unsigned bits)
{
*address &= ~(bits & mask);
}
static void ICODE_ATTR fginvblock(FBFN(data) *address, unsigned mask,
unsigned bits)
{
*address |= ~bits & mask;
}
static void ICODE_ATTR solidinvblock(FBFN(data) *address, unsigned mask,
unsigned bits)
{
unsigned data = *(char *)address;
bits = ~bits ^ data;
*address = data ^ (bits & mask);
}
LCDFN(blockfunc_type)* const LCDFN(blockfuncs)[8] = {
flipblock, bgblock, fgblock, solidblock,
flipinvblock, bginvblock, fginvblock, solidinvblock
};
/*** drawing functions ***/
/* Clear the whole display */
void LCDFN(clear_display)(void)
{
unsigned bits = (current_vp->drawmode & DRMODE_INVERSEVID) ? 0xFFu : 0;
memset(LCDFN(framebuffer), bits, sizeof LCDFN(framebuffer));
LCDFN(scroll_info).lines = 0;
}
/* Clear the current viewport */
void LCDFN(clear_viewport)(void)
{
int oldmode;
if (current_vp == &default_vp)
{
LCDFN(clear_display)();
}
else
{
oldmode = current_vp->drawmode;
/* Invert the INVERSEVID bit and set basic mode to SOLID */
current_vp->drawmode = (~current_vp->drawmode & DRMODE_INVERSEVID) |
DRMODE_SOLID;
LCDFN(fillrect)(0, 0, current_vp->width, current_vp->height);
current_vp->drawmode = oldmode;
LCDFN(scroll_stop)(current_vp);
}
}
/* Set a single pixel */
void LCDFN(drawpixel)(int x, int y)
{
if (((unsigned)x < (unsigned)current_vp->width) &&
((unsigned)y < (unsigned)current_vp->height))
LCDFN(pixelfuncs)[current_vp->drawmode](current_vp->x + x, current_vp->y + y);
}
/* Draw a line */
void LCDFN(drawline)(int x1, int y1, int x2, int y2)
{
int numpixels;
int i;
int deltax, deltay;
int d, dinc1, dinc2;
int x, xinc1, xinc2;
int y, yinc1, yinc2;
LCDFN(pixelfunc_type) *pfunc = LCDFN(pixelfuncs)[current_vp->drawmode];
deltax = abs(x2 - x1);
if (deltax == 0)
{
DEBUGF(LCDNAME "drawline() called for vertical line - optimisation.\n");
LCDFN(vline)(x1, y1, y2);
return;
}
deltay = abs(y2 - y1);
if (deltay == 0)
{
DEBUGF(LCDNAME "drawline() called for horizontal line - optimisation.\n");
LCDFN(hline)(x1, x2, y1);
return;
}
xinc2 = 1;
yinc2 = 1;
if (deltax >= deltay)
{
numpixels = deltax;
d = 2 * deltay - deltax;
dinc1 = deltay * 2;
dinc2 = (deltay - deltax) * 2;
xinc1 = 1;
yinc1 = 0;
}
else
{
numpixels = deltay;
d = 2 * deltax - deltay;
dinc1 = deltax * 2;
dinc2 = (deltax - deltay) * 2;
xinc1 = 0;
yinc1 = 1;
}
numpixels++; /* include endpoints */
if (x1 > x2)
{
xinc1 = -xinc1;
xinc2 = -xinc2;
}
if (y1 > y2)
{
yinc1 = -yinc1;
yinc2 = -yinc2;
}
x = x1;
y = y1;
for (i = 0; i < numpixels; i++)
{
if (((unsigned)x < (unsigned)current_vp->width)
&& ((unsigned)y < (unsigned)current_vp->height))
pfunc(current_vp->x + x, current_vp->y + y);
if (d < 0)
{
d += dinc1;
x += xinc1;
y += yinc1;
}
else
{
d += dinc2;
x += xinc2;
y += yinc2;
}
}
}
/* Draw a horizontal line (optimised) */
void LCDFN(hline)(int x1, int x2, int y)
{
int x, width;
unsigned char *dst, *dst_end;
unsigned mask;
LCDFN(blockfunc_type) *bfunc;
/* direction flip */
if (x2 < x1)
{
x = x1;
x1 = x2;
x2 = x;
}
/* nothing to draw? */
if (((unsigned)y >= (unsigned)current_vp->height) || (x1 >= current_vp->width)
|| (x2 < 0))
return;
/* clipping */
if (x1 < 0)
x1 = 0;
if (x2 >= current_vp->width)
x2 = current_vp->width-1;
width = x2 - x1 + 1;
/* adjust to viewport */
x1 += current_vp->x;
y += current_vp->y;
bfunc = LCDFN(blockfuncs)[current_vp->drawmode];
dst = &LCDFN(framebuffer)[y>>3][x1];
mask = 1 << (y & 7);
dst_end = dst + width;
do
bfunc(dst++, mask, 0xFFu);
while (dst < dst_end);
}
/* Draw a vertical line (optimised) */
void LCDFN(vline)(int x, int y1, int y2)
{
int ny;
FBFN(data) *dst;
unsigned mask, mask_bottom;
LCDFN(blockfunc_type) *bfunc;
/* direction flip */
if (y2 < y1)
{
ny = y1;
y1 = y2;
y2 = ny;
}
/* nothing to draw? */
if (((unsigned)x >= (unsigned)current_vp->width) || (y1 >= current_vp->height)
|| (y2 < 0))
return;
/* clipping */
if (y1 < 0)
y1 = 0;
if (y2 >= current_vp->height)
y2 = current_vp->height-1;
/* adjust for viewport */
y1 += current_vp->y;
y2 += current_vp->y;
x += current_vp->x;
bfunc = LCDFN(blockfuncs)[current_vp->drawmode];
dst = &LCDFN(framebuffer)[y1>>3][x];
ny = y2 - (y1 & ~7);
mask = 0xFFu << (y1 & 7);
mask_bottom = 0xFFu >> (~ny & 7);
for (; ny >= 8; ny -= 8)
{
bfunc(dst, mask, 0xFFu);
dst += LCDM(WIDTH);
mask = 0xFFu;
}
mask &= mask_bottom;
bfunc(dst, mask, 0xFFu);
}
/* Draw a rectangular box */
void LCDFN(drawrect)(int x, int y, int width, int height)
{
if ((width <= 0) || (height <= 0))
return;
int x2 = x + width - 1;
int y2 = y + height - 1;
LCDFN(vline)(x, y, y2);
LCDFN(vline)(x2, y, y2);
LCDFN(hline)(x, x2, y);
LCDFN(hline)(x, x2, y2);
}
/* Fill a rectangular area */
void LCDFN(fillrect)(int x, int y, int width, int height)
{
int ny;
FBFN(data) *dst, *dst_end;
unsigned mask, mask_bottom;
unsigned bits = 0;
LCDFN(blockfunc_type) *bfunc;
bool fillopt = false;
/* nothing to draw? */
if ((width <= 0) || (height <= 0) || (x >= current_vp->width)
|| (y >= current_vp->height) || (x + width <= 0) || (y + height <= 0))
return;
/* clipping */
if (x < 0)
{
width += x;
x = 0;
}
if (y < 0)
{
height += y;
y = 0;
}
if (x + width > current_vp->width)
width = current_vp->width - x;
if (y + height > current_vp->height)
height = current_vp->height - y;
/* adjust for viewport */
x += current_vp->x;
y += current_vp->y;
if (current_vp->drawmode & DRMODE_INVERSEVID)
{
if (current_vp->drawmode & DRMODE_BG)
{
fillopt = true;
}
}
else
{
if (current_vp->drawmode & DRMODE_FG)
{
fillopt = true;
bits = 0xFFu;
}
}
bfunc = LCDFN(blockfuncs)[current_vp->drawmode];
dst = &LCDFN(framebuffer)[y>>3][x];
ny = height - 1 + (y & 7);
mask = 0xFFu << (y & 7);
mask_bottom = 0xFFu >> (~ny & 7);
for (; ny >= 8; ny -= 8)
{
if (fillopt && (mask == 0xFFu))
memset(dst, bits, width);
else
{
FBFN(data) *dst_row = dst;
dst_end = dst_row + width;
do
bfunc(dst_row++, mask, 0xFFu);
while (dst_row < dst_end);
}
dst += LCDM(WIDTH);
mask = 0xFFu;
}
mask &= mask_bottom;
if (fillopt && (mask == 0xFFu))
memset(dst, bits, width);
else
{
dst_end = dst + width;
do
bfunc(dst++, mask, 0xFFu);
while (dst < dst_end);
}
}
/* About Rockbox' internal bitmap format:
*
* A bitmap contains one bit for every pixel that defines if that pixel is
* black (1) or white (0). Bits within a byte are arranged vertically, LSB
* at top.
* The bytes are stored in row-major order, with byte 0 being top left,
* byte 1 2nd from left etc. The first row of bytes defines pixel rows
* 0..7, the second row defines pixel row 8..15 etc.
*
* This is the same as the internal lcd hw format. */
/* Draw a partial bitmap */
void ICODE_ATTR LCDFN(bitmap_part)(const unsigned char *src, int src_x,
int src_y, int stride, int x, int y,
int width, int height)
{
int shift, ny;
FBFN(data) *dst, *dst_end;
unsigned mask, mask_bottom;
LCDFN(blockfunc_type) *bfunc;
/* nothing to draw? */
if ((width <= 0) || (height <= 0) || (x >= current_vp->width)
|| (y >= current_vp->height) || (x + width <= 0) || (y + height <= 0))
return;
/* clipping */
if (x < 0)
{
width += x;
src_x -= x;
x = 0;
}
if (y < 0)
{
height += y;
src_y -= y;
y = 0;
}
if (x + width > current_vp->width)
width = current_vp->width - x;
if (y + height > current_vp->height)
height = current_vp->height - y;
/* adjust for viewport */
x += current_vp->x;
y += current_vp->y;
src += stride * (src_y >> 3) + src_x; /* move starting point */
src_y &= 7;
y -= src_y;
dst = &LCDFN(framebuffer)[y>>3][x];
shift = y & 7;
ny = height - 1 + shift + src_y;
bfunc = LCDFN(blockfuncs)[current_vp->drawmode];
mask = 0xFFu << (shift + src_y);
mask_bottom = 0xFFu >> (~ny & 7);
if (shift == 0)
{
bool copyopt = (current_vp->drawmode == DRMODE_SOLID);
for (; ny >= 8; ny -= 8)
{
if (copyopt && (mask == 0xFFu))
memcpy(dst, src, width);
else
{
const unsigned char *src_row = src;
FBFN(data) *dst_row = dst;
dst_end = dst_row + width;
do
bfunc(dst_row++, mask, *src_row++);
while (dst_row < dst_end);
}
src += stride;
dst += LCDM(WIDTH);
mask = 0xFFu;
}
mask &= mask_bottom;
if (copyopt && (mask == 0xFFu))
memcpy(dst, src, width);
else
{
dst_end = dst + width;
do
bfunc(dst++, mask, *src++);
while (dst < dst_end);
}
}
else
{
dst_end = dst + width;
do
{
const unsigned char *src_col = src++;
FBFN(data) *dst_col = dst++;
unsigned mask_col = mask;
unsigned data = 0;
for (y = ny; y >= 8; y -= 8)
{
data |= *src_col << shift;
if (mask_col & 0xFFu)
{
bfunc(dst_col, mask_col, data);
mask_col = 0xFFu;
}
else
mask_col >>= 8;
src_col += stride;
dst_col += LCDM(WIDTH);
data >>= 8;
}
data |= *src_col << shift;
bfunc(dst_col, mask_col & mask_bottom, data);
}
while (dst < dst_end);
}
}
/* Draw a full bitmap */
void LCDFN(bitmap)(const unsigned char *src, int x, int y, int width,
int height)
{
LCDFN(bitmap_part)(src, 0, 0, width, x, y, width, height);
}
/* put a string at a given pixel position, skipping first ofs pixel columns */
static void LCDFN(putsxyofs)(int x, int y, int ofs, const unsigned char *str)
{
unsigned short ch;
unsigned short *ucs;
struct font* pf = font_get(current_vp->font);
ucs = bidi_l2v(str, 1);
while ((ch = *ucs++) != 0 && x < current_vp->width)
{
int width;
const unsigned char *bits;
/* get proportional width and glyph bits */
width = font_get_width(pf, ch);
if (ofs > width)
{
ofs -= width;
continue;
}
bits = font_get_bits(pf, ch);
LCDFN(mono_bitmap_part)(bits, ofs, 0, width, x, y, width - ofs,
pf->height);
x += width - ofs;
ofs = 0;
}
}
/* put a string at a given pixel position */
void LCDFN(putsxy)(int x, int y, const unsigned char *str)
{
LCDFN(putsxyofs)(x, y, 0, str);
}
/*** Line oriented text output ***/
/* put a string at a given char position */
void LCDFN(puts)(int x, int y, const unsigned char *str)
{
LCDFN(puts_style_offset)(x, y, str, STYLE_DEFAULT, 0);
}
void LCDFN(puts_style)(int x, int y, const unsigned char *str, int style)
{
LCDFN(puts_style_offset)(x, y, str, style, 0);
}
void LCDFN(puts_offset)(int x, int y, const unsigned char *str, int offset)
{
LCDFN(puts_style_offset)(x, y, str, STYLE_DEFAULT, offset);
}
/* put a string at a given char position, style, and pixel position,
* skipping first offset pixel columns */
void LCDFN(puts_style_offset)(int x, int y, const unsigned char *str,
int style, int offset)
{
int xpos,ypos,w,h,xrect;
int lastmode = current_vp->drawmode;
/* make sure scrolling is turned off on the line we are updating */
LCDFN(scroll_stop_line)(current_vp, y);
if(!str || !str[0])
return;
LCDFN(getstringsize)(str, &w, &h);
xpos = x*w / utf8length(str);
ypos = y*h;
current_vp->drawmode = (style & STYLE_INVERT) ?
(DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
LCDFN(putsxyofs)(xpos, ypos, offset, str);
current_vp->drawmode ^= DRMODE_INVERSEVID;
xrect = xpos + MAX(w - offset, 0);
LCDFN(fillrect)(xrect, ypos, current_vp->width - xrect, h);
current_vp->drawmode = lastmode;
}
/*** scrolling ***/
void LCDFN(puts_scroll)(int x, int y, const unsigned char *string)
{
LCDFN(puts_scroll_style)(x, y, string, STYLE_DEFAULT);
}
void LCDFN(puts_scroll_style)(int x, int y, const unsigned char *string,
int style)
{
LCDFN(puts_scroll_style_offset)(x, y, string, style, 0);
}
void LCDFN(puts_scroll_offset)(int x, int y, const unsigned char *string,
int offset)
{
LCDFN(puts_scroll_style_offset)(x, y, string, STYLE_DEFAULT, offset);
}
void LCDFN(puts_scroll_style_offset)(int x, int y, const unsigned char *string,
int style, int offset)
{
struct scrollinfo* s;
int w, h;
if ((unsigned)y >= (unsigned)current_vp->height)
return;
/* remove any previously scrolling line at the same location */
LCDFN(scroll_stop_line)(current_vp, y);
if (LCDFN(scroll_info.lines) >= LCDM(SCROLLABLE_LINES)) return;
s = &LCDFN(scroll_info).scroll[LCDFN(scroll_info).lines];
s->start_tick = current_tick + LCDFN(scroll_info).delay;
s->style = style;
if (style & STYLE_INVERT) {
LCDFN(puts_style_offset)(x,y,string,STYLE_INVERT,offset);
}
else
LCDFN(puts_offset)(x,y,string,offset);
LCDFN(getstringsize)(string, &w, &h);
if (current_vp->width - x * 8 < w) {
/* prepare scroll line */
char *end;
memset(s->line, 0, sizeof s->line);
strcpy(s->line, string);
/* get width */
s->width = LCDFN(getstringsize)(s->line, &w, &h);
/* scroll bidirectional or forward only depending on the string
width */
if ( LCDFN(scroll_info).bidir_limit ) {
s->bidir = s->width < (current_vp->width) *
(100 + LCDFN(scroll_info).bidir_limit) / 100;
}
else
s->bidir = false;
if (!s->bidir) { /* add spaces if scrolling in the round */
strcat(s->line, " ");
/* get new width incl. spaces */
s->width = LCDFN(getstringsize)(s->line, &w, &h);
}
end = strchr(s->line, '\0');
strncpy(end, string, current_vp->width/2);
s->vp = current_vp;
s->y = y;
s->len = utf8length(string);
s->offset = offset;
s->startx = x * s->width / s->len;;
s->backward = false;
LCDFN(scroll_info).lines++;
}
}
void LCDFN(scroll_fn)(void)
{
struct font* pf;
struct scrollinfo* s;
int index;
int xpos, ypos;
int lastmode;
struct viewport* old_vp = current_vp;
for ( index = 0; index < LCDFN(scroll_info).lines; index++ ) {
s = &LCDFN(scroll_info).scroll[index];
/* check pause */
if (TIME_BEFORE(current_tick, s->start_tick))
continue;
LCDFN(set_viewport)(s->vp);
if (s->backward)
s->offset -= LCDFN(scroll_info).step;
else
s->offset += LCDFN(scroll_info).step;
pf = font_get(current_vp->font);
xpos = s->startx;
ypos = s->y * pf->height;
if (s->bidir) { /* scroll bidirectional */
if (s->offset <= 0) {
/* at beginning of line */
s->offset = 0;
s->backward = false;
s->start_tick = current_tick + LCDFN(scroll_info).delay * 2;
}
if (s->offset >= s->width - (current_vp->width - xpos)) {
/* at end of line */
s->offset = s->width - (current_vp->width - xpos);
s->backward = true;
s->start_tick = current_tick + LCDFN(scroll_info).delay * 2;
}
}
else {
/* scroll forward the whole time */
if (s->offset >= s->width)
s->offset %= s->width;
}
lastmode = current_vp->drawmode;
current_vp->drawmode = (s->style&STYLE_INVERT) ?
(DRMODE_SOLID|DRMODE_INVERSEVID) : DRMODE_SOLID;
LCDFN(putsxyofs)(xpos, ypos, s->offset, s->line);
current_vp->drawmode = lastmode;
LCDFN(update_viewport_rect)(xpos, ypos, current_vp->width - xpos, pf->height);
}
LCDFN(set_viewport)(old_vp);
}