b37e6bc8c1
Change all lcd drivers to using a pointer to the static framebuffer instead of directly accessing the static array. This will let us later do fun things like dynamic framebuffer sizes (RaaA) or ability to use different buffers for different layers (dynamic skin backdrops!) Change-Id: I0a4d58a9d7b55e6c932131b929e5d4c9f9414b06
554 lines
14 KiB
C
554 lines
14 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2005 by Dave Chapman
|
|
* Copyright (C) 2009 by Karl Kurbjun
|
|
*
|
|
* Rockbox driver for 16-bit colour LCDs
|
|
*
|
|
* 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 "cpu.h"
|
|
#include "lcd.h"
|
|
#include "kernel.h"
|
|
#include "thread.h"
|
|
#include <stdlib.h>
|
|
#include "string-extra.h" /* mem*() */
|
|
#include "file.h"
|
|
#include "debug.h"
|
|
#include "system.h"
|
|
#include "font.h"
|
|
#include "rbunicode.h"
|
|
#include "bidi.h"
|
|
#include "scroll_engine.h"
|
|
|
|
#define ROW_INC LCD_WIDTH
|
|
#define COL_INC 1
|
|
|
|
#include "lcd-16bit-common.c"
|
|
#include "lcd-bitmap-common.c"
|
|
|
|
/*** drawing functions ***/
|
|
|
|
/* Clear the current viewport */
|
|
void lcd_clear_viewport(void)
|
|
{
|
|
fb_data *dst, *dst_end;
|
|
|
|
dst = FBADDR(current_vp->x, current_vp->y);
|
|
dst_end = dst + current_vp->height * LCD_WIDTH;
|
|
|
|
if (current_vp->drawmode & DRMODE_INVERSEVID)
|
|
{
|
|
do
|
|
{
|
|
memset16(dst, current_vp->fg_pattern, current_vp->width);
|
|
dst += LCD_WIDTH;
|
|
}
|
|
while (dst < dst_end);
|
|
}
|
|
else
|
|
{
|
|
if (!lcd_backdrop)
|
|
{
|
|
do
|
|
{
|
|
memset16(dst, current_vp->bg_pattern, current_vp->width);
|
|
dst += LCD_WIDTH;
|
|
}
|
|
while (dst < dst_end);
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
memcpy(dst, (void *)((long)dst + lcd_backdrop_offset),
|
|
current_vp->width * sizeof(fb_data));
|
|
dst += LCD_WIDTH;
|
|
}
|
|
while (dst < dst_end);
|
|
}
|
|
}
|
|
|
|
if (current_vp == &default_vp)
|
|
{
|
|
lcd_scroll_info.lines = 0;
|
|
}
|
|
else
|
|
{
|
|
lcd_scroll_stop(current_vp);
|
|
}
|
|
}
|
|
|
|
/* Draw a horizontal line (optimised) */
|
|
void lcd_hline(int x1, int x2, int y)
|
|
{
|
|
int x, width;
|
|
unsigned bits = 0;
|
|
enum fill_opt fillopt = OPT_NONE;
|
|
fb_data *dst, *dst_end;
|
|
|
|
/* direction flip */
|
|
if (x2 < x1)
|
|
{
|
|
x = x1;
|
|
x1 = x2;
|
|
x2 = x;
|
|
}
|
|
|
|
/******************** In viewport clipping **********************/
|
|
/* nothing to draw? */
|
|
if (((unsigned)y >= (unsigned)current_vp->height) ||
|
|
(x1 >= current_vp->width) ||
|
|
(x2 < 0))
|
|
return;
|
|
|
|
if (x1 < 0)
|
|
x1 = 0;
|
|
if (x2 >= current_vp->width)
|
|
x2 = current_vp->width-1;
|
|
|
|
/* Adjust x1 and y to viewport */
|
|
x1 += current_vp->x;
|
|
x2 += current_vp->x;
|
|
y += current_vp->y;
|
|
|
|
#if defined(HAVE_VIEWPORT_CLIP)
|
|
/********************* Viewport on screen clipping ********************/
|
|
/* nothing to draw? */
|
|
if (((unsigned)y >= (unsigned) LCD_HEIGHT) || (x1 >= LCD_WIDTH)
|
|
|| (x2 < 0))
|
|
return;
|
|
|
|
/* clipping */
|
|
if (x1 < 0)
|
|
x1 = 0;
|
|
if (x2 >= LCD_WIDTH)
|
|
x2 = LCD_WIDTH-1;
|
|
#endif
|
|
|
|
width = x2 - x1 + 1;
|
|
|
|
/* drawmode and optimisation */
|
|
if (current_vp->drawmode & DRMODE_INVERSEVID)
|
|
{
|
|
if (current_vp->drawmode & DRMODE_BG)
|
|
{
|
|
if (!lcd_backdrop)
|
|
{
|
|
fillopt = OPT_SET;
|
|
bits = current_vp->bg_pattern;
|
|
}
|
|
else
|
|
fillopt = OPT_COPY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (current_vp->drawmode & DRMODE_FG)
|
|
{
|
|
fillopt = OPT_SET;
|
|
bits = current_vp->fg_pattern;
|
|
}
|
|
}
|
|
if (fillopt == OPT_NONE && current_vp->drawmode != DRMODE_COMPLEMENT)
|
|
return;
|
|
|
|
dst = FBADDR(x1, y);
|
|
|
|
switch (fillopt)
|
|
{
|
|
case OPT_SET:
|
|
memset16(dst, bits, width);
|
|
break;
|
|
|
|
case OPT_COPY:
|
|
memcpy(dst, (void *)((long)dst + lcd_backdrop_offset),
|
|
width * sizeof(fb_data));
|
|
break;
|
|
|
|
case OPT_NONE: /* DRMODE_COMPLEMENT */
|
|
dst_end = dst + width;
|
|
do
|
|
*dst = ~(*dst);
|
|
while (++dst < dst_end);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Draw a vertical line (optimised) */
|
|
void lcd_vline(int x, int y1, int y2)
|
|
{
|
|
int y;
|
|
fb_data *dst, *dst_end;
|
|
lcd_fastpixelfunc_type *pfunc = lcd_fastpixelfuncs[current_vp->drawmode];
|
|
|
|
/* direction flip */
|
|
if (y2 < y1)
|
|
{
|
|
y = y1;
|
|
y1 = y2;
|
|
y2 = y;
|
|
}
|
|
|
|
/******************** In viewport clipping **********************/
|
|
/* nothing to draw? */
|
|
if (((unsigned)x >= (unsigned)current_vp->width) ||
|
|
(y1 >= current_vp->height) ||
|
|
(y2 < 0))
|
|
return;
|
|
|
|
if (y1 < 0)
|
|
y1 = 0;
|
|
if (y2 >= current_vp->height)
|
|
y2 = current_vp->height-1;
|
|
|
|
/* adjust for viewport */
|
|
x += current_vp->x;
|
|
y1 += current_vp->y;
|
|
y2 += current_vp->y;
|
|
|
|
#if defined(HAVE_VIEWPORT_CLIP)
|
|
/********************* Viewport on screen clipping ********************/
|
|
/* nothing to draw? */
|
|
if (( (unsigned) x >= (unsigned)LCD_WIDTH) || (y1 >= LCD_HEIGHT)
|
|
|| (y2 < 0))
|
|
return;
|
|
|
|
/* clipping */
|
|
if (y1 < 0)
|
|
y1 = 0;
|
|
if (y2 >= LCD_HEIGHT)
|
|
y2 = LCD_HEIGHT-1;
|
|
#endif
|
|
|
|
dst = FBADDR(x , y1);
|
|
dst_end = dst + (y2 - y1) * LCD_WIDTH;
|
|
|
|
do
|
|
{
|
|
pfunc(dst);
|
|
dst += LCD_WIDTH;
|
|
}
|
|
while (dst <= dst_end);
|
|
}
|
|
|
|
/* Fill a rectangular area */
|
|
void lcd_fillrect(int x, int y, int width, int height)
|
|
{
|
|
unsigned bits = 0;
|
|
enum fill_opt fillopt = OPT_NONE;
|
|
fb_data *dst, *dst_end;
|
|
|
|
/******************** In viewport clipping **********************/
|
|
/* nothing to draw? */
|
|
if ((width <= 0) || (height <= 0) || (x >= current_vp->width) ||
|
|
(y >= current_vp->height) || (x + width <= 0) || (y + height <= 0))
|
|
return;
|
|
|
|
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 defined(HAVE_VIEWPORT_CLIP)
|
|
/********************* Viewport on screen clipping ********************/
|
|
/* nothing to draw? */
|
|
if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
|
|
|| (x + width <= 0) || (y + height <= 0))
|
|
return;
|
|
|
|
/* clip image in viewport in screen */
|
|
if (x < 0)
|
|
{
|
|
width += x;
|
|
x = 0;
|
|
}
|
|
if (y < 0)
|
|
{
|
|
height += y;
|
|
y = 0;
|
|
}
|
|
if (x + width > LCD_WIDTH)
|
|
width = LCD_WIDTH - x;
|
|
if (y + height > LCD_HEIGHT)
|
|
height = LCD_HEIGHT - y;
|
|
#endif
|
|
|
|
/* drawmode and optimisation */
|
|
if (current_vp->drawmode & DRMODE_INVERSEVID)
|
|
{
|
|
if (current_vp->drawmode & DRMODE_BG)
|
|
{
|
|
if (!lcd_backdrop)
|
|
{
|
|
fillopt = OPT_SET;
|
|
bits = current_vp->bg_pattern;
|
|
}
|
|
else
|
|
fillopt = OPT_COPY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (current_vp->drawmode & DRMODE_FG)
|
|
{
|
|
fillopt = OPT_SET;
|
|
bits = current_vp->fg_pattern;
|
|
}
|
|
}
|
|
if (fillopt == OPT_NONE && current_vp->drawmode != DRMODE_COMPLEMENT)
|
|
return;
|
|
|
|
dst = FBADDR(x, y);
|
|
dst_end = dst + height * LCD_WIDTH;
|
|
|
|
do
|
|
{
|
|
fb_data *dst_row, *row_end;
|
|
|
|
switch (fillopt)
|
|
{
|
|
case OPT_SET:
|
|
memset16(dst, bits, width);
|
|
break;
|
|
|
|
case OPT_COPY:
|
|
memcpy(dst, (void *)((long)dst + lcd_backdrop_offset),
|
|
width * sizeof(fb_data));
|
|
break;
|
|
|
|
case OPT_NONE: /* DRMODE_COMPLEMENT */
|
|
dst_row = dst;
|
|
row_end = dst_row + width;
|
|
do
|
|
*dst_row = ~(*dst_row);
|
|
while (++dst_row < row_end);
|
|
break;
|
|
}
|
|
dst += LCD_WIDTH;
|
|
}
|
|
while (dst < dst_end);
|
|
}
|
|
|
|
/* Draw a partial native bitmap */
|
|
void ICODE_ATTR lcd_bitmap_part(const fb_data *src, int src_x, int src_y,
|
|
int stride, int x, int y, int width,
|
|
int height)
|
|
{
|
|
fb_data *dst;
|
|
|
|
/******************** Image in viewport clipping **********************/
|
|
/* nothing to draw? */
|
|
if ((width <= 0) || (height <= 0) || (x >= current_vp->width) ||
|
|
(y >= current_vp->height) || (x + width <= 0) || (y + height <= 0))
|
|
return;
|
|
|
|
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;
|
|
|
|
#if defined(HAVE_VIEWPORT_CLIP)
|
|
/********************* Viewport on screen clipping ********************/
|
|
/* nothing to draw? */
|
|
if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
|
|
|| (x + width <= 0) || (y + height <= 0))
|
|
return;
|
|
|
|
/* clip image in viewport in screen */
|
|
if (x < 0)
|
|
{
|
|
width += x;
|
|
src_x -= x;
|
|
x = 0;
|
|
}
|
|
if (y < 0)
|
|
{
|
|
height += y;
|
|
src_y -= y;
|
|
y = 0;
|
|
}
|
|
if (x + width > LCD_WIDTH)
|
|
width = LCD_WIDTH - x;
|
|
if (y + height > LCD_HEIGHT)
|
|
height = LCD_HEIGHT - y;
|
|
#endif
|
|
|
|
src += stride * src_y + src_x; /* move starting point */
|
|
dst = FBADDR(x, y);
|
|
|
|
do
|
|
{
|
|
memcpy(dst, src, width * sizeof(fb_data));
|
|
src += stride;
|
|
dst += LCD_WIDTH;
|
|
}
|
|
while (--height > 0);
|
|
}
|
|
|
|
/* Draw a partial native bitmap with transparency and foreground colors */
|
|
void ICODE_ATTR lcd_bitmap_transparent_part(const fb_data *src, int src_x,
|
|
int src_y, int stride, int x,
|
|
int y, int width, int height)
|
|
{
|
|
fb_data *dst;
|
|
unsigned fg = current_vp->fg_pattern;
|
|
|
|
/******************** Image in viewport clipping **********************/
|
|
/* nothing to draw? */
|
|
if ((width <= 0) || (height <= 0) || (x >= current_vp->width) ||
|
|
(y >= current_vp->height) || (x + width <= 0) || (y + height <= 0))
|
|
return;
|
|
|
|
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;
|
|
|
|
#if defined(HAVE_VIEWPORT_CLIP)
|
|
/********************* Viewport on screen clipping ********************/
|
|
/* nothing to draw? */
|
|
if ((x >= LCD_WIDTH) || (y >= LCD_HEIGHT)
|
|
|| (x + width <= 0) || (y + height <= 0))
|
|
return;
|
|
|
|
/* clip image in viewport in screen */
|
|
if (x < 0)
|
|
{
|
|
width += x;
|
|
src_x -= x;
|
|
x = 0;
|
|
}
|
|
if (y < 0)
|
|
{
|
|
height += y;
|
|
src_y -= y;
|
|
y = 0;
|
|
}
|
|
if (x + width > LCD_WIDTH)
|
|
width = LCD_WIDTH - x;
|
|
if (y + height > LCD_HEIGHT)
|
|
height = LCD_HEIGHT - y;
|
|
#endif
|
|
|
|
src += stride * src_y + src_x; /* move starting point */
|
|
dst = FBADDR(x, y);
|
|
|
|
#ifdef CPU_ARM
|
|
{
|
|
int w, px;
|
|
asm volatile (
|
|
".rowstart: \n"
|
|
"mov %[w], %[width] \n" /* Load width for inner loop */
|
|
".nextpixel: \n"
|
|
"ldrh %[px], [%[s]], #2 \n" /* Load src pixel */
|
|
"add %[d], %[d], #2 \n" /* Uncoditionally increment dst */
|
|
/* done here for better pipelining */
|
|
"cmp %[px], %[fgcolor] \n" /* Compare to foreground color */
|
|
"streqh %[fgpat], [%[d], #-2] \n" /* Store foregroud if match */
|
|
"cmpne %[px], %[transcolor] \n" /* Compare to transparent color */
|
|
"strneh %[px], [%[d], #-2] \n" /* Store dst if not transparent */
|
|
"subs %[w], %[w], #1 \n" /* Width counter has run down? */
|
|
"bgt .nextpixel \n" /* More in this row? */
|
|
"add %[s], %[s], %[sstp], lsl #1 \n" /* Skip over to start of next line */
|
|
"add %[d], %[d], %[dstp], lsl #1 \n"
|
|
"subs %[h], %[h], #1 \n" /* Height counter has run down? */
|
|
"bgt .rowstart \n" /* More rows? */
|
|
: [w]"=&r"(w), [h]"+&r"(height), [px]"=&r"(px),
|
|
[s]"+&r"(src), [d]"+&r"(dst)
|
|
: [width]"r"(width),
|
|
[sstp]"r"(stride - width),
|
|
[dstp]"r"(LCD_WIDTH - width),
|
|
[transcolor]"r"(TRANSPARENT_COLOR),
|
|
[fgcolor]"r"(REPLACEWITHFG_COLOR),
|
|
[fgpat]"r"(fg)
|
|
);
|
|
}
|
|
#else /* optimized C version */
|
|
do
|
|
{
|
|
const fb_data *src_row = src;
|
|
fb_data *dst_row = dst;
|
|
fb_data *row_end = dst_row + width;
|
|
do
|
|
{
|
|
unsigned data = *src_row++;
|
|
if (data != TRANSPARENT_COLOR)
|
|
{
|
|
if (data == REPLACEWITHFG_COLOR)
|
|
data = fg;
|
|
*dst_row = data;
|
|
}
|
|
}
|
|
while (++dst_row < row_end);
|
|
src += stride;
|
|
dst += LCD_WIDTH;
|
|
}
|
|
while (--height > 0);
|
|
#endif
|
|
}
|