/* 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; iclip.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); }