diff --git a/cucul/cucul.c b/cucul/cucul.c index 6ac58eb..8d97a81 100644 --- a/cucul/cucul.c +++ b/cucul/cucul.c @@ -308,8 +308,7 @@ int cucul_free_canvas(cucul_canvas_t *cv) free(cv->frames[f].name); } - if(cv->ff) - _cucul_free_figfont(cv->ff); + cucul_canvas_set_figfont(cv, NULL); free(cv->frames); free(cv); diff --git a/cucul/cucul.h b/cucul/cucul.h index 629012c..1e059ca 100644 --- a/cucul/cucul.h +++ b/cucul/cucul.h @@ -286,6 +286,7 @@ __extern int cucul_free_font(cucul_font_t *); * * @{ */ __extern int cucul_canvas_set_figfont(cucul_canvas_t *, char const *); +__extern int cucul_put_figchar(cucul_canvas_t *, unsigned long int); /* @} */ /** \defgroup cucul_importexport libcucul importers/exporters from/to various diff --git a/cucul/cucul_internals.h b/cucul/cucul_internals.h index 59be7a5..d4f7a1d 100644 --- a/cucul/cucul_internals.h +++ b/cucul/cucul_internals.h @@ -85,8 +85,4 @@ extern int _cucul_file_close(cucul_file_t *); extern int _cucul_file_eof(cucul_file_t *); extern char *_cucul_file_gets(char *, int, cucul_file_t *); -/* FIGfont functions */ -extern cucul_figfont_t * _cucul_open_figfont(char const *); -extern int _cucul_free_figfont(cucul_figfont_t *); - #endif /* __CUCUL_INTERNALS_H__ */ diff --git a/cucul/figfont.c b/cucul/figfont.c index dc60a02..80e5536 100644 --- a/cucul/figfont.c +++ b/cucul/figfont.c @@ -16,6 +16,10 @@ * This file contains FIGlet and TOIlet font handling functions. */ +/* + * FIXME: this file needs huge cleanup to be usable + */ + #include "config.h" #include "common.h" @@ -30,39 +34,257 @@ struct cucul_figfont { - /* Used by the FIGlet driver */ + unsigned int term_width; + int x, y, w, h, lines; + + enum { H_DEFAULT, H_KERN, H_SMUSH, H_NONE, H_OVERLAP } hmode; + unsigned int hsmushrule; unsigned long int hardblank; unsigned int height, baseline, max_length; int old_layout; unsigned int print_direction, full_layout, codetag_count; unsigned int glyphs; - cucul_canvas_t *fontcv; + cucul_canvas_t *fontcv, *charcv; + int *left, *right; /* Unused yet */ unsigned int *lookup; }; +static uint32_t hsmush(uint32_t ch1, uint32_t ch2, unsigned int rule); +static cucul_figfont_t * open_figfont(char const *); +static int free_figfont(cucul_figfont_t *); + int cucul_canvas_set_figfont(cucul_canvas_t *cv, char const *path) { cucul_figfont_t *ff = NULL; if(path) { - ff = _cucul_open_figfont(path); + ff = open_figfont(path); if(!ff) return -1; } if(cv->ff) - _cucul_free_figfont(cv->ff); + { + cucul_free_canvas(cv->ff->charcv); + free(cv->ff->left); + free(cv->ff->right); + free_figfont(cv->ff); + } + + cv->ff = ff; + + if(!path) + return 0; + + /* from TOIlet’s main.c */ + ff->term_width = 80; + ff->hmode = H_DEFAULT; + + /* from TOIlet’s render.c */ + ff->x = ff->y = 0; + ff->w = ff->h = 0; + ff->lines = 0; + cucul_set_canvas_size(cv, 0, 0); /* XXX */ + + /* from TOIlet’s figlet.c */ + if(ff->full_layout & 0x3f) + ff->hsmushrule = ff->full_layout & 0x3f; + else if(ff->old_layout > 0) + ff->hsmushrule = ff->old_layout; + + switch(ff->hmode) + { + case H_DEFAULT: + if(ff->old_layout == -1) + ff->hmode = H_NONE; + else if(ff->old_layout == 0 && (ff->full_layout & 0xc0) == 0x40) + ff->hmode = H_KERN; + else if((ff->old_layout & 0x3f) && (ff->full_layout & 0x3f) + && (ff->full_layout & 0x80)) + { + ff->hmode = H_SMUSH; + ff->hsmushrule = ff->full_layout & 0x3f; + } + else if(ff->old_layout == 0 && (ff->full_layout & 0xbf) == 0x80) + { + ff->hmode = H_SMUSH; + ff->hsmushrule = 0x3f; + } + else + ff->hmode = H_OVERLAP; + break; + default: + break; + } + + ff->charcv = cucul_create_canvas(ff->max_length - 2, ff->height); + + ff->left = malloc(ff->height * sizeof(int)); + ff->right = malloc(ff->height * sizeof(int)); cv->ff = ff; return 0; } +int cucul_put_figchar(cucul_canvas_t *cv, unsigned long int ch) +{ + cucul_figfont_t *ff = cv->ff; + unsigned int c, w, h, x, y, overlap, extra, xleft, xright; + + switch(ch) + { + case (uint32_t)'\r': + return 0; + case (uint32_t)'\n': + ff->x = 0; + ff->y += ff->height; + return 0; + /* FIXME: handle '\t' */ + } + + /* Look whether our glyph is available */ + for(c = 0; c < ff->glyphs; c++) + if(ff->lookup[c * 2] == ch) + break; + + if(c == ff->glyphs) + return 0; + + w = ff->lookup[c * 2 + 1]; + h = ff->height; + + cucul_set_canvas_handle(ff->fontcv, 0, c * ff->height); + cucul_blit(ff->charcv, 0, 0, ff->fontcv, NULL); + + /* Check whether we reached the end of the screen */ + if(ff->x && ff->x + w > ff->term_width) + { + ff->x = 0; + ff->y += h; + } + + /* Compute how much the next character will overlap */ + switch(ff->hmode) + { + case H_SMUSH: + case H_KERN: + case H_OVERLAP: + extra = (ff->hmode == H_OVERLAP); + overlap = w; + for(y = 0; y < h; y++) + { + /* Compute how much spaces we can eat from the new glyph */ + for(xright = 0; xright < overlap; xright++) + if(cucul_get_char(ff->charcv, xright, y) != ' ') + break; + + /* Compute how much spaces we can eat from the previous glyph */ + for(xleft = 0; xright + xleft < overlap && xleft < ff->x; xleft++) + if(cucul_get_char(cv, ff->x - 1 - xleft, ff->y + y) != ' ') + break; + + /* Handle overlapping */ + if(ff->hmode == H_OVERLAP && xleft < ff->x) + xleft++; + + /* Handle smushing */ + if(ff->hmode == H_SMUSH) + { + if(xleft < ff->x && + hsmush(cucul_get_char(cv, ff->x - 1 - xleft, ff->y + y), + cucul_get_char(ff->charcv, xright, y), + ff->hsmushrule)) + xleft++; + } + + if(xleft + xright < overlap) + overlap = xleft + xright; + } + break; + case H_NONE: + overlap = 0; + break; + default: + return -1; + } + + /* Check whether the current canvas is large enough */ + if(ff->x + w - overlap > ff->w) + ff->w = ff->x + w - overlap < ff->term_width + ? ff->x + w - overlap : ff->term_width; + + if(ff->y + h > ff->h) + ff->h = ff->y + h; + +#if 0 /* deactivated for libcaca insertion */ + if(attr) + cucul_set_attr(cv, attr); +#endif + cucul_set_canvas_size(cv, ff->w, ff->h); + + /* Render our char (FIXME: create a rect-aware cucul_blit_canvas?) */ + for(y = 0; y < h; y++) + for(x = 0; x < w; x++) + { + uint32_t ch1, ch2; + //uint32_t tmpat = cucul_get_attr(ff->fontcv, x, y + c * ff->height); + ch2 = cucul_get_char(ff->charcv, x, y); + if(ch2 == ' ') + continue; + ch1 = cucul_get_char(cv, ff->x + x - overlap, ff->y + y); + /* FIXME: this could be changed to cucul_put_attr() when the + * function is fixed in libcucul */ + //cucul_set_attr(cv, tmpat); + if(ch1 == ' ' || ff->hmode != H_SMUSH) + cucul_put_char(cv, ff->x + x - overlap, ff->y + y, ch2); + else + cucul_put_char(cv, ff->x + x - overlap, ff->y + y, + hsmush(ch1, ch2, ff->hsmushrule)); + //cucul_put_attr(cv, ff->x + x, ff->y + y, tmpat); + } + + /* Advance cursor */ + ff->x += w - overlap; + + return 0; +} + +static int flush_figlet(cucul_canvas_t *cv) +{ + cucul_figfont_t *ff = cv->ff; + unsigned int x, y; + + //ff->torender = cv; + //cucul_set_canvas_size(ff->torender, ff->w, ff->h); + cucul_set_canvas_size(cv, ff->w, ff->h); + + /* FIXME: do this somewhere else, or record hardblank positions */ + for(y = 0; y < ff->h; y++) + for(x = 0; x < ff->w; x++) + if(cucul_get_char(cv, x, y) == 0xa0) + { + uint32_t attr = cucul_get_attr(cv, x, y); + cucul_put_char(cv, x, y, ' '); + cucul_put_attr(cv, x, y, attr); + } + + ff->x = ff->y = 0; + ff->w = ff->h = 0; + + //cv = cucul_create_canvas(1, 1); /* XXX */ + + /* from render.c */ + ff->lines += cucul_get_canvas_height(cv); + + return 0; +} + #define STD_GLYPHS (127 - 32) #define EXT_GLYPHS (STD_GLYPHS + 7) -cucul_figfont_t * _cucul_open_figfont(char const *path) +cucul_figfont_t * open_figfont(char const *path) { char altpath[2048]; char buf[2048]; @@ -250,7 +472,7 @@ cucul_figfont_t * _cucul_open_figfont(char const *path) return ff; } -int _cucul_free_figfont(cucul_figfont_t *ff) +int free_figfont(cucul_figfont_t *ff) { cucul_free_canvas(ff->fontcv); free(ff->lookup); @@ -259,3 +481,68 @@ int _cucul_free_figfont(cucul_figfont_t *ff) return 0; } +static uint32_t hsmush(uint32_t ch1, uint32_t ch2, unsigned int rule) +{ + /* Rule 1 */ + if((rule & 0x01) && ch1 == ch2 && ch1 != 0xa0) + return ch2; + + if(ch1 < 0x80 && ch2 < 0x80) + { + char const charlist[] = "|/\\[]{}()<>"; + char *tmp1, *tmp2; + + /* Rule 2 */ + if(rule & 0x02) + { + if(ch1 == '_' && strchr(charlist, ch2)) + return ch2; + + if(ch2 == '_' && strchr(charlist, ch1)) + return ch1; + } + + /* Rule 3 */ + if((rule & 0x04) && + (tmp1 = strchr(charlist, ch1)) && (tmp2 = strchr(charlist, ch2))) + { + int cl1 = (tmp1 + 1 - charlist) / 2; + int cl2 = (tmp2 + 1 - charlist) / 2; + + if(cl1 < cl2) + return ch2; + if(cl1 > cl2) + return ch1; + } + + /* Rule 4 */ + if(rule & 0x08) + { + uint16_t s = ch1 + ch2; + uint16_t p = ch1 * ch2; + + if(p == 15375 /* '{' * '}' */ + || p == 8463 /* '[' * ']' */ + || (p == 1640 && s == 81)) /* '(' *|+ ')' */ + return '|'; + } + + /* Rule 5 */ + if(rule & 0x10) + { + switch((ch1 << 8) | ch2) + { + case 0x2f5c: return '|'; /* /\ */ + case 0x5c2f: return 'Y'; /* \/ */ + case 0x3e3c: return 'X'; /* >< */ + } + } + + /* Rule 6 */ + if((rule & 0x20) && ch1 == ch2 && ch1 == 0xa0) + return 0xa0; + } + + return 0; +} + diff --git a/examples/figfont.c b/examples/figfont.c index 8ee269b..31cf063 100644 --- a/examples/figfont.c +++ b/examples/figfont.c @@ -17,6 +17,7 @@ #if !defined(__KERNEL__) # include +# include #endif #include "cucul.h" @@ -24,8 +25,11 @@ int main(int argc, char *argv[]) { cucul_canvas_t *cv; + void *buffer; + unsigned long int len; + uint8_t color = 0; - if(argc < 2) + if(argc < 3) { fprintf(stderr, "Too few arguments\n"); return -1; @@ -38,6 +42,16 @@ int main(int argc, char *argv[]) return -1; } + while(argv[2][0]) + { + cucul_set_color_ansi(cv, 1 + ((color += 4) % 15), CUCUL_TRANSPARENT); + cucul_put_figchar(cv, argv[2]++[0]); + } + + buffer = cucul_export_memory(cv, "utf8", &len); + fwrite(buffer, len, 1, stdout); + free(buffer); + cucul_free_canvas(cv); return 0;