1 /*
2 * Font scaler
3 *
4 * Copyright © 1997-1998 Herbert Duerr
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free Softaware
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 */
21
22 #include "ttf.h"
23 #include <climits>
24
Rasterizer(int _grid_fitting,int _anti_aliasing,int _sizeTwilight,int _sizePoints,int _sizeContours,int _sizeStack,int _sizeCvt,int _sizeStor,int _sizeFDefs)25 Rasterizer::Rasterizer(int _grid_fitting, int _anti_aliasing,
26 int _sizeTwilight, int _sizePoints, int _sizeContours,
27 int _sizeStack, int _sizeCvt, int _sizeStor,
28 int _sizeFDefs):
29 stackbase(nullptr), ttFont(nullptr), status(INVALID_FONT),
30 sizeContours(_sizeContours), sizeStack(_sizeStack), sizeCvt(_sizeCvt),
31 sizeStor(_sizeStor), sizeFDefs(_sizeFDefs), sizeIDefs(0),
32 format(LOGSLP - 3),
33 grid_fitting(_grid_fitting), anti_aliasing(_anti_aliasing)
34 {
35 sizePoints[0] = _sizeTwilight;
36 sizePoints[1] = _sizePoints;
37 openDraw();
38 }
39
~Rasterizer()40 Rasterizer::~Rasterizer()
41 {
42 endInterpreter();
43 closeDraw();
44
45 if (sizeContours)
46 delete[] endPoints;
47 if (sizePoints[1])
48 delete[] p[1];
49 }
50
51 // useTTFont must be executed before setP*Size!
52 void
useTTFont(TTFont * _ttFont,int _flags)53 Rasterizer::useTTFont(TTFont *_ttFont, int _flags)
54 {
55 flags = _flags;
56
57 // set scaling defaults = 16points
58 xx = yy = (16 * VGARES + 36) / 72;
59 xy = yx = 0;
60
61 if (!_ttFont || _ttFont->badFont()) {
62 status = INVALID_FONT;
63 return;
64 }
65
66 /* XXX */
67 #if 0
68 if (ttFont == _ttFont)
69 return;
70 #endif
71
72 ttFont = _ttFont;
73 status = NOT_READY;
74 initInterpreter();
75
76 #define MEMSLACK 2
77 int i = ttFont->maxpTable->maxPoints
78 + ttFont->maxpTable->maxCompPoints + 2;
79 if (sizePoints[1] < i) {
80 if (sizePoints[1])
81 delete[] p[1];
82 sizePoints[1] = MEMSLACK * i;
83 p[1] = new Point[sizePoints[1]];
84 }
85
86 i = ttFont->maxpTable->maxContours + ttFont->maxpTable->maxCompContours;
87 if (sizeContours < i) {
88 if (sizeContours)
89 delete[] endPoints;
90 sizeContours = MEMSLACK * i;
91 endPoints = new int[sizeContours];
92 }
93
94 ttFont->points = p[1];
95 ttFont->endPoints = endPoints;
96 }
97
98 // prepare scaling of outlines
99 // it can only be called when useTTFont has defined a font
100 void
setPointSize(int _xx,int _xy,int _yx,int _yy,int xres,int yres)101 Rasterizer::setPointSize(int _xx, int _xy, int _yx, int _yy, int xres, int yres)
102 {
103 debug("_xx = %d,\t_xy = %d\n", _xx, _xy);
104 debug("_yx = %d,\t_yy = %d\n", _xy, _yy);
105
106 if (!(_xx | _xy) || !(_yx | _yy))
107 _xx = _yy = 12;
108
109 if (!xres || !yres)
110 xres = yres = VGARES;
111
112 pointSize = (_xx + _yy) / 2; // XXX: quick hack
113
114 _xx = (_xx * xres + 36) / 72;
115 _xy = (_xy * xres + 36) / 72;
116 _yx = (_yx * yres + 36) / 72;
117 _yy = (_yy * yres + 36) / 72;
118
119 setPixelSize(_xx, _xy, _yx, _yy);
120 }
121
122 void
setPixelSize(int _xx,int _xy,int _yx,int _yy)123 Rasterizer::setPixelSize(int _xx, int _xy, int _yx, int _yy)
124 {
125 mppemx = xx = _xx ? _xx : 16;
126 xy = _xy;
127 yx = _yx;
128 mppemy = yy = _yy ? _yy : 16;
129
130 // 0177 would make more sense than 177, wouldn't it?
131 // but since the reference implementation does it we should do it too
132 for (xxexp = 0; xx > 177 || xx < -177 || yy > 177 || yy < -177; ++xxexp) {
133 xx >>= 1; xy >>= 1;
134 yx >>= 1; yy >>= 1;
135 }
136 debug("xx = %d, xy = %d\n", xx, xy);
137 debug("yx = %d, yy = %d\n", yx, yy);
138 debug("exp = %d\n", xxexp);
139
140 if (ttFont)
141 applyTransformation();
142 else
143 status = NOT_READY;
144 }
145
146 // only needed when emUnits != 2048, but since we don't know this until
147 // we know the TTFont we have to separate it from setP*Size()
148 void
applyTransformation()149 Rasterizer::applyTransformation()
150 {
151 int emUnits = ttFont->getEmUnits();
152 debug("emUnits = %d\n", emUnits);
153
154 for (; emUnits > 2048 && xxexp > 0; --xxexp)
155 emUnits >>= 1;
156 for (; emUnits < 2048; ++xxexp)
157 emUnits <<= 1;
158 if (emUnits != 2048) {
159 xx = (xx << 11) / emUnits;
160 xy = (xy << 11) / emUnits;
161 yx = (yx << 11) / emUnits;
162 yy = (yy << 11) / emUnits;
163 }
164
165 mppem = (mppemx + mppemy) >> 1;
166
167 debug("xx = %d, xy = %d\n", xx, xy);
168 debug("yx = %d, yy = %d\n", yx, yy);
169 debug("exp = %d\n", xxexp);
170
171 debug("mppem = %d, mppemx = %d, mppemy = %d\n", mppem, mppemx, mppemy);
172
173 if (grid_fitting)
174 calcCVT();
175
176 status = TRAFO_APPLIED;
177 }
178
179 /* get font extent
180 * XXX: is there a way to avoid going through all glyphs and interpret them?
181 * we need max/min-width, max/min-left, max/min-right, max/min-height, ...
182 * would it have advantages to delay the scan line conversion?
183 */
184 void
getFontExtent(FontExtent * fe)185 Rasterizer::getFontExtent(FontExtent *fe)
186 {
187 if (status == NOT_READY)
188 applyTransformation();
189 if (status == FONT_DONE)
190 return;
191
192 if (ttFont->os2Table) {
193 int i = ttFont->os2Table->winAscent;
194 fe->yWinAscent = ((yy * i) << xxexp) >> 11;
195 i = ttFont->os2Table->winDescent;
196 fe->yWinDescent = ((yy * i) << xxexp) >> 11;
197 } else {
198 int i = ttFont->hheaTable->yAscent;
199 fe->yWinAscent = ((yy * i) << xxexp) >> 11;
200 i = -ttFont->hheaTable->yDescent;
201 fe->yWinDescent = ((yy * i) << xxexp) >> 11;
202 }
203
204 fe->xLeftMin = fe->xRightMin =
205 fe->xAdvanceMin = fe->yAdvanceMin =
206 fe->yAscentMin = fe->yDescentMin =
207 fe->xBlackboxMin = fe->yBlackboxMin = INT_MAX;
208
209 fe->xLeftMax = fe->xRightMax =
210 fe->xAdvanceMax = fe->yAdvanceMax =
211 fe->yAscentMax = fe->yDescentMax =
212 fe->xBlackboxMax = fe->yBlackboxMax = INT_MIN;
213
214 if (format < 0) {
215 // fixed xBlackbox for all glyphs
216 fe->xBlackboxMax = ttFont->getMaxWidth(mppemx);
217 dX = (((fe->xBlackboxMax - 1) | ((8 << ~format) - 1)) + 1) >> 3;
218 }
219
220 uint32_t buflen = fe->buflen;
221 uint8_t *endbmp = fe->buffer + buflen;
222
223 CharInfo *ci = (CharInfo *)fe->buffer;
224 fe->bmpFormat = MSB_BIT_FIRST | MSB_BYTE_FIRST;
225 fe->numGlyphs = ttFont->maxpTable->numGlyphs;
226 fe->bitmaps = (uint8_t *)&ci[fe->numGlyphs];
227 uint8_t *buffer = fe->bitmaps;
228 if (buffer >= endbmp) {
229 // XXX: not even enough room for charinfo!!! what to do???
230 return;
231 }
232 for (int glyphNo = 0; glyphNo < fe->numGlyphs; ++glyphNo, ++ci) {
233 GlyphMetrics *gm = &ci->gm;
234
235 ci->length = putGlyphBitmap(glyphNo, buffer, endbmp, gm);
236 ci->offset = buffer - fe->bitmaps;
237 buffer += ci->length;
238
239 #define MINMAX(x, y, z) \
240 if (z < fe->x) \
241 fe->x = z; \
242 if (z > fe->y) \
243 fe->y = z;
244
245 MINMAX(xBlackboxMin, xBlackboxMax, gm->xBlackbox);
246 MINMAX(yBlackboxMin, yBlackboxMax, gm->yBlackbox);
247
248 MINMAX(xLeftMin, xLeftMax, -gm->xOrigin);
249 MINMAX(xRightMin, xRightMax, gm->xBlackbox - gm->xOrigin);
250
251 MINMAX(yAscentMin, yAscentMax, gm->yBlackbox - gm->yOrigin);
252 MINMAX(yDescentMin, yDescentMax, gm->yOrigin);
253
254 MINMAX(xAdvanceMin, xAdvanceMax, gm->xAdvance);
255 MINMAX(yAdvanceMin, yAdvanceMax, gm->yAdvance);
256 }
257
258 fe->bmplen = buffer - fe->bitmaps;
259 status = FONT_DONE;
260 }
261
262 int
putChar8Bitmap(char c,uint8_t * bmp,uint8_t * endbmp,GlyphMetrics * gm)263 Rasterizer::putChar8Bitmap(char c, uint8_t *bmp, uint8_t *endbmp,
264 GlyphMetrics *gm)
265 {
266 debug("charNo8 = %d", c);
267 int glyphNo = ttFont->getGlyphNo8(c);
268 return putGlyphBitmap(glyphNo, bmp, endbmp, gm);
269 }
270
271 int
putChar16Bitmap(int c,uint8_t * bmp,uint8_t * endbmp,GlyphMetrics * gm)272 Rasterizer::putChar16Bitmap(int c, uint8_t *bmp, uint8_t *endbmp,
273 GlyphMetrics *gm)
274 {
275 int glyphNo = ttFont->getGlyphNo16(c);
276 debug("charNo16 = %d", c);
277 return putGlyphBitmap(glyphNo, bmp, endbmp, gm);
278 }
279
280 int
putGlyphBitmap(int glyphNo,uint8_t * bmp,uint8_t * endbmp,GlyphMetrics * gm)281 Rasterizer::putGlyphBitmap(int glyphNo, uint8_t *bmp, uint8_t *endbmp,
282 GlyphMetrics *gm)
283 {
284 debug("\n=============== glyphNo %d ==================\n", glyphNo);
285
286 GlyphTable *g = ttFont->glyphTable;
287 g->setupGlyph(ttFont->points, ttFont->endPoints);
288 if (bmp >= endbmp || !g->getGlyphData(glyphNo, ttFont->locaTable, this)) {
289 gm->xAdvance = ttFont->getGlyphWidth(mppemx, glyphNo);
290 gm->yAdvance = 0;
291 gm->xOrigin = gm->yOrigin = 0;
292 gm->xBlackbox = gm->yBlackbox = 0;
293 return (length = 0);
294 }
295
296 int xmin = INT_MAX, ymin = xmin;
297 int xmax = INT_MIN, ymax = xmax;
298
299 Point *pp = p[1];
300 for (int i = nPoints[1]; --i >= 0; ++pp) {
301 if (MAGNIFY > 1) {
302 pp->xnow *= MAGNIFY;
303 pp->ynow *= MAGNIFY;
304 }
305 if (anti_aliasing > 0) {
306 pp->xnow <<= anti_aliasing;
307 pp->ynow <<= anti_aliasing;
308 }
309
310 int xt = pp->xnow;
311 if (xmin > xt)
312 xmin = xt;
313 if (xmax < xt)
314 xmax = xt;
315
316 int yt = pp->ynow;
317 if (ymin > yt)
318 ymin = yt;
319 if (ymax < yt)
320 ymax = yt;
321 }
322
323 if (anti_aliasing > 0) {
324 xmin &= -64 << anti_aliasing;
325 ymin &= -64 << anti_aliasing;
326 ymax |= ~(-64 << anti_aliasing);
327 } else {
328 xmin &= -64;
329 ymin &= -64;
330 xmax += 63;
331 ymax |= 63;
332 }
333
334 #ifdef DEBUG
335 printOutline();
336 #endif
337
338 pp = p[1];
339 nPoints[1] += 2;
340 for (int j = nPoints[1]; --j >= 0; ++pp) {
341 pp->xnow -= xmin;
342 pp->ynow -= ymin;
343 }
344
345 xmax >>= SHIFT;
346 ymax >>= SHIFT;
347 xmin >>= SHIFT;
348 ymin >>= SHIFT;
349
350 gm->xBlackbox = width = xmax - xmin + 1;
351 gm->yBlackbox = height = ymax - ymin + 1;
352
353 pp -= 2;
354 gm->xOrigin = pp[0].xnow >> SHIFT;
355 gm->yOrigin = pp[0].ynow >> SHIFT;
356 gm->xAdvance = (pp[1].xnow - pp[0].xnow) >> SHIFT;
357 gm->yAdvance = (pp[1].ynow - pp[0].ynow) >> SHIFT;
358
359 if (format >= 0)
360 dX = (((width - 1) | ((8 << format) - 1)) + 1) >> 3;
361
362 length = dX * height;
363
364 if (ttFont->hdmxTable) { // XXX: hack
365 int hdmx = ttFont->hdmxTable->getGlyphWidth(mppemx, glyphNo);
366 #if 0
367 if (hdmx && gm->xAdvance != hdmx)
368 debug("adv(%d) = %3d <-> %3d\n",
369 glyphNo, gm->xAdvance, hdmx);
370 #endif
371 if (hdmx)
372 gm->xAdvance = hdmx;
373 }
374
375 debug("width = %d, dX = %d, height = %d\n", width, dX, height);
376 debug("gn=%d, length= %d\n", glyphNo, length);
377
378 drawGlyph(bmp, endbmp);
379
380 if (anti_aliasing == 1)
381 antiAliasing2(bmp);
382 return length;
383 }
384
385 void
putGlyphData(int ne,int np,int * ep,Point * pp,int glyphNo,int xmin)386 Rasterizer::putGlyphData(int ne, int np, int *ep, Point *pp, int glyphNo,
387 int xmin)
388 {
389 nEndPoints = ne;
390 nPoints[1] = np;
391 endPoints = ep;
392 p[1] = pp;
393
394 // prepare phantom point 0
395 pp += np;
396 int advanceWidth, lsb;
397 ttFont->hmtxTable->getHMetrics(glyphNo, &advanceWidth, &lsb);
398 int val = xmin - lsb;
399 pp->xold = scaleX(val, 0);
400 pp->yold = scaleY(0, val);
401 debug("xmin = %d, adv = %d, lsb = %d\n", xmin, advanceWidth, lsb);
402 pp->xnow = (pp->xold + 32) & -64;
403 pp->ynow = (pp->yold + 32) & -64;
404 #if 0
405 // XXX: reference implementation always has (0 0)!
406 val = pp->xnow = pp->ynow = 0;
407 pp->xold = pp->yold = 0;
408 #endif
409 debug("phantom[0] = %5d -> %5d -> %5d\n", val, pp->xold, pp->xnow);
410
411 // prepare phantom point 1
412 val += advanceWidth;
413 ++pp;
414 pp->xold = scaleX(val, 0);
415 pp->yold = scaleY(0, val);
416 pp->xnow = (pp->xold + 32) & -64;
417 pp->ynow = (pp->yold + 32) & -64;
418 }
419
420 void
scaleGlyph()421 Rasterizer::scaleGlyph()
422 {
423 // scale outline
424 Point *pp = p[1];
425 for (int i = nPoints[1]; --i >= 0; ++pp) {
426 pp->xold = scaleX(pp->xnow, pp->ynow);
427 pp->yold = scaleY(pp->ynow, pp->xnow);
428 pp->xnow = pp->xold;
429 pp->ynow = pp->yold;
430 }
431
432 switch (status) {
433 case INVALID_FONT:
434 return;
435 case FONT_DONE:
436 debug("### We shouldn't waste time like this ###\n");
437 return;
438 case NOT_READY:
439 applyTransformation();
440 // fall through
441 case TRAFO_APPLIED:
442 break;
443 }
444 }
445
446 void
printOutline(void)447 Rasterizer::printOutline(void)
448 {
449 debug("\n=== grid fitted outline ===\n");
450 Point *pp = p[1];
451 for (int i = 0, j = 0; i < nPoints[1] + 2; ++i, ++pp) {
452 debug("p[%d]\t%6d %6d ", i, pp->xold, pp->yold);
453 debug("-> %6d %6d", pp->xnow, pp->ynow);
454 debug(" %d%d", (pp->flags & X_TOUCHED) != 0,
455 (pp->flags & Y_TOUCHED) != 0);
456
457 debug(" %c", (pp->flags & ON_CURVE) ? '*' : ' ');
458
459 debug("\n");
460 if (i == endPoints[j]) {
461 ++j;
462 debug("----\n");
463 }
464 }
465 }
466