blob: b2543b877a3a61540cc6f911fcbcd20393c17f9e [file] [log] [blame]
/*
* Generic drawing operations
*
* (C) Copyright 2001-2002 Geert Uytterhoeven
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
#include "types.h"
#include "drawops.h"
#include "fb.h"
#include "util.h"
/*
* Draw a horizontal line
*/
void generic_draw_hline(u32 x, u32 y, u32 length, pixel_t pixel)
{
while (length--)
set_pixel(x++, y, pixel);
}
/*
* Draw a vertical line
*/
void generic_draw_vline(u32 x, u32 y, u32 length, pixel_t pixel)
{
while (length--)
set_pixel(x, y++, pixel);
}
/*
* Draw a rectangle
*/
void generic_draw_rect(u32 x, u32 y, u32 width, u32 height, pixel_t pixel)
{
draw_hline(x, y, width, pixel);
if (height >= 1) {
if (height >= 2) {
draw_vline(x, y+1, height-2, pixel);
if (width >= 1)
draw_vline(x+width-1, y+1, height-2, pixel);
}
draw_hline(x, y+height-1, width, pixel);
}
}
/*
* Draw a filled rectangle
*/
void generic_fill_rect(u32 x, u32 y, u32 width, u32 height, pixel_t pixel)
{
while (height--)
draw_hline(x, y++, width, pixel);
}
/*
* Draw a line using the Bresenham algorithm for lines
*/
void generic_draw_line(u32 x1, u32 y1, u32 x2, u32 y2, pixel_t pixel)
{
int dx, dy, sx, sy, e;
dx = x2-x1;
dy = y2-y1;
if (dy == 0) {
if (dx < 0) {
dx = -dx;
x1 = x2;
}
draw_hline(x1, y1, dx+1, pixel);
} else if (dx == 0) {
if (dy < 0) {
dy = -dy;
y1 = y2;
}
draw_vline(x1, y1, dy+1, pixel);
} else {
if (dy < 0) {
dy = -dy;
sy = -1;
} else {
sy = 1;
}
if (dx < 0) {
dx = -dx;
sx = -1;
} else {
sx = 1;
}
if (dx > dy) {
e = -dx/2;
set_pixel(x1, y1, pixel);
while (x1 != x2) {
e += dy;
if (e >= 0) {
y1 += sy;
e -= dx;
}
x1 += sx;
set_pixel(x1, y1, pixel);
}
} else {
e = -dy/2;
set_pixel(x1, y1, pixel);
while (y1 != y2) {
e += dx;
if (e >= 0) {
x1 += sx;
e -= dy;
}
y1 += sy;
set_pixel(x1, y1, pixel);
}
}
}
}
/*
* Monochrome bitmap expansion
*/
void generic_expand_bitmap(u32 x, u32 y, u32 width, u32 height, const u8 *data,
u32 pitch, pixel_t pixel0, pixel_t pixel1)
{
u32 w, x0;
const u8 *data0;
int i;
u8 bits;
while (height--) {
w = width;
x0 = x;
data0 = data;
while (w > 7) {
bits = *data0++;
for (i = 0; i < 8; i++, bits <<= 1)
set_pixel(x0++, y, bits & 0x80 ? pixel1 : pixel0);
w -= 8;
}
if (w > 0) {
bits = *data0++;
while (w-- > 0) {
set_pixel(x0++, y, bits & 0x80 ? pixel1 : pixel0);
bits <<= 1;
}
}
y++;
data += pitch;
}
}
/*
* Draw a pixmap
*/
void generic_draw_pixmap(u32 x, u32 y, u32 width, u32 height,
const pixel_t *pixmap)
{
u32 w, x0;
while (height--) {
for (w = width, x0 = x; w > 0; w--)
set_pixel(x0++, y, *pixmap++);
y++;
}
}
/*
* Draw a circle using the differential version of the midpoint algorithm
*
* Cfr. Computer Graphics: Principles and Practices, Second Edition in C,
* Foley et al., 1997, p. 87
*/
static void draw_circle_points(u32 cx, u32 cy, u32 x, u32 y, pixel_t pixel)
{
if (x == 0) {
set_pixel(cx, cy-y, pixel);
set_pixel(cx-y, cy, pixel);
set_pixel(cx+y, cy, pixel);
set_pixel(cx, cy+y, pixel);
} else {
set_pixel(cx-x, cy-y, pixel);
set_pixel(cx+x, cy-y, pixel);
set_pixel(cx-x, cy+y, pixel);
set_pixel(cx+x, cy+y, pixel);
if (x < y) {
set_pixel(cx-y, cy-x, pixel);
set_pixel(cx+y, cy-x, pixel);
set_pixel(cx-y, cy+x, pixel);
set_pixel(cx+y, cy+x, pixel);
}
}
}
void generic_draw_circle(u32 x, u32 y, u32 r, pixel_t pixel)
{
u32 x1 = 0;
u32 y1 = r;
int d = 1-r;
int de = 3;
int dse = -2*r+5;
do {
draw_circle_points(x, y, x1, y1, pixel);
if (d < 0) { // Select E
d += de;
de += 2;
dse += 2;
} else { // Select SE
d += dse;
de += 2;
dse += 4;
y1--;
}
x1++;
} while (x1 <= y1);
}
/*
* Draw a filled circle
*/
static void fill_circle_points_x(u32 cx, u32 cy, u32 x, u32 y, pixel_t pixel)
{
if (x == 0) {
set_pixel(cx, cy-y, pixel);
set_pixel(cx, cy+y, pixel);
} else {
draw_hline(cx-x, cy-y, 2*x+1, pixel);
draw_hline(cx-x, cy+y, 2*x+1, pixel);
}
}
static void fill_circle_points_y(u32 cx, u32 cy, u32 x, u32 y, pixel_t pixel)
{
if (x == 0)
draw_hline(cx-y, cy, 2*y+1, pixel);
else {
draw_hline(cx-y, cy-x, 2*y+1, pixel);
draw_hline(cx-y, cy+x, 2*y+1, pixel);
}
}
void generic_fill_circle(u32 x, u32 y, u32 r, pixel_t pixel)
{
u32 x1 = 0;
u32 y1 = r;
int d = 1-r;
int de = 3;
int dse = -2*r+5;
do {
fill_circle_points_y(x, y, x1, y1, pixel);
if (d < 0) { // Select E
d += de;
de += 2;
dse += 2;
} else { // Select SE
d += dse;
de += 2;
dse += 4;
if (x1 != y1)
fill_circle_points_x(x, y, x1, y1, pixel);
y1--;
}
x1++;
} while (x1 <= y1);
}
/*
* Draw an ellipse using a differential version of the Bresenham algorithm
* for ellipses
*/
static void draw_ellipse_points(u32 cx, u32 cy, u32 x, u32 y, pixel_t pixel)
{
if (x == 0) {
set_pixel(cx, cy-y, pixel);
set_pixel(cx, cy+y, pixel);
} else if (0 == y) {
set_pixel(cx-x, cy, pixel);
set_pixel(cx+x, cy, pixel);
} else {
set_pixel(cx-x, cy-y, pixel);
set_pixel(cx+x, cy-y, pixel);
set_pixel(cx-x, cy+y, pixel);
set_pixel(cx+x, cy+y, pixel);
}
}
void generic_draw_ellipse(u32 x, u32 y, u32 a, u32 b, pixel_t pixel)
{
if (a == b)
draw_circle(x, y, a, pixel);
else {
u32 a2 = a*a;
u32 b2 = b*b;
if (a <= b) {
u32 x1 = 0;
u32 y1 = b;
int S = a2*(1-2*b)+2*b2;
int T = b2-2*a2*(2*b-1);
int dT1 = 4*b2;
int dS1 = dT1+2*b2;
int dS2 = -4*a2*(b-1);
int dT2 = dS2+2*a2;
draw_ellipse_points(x, y, x1, y1, pixel);
do {
if (S < 0) {
S += dS1;
T += dT1;
dS1 += 4*b2;
dT1 += 4*b2;
x1++;
} else if (T < 0) {
S += dS1+dS2;
T += dT1+dT2;
dS1 += 4*b2;
dT1 += 4*b2;
dS2 += 4*a2;
dT2 += 4*a2;
x1++;
y1--;
} else {
S += dS2;
T += dT2;
dS2 += 4*a2;
dT2 += 4*a2;
y1--;
}
draw_ellipse_points(x, y, x1, y1, pixel);
} while (y1 > 0);
} else {
u32 x1 = a;
u32 y1 = 0;
int S = b2*(1-2*a)+2*a2;
int T = a2-2*b2*(2*a-1);
int dT1 = 4*a2;
int dS1 = dT1+2*a2;
int dS2 = -4*b2*(a-1);
int dT2 = dS2+2*b2;
draw_ellipse_points(x, y, x1, y1, pixel);
do {
if (S < 0) {
S += dS1;
T += dT1;
dS1 += 4*a2;
dT1 += 4*a2;
y1++;
} else if (T < 0) {
S += dS1+dS2;
T += dT1+dT2;
dS1 += 4*a2;
dT1 += 4*a2;
dS2 += 4*b2;
dT2 += 4*b2;
x1--;
y1++;
} else {
S += dS2;
T += dT2;
dS2 += 4*b2;
dT2 += 4*b2;
x1--;
}
draw_ellipse_points(x, y, x1, y1, pixel);
} while (x1 > 0);
}
}
}
/*
* Draw a filled ellipse
*/
static void fill_ellipse_points(u32 cx, u32 cy, u32 x, u32 y, pixel_t pixel)
{
if (x == 0) {
set_pixel(cx, cy-y, pixel);
set_pixel(cx, cy+y, pixel);
} else if (y == 0) {
draw_hline(cx-x, cy, 2*x+1, pixel);
} else {
draw_hline(cx-x, cy-y, 2*x+1, pixel);
draw_hline(cx-x, cy+y, 2*x+1, pixel);
}
}
void generic_fill_ellipse(u32 x, u32 y, u32 a, u32 b, pixel_t pixel)
{
if (a == b)
fill_circle(x, y, a, pixel);
else {
u32 a2 = a*a;
u32 b2 = b*b;
if (a <= b) {
u32 x1 = 0;
u32 y1 = b;
int S = a2*(1-2*b)+2*b2;
int T = b2-2*a2*(2*b-1);
int dT1 = 4*b2;
int dS1 = dT1+2*b2;
int dS2 = -4*a2*(b-1);
int dT2 = dS2+2*a2;
while (1) {
if (S < 0) {
S += dS1;
T += dT1;
dS1 += 4*b2;
dT1 += 4*b2;
x1++;
} else if (T < 0) {
fill_ellipse_points(x, y, x1, y1, pixel);
if (y1 == 0)
break;
S += dS1+dS2;
T += dT1+dT2;
dS1 += 4*b2;
dT1 += 4*b2;
dS2 += 4*a2;
dT2 += 4*a2;
x1++;
y1--;
} else {
fill_ellipse_points(x, y, x1, y1, pixel);
if (y1 == 0)
break;
S += dS2;
T += dT2;
dS2 += 4*a2;
dT2 += 4*a2;
y1--;
}
}
} else {
u32 x1 = a;
u32 y1 = 0;
int S = b2*(1-2*a)+2*a2;
int T = a2-2*b2*(2*a-1);
int dT1 = 4*a2;
int dS1 = dT1+2*a2;
int dS2 = -4*b2*(a-1);
int dT2 = dS2+2*b2;
fill_ellipse_points(x, y, x1, y1, pixel);
do {
if (S < 0) {
S += dS1;
T += dT1;
dS1 += 4*a2;
dT1 += 4*a2;
y1++;
fill_ellipse_points(x, y, x1, y1, pixel);
} else if (T < 0) {
S += dS1+dS2;
T += dT1+dT2;
dS1 += 4*a2;
dT1 += 4*a2;
dS2 += 4*b2;
dT2 += 4*b2;
x1--;
if (x1 < 0)
break;
y1++;
fill_ellipse_points(x, y, x1, y1, pixel);
} else {
S += dS2;
T += dT2;
dS2 += 4*b2;
dT2 += 4*b2;
x1--;
}
} while (x1 > 0);
}
}
}
/*
* Copy a rectangular area
*/
void generic_copy_rect(u32 dx, u32 dy, u32 width, u32 height, u32 sx, u32 sy)
{
u32 w, dx0, sx0;
if (dy > sy || (dy == sy && dx > sx)) {
dx += width;
dy += height;
sx += width;
sy += height;
while (height--) {
dy--;
sy--;
for (w = width, dx0 = dx, sx0 = sx; w > 0; w--) {
dx0--;
sx0--;
set_pixel(dx0, dy, get_pixel(sx0, sy));
}
}
} else {
while (height--) {
for (w = width, dx0 = dx, sx0 = sx; w > 0; w--) {
set_pixel(dx0, dy, get_pixel(sx0, sy));
dx0++;
sx0++;
}
dy++;
sy++;
}
}
}