URI: 
       ttext-common.c - ltkx - GUI toolkit for X11 (WIP)
  HTML git clone git://lumidify.org/ltkx.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       ttext-common.c (10432B)
       ---
            1 /*
            2  * This file is part of the Lumidify ToolKit (LTK)
            3  * Copyright (c) 2017, 2018, 2020 lumidify <nobody@lumidify.org>
            4  *
            5  * Permission is hereby granted, free of charge, to any person obtaining a copy
            6  * of this software and associated documentation files (the "Software"), to deal
            7  * in the Software without restriction, including without limitation the rights
            8  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
            9  * copies of the Software, and to permit persons to whom the Software is
           10  * furnished to do so, subject to the following conditions:
           11  *
           12  * The above copyright notice and this permission notice shall be included in all
           13  * copies or substantial portions of the Software.
           14  *
           15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
           16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
           17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
           18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
           19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
           20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
           21  * SOFTWARE.
           22  */
           23 
           24 #include <stdio.h>
           25 #include <stdlib.h>
           26 #include <stdint.h>
           27 #include <limits.h>
           28 #include <X11/Xlib.h>
           29 #include <X11/Xutil.h>
           30 #include "stb_truetype.h" /* http://nothings.org/stb/stb_truetype.h */
           31 #include <fontconfig/fontconfig.h>
           32 #include "khash.h"
           33 #include <fribidi.h>
           34 #include <harfbuzz/hb.h>
           35 #include <harfbuzz/hb-ot.h>
           36 #include "text-common.h"
           37 #include "ltk.h"
           38 
           39 extern Ltk *ltk_global;
           40 
           41 /* These unicode routines are taken from
           42  * https://github.com/JeffBezanson/cutef8 */
           43 
           44 /* is c the start of a utf8 sequence? */
           45 #define isutf(c) (((c)&0xC0)!=0x80)
           46 
           47 static const uint32_t offsetsFromUTF8[6] = {
           48     0x00000000UL, 0x00003080UL, 0x000E2080UL,
           49     0x03C82080UL, 0xFA082080UL, 0x82082080UL
           50 };
           51 
           52 /* next character without NUL character terminator */
           53 uint32_t u8_nextmemchar(const char *s, size_t *i)
           54 {
           55     uint32_t ch = 0;
           56     size_t sz = 0;
           57     do {
           58         ch <<= 6;
           59         ch += (unsigned char)s[(*i)++];
           60         sz++;
           61     } while (!isutf(s[*i]));
           62     ch -= offsetsFromUTF8[sz-1];
           63 
           64     return ch;
           65 }
           66 
           67 /* number of characters in NUL-terminated string */
           68 size_t u8_strlen(const char *s)
           69 {
           70     size_t count = 0;
           71     size_t i = 0, lasti;
           72 
           73     while (1) {
           74         lasti = i;
           75         while (s[i] > 0)
           76             i++;
           77         count += (i-lasti);
           78         if (s[i++]==0) break;
           79         (void)(isutf(s[++i]) || isutf(s[++i]) || ++i);
           80         count++;
           81     }
           82     return count;
           83 }
           84 
           85 size_t u8_wc_toutf8(char *dest, uint32_t ch)
           86 {
           87     if (ch < 0x80) {
           88         dest[0] = (char)ch;
           89         return 1;
           90     }
           91     if (ch < 0x800) {
           92         dest[0] = (ch>>6) | 0xC0;
           93         dest[1] = (ch & 0x3F) | 0x80;
           94         return 2;
           95     }
           96     if (ch < 0x10000) {
           97         dest[0] = (ch>>12) | 0xE0;
           98         dest[1] = ((ch>>6) & 0x3F) | 0x80;
           99         dest[2] = (ch & 0x3F) | 0x80;
          100         return 3;
          101     }
          102     if (ch < 0x110000) {
          103         dest[0] = (ch>>18) | 0xF0;
          104         dest[1] = ((ch>>12) & 0x3F) | 0x80;
          105         dest[2] = ((ch>>6) & 0x3F) | 0x80;
          106         dest[3] = (ch & 0x3F) | 0x80;
          107         return 4;
          108     }
          109     return 0;
          110 }
          111 
          112 LtkTextManager *
          113 ltk_init_text(char *font_name)
          114 {
          115         LtkTextManager *tm = malloc(sizeof(LtkTextManager));
          116         if (!tm) {
          117                 (void)printf("Memory exhausted when trying to create text manager.");
          118                 exit(1);
          119         }
          120         tm->font_paths = kh_init(fontid);
          121         tm->font_cache = kh_init(fontstruct);
          122         tm->glyph_cache = kh_init(glyphcache);
          123         /* FIXME: THIS REALLY SHOULD NOT BE UINT16_T! IT GETS MESSY WITH BIT-SHIFTING */
          124         tm->font_id_cur = 1;
          125         ltk_load_default_font(tm, font_name);
          126 
          127         return tm;
          128 }
          129 
          130 void
          131 ltk_destroy_text_manager(LtkTextManager *tm)
          132 {
          133         int k;
          134 
          135         kh_destroy(fontid, tm->font_paths);
          136 
          137         for (k = kh_begin(tm->font_cache); k != kh_end(tm->font_cache); k++) {
          138                 if (kh_exist(tm->font_cache, k)) {
          139                         ltk_destroy_font(kh_value(tm->font_cache, k));
          140                 }
          141         }
          142         kh_destroy(fontstruct, tm->font_cache);
          143 
          144         for (k = kh_begin(tm->glyph_cache); k != kh_end(tm->glyph_cache); k++) {
          145                 if (kh_exist(tm->glyph_cache, k)) {
          146                         ltk_destroy_glyph_cache(kh_value(tm->glyph_cache, k));
          147                 }
          148         }
          149         kh_destroy(glyphcache, tm->glyph_cache);
          150 
          151         free(tm);
          152 }
          153 
          154 LtkGlyphInfo *
          155 ltk_create_glyph_info(LtkFont *font, unsigned int id, float scale)
          156 {
          157         LtkGlyphInfo *glyph = malloc(sizeof(LtkGlyphInfo));
          158         if (!glyph) {
          159                 (void)printf("Out of memory!\n");
          160                 exit(1);
          161         }
          162 
          163         glyph->id = id;
          164         glyph->refs = 0;
          165         glyph->alphamap = stbtt_GetGlyphBitmap(
          166                 &font->info, scale, scale, id, &glyph->w,
          167                 &glyph->h, &glyph->xoff, &glyph->yoff
          168         );
          169 
          170         return glyph;
          171 }
          172 
          173 void
          174 ltk_destroy_glyph_info(LtkGlyphInfo *gi)
          175 {
          176         free(gi->alphamap);
          177         free(gi);
          178 }
          179 
          180 LtkGlyphInfo *
          181 ltk_get_glyph_info(LtkFont *font, unsigned int id, float scale, khash_t(glyphinfo) *cache)
          182 {
          183         int ret;
          184         khint_t k;
          185         LtkGlyphInfo *glyph;
          186         k = kh_get(glyphinfo, cache, id);
          187         if (k == kh_end(cache)) {
          188                 glyph = ltk_create_glyph_info(font, id, scale);
          189                 /* FIXME: error checking with ret */
          190                 k = kh_put(glyphinfo, cache, id, &ret);
          191                 kh_value(cache, k) = glyph;
          192         } else {
          193                 glyph = kh_value(cache, k);
          194         }
          195 
          196         return glyph;
          197 }
          198 
          199 khint_t
          200 ltk_create_glyph_cache(LtkTextManager *tm, uint16_t font_id, uint16_t font_size)
          201 {
          202         khash_t(glyphinfo) *cache = kh_init(glyphinfo);
          203         int ret;
          204         khint_t k;
          205         /* I guess I can just ignore ret for now */
          206         k = kh_put(glyphcache, tm->glyph_cache, font_id << 16 + font_size, &ret);
          207         kh_value(tm->glyph_cache, k) = cache;
          208 
          209         return k;
          210 }
          211 
          212 void
          213 ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache)
          214 {
          215         int k;
          216         for (k = kh_begin(cache); k != kh_end(cache); k++) {
          217                 if (kh_exist(cache, k)) {
          218                         ltk_destroy_glyph_info(kh_value(cache, k));
          219                 }
          220         }
          221         kh_destroy(glyphinfo, cache);
          222 }
          223 
          224 void
          225 ltk_load_default_font(LtkTextManager *tm, char *name)
          226 {
          227         FcPattern *match;
          228         FcResult result;
          229         char *file;
          230         int index;
          231         uint16_t font;
          232 
          233         tm->fcpattern = FcNameParse(name);
          234         FcPatternAddString(tm->fcpattern, FC_FONTFORMAT, "truetype");
          235         FcConfigSubstitute(NULL, tm->fcpattern, FcMatchPattern);
          236         FcDefaultSubstitute(tm->fcpattern);
          237         match = FcFontMatch(NULL, tm->fcpattern, &result);
          238 
          239         FcPatternGetString (match, FC_FILE, 0, (FcChar8 **) &file);
          240         /* FIXME: Why is index never used? This is the index within the font file,
          241            so it might be important, although I'm not sure if stb_truetype even
          242            supports it */
          243         FcPatternGetInteger (match, FC_INDEX, 0, &index);
          244 
          245         tm->default_font = ltk_get_font(tm, file);
          246 
          247         FcPatternDestroy (match);
          248 }
          249 
          250 LtkFont *
          251 ltk_create_font(char *path, uint16_t id)
          252 {
          253         long len;
          254         LtkFont *font = malloc(sizeof(LtkFont));
          255         if (!font) {
          256                 (void)fprintf(stderr, "Out of memory!\n");
          257                 exit(1);
          258         }
          259         char *contents = ltk_read_file(path, &len);
          260         if (!stbtt_InitFont(&font->info, contents, 0))
          261         {
          262                 (void)fprintf(stderr, "Failed to load font %s\n", path);
          263                 exit(1);
          264         }
          265         /* FIXME: make use of the destroy function (last argument to hb_blob_create - see hb-blob.cc in harfbuzz source) */
          266         hb_blob_t *blob = hb_blob_create(contents, len, HB_MEMORY_MODE_READONLY, NULL, NULL);
          267         hb_face_t *face = hb_face_create(blob, 0);
          268         /* FIXME: need to use destroy function in order for the original file data to be freed? */
          269         hb_blob_destroy(blob);
          270         font->hb = hb_font_create(face);
          271         hb_face_destroy(face);
          272         hb_ot_font_set_funcs(font->hb);
          273         font->id = id;
          274         font->refs = 0;
          275 
          276         return font;
          277 }
          278 
          279 void
          280 ltk_destroy_font(LtkFont *font)
          281 {
          282         free(font->info.data);
          283         hb_font_destroy(font->hb);
          284         free(font);
          285 }
          286 
          287 uint16_t
          288 ltk_load_font(LtkTextManager *tm, char *path)
          289 {
          290         LtkFont *font = ltk_create_font(path, tm->font_id_cur++);
          291         int ret;
          292         khint_t k;
          293         /* FIXME: does kh_destroy also free these copied strings properly? */
          294         char *key = strdup(path);
          295         k = kh_put(fontid, tm->font_paths, key, &ret);
          296         kh_value(tm->font_paths, k) = font->id;
          297         k = kh_put(fontstruct, tm->font_cache, (khint_t) font->id, &ret);
          298         kh_value(tm->font_cache, k) = font;
          299         k = kh_get(fontid, tm->font_paths, path);
          300 
          301         return font->id;
          302 }
          303 
          304 uint16_t
          305 ltk_get_font(LtkTextManager *tm, char *path)
          306 {
          307         int ret;
          308         khint_t k;
          309         uint16_t id;
          310         k = kh_get(fontid, tm->font_paths, path);
          311         if (k == kh_end(tm->font_paths)) {
          312                 id = ltk_load_font(tm, path);
          313         } else {
          314                 id = kh_value(tm->font_paths, k);
          315         }
          316 
          317         return id;
          318 }
          319 
          320 void
          321 ltk_destroy_glyph(LtkGlyph *glyph, khash_t(glyphinfo) *cache)
          322 {
          323         int k;
          324         if (--glyph->info->refs < 1) {
          325                 k = kh_get(glyphinfo, cache, glyph->info->id);
          326                 kh_del(glyphinfo, cache, k);
          327                 ltk_destroy_glyph_info(glyph->info);
          328         }
          329         free(glyph);
          330 }
          331 
          332 #if 0
          333 /* based on http://codemadness.org/git/dwm-font/file/drw.c.html#l315 */
          334 XImage *
          335 ltk_render_text_line(
          336         LtkTextLine *tl,
          337         Display *dpy,
          338         Window window,
          339         GC gc,
          340         Colormap colormap,
          341         XColor fg,
          342         XColor bg)
          343 {
          344         XWindowAttributes attrs;
          345         XGetWindowAttributes(dpy, window, &attrs);
          346         int depth = attrs.depth;
          347         XImage *img = XCreateImage(dpy, CopyFromParent, depth, ZPixmap, 0, NULL, tl->w, tl->h, 32, 0);
          348         img->data = calloc(img->bytes_per_line, img->height);
          349         XInitImage(img);
          350         int b;
          351         for (int i = 0; i < tl->h; i++) {
          352                 b = img->bytes_per_line * i;
          353                 for (int j = 0; j < tl->w; j++) {
          354                         img->data[b++] = bg.blue / 257;
          355                         img->data[b++] = bg.green / 257;
          356                         img->data[b++] = bg.red / 257;
          357                         b++;
          358                 }
          359         }
          360 
          361         LtkTextSegment *ts = tl->start_segment;
          362         int x = 0;
          363         int y = 0;
          364         int is_hor = HB_DIRECTION_IS_HORIZONTAL(ts->dir);
          365         do {
          366                 if (is_hor) {
          367                         y = tl->h - tl->y_max;
          368                         ltk_render_text_segment(ts, x + ts->start_x, y, img, fg);
          369                         x += ts->w;
          370                 } else {
          371                         x = tl->w - tl->x_max;
          372                         ltk_render_text_segment(ts, x, y + ts->start_y, img, fg);
          373                         y += ts->h;
          374                 }
          375         } while (ts = ts->next);
          376 
          377         return img;
          378 }
          379 
          380 void
          381 ltk_render_text_segment(
          382         LtkTextSegment *ts,
          383         unsigned int start_x,
          384         unsigned int start_y,
          385         XImage *img,
          386         XColor fg)
          387 {
          388         LtkGlyph *glyph = ts->start_glyph;
          389         int x_cur = start_x;
          390         int y_cur = start_y;
          391         int x, y;
          392         double a;
          393         int b;
          394         do {
          395                 x = x_cur + glyph->info->xoff + glyph->x_offset;
          396                 y = y_cur + glyph->info->yoff - glyph->y_offset;
          397                 for (int i = 0; i < glyph->info->h; i++) {
          398                         for (int j = 0; j < glyph->info->w; j++) {
          399                                 b = (y + i) * img->bytes_per_line + (x + j) * 4;
          400                                 a = glyph->info->alphamap[i * glyph->info->w + j] / 255.0;
          401                                 img->data[b] = (fg.blue * a + (1 - a) * (uint16_t)img->data[b] * 257) / 257;
          402                                 img->data[b + 1] = (fg.green * a + (1 - a) * (uint16_t)img->data[b + 1] * 257) / 257;
          403                                 img->data[b + 2] = (fg.red * a + (1 - a) * (uint16_t)img->data[b + 2] * 257) / 257;
          404                         }
          405                 }
          406                 x_cur += glyph->x_advance;
          407                 y_cur -= glyph->y_advance;
          408         } while (glyph = glyph->next);
          409 }
          410 #endif