#include #define _GNU_SOURCE #include #include #include #include #include #include #include #include "drw.h" #include "util.h" extern int boldstokemode; /* static function declarations */ static Color to_color(uint32_t color); static uint32_t to_uint32_t(Color color); static void draw_bitmap(Canvas *canvas, FT_Bitmap *bitmap, unsigned x, unsigned y, uint32_t color); static void init_font(Font *font); static FontFamily *create_font_family(FontPath fontpath); static void free_font_family(FontFamily *fontfamily); static Font *get_font_with_charcode(FontCache *fontcache, uint_least32_t charcode); Color to_color(uint32_t color) { return *(Color*)(&color); } uint32_t to_uint32_t(Color color) { return *(uint32_t*)(&color); } Canvas* create_drw(struct wl_shm *shm, unsigned width, unsigned height) { Canvas *canvas; canvas = malloc(sizeof(Canvas)); canvas->width = width; canvas->height = height; canvas->shm = shm; canvas->stride = width * sizeof(uint32_t); canvas->size = canvas->stride * height; canvas->buffer_data_fd = allocate_shm_file(canvas->size); if (canvas->buffer_data_fd == -1) { return NULL; } canvas->buffer_data = mmap( NULL, canvas->size, PROT_READ | PROT_WRITE, MAP_SHARED, canvas->buffer_data_fd, 0 ); if (canvas->buffer_data == MAP_FAILED) die("mmap failed: "); canvas->pool = wl_shm_create_pool( shm, canvas->buffer_data_fd, canvas->size ); canvas->buffer = wl_shm_pool_create_buffer(canvas->pool, 0, canvas->width, canvas->height, canvas->size / canvas->height, WL_SHM_FORMAT_XRGB8888); wl_shm_pool_destroy(canvas->pool); canvas->data_fd = allocate_shm_file(canvas->size); if (canvas->data_fd == -1) { return NULL; } canvas->data = mmap(NULL, canvas->size, PROT_READ | PROT_WRITE, MAP_PRIVATE, canvas->data_fd, 0); if (canvas->data == MAP_FAILED) die("mmap failed: "); close(canvas->buffer_data_fd); close(canvas->data_fd); return canvas; } void resize_drw(Canvas *canvas, unsigned width, unsigned height) { munmap(canvas->data, canvas->size); munmap(canvas->buffer_data, canvas->size); canvas->width = width; canvas->height = height; canvas->stride = width * sizeof(uint32_t); canvas->size = canvas->stride * height; canvas->buffer_data_fd = allocate_shm_file(canvas->size); if (canvas->buffer_data_fd == -1) { return; } canvas->buffer_data = mmap( NULL, canvas->size, PROT_READ | PROT_WRITE, MAP_SHARED, canvas->buffer_data_fd, 0 ); if (canvas->buffer_data == MAP_FAILED) die("mmap failed: "); canvas->pool = wl_shm_create_pool( canvas->shm, canvas->buffer_data_fd, canvas->size ); canvas->buffer = wl_shm_pool_create_buffer(canvas->pool, 0, canvas->width, canvas->height, canvas->size / canvas->height, WL_SHM_FORMAT_XRGB8888); wl_shm_pool_destroy(canvas->pool); canvas->data_fd = allocate_shm_file(canvas->size); if (canvas->data_fd == -1) { return; } canvas->data = mmap(NULL, canvas->size, PROT_READ | PROT_WRITE, MAP_PRIVATE, canvas->data_fd, 0); if (canvas->data == MAP_FAILED) die("mmap failed: "); close(canvas->buffer_data_fd); close(canvas->data_fd); } void free_drw(Canvas *canvas) { munmap(canvas->data, canvas->size); munmap(canvas->buffer_data, canvas->size); free(canvas); } void push_buffer(Canvas *canvas) { memcpy(canvas->buffer_data, canvas->data, canvas->size); } void draw_point(Canvas *canvas, unsigned x, unsigned y, uint32_t color) { if (!(x < canvas->width && y < canvas->height)) return; canvas->data[x + canvas->width * y] = color; } void draw_rect(Canvas *canvas, unsigned x, unsigned y, unsigned width, unsigned height, uint32_t color) { int py; int px; for (py = y; py < y + height; ++py) { for (px = x; px < x + width; ++px) { draw_point(canvas, px, py, color); } } } FontCache * create_font_cache(FontPath *fontpath, int size, unsigned fontsize) { FontCache *fontcache = malloc(sizeof(FontCache)); FontCacheElement *current; Font *font; int i; fontcache->fontsize = fontsize; fontcache->fonttype = FONT_NORMAL; if (size == 0) { die("fontcache has to contain at least one font"); } fontcache->first = malloc(sizeof(FontCacheElement)); current = fontcache->first; current->family = create_font_family(fontpath[0]); current->next = 0; for (i = 1; i < size; ++i) { current->next = malloc(sizeof(FontCacheElement)); current = current->next; current->family = create_font_family(fontpath[i]); current->next = 0; } font_cache_generate_box(fontcache); return fontcache; } void font_cache_generate_box(FontCache *fontcache) { Font *font = get_font_with_charcode(fontcache, 0); FT_Set_Pixel_Sizes(font->face, 0, fontcache->fontsize); fontcache->box.width = ((font->face->size->metrics.max_advance) >> 6); fontcache->box.height = (font->face->size->metrics.height) >> 6; } void free_font_cache(FontCache *fontcache) { FontCacheElement *current = fontcache->first; FontCacheElement *next; for (; current; current = next) { next = current->next; free_font_family(current->family); free(current); } free(fontcache); } FontFamily * create_font_family(FontPath fontpath) { int i; FontFamily *fontfamily = malloc(sizeof(FontFamily)); for (i = 0; i < FONT_TYPE_SIZE; ++i) { (*fontfamily)[i].path = 0; (*fontfamily)[i].loaded = 0; (*fontfamily)[i].face = 0; (*fontfamily)[i].library = 0; (*fontfamily)[i].stroke = 0; if (fontpath[i]) { (*fontfamily)[i].path = fontpath[i]; } } return fontfamily; } void free_font_family(FontFamily *fontfamily) { int i; for (i = 0; i < FONT_TYPE_SIZE; ++i) { if ((*fontfamily)[i].loaded) { FT_Done_Face((*fontfamily)[i].face); FT_Done_FreeType((*fontfamily)[i].library); } } } void init_font(Font *font) { FT_Error error; error = FT_Init_FreeType(&font->library); if (error) die("cannot init library:"); error = FT_New_Face(font->library, font->path, 0, &font->face); if (error) die("cannot open face:"); error = FT_Select_Charmap(font->face, ft_encoding_unicode); if (error) die("cannot set unicode:"); FT_Stroker_New(font->library, &font->stroke); font->loaded = 1; } void draw_bitmap(Canvas *canvas, FT_Bitmap *bitmap, unsigned x, unsigned y, uint32_t color) { FT_Int i, j, p, q; double strength; uint32_t pixel; Color fg = to_color(color); Color bg; Color c; c.a = fg.a; i = x; p = 0; while (p < bitmap->width) { j = y; q = 0; while (q < bitmap->rows) { if (i < 0 || j < 0 || i >= canvas->width || j >= canvas->height) { j++; q++; continue; } bg = to_color(canvas->data[i + j * canvas->width]); strength = bitmap->buffer[p + q * bitmap->width]; strength /= 255; c.r = (strength * fg.r) + ((1 - strength) * bg.r); c.g = (strength * fg.g) + ((1 - strength) * bg.g); c.b = (strength * fg.b) + ((1 - strength) * bg.b); pixel = to_uint32_t(c); draw_point(canvas, i, j, pixel); j++; q++; } i++; p++; } } Font * get_font_with_charcode(FontCache *fontcache, uint_least32_t charcode) { FT_UInt glyph_index; FontCacheElement *element = fontcache->first; Font *font; for (; element; element = element->next) { font = &(*element->family)[fontcache->fonttype]; if (!font->path) continue; if (!font->loaded) init_font(font); glyph_index = FT_Get_Char_Index(font->face, charcode); if (glyph_index) return font; } font = &(*fontcache->first->family)[fontcache->fonttype]; if (!font->loaded) init_font(font); return font; } unsigned draw_char(Canvas *canvas, FontCache *fontcache, uint_least32_t charcode, unsigned x, unsigned y, uint32_t color) { FT_GlyphSlot slot; FT_Error error; FT_UInt glyph_index; FT_UInt previous = 0; FT_Bool has_kerning; FT_Vector delta; Font *font; FT_BitmapGlyph bg; FT_Glyph glyph; font = get_font_with_charcode(fontcache, charcode); FT_Set_Pixel_Sizes(font->face, 0, fontcache->fontsize); if (boldstokemode && fontcache->fonttype == FONT_BOLD) { FT_Stroker_Set(font->stroke, fontcache->fontsize * 2, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, fontcache->fontsize * 2); y -= fontcache->fontsize * 2 / 64 + 1; } else { FT_Stroker_Set(font->stroke, 0, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); } slot = font->face->glyph; glyph_index = FT_Get_Char_Index(font->face, charcode); has_kerning = FT_HAS_KERNING(font->face); if (has_kerning && previous && glyph_index) { FT_Get_Kerning(font->face, previous, glyph_index, FT_KERNING_DEFAULT, &delta); x += delta.x >> 6; } error = FT_Load_Glyph(font->face, glyph_index, FT_LOAD_DEFAULT); if (error) { fprintf(stderr, "warning: char not loaded, skipping...\n"); return x; } FT_Get_Glyph(slot, &glyph); FT_Glyph_StrokeBorder(&glyph, font->stroke, 0, 1); if (error) { fprintf(stderr, "warning: could not apply stroke\n"); return x; } FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1); bg = (FT_BitmapGlyph)glyph; draw_bitmap(canvas, &bg->bitmap, x + bg->left, y - bg->top, color); FT_Done_Glyph(glyph); x += fontcache->box.width >> 6; return x; }