]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libttf/render.c
ip/cifsd: fix missing int return type for vpack() (thanks pr)
[plan9front.git] / sys / src / libttf / render.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <ttf.h>
5 #include "impl.h"
6
7 static int
8 ttfparsekern(TTFontU *f)
9 {
10         u16int ver, len, cov, ntab;
11         int i;
12
13         if(ttfgototable(f, "kern") < 0)
14                 return -1;
15         ttfunpack(f, "ww", &ver, &ntab);
16         if(ver != 0)
17                 return -1;
18         if(ntab == 0)
19                 return -1;
20         for(i = 0; i < ntab; i++){
21                 ttfunpack(f, "www", &ver, &len, &cov);
22                 if((cov & 1) != 0) break;
23                 Bseek(f->bin, len - 6, 1);
24         }
25         ttfunpack(f, "w6", &len);
26         f->nkern = len;
27         f->kern = mallocz(sizeof(TTKern) * len, 1);
28         for(i = 0; i < len; i++)
29                 ttfunpack(f, "lS", &f->kern[i].idx, &f->kern[i].val);
30         return 0;
31 }
32
33 static int
34 ttfkern(TTFont *f, int l, int r)
35 {
36         u32int idx;
37         int a, b, c;
38         TTFontU *u;
39         
40         u = f->u;
41         if(u->nkern == 0)
42                 return 0;
43         idx = l << 16 | r;
44         a = 0;
45         b = u->nkern - 1;
46         if(u->kern[a].idx > idx || u->kern[b].idx < idx)
47                 return 0;
48         while(a <= b){
49                 c = (a + b) / 2;
50                 if(u->kern[c].idx < idx){
51                         a = c + 1;
52                 }else if(u->kern[c].idx > idx){
53                         b = c - 1;
54                 }else
55                         return ttfrounddiv(u->kern[c].val * f->ppem, u->emsize);
56         }
57         return 0;
58 }
59
60 typedef struct {
61         TTBitmap *b;
62         TTFont *font;
63         TTGlyph **glyph;
64         int *gwidth;
65         char **cpos;
66         char *pp;
67         int nglyph, aglyph;
68         int linew;
69         int adj;
70         int nspc;
71         int oy, lh;
72         int spcidx;
73         int flags;
74         int spcw;
75         int spcminus;
76 } Render;
77
78 static int
79 addglyph(Render *r, char *p, TTGlyph *g)
80 {
81         void *v;
82         int k;
83
84         if(r->nglyph >= r->aglyph){
85                 r->aglyph += 32;
86                 v = realloc(r->glyph, sizeof(TTGlyph *) * r->aglyph);
87                 if(v == nil) return -1;
88                 r->glyph = v;
89                 v = realloc(r->gwidth, sizeof(int) * r->aglyph);
90                 if(v == nil) return -1;
91                 r->gwidth = v;
92                 v = realloc(r->cpos, sizeof(char *) * r->aglyph);
93                 if(v == nil) return -1;
94                 r->cpos = v;
95         }
96         r->glyph[r->nglyph] = g;
97         r->cpos[r->nglyph] = p;
98         r->gwidth[r->nglyph] = g->advanceWidthpx;
99         if(r->nglyph > 0){
100                 k = ttfkern(r->font, r->glyph[r->nglyph-1]->idx, g->idx);
101                 r->gwidth[r->nglyph-1] += k;
102                 r->linew += k;
103         }
104         r->nglyph++;
105         r->linew += r->gwidth[r->nglyph-1];
106         if(g->idx == r->spcidx)
107                 r->nspc++;
108         return 0;
109 }
110
111 static void
112 flushglyphs(Render *r, int justify)
113 {
114         int i, n, k, x, y;
115         int llen;
116         int adj, spcw, nspc, c;
117         TTFont *f;
118
119         f = r->font;
120         if((r->flags & TTFMODE) == TTFLALIGN && !justify)
121                 while(r->nglyph > 0 && r->glyph[r->nglyph - 1]->idx == r->spcidx){
122                         r->linew -= r->gwidth[--r->nglyph];
123                         r->nspc--;
124                 }
125         llen = r->linew;
126         k = n = r->nglyph;
127         nspc = r->nspc;
128         adj = (nspc * r->spcminus + 63) / 64;
129         if(r->linew - adj > r->b->width){
130                 n = r->nglyph;
131                 while(n > 0 && r->glyph[n - 1]->idx != r->spcidx)
132                         llen -= r->gwidth[--n];
133                 k = n;
134                 while(n > 0 && r->glyph[n - 1]->idx == r->spcidx){
135                         llen -= r->gwidth[--n];
136                         nspc--;
137                 }
138                 if(n == 0){
139                         while(n < r->nglyph && llen + r->gwidth[n] < r->b->width)
140                                 llen += r->gwidth[n++];
141                         k = n;
142                 }
143         }
144         if(justify){
145                 if(nspc == 0)
146                         spcw = 0;
147                 else
148                         spcw = (r->b->width - llen + nspc * r->spcw) * 64 / nspc;
149         }else
150                 spcw = r->spcw * 64;
151         switch(r->flags & TTFMODE | justify * TTFJUSTIFY){
152         case TTFRALIGN:
153                 x = r->b->width - llen;
154                 break;
155         case TTFCENTER:
156                 x = (r->b->width - llen)/2;
157                 break;
158         default:
159                 x = 0;
160         }
161         y = r->oy + f->ascentpx;
162         c = 0;
163         for(i = 0; i < k; i++){
164                 if(r->glyph[i]->idx == r->spcidx){
165                         c += spcw;
166                         x += c >> 6;
167                         c &= 63;
168                         r->nspc--;
169                 }else{
170                         ttfblit(r->b, x + r->glyph[i]->xminpx, y - r->glyph[i]->ymaxpx, r->glyph[i], 0, 0, r->glyph[i]->width, r->glyph[i]->height);
171                         x += r->gwidth[i];
172                 }
173                 r->linew -= r->gwidth[i];
174                 ttfputglyph(r->glyph[i]);
175         }
176         if(n > 0)
177                 r->pp = r->cpos[n-1];
178         r->oy += r->lh;
179         memmove(r->glyph, r->glyph + k, (r->nglyph - k) * sizeof(TTGlyph *));
180         memmove(r->cpos, r->cpos + k, (r->nglyph - k) * sizeof(char *));
181         memmove(r->gwidth, r->gwidth + k, (r->nglyph - k) * sizeof(int));
182         r->nglyph -= k;
183 }
184
185 TTBitmap *
186 _ttfrender(TTFont *f, int (*getrune)(Rune *, char *), char *p, char *end, int w, int h, int flags, char **rp)
187 {
188         Render r;
189         Rune ch;
190         int i, adj;
191         TTGlyph *g;
192         
193         if(rp != nil) *rp = p;
194         if(f->u->nkern < 0 && ttfparsekern(f->u) < 0)
195                 f->u->nkern = 0;
196         memset(&r, 0, sizeof(Render));
197         r.flags = flags;
198         r.font = f;
199         r.b = ttfnewbitmap(w, h);
200         if(r.b == nil) goto error;
201         r.oy = 0;
202         r.lh = f->ascentpx + f->descentpx;
203         r.pp = p;
204         
205         g = ttfgetglyph(f, ttffindchar(f, ' '), 1);
206         r.spcidx = g->idx;
207         r.spcw = g->advanceWidthpx;
208         if((flags & TTFJUSTIFY) != 0)
209                 r.spcminus = r.spcw * 21;
210         else
211                 r.spcminus = 0;
212                 
213         while(p < end && r.oy + r.lh < h){
214                 p += getrune(&ch, p);
215                 if(ch == '\n'){
216                         flushglyphs(&r, 0);
217                         continue;
218                 }
219                 g = ttfgetglyph(f, ttffindchar(f, ch), 1);
220                 if(g == nil){
221                         g = ttfgetglyph(f, 0, 1);
222                         if(g == nil)
223                                 continue;
224                 }
225                 if(addglyph(&r, p, g) < 0)
226                         goto error;
227                 adj = (r.nspc * r.spcminus + 63) / 64;
228                 if(r.linew - adj > r.b->width){
229                         flushglyphs(&r, (flags & TTFJUSTIFY) != 0);
230                 }
231         }
232         if(r.oy + r.lh < h)
233                 flushglyphs(&r, 0);
234         for(i = 0; i < r.nglyph; i++)
235                 ttfputglyph(r.glyph[i]);
236         free(r.glyph);
237         free(r.gwidth);
238         free(r.cpos);
239         if(rp != nil)
240                 *rp = r.pp;
241         return r.b;
242 error:
243         ttffreebitmap(r.b);
244         free(r.glyph);
245         free(r.gwidth);
246         return nil;
247 }
248
249 TTBitmap *
250 ttfrender(TTFont *f, char *str, char *end, int w, int h, int flags, char **rstr)
251 {
252         if(str == nil)
253                 end = nil;
254         else if(end == nil)
255                 end = str + strlen(str);
256         return _ttfrender(f, chartorune, str, end, w, h, flags, rstr);
257 }
258
259 static int
260 incrune(Rune *r, char *s)
261 {
262         *r = *(Rune*)s;
263         return sizeof(Rune);
264 }
265
266 TTBitmap *
267 ttfrunerender(TTFont *f, Rune *str, Rune *end, int w, int h, int flags, Rune **rstr)
268 {
269         if(str == nil)
270                 end = nil;
271         else if(end == nil)
272                 end = str + runestrlen(str);
273         return _ttfrender(f, incrune, (char *) str, (char *) end, w, h, flags, (char **) rstr);
274 }