aboutsummaryrefslogblamecommitdiff
path: root/src/lib/libkogata/draw.c
blob: f11903d66bac76c51531b972f73cb6915f4361ae (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                   
                   


                            

                                
 

                           
 







                                









                                                                                      
                                                                                                       

                                

                       







                                                     
                                                                    






                                                    


                                 


                   

























                                                           













                                              
         





                                                               
                                                                                                       
                                                                                  
                                                                                 






                                                                                                       
                                                    























                                                                                 
                                                              




















                                                                                         
                                                              

































                                                                                         
                                                              





















                                                                                                 
                                                         


                                                  
                                                         






















                                                                         









                                                                                                           



                                                                  




                                                                                                        







































                                                                                                                   
                
                                                                                                                                            


         
                                    
                                                        





                                                                      













                                                                               
                  






                                          
                                                       








                                                           




                                             


                                                                    
                                                                                           

                                              

                        




                                                                           
                    


                 

                                            




                                                       


                               
 








                                                   






















                                                                                                    
                                                                               
                                                                     
                                                                                      













                                                                                    
#include <stdlib.h>
#include <string.h>

#include <proto/font_file.h>

#include <kogata/printf.h>
#include <kogata/region_alloc.h>

#include <kogata/syscall.h>
#include <kogata/draw.h>

// ----

#define STB_IMAGE_IMPLEMENTATION
#define STBI_NO_HDR
#include "stb/stb_image.h"

// ----

fb_t *g_fb_from_file(fd_t file, fb_info_t *geom) {
	fb_t *ret = (fb_t*)malloc(sizeof(fb_t));
	if (ret == 0) return 0;

	memcpy(&ret->geom, geom, sizeof(fb_info_t));
	ret->fd = file;

	ret->data = (uint8_t*)region_alloc(geom->height * geom->pitch, "Framebuffer");
	if (ret->data == 0) goto error;

	bool map_ok = sc_mmap_file(file, 0, ret->data, geom->height * geom->pitch, MM_READ | MM_WRITE);
	if (!map_ok) goto error;

	ret->nrefs = 1;

	return ret;

error:
	if (ret && ret->data) region_free(ret->data);
	if (ret) free(ret);
	return 0;
}

fb_t *g_fb_from_mem(uint8_t* data, fb_info_t *geom, bool own_data) {
	fb_t *ret = (fb_t*)malloc(sizeof(fb_t));
	if (ret == 0) return 0;

	memcpy(&ret->geom, geom, sizeof(fb_info_t));
	ret->fd = 0;
	ret->data = data;

	ret->nrefs = 1;
	ret->own_data = own_data;

	return ret;
}

fb_t *g_load_image(const char* filename) {
	int w, h, n;
	uint8_t *data = stbi_load(filename, &w, &h, &n, 0);
	if (data == NULL) {
		return NULL;
	}

	fb_info_t geom;
	geom.width = w;
	geom.height = h;
	geom.pitch = w * n;
	geom.bpp = n * 8;
	if (n == 1) geom.memory_model = FB_MM_GREY8;
	if (n == 2) geom.memory_model = FB_MM_GA16;
	if (n == 3) geom.memory_model = FB_MM_RGB24;
	if (n == 4) geom.memory_model = FB_MM_RGBA32;

	fb_t *fb = g_fb_from_mem(data, &geom, true);
	if (fb == NULL) {
		free(data);
		return NULL;
	}

	return fb;
}

void g_incref_fb(fb_t *fb) {
	fb->nrefs++;
}

void g_decref_fb(fb_t *fb) {
	fb->nrefs--;
	if (fb->nrefs == 0) {
		if (fb->fd != 0) {
			sc_munmap(fb->data);
			region_free(fb->data);
		} else if (fb->own_data) {
			free(fb->data);
		}
		free(fb);
	}
}

//  ---- Color management

color_t g_color_rgb(fb_t *f, uint8_t r, uint8_t g, uint8_t b) {
	int m = f->geom.memory_model;
	if (m == FB_MM_RGB24 || m == FB_MM_RGB32 || m == FB_MM_RGBA32) return (r << 16) | (g << 8) | b;
	if (m == FB_MM_BGR24 || m == FB_MM_BGR32) return (b << 16) | (g << 8) | r;
	if (m == FB_MM_GREY8 || m == FB_MM_GA16) return ((r + g + b) / 3) & 0xFF;
	if (m == FB_MM_RGB16 || m == FB_MM_RGB15) return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
	if (m == FB_MM_BGR16 || m == FB_MM_BGR15) return ((b >> 3) << 10) | ((g >> 3) << 5) | (r >> 3);
	return 0;	// unknown?
}

//  ---- Plot

static inline void g_plot24(uint8_t* p, color_t c) {
	p[0] = c & 0xFF;
	p[1] = (c >> 8) & 0xFF;
	p[2] = (c >> 16) & 0xFF;
}

void g_plot(fb_t *fb, int x, int y, color_t c) {
	if (fb->geom.bpp == 8) {
		fb->data[y * fb->geom.pitch + x] = (c & 0xFF);
	} else if (fb->geom.bpp == 15 || fb->geom.bpp == 16) {
		uint16_t *p = (uint16_t*)(fb->data + y * fb->geom.pitch + 2 * x);
		*p = (c & 0xFFFF);
	} else if (fb->geom.bpp == 24) {
		g_plot24(fb->data + y * fb->geom.pitch + 3 * x, c);
	} else if (fb->geom.bpp == 32) {
		uint32_t *p = (uint32_t*)(fb->data + y * fb->geom.pitch + 4 * x);
		*p = c;
	}
}

void g_hline(fb_t *fb, int x, int y, int w, color_t c) {
	if (fb->geom.bpp == 8) {
		for (int u = x; u < x + w; u++) {
			fb->data[y * fb->geom.pitch + u] = (c & 0xFF);
		}
	} else if (fb->geom.bpp == 15 || fb->geom.bpp == 16) {
		for (int u = x; u < x + w; u++) {
			uint16_t *p = (uint16_t*)(fb->data + y * fb->geom.pitch + 2 * u);
			*p = (c & 0xFFFF);
		}
	} else if (fb->geom.bpp == 24) {
		for (int u = x; u < x + w; u++) {
			g_plot24(fb->data + y * fb->geom.pitch + 3 * u, c);
		}
	} else if (fb->geom.bpp == 32) {
		for (int u = x; u < x + w; u++) {
			uint32_t *p = (uint32_t*)(fb->data + y * fb->geom.pitch + 4 * u);
			*p = c;
		}
	}
}

void g_vline(fb_t *fb, int x, int y, int h, color_t c) {
	if (fb->geom.bpp == 8) {
		for (int v = y; v < y + h; v++) {
			fb->data[v * fb->geom.pitch + x] = (c & 0xFF);
		}
	} else if (fb->geom.bpp == 15 || fb->geom.bpp == 16) {
		for (int v = y; v < y + h; v++) {
			uint16_t *p = (uint16_t*)(fb->data + v * fb->geom.pitch + 2 * x);
			*p = (c & 0xFFFF);
		}
	} else if (fb->geom.bpp == 24) {
		for (int v = y; v < y + h; v++) {
			g_plot24(fb->data + v * fb->geom.pitch + 3 * x, c);
		}
	} else if (fb->geom.bpp == 32) {
		for (int v = y; v < y + h; v++) {
			uint32_t *p = (uint32_t*)(fb->data + v * fb->geom.pitch + 4 * x);
			*p = c;
		}
	}
}

void g_line(fb_t *fb, int x1, int y1, int x2, int y2, color_t c) {
	// TODO
}

void g_rect(fb_t *fb, int x, int y, int w, int h, color_t c) {
	g_hline(fb, x, y, w, c);
	g_hline(fb, x, y+h-1, w, c);
	g_vline(fb, x, y, h, c);
	g_vline(fb, x+w-1, y, h, c);
}

void g_fillrect(fb_t *fb, int x, int y, int w, int h, color_t c) {
	if (fb->geom.bpp == 8) {
		for (int v = y; v < y + h; v++) {
			for (int u = x; u < x + w; u++) {
				fb->data[v * fb->geom.pitch + u] = (c & 0xFF);
			}
		}
	} else if (fb->geom.bpp == 15 || fb->geom.bpp == 16) {
		for (int v = y; v < y + h; v++) {
			for (int u = x; u < x + w; u++) {
				uint16_t *p = (uint16_t*)(fb->data + v * fb->geom.pitch + 2 * u);
				*p = (c & 0xFFFF);
			}
		}
	} else if (fb->geom.bpp == 24) {
		for (int v = y; v < y + h; v++) {
			for (int u = x; u < x + w; u++) {
				g_plot24(fb->data + v * fb->geom.pitch + 3 * u, c);
			}
		}
	} else if (fb->geom.bpp == 32) {
		for (int v = y; v < y + h; v++) {
			for (int u = x; u < x + w; u++) {
				uint32_t *p = (uint32_t*)(fb->data + v * fb->geom.pitch + 4 * u);
				*p = c;
			}
		}
	}
}

void g_rectregion(fb_t *fb, fb_region_t reg, color_t c) {
	g_rect(fb, reg.x, reg.y, reg.w, reg.h, c);
}

void g_fillregion(fb_t *fb, fb_region_t reg, color_t c) {
	g_fillrect(fb, reg.x, reg.y, reg.w, reg.h, c);
}

void g_circle(fb_t *fb, int cx, int cy, int r, color_t c) {
	// TODO
}

void g_fillcircle(fb_t *fb, int cx, int cy, int r, color_t c) {
	// TODO
}

void g_blit(fb_t *dst, int x, int y, fb_t *src) {
	fb_region_t r;

	r.x = 0;
	r.y = 0;
	r.w = src->geom.width;
	r.h = src->geom.height;

	g_blit_region(dst, x, y, src, r);
}

void g_blit_region(fb_t *dst, int x, int y, fb_t *src, fb_region_t reg) {
	if (x < 0 || y < 0 || reg.x < 0 || reg.y < 0 || reg.w < 0 || reg.h < 0) return;	// invalid argument

	if (reg.x + reg.w > src->geom.width) reg.w = src->geom.width - reg.x;
	if (reg.y + reg.h > src->geom.height) reg.h = src->geom.height - reg.y;
	if (reg.w <= 0 || reg.h <= 0) return;

	if (x + reg.w > dst->geom.width) reg.w = dst->geom.width - x;
	if (y + reg.h > dst->geom.height) reg.h = dst->geom.height - y;
	if (reg.w <= 0 || reg.h <= 0) return;

	if (src->geom.memory_model == dst->geom.memory_model
			&& src->geom.memory_model != FB_MM_RGBA32
			&& src->geom.memory_model != FB_MM_GA16) {
		for (int i = 0; i < reg.h; i++) {
			memcpy(
				dst->data + (y + i) * dst->geom.pitch + x * (dst->geom.bpp / 8),
				src->data + (reg.y + i) * src->geom.pitch + reg.x * (src->geom.bpp / 8),
				reg.w * (src->geom.bpp / 8));
		}
	} else if (src->geom.memory_model == FB_MM_RGBA32 && dst->geom.memory_model == FB_MM_RGB32) {
		for (int i = 0; i < reg.h; i++) {
			uint8_t *dln = dst->data + (y + i) * dst->geom.pitch + x * 4;
			uint8_t *sln = src->data + (reg.y + i) * src->geom.pitch + reg.x * 4;
			uint32_t *dln32 = (uint32_t*) dln;
			uint32_t *sln32 = (uint32_t*) sln;
			for (int j = 0; j < reg.w; j++) {
				uint8_t a = sln[4*j+3];
				if (a == 0xFF) {
					dln32[j] = sln32[j];
				} else if (a > 0) {
					uint16_t r = (0x100 - a) * (uint16_t)dln[4*j] + a * (uint16_t)sln[4*j];
					uint16_t g = (0x100 - a) * (uint16_t)dln[4*j+1] + a * (uint16_t)sln[4*j+1];
					uint16_t b = (0x100 - a) * (uint16_t)dln[4*j+2] + a * (uint16_t)sln[4*j+2];
					dln[4*j] = r >> 8;
					dln[4*j+1] = g >> 8;
					dln[4*j+2] = b >> 8;
				}
			}
		}
	} else if (src->geom.memory_model == FB_MM_RGBA32 && dst->geom.memory_model == FB_MM_RGB24) {
		for (int i = 0; i < reg.h; i++) {
			uint8_t *dln = dst->data + (y + i) * dst->geom.pitch + x * 4;
			uint8_t *sln = src->data + (reg.y + i) * src->geom.pitch + reg.x * 4;
			for (int j = 0; j < reg.w; j++) {
				uint8_t a = sln[4*j+3];
				if (a == 0xFF) {
					dln[3*j] = sln[4*j];
					dln[3*j+1] = sln[4*j+1];
					dln[3*j+2] = sln[4*j+2];
				} else if (a > 0) {
					uint16_t r = (0x100 - a) * (uint16_t)dln[3*j] + a * (uint16_t)sln[4*j];
					uint16_t g = (0x100 - a) * (uint16_t)dln[3*j+1] + a * (uint16_t)sln[4*j+1];
					uint16_t b = (0x100 - a) * (uint16_t)dln[3*j+2] + a * (uint16_t)sln[4*j+2];
					dln[3*j] = r >> 8;
					dln[3*j+1] = g >> 8;
					dln[3*j+2] = b >> 8;
				}
			}
		}
	} else {
		dbg_printf("Unsupported blit between different memory models %d and %d.\n", src->geom.memory_model, dst->geom.memory_model);
	}
}

void g_scroll_up(fb_t *dst, int l) {
	for (int y = 0; y < dst->geom.height - l; y++) {
		memcpy(dst->data + y * dst->geom.pitch,
				dst->data + (y + l) * dst->geom.pitch,
				dst->geom.pitch);
	}
}

//  ---- Text manipulation

#define FONT_ASCII_BITMAP	1
// more font types to come

typedef struct font {
	int type;
	union {
		struct {
			uint8_t* data;
			uint8_t cw, ch;		// width, height of a character
			uint32_t nchars;
		} ascii_bitmap;
	};
	int nrefs;
} font_t;

font_t *g_load_ascii_bitmap_font(fd_t f) {
	font_t *font = 0;

	ascii_bitmap_font_header h;

	size_t s = sc_read(f, 0, sizeof(h), (char*)&h);
	if (s != sizeof(h)) goto error;

	if (h.magic != ASCII_BITMAP_FONT_MAGIC) goto error;
	if (h.cw != 8) goto error;

	font = malloc(sizeof(font_t));
	if (font == 0) goto error;
	memset(font, 0, sizeof(font_t));

	font->type = FONT_ASCII_BITMAP;
	font->ascii_bitmap.cw = h.cw;
	font->ascii_bitmap.ch = h.ch;
	font->ascii_bitmap.nchars = h.nchars;

	font->ascii_bitmap.data = (uint8_t*)malloc(h.ch * h.nchars);
	if (font->ascii_bitmap.data == 0) goto error;

	size_t rd = sc_read(f, sizeof(h), h.ch * h.nchars, (char*)font->ascii_bitmap.data);
	if (rd != h.ch * h.nchars) goto error;

	font->nrefs = 1;

	return font;

error:
	if (font && font->ascii_bitmap.data) free(font->ascii_bitmap.data);
	if (font) free(font);
	sc_close(f);
	return 0;
}

font_t *g_load_font(const char* filename) {
	fd_t f = sc_open(filename, FM_READ);
	if (f != 0) return g_load_ascii_bitmap_font(f);

	return 0;
}

void g_incref_font(font_t *f) {
	f->nrefs++;
}

void g_decref_font(font_t *f) {
	f->nrefs--;

	if (f->nrefs == 0) {
		if (f->type == FONT_ASCII_BITMAP) {
			free(f->ascii_bitmap.data);
		}
		free(f);
	}
}

int g_text_width(font_t *font, const char* text) {
	if (font->type == FONT_ASCII_BITMAP) {
		return font->ascii_bitmap.cw * strlen(text);
	}
	return 0;
}

int g_text_height(font_t *font, const char* text) {
	if (font->type == FONT_ASCII_BITMAP) {
		return font->ascii_bitmap.ch;
	}
	return 0;
}

void g_write(fb_t *fb, int x, int y, const char* text, font_t *font, color_t c) {
	if (font->type == FONT_ASCII_BITMAP) {
		while (*text != 0) {
			uint8_t id = (uint8_t)*text;
			if (id < font->ascii_bitmap.nchars) {
				uint8_t *d = font->ascii_bitmap.data + (id * font->ascii_bitmap.ch);
				for (int r = 0; r < font->ascii_bitmap.ch; r++) {
					if (y + r >= fb->geom.height) continue;
					for (int j = 0; j < 8; j++) {
						if (x + j >= fb->geom.width) continue;
						if (d[r] & (0x80 >> j)) {
							g_plot(fb, x + j, y + r, c);
						}
					}
				}
			}
			text++;
			x += font->ascii_bitmap.cw;
		}
	}
}


/* vim: set ts=4 sw=4 tw=0 noet :*/