basekernel/kernel/graphics.c

422 lines
8.1 KiB
C

/*
Copyright (C) 2016-2019 The University of Notre Dame
This software is distributed under the GNU General Public License.
See the file LICENSE for details.
*/
#include "graphics.h"
#include "kernel/types.h"
#include "kernel/error.h"
#include "ioports.h"
#include "font.h"
#include "string.h"
#include "kmalloc.h"
#include "bitmap.h"
#include "string.h"
#include "process.h"
#define FACTOR 256
struct graphics_clip {
uint32_t x;
uint32_t y;
uint32_t w;
uint32_t h;
};
struct graphics {
struct bitmap *bitmap;
struct graphics_color fgcolor;
struct graphics_color bgcolor;
struct graphics_clip clip;
struct graphics *parent;
int refcount;
};
static struct graphics_color color_black = { 0, 0, 0, 0 };
static struct graphics_color color_white = { 255, 255, 255, 0 };
struct graphics graphics_root;
struct graphics *graphics_create_root()
{
struct graphics *g = &graphics_root;
g->bitmap = bitmap_create_root();
g->fgcolor = color_white;
g->bgcolor = color_black;
g->clip.x = 0;
g->clip.y = 0;
g->clip.w = g->bitmap->width;
g->clip.h = g->bitmap->height;
g->parent = 0;
g->refcount = 1;
return g;
}
struct graphics *graphics_create(struct graphics *parent )
{
struct graphics *g = kmalloc(sizeof(*g));
if(!g) return 0;
memcpy(g, parent, sizeof(*g));
g->parent = graphics_addref(parent);
g->refcount = 1;
return g;
}
struct graphics *graphics_addref( struct graphics *g )
{
g->refcount++;
return g;
}
void graphics_delete( struct graphics *g )
{
if(!g) return;
/* Cannot delete the statically allocated root graphics */
if(g==&graphics_root) return;
g->refcount--;
if(g->refcount==0) {
graphics_delete(g->parent);
kfree(g);
}
}
#define ADVANCE(n) { cmd+=n; length-=n; }
int graphics_write(struct graphics *g, int *cmd, int length )
{
struct graphics_color c;
while(length>0) {
switch (*cmd) {
case GRAPHICS_FGCOLOR:
c.r = cmd[1];
c.g = cmd[2];
c.b = cmd[3];
c.a = 0;
graphics_fgcolor(g, c);
ADVANCE(4)
break;
case GRAPHICS_BGCOLOR:
c.r = cmd[1];
c.g = cmd[2];
c.b = cmd[3];
c.a = 0;
graphics_bgcolor(g, c);
ADVANCE(4)
break;
case GRAPHICS_RECT:
graphics_rect(g, cmd[1], cmd[2], cmd[3], cmd[4]);
ADVANCE(5)
break;
case GRAPHICS_CLEAR:
graphics_clear(g, cmd[1], cmd[2], cmd[3], cmd[4]);
ADVANCE(5)
break;
case GRAPHICS_LINE:
graphics_line(g, cmd[1], cmd[2], cmd[3], cmd[4]);
ADVANCE(5)
break;
case GRAPHICS_TEXT: {
int x = cmd[1];
int y = cmd[2];
int strlength = cmd[3];
int i;
for(i = 0; i<strlength; i++) {
graphics_char(g,x+i*FONT_WIDTH,y,cmd[4+i]);
}
ADVANCE(4+strlength)
break;
}
default:
return KERROR_INVALID_REQUEST;
break;
}
}
return 0;
}
uint32_t graphics_width(struct graphics * g)
{
return g->clip.w;
}
uint32_t graphics_height(struct graphics * g)
{
return g->clip.h;
}
void graphics_fgcolor(struct graphics *g, struct graphics_color c)
{
g->fgcolor = c;
}
void graphics_bgcolor(struct graphics *g, struct graphics_color c)
{
g->bgcolor = c;
}
int graphics_clip(struct graphics *g, int x, int y, int w, int h)
{
// Clip values may not be negative
if(x<0 || y<0 || w<0 || h<0) return 0;
// Child origin is relative to parent's clip origin.
x += g->clip.x;
y += g->clip.y;
// Child origin must fall within parent clip
if(x>=g->bitmap->width || y>=g->bitmap->width) return 0;
// Child width must fall within parent size
if((x + w) >= g->bitmap->width || (y + h) >= g->bitmap->height) return 0;
// Apply the clip
g->clip.x = x;
g->clip.y = y;
g->clip.w = w;
g->clip.h = h;
return 1;
}
static inline void plot_pixel(struct bitmap *b, int x, int y, struct graphics_color c)
{
uint8_t *v = b->data + (b->width * y + x) * 3;
if(c.a == 0) {
v[2] = c.r;
v[1] = c.g;
v[0] = c.b;
} else {
uint16_t a = c.a;
uint16_t b = 256 - a;
v[0] = (c.r * b + v[0] * a) >> 8;
v[1] = (c.g * b + v[1] * a) >> 8;
v[2] = (c.b * b + v[2] * a) >> 8;
}
}
static void graphics_rect_internal(struct graphics *g, int x, int y, int w, int h, struct graphics_color c )
{
int i, j;
if(x<0) { w+=x; x=0; }
if(y<0) { h+=y; y=0; }
if(x>g->clip.w || y>g->clip.h) return;
w = MIN(g->clip.w - x, w);
h = MIN(g->clip.h - y, h);
x += g->clip.x;
y += g->clip.y;
for(j = 0; j < h; j++) {
for(i = 0; i < w; i++) {
plot_pixel(g->bitmap, x + i, y + j,c);
}
}
}
void graphics_rect(struct graphics *g, int x, int y, int w, int h )
{
graphics_rect_internal(g,x,y,w,h,g->fgcolor);
}
void graphics_clear(struct graphics *g, int x, int y, int w, int h)
{
graphics_rect_internal(g,x,y,w,h,g->bgcolor);
}
static inline void graphics_line_vert(struct graphics *g, int x, int y, int w, int h)
{
do {
plot_pixel(g->bitmap, x, y, g->fgcolor);
y++;
h--;
} while(h > 0);
}
static inline void graphics_line_q1(struct graphics *g, int x, int y, int w, int h)
{
int slope = FACTOR * w / h;
int counter = 0;
do {
plot_pixel(g->bitmap, x, y, g->fgcolor);
y++;
h--;
counter += slope;
if(counter > FACTOR) {
counter = counter - FACTOR;
x++;
w--;
}
} while(h > 0);
}
static inline void graphics_line_q2(struct graphics *g, int x, int y, int w, int h)
{
int slope = FACTOR * h / w;
int counter = 0;
do {
plot_pixel(g->bitmap, x, y, g->fgcolor);
x++;
w--;
counter += slope;
if(counter > FACTOR) {
counter = counter - FACTOR;
y++;
h--;
}
} while(w > 0);
}
/* h<0, w>0, abs(h) < w */
static inline void graphics_line_q3(struct graphics *g, int x, int y, int w, int h)
{
int slope = -FACTOR * h / w;
int counter = 0;
do {
plot_pixel(g->bitmap, x, y, g->fgcolor);
x++;
w--;
counter += slope;
if(counter > FACTOR) {
counter = counter - FACTOR;
y--;
h--;
}
} while(w>0);
}
/* h<0, w>0, abs(h) > w */
static inline void graphics_line_q4(struct graphics *g, int x, int y, int w, int h)
{
int slope = -FACTOR * w / h;
int counter = 0;
do {
plot_pixel(g->bitmap, x, y, g->fgcolor);
y--;
h++;
counter += slope;
if(counter > FACTOR) {
counter = counter - FACTOR;
x++;
w--;
}
} while(h<0);
}
static inline void graphics_line_hozo(struct graphics *g, int x, int y, int w, int h)
{
do {
plot_pixel(g->bitmap, x, y, g->fgcolor);
x++;
w--;
} while(w > 0);
}
void graphics_line(struct graphics *g, int x, int y, int w, int h)
{
// If width is negative, reverse direction to simplify.
if(w < 0) {
x = x + w;
y = y + h;
w = -w;
h = -h;
}
// If line falls outside of clip region, bail out.
if(x<0 || y<0 || x>g->clip.w || y>g->clip.h) return;
if((x+w)>=g->clip.w || (y+h)>=g->clip.h || (y+h)<0 ) return;
// Adjust origin to clip region.
x += g->clip.x;
y += g->clip.y;
if(h>0) {
if(w==0) {
graphics_line_vert(g, x, y, w, h);
} else if(h > w) {
graphics_line_q1(g, x, y, w, h);
} else {
graphics_line_q2(g, x, y, w, h);
}
} else if(h<0) {
if(w==0) {
graphics_line_vert(g, x, y+h, w, -h);
} else if(-h < w) {
graphics_line_q3(g, x, y, w, h);
} else {
graphics_line_q4(g, x, y, w, h);
}
} else { //h==0
graphics_line_hozo(g, x, y, w, h);
}
}
void graphics_bitmap(struct graphics *g, int x, int y, int width, int height, uint8_t * data)
{
int i, j, b;
int value;
width = MIN(g->clip.w - x, width);
height = MIN(g->clip.h - y, height);
x += g->clip.x;
y += g->clip.y;
b = 0;
for(j = 0; j < height; j++) {
for(i = 0; i < width; i++) {
value = ((*data) << b) & 0x80;
if(value) {
plot_pixel(g->bitmap, x + i, y + j, g->fgcolor);
} else {
plot_pixel(g->bitmap, x + i, y + j, g->bgcolor);
}
b++;
if(b == 8) {
data++;
b = 0;
}
}
}
}
void graphics_char(struct graphics *g, int x, int y, unsigned char c)
{
uint32_t u = ((uint32_t) c) * FONT_WIDTH * FONT_HEIGHT / 8;
return graphics_bitmap(g, x, y, FONT_WIDTH, FONT_HEIGHT, &fontdata[u]);
}
void graphics_scrollup(struct graphics *g, int x, int y, int w, int h, int dy)
{
int j;
w = MIN(g->clip.w - x, w);
h = MIN(g->clip.h - y, h);
x += g->clip.x;
y += g->clip.y;
if(dy > h)
dy = h;
for(j = 0; j < (h - dy); j++) {
memcpy(&g->bitmap->data[((y + j) * g->bitmap->width + x) * 3], &g->bitmap->data[((y + j + dy) * g->bitmap->width + x) * 3], w * 3);
}
graphics_clear(g, x, y + h - dy, w, dy);
}