/* * FbLib graphic library * * Created by Manoƫl TRAPIER. * Copyright (c) 2003-2019 986-Studio. All rights reserved. * */ #include #include #include #include #include #include #define DEFAULT_FONT "default_font.psf" FBLibFont *defaultFont = NULL; /* Function will fail, if no string terminator */ static int getNextWordLen(char *str) { int ret = 0, i; /* Word delimiters */ char word_lim[] = { ' ', '\t', '\n', 0 }; while (1) { for (i = 0 ; word_lim[i] != 0 ; i++) { if (*str == word_lim[i]) { return ret; } } str++; ret++; } } void graphics_text_line(int x, int y, int w, int charw, uint32_t color, int valign, void *font, char *text) { uint32_t len = strlen(text); switch (valign) { default: case TEXT_VALIGN_LEFT: graphics_draw_text(x, y, color, (const FBLibFont *)font, text); break; case TEXT_VALIGN_CENTER: graphics_draw_text(x + ((w - len * charw) / 2), y, color, (const FBLibFont *)font, text); break; case TEXT_VALIGN_RIGHT: graphics_draw_text(x + (w - (len * charw)), y, color, (const FBLibFont *)font, text); break; } } /* Currently halign is not honored, but valign is */ int graphics_text_ex(int x, int y, int w, int h, void *font, uint32_t bgcolor, uint32_t fgcolor, char valign, char halign, uint16_t options, void *format, ...) { char string[1024]; char line[300]; int charWidth, charHeight; int textPos = 0; int wordLen = 0; int nextWordLen = 0; va_list va; uint16_t curColPos = 0, curLinePos = 0; uint16_t maxCharPerLine, maxTextLine; FBLibFont *userFont = font; if (defaultFont == NULL) { defaultFont = load_psf(DEFAULT_FONT); } if (font == NULL) { userFont = defaultFont; } /* Do some usefull calculation */ /* We use fixed size font */ graphics_get_text_size(&charWidth, &charHeight, userFont, "A"); maxCharPerLine = w / charWidth; maxTextLine = h / charHeight; /* Now convert to a useable string */ va_start(va, format); vsnprintf(string, 1024, format, va); va_end(va); /* Fill rect with bg color */ graphics_drawFillrect(x, y, w, h, bgcolor); /* Now fill as much as possible */ memset(line, 0, 300); while (curLinePos < maxTextLine) { if (options & TEXT_OPT_WORDWRAP) { /* Do thoses check only one time per word, not per characters */ if (wordLen <= 0) { /* check if next word is too large for width */ nextWordLen = getNextWordLen(&string[textPos]); //printf("\nNextword len = %d", nextWordLen); if (nextWordLen <= maxCharPerLine) { if ((curColPos + nextWordLen) > maxCharPerLine) { /* Go next line... */ line[curColPos] = 0; graphics_text_line(x, y + curLinePos * charHeight, w, charWidth, fgcolor, valign, userFont, line); curColPos = 0; curLinePos++; memset(line, 0, 300); } } wordLen = nextWordLen; } /* Now when the word is too long for a line, it will be automatically wrapped to the next line */ } if ((string[textPos] == '\n') || (string[textPos] == '\r')) { textPos++; line[curColPos] = 0; graphics_text_line(x, y + curLinePos * charHeight, w, charWidth, fgcolor, valign, userFont, line); curColPos = 0; curLinePos++; memset(line, 0, 300); } else if (string[textPos] == 0) { line[curColPos] = 0; graphics_text_line(x, y + curLinePos * charHeight, w, charWidth, fgcolor, valign, userFont, line); goto exit; } else if (curColPos >= maxCharPerLine) { /* display the line */ line[curColPos] = 0; graphics_text_line(x, y + curLinePos * charHeight, w, charWidth, fgcolor, valign, userFont, line); /* skip until a "\n" (and exit is "\0" found)) */ if (options & TEXT_OPT_WORDWRAP) { curColPos = 0; curLinePos++; memset(line, 0, 300); } else { while (1) { if ((string[textPos] == '\r') || (string[textPos] == '\n')) { curColPos = 0; curLinePos++; memset(line, 0, 300); break; } else if (string[textPos] == 0) { goto exit; } textPos++; } } } else { line[curColPos++] = string[textPos++]; } if (options & TEXT_OPT_WORDWRAP) { wordLen--; } } exit: return 0; } void *fblib_loadfont(char *filename) { return (void *)load_psf(filename); } /* PSF management */ #define PSF1_MAGIC0 0x36 #define PSF1_MAGIC1 0x04 #define PSF1_MODE512 0x01 #define PSF1_MODEHASTAB 0x02 #define PSF1_MODEHASSEQ 0x04 #define PSF1_MAXMODE 0x05 #define PSF1_SEPARATOR 0xFFFF #define PSF1_STARTSEQ 0xFFFE struct psf1_header { unsigned char magic[2]; /* Magic number */ unsigned char mode; /* PSF font mode */ unsigned char charsize; /* Character size */ }; #define PSF2_MAGIC0 0x72 #define PSF2_MAGIC1 0xb5 #define PSF2_MAGIC2 0x4a #define PSF2_MAGIC3 0x86 /* bits used in flags */ #define PSF2_HAS_UNICODE_TABLE 0x01 /* max version recognized so far */ #define PSF2_MAXVERSION 0 /* UTF8 separators */ #define PSF2_SEPARATOR 0xFF #define PSF2_STARTSEQ 0xFE struct psf2_header { unsigned char magic[4]; unsigned int version; unsigned int headersize; /* offset of bitmaps in file */ unsigned int flags; unsigned int length; /* number of glyphs */ unsigned int charsize; /* number of bytes for each character */ unsigned int height, width; /* max dimensions of glyphs */ /* charsize = height * ((width + 7) / 8) */ }; static FBLibFont *load_psf1(char *filename, FILE *fp) { struct psf1_header head; struct FBLibFont *font; fread(&head, sizeof(head), 1, fp); if ((head.magic[0] != PSF1_MAGIC0) || (head.magic[1] != PSF1_MAGIC1)) { return NULL; } font = (FBLibFont *)malloc(sizeof(FBLibFont)); if (font != NULL) { font->height = head.charsize; font->index_mask = 0xFF; } return NULL; } void printbin(uint32_t val, uint8_t bitlen) { int i; for (i = 0 ; i < bitlen ; i++) { if (val & (1 << (bitlen - 1))) { printf("*"); } else { printf("_"); } val <<= 1; } } static FBLibFont *load_psf2(char *filename, FILE *fp) { struct psf2_header head; struct FBLibFont *font, *ret = NULL; uint32_t charWidth; uint32_t i, j, k; uint8_t *bitmap; fread(&head, sizeof(head), 1, fp); if ((head.magic[0] != PSF2_MAGIC0) || (head.magic[1] != PSF2_MAGIC1) || (head.magic[2] != PSF2_MAGIC2) || (head.magic[3] != PSF2_MAGIC3) ) { goto exit; } font = (FBLibFont *)malloc(sizeof(FBLibFont)); assert(head.width <= 32); /* For now, do not support font with width larger than 32 pixels */ if (font != NULL) { font->height = head.height; bitmap = (uint8_t *)malloc(sizeof(uint8_t) * head.charsize * head.length); font->index_mask = 0xFF; font->offset = (int *)malloc(sizeof(int) * head.length); font->index = (int *)malloc(sizeof(int) * head.length * 3); font->content = (uint32_t *)malloc(sizeof(uint32_t) * head.length * head.height); charWidth = ((head.width + 7) / 8); assert(bitmap != NULL); assert(font->offset != NULL); assert(font->index != NULL); assert(font->content != NULL); fread(bitmap, sizeof(uint8_t), head.charsize * head.length, fp); for (i = 0 ; i < head.length ; i++) { font->offset[i] = i * 3; font->index[(i * 3) + 0] = head.width; font->index[(i * 3) + 1] = i * head.height; font->index[(i * 3) + 2] = 0; for (j = 0 ; j < head.height ; j++) { font->content[(i * head.height) + j] = 0; for (k = 0 ; k < charWidth ; k++) { font->content[(i * head.height) + j] |= (bitmap[(i * head.charsize) + (j * charWidth) + k]) << 8 * (3 - k); } } } ret = font; free(bitmap); } exit: fclose(fp); return ret; } FBLibFont *load_psf(char *filename) { FILE *fp; uint8_t byte; console_printf(Console_Default, "Loading font '%s'\n", filename); fp = fopen(filename, "rb"); if (fp != NULL) { byte = fgetc(fp); rewind(fp); switch (byte) { default: fclose(fp); return NULL; // Unsuported format case PSF1_MAGIC0: return load_psf1(filename, fp); case PSF2_MAGIC0: return load_psf2(filename, fp); } } return NULL; } /* Font rendering code based on BOGL by Ben Pfaff */ static int fblib_draw_glyph(const FBLibFont *font, uint8_t wc, uint32_t **bitmap) { int mask = font->index_mask; int i; for (;;) { for (i = font->offset[wc & mask] ; font->index[i] ; i += 3) { if ((font->index[i] & ~mask) == (wc & ~mask)) { if (bitmap != NULL) { *bitmap = &font->content[font->index[i + 1]]; } return font->index[i] & mask; } } } return 0; } void graphics_get_text_size(int *width, int *height, const FBLibFont *font, const char *text) { uint8_t *c = (uint8_t *)text; uint8_t wc; int k, n, w, h, mw; if (defaultFont == NULL) { defaultFont = load_psf(DEFAULT_FONT); } if (font == NULL) { font = defaultFont; } n = strlen(text); mw = h = w = 0; for (k = 0 ; k < n ; k++) { wc = *(c++); if (wc == '\n') { if (w > mw) { mw = 0; } h += font->height; continue; } w += fblib_draw_glyph(font, wc, NULL); } if (width != NULL) { *width = (w > mw) ? w : mw; } if (height != NULL) { *height = (h == 0) ? font->height : h; } } void graphics_draw_text(int x, int y, uint32_t colour, const FBLibFont *font, const char *text) { int32_t h, w, k, n, cx, cy, dx, dy; uint8_t *c = (uint8_t *)text; uint8_t wc; if (defaultFont == NULL) { defaultFont = load_psf(DEFAULT_FONT); } if (font == NULL) { font = defaultFont; } n = strlen(text); h = font->height; dx = dy = 0; for (k = 0 ; k < n ; k++) { uint32_t *glyph = NULL; wc = *(c++); if (wc == '\n') { dy += h; dx = 0; continue; } w = fblib_draw_glyph(font, wc, &glyph); if (glyph == NULL) { continue; } for (cy = 0 ; cy < h ; cy++) { uint32_t g = *glyph++; for (cx = 0 ; cx < w ; cx++) { if (g & 0x80000000) { graphics_drawpixel(x + dx + cx, y + dy + cy, colour); } g <<= 1; } } dx += w; } } /* End of PSF */