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