422 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			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);
 | |
| }
 |