|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtGui module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include <qdebug.h> |
|
43 #include <private/qfontengine_p.h> |
|
44 |
|
45 #include "qbitmap.h" |
|
46 #include "qpainter.h" |
|
47 #include "qpainterpath.h" |
|
48 #include "qvarlengtharray.h" |
|
49 #include <private/qpdf_p.h> |
|
50 #include <qmath.h> |
|
51 #include <qendian.h> |
|
52 #include <private/qharfbuzz_p.h> |
|
53 |
|
54 QT_BEGIN_NAMESPACE |
|
55 |
|
56 static inline bool qtransform_equals_no_translate(const QTransform &a, const QTransform &b) |
|
57 { |
|
58 if (a.type() <= QTransform::TxTranslate && b.type() <= QTransform::TxTranslate) { |
|
59 return true; |
|
60 } else { |
|
61 // We always use paths for perspective text anyway, so no |
|
62 // point in checking the full matrix... |
|
63 Q_ASSERT(a.type() < QTransform::TxProject); |
|
64 Q_ASSERT(b.type() < QTransform::TxProject); |
|
65 |
|
66 return a.m11() == b.m11() |
|
67 && a.m12() == b.m12() |
|
68 && a.m21() == b.m21() |
|
69 && a.m22() == b.m22(); |
|
70 } |
|
71 } |
|
72 |
|
73 // Harfbuzz helper functions |
|
74 |
|
75 static HB_Bool hb_stringToGlyphs(HB_Font font, const HB_UChar16 *string, hb_uint32 length, HB_Glyph *glyphs, hb_uint32 *numGlyphs, HB_Bool rightToLeft) |
|
76 { |
|
77 QFontEngine *fe = (QFontEngine *)font->userData; |
|
78 |
|
79 QVarLengthGlyphLayoutArray qglyphs(*numGlyphs); |
|
80 |
|
81 QTextEngine::ShaperFlags shaperFlags(QTextEngine::GlyphIndicesOnly); |
|
82 if (rightToLeft) |
|
83 shaperFlags |= QTextEngine::RightToLeft; |
|
84 |
|
85 int nGlyphs = *numGlyphs; |
|
86 bool result = fe->stringToCMap(reinterpret_cast<const QChar *>(string), length, &qglyphs, &nGlyphs, shaperFlags); |
|
87 *numGlyphs = nGlyphs; |
|
88 if (!result) |
|
89 return false; |
|
90 |
|
91 for (hb_uint32 i = 0; i < *numGlyphs; ++i) |
|
92 glyphs[i] = qglyphs.glyphs[i]; |
|
93 |
|
94 return true; |
|
95 } |
|
96 |
|
97 static void hb_getAdvances(HB_Font font, const HB_Glyph *glyphs, hb_uint32 numGlyphs, HB_Fixed *advances, int flags) |
|
98 { |
|
99 QFontEngine *fe = (QFontEngine *)font->userData; |
|
100 |
|
101 QVarLengthGlyphLayoutArray qglyphs(numGlyphs); |
|
102 |
|
103 for (hb_uint32 i = 0; i < numGlyphs; ++i) |
|
104 qglyphs.glyphs[i] = glyphs[i]; |
|
105 |
|
106 fe->recalcAdvances(&qglyphs, flags & HB_ShaperFlag_UseDesignMetrics ? QFlags<QTextEngine::ShaperFlag>(QTextEngine::DesignMetrics) : QFlags<QTextEngine::ShaperFlag>(0)); |
|
107 |
|
108 for (hb_uint32 i = 0; i < numGlyphs; ++i) |
|
109 advances[i] = qglyphs.advances_x[i].value(); |
|
110 } |
|
111 |
|
112 static HB_Bool hb_canRender(HB_Font font, const HB_UChar16 *string, hb_uint32 length) |
|
113 { |
|
114 QFontEngine *fe = (QFontEngine *)font->userData; |
|
115 return fe->canRender(reinterpret_cast<const QChar *>(string), length); |
|
116 } |
|
117 |
|
118 static void hb_getGlyphMetrics(HB_Font font, HB_Glyph glyph, HB_GlyphMetrics *metrics) |
|
119 { |
|
120 QFontEngine *fe = (QFontEngine *)font->userData; |
|
121 glyph_metrics_t m = fe->boundingBox(glyph); |
|
122 metrics->x = m.x.value(); |
|
123 metrics->y = m.y.value(); |
|
124 metrics->width = m.width.value(); |
|
125 metrics->height = m.height.value(); |
|
126 metrics->xOffset = m.xoff.value(); |
|
127 metrics->yOffset = m.yoff.value(); |
|
128 } |
|
129 |
|
130 static HB_Fixed hb_getFontMetric(HB_Font font, HB_FontMetric metric) |
|
131 { |
|
132 if (metric == HB_FontAscent) { |
|
133 QFontEngine *fe = (QFontEngine *)font->userData; |
|
134 return fe->ascent().value(); |
|
135 } |
|
136 return 0; |
|
137 } |
|
138 |
|
139 HB_Error QFontEngine::getPointInOutline(HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints) |
|
140 { |
|
141 Q_UNUSED(glyph) |
|
142 Q_UNUSED(flags) |
|
143 Q_UNUSED(point) |
|
144 Q_UNUSED(xpos) |
|
145 Q_UNUSED(ypos) |
|
146 Q_UNUSED(nPoints) |
|
147 return HB_Err_Not_Covered; |
|
148 } |
|
149 |
|
150 static HB_Error hb_getPointInOutline(HB_Font font, HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints) |
|
151 { |
|
152 QFontEngine *fe = (QFontEngine *)font->userData; |
|
153 return fe->getPointInOutline(glyph, flags, point, xpos, ypos, nPoints); |
|
154 } |
|
155 |
|
156 static const HB_FontClass hb_fontClass = { |
|
157 hb_stringToGlyphs, hb_getAdvances, hb_canRender, hb_getPointInOutline, |
|
158 hb_getGlyphMetrics, hb_getFontMetric |
|
159 }; |
|
160 |
|
161 static HB_Error hb_getSFntTable(void *font, HB_Tag tableTag, HB_Byte *buffer, HB_UInt *length) |
|
162 { |
|
163 QFontEngine *fe = (QFontEngine *)font; |
|
164 if (!fe->getSfntTableData(tableTag, buffer, length)) |
|
165 return HB_Err_Invalid_Argument; |
|
166 return HB_Err_Ok; |
|
167 } |
|
168 |
|
169 // QFontEngine |
|
170 |
|
171 QFontEngine::QFontEngine() |
|
172 : QObject() |
|
173 { |
|
174 ref = 0; |
|
175 cache_count = 0; |
|
176 fsType = 0; |
|
177 symbol = false; |
|
178 memset(&hbFont, 0, sizeof(hbFont)); |
|
179 hbFont.klass = &hb_fontClass; |
|
180 hbFont.userData = this; |
|
181 |
|
182 hbFace = 0; |
|
183 glyphFormat = -1; |
|
184 } |
|
185 |
|
186 QFontEngine::~QFontEngine() |
|
187 { |
|
188 for (GlyphPointerHash::const_iterator it = m_glyphPointerHash.constBegin(), |
|
189 end = m_glyphPointerHash.constEnd(); it != end; ++it) { |
|
190 for (QList<QFontEngineGlyphCache*>::const_iterator it2 = it.value().constBegin(), |
|
191 end2 = it.value().constEnd(); it2 != end2; ++it2) { |
|
192 delete *it2; |
|
193 } |
|
194 } |
|
195 m_glyphPointerHash.clear(); |
|
196 for (GlyphIntHash::const_iterator it = m_glyphIntHash.constBegin(), |
|
197 end = m_glyphIntHash.constEnd(); it != end; ++it) { |
|
198 for (QList<QFontEngineGlyphCache*>::const_iterator it2 = it.value().constBegin(), |
|
199 end2 = it.value().constEnd(); it2 != end2; ++it2) { |
|
200 delete *it2; |
|
201 } |
|
202 } |
|
203 m_glyphIntHash.clear(); |
|
204 qHBFreeFace(hbFace); |
|
205 } |
|
206 |
|
207 QFixed QFontEngine::lineThickness() const |
|
208 { |
|
209 // ad hoc algorithm |
|
210 int score = fontDef.weight * fontDef.pixelSize; |
|
211 int lw = score / 700; |
|
212 |
|
213 // looks better with thicker line for small pointsizes |
|
214 if (lw < 2 && score >= 1050) lw = 2; |
|
215 if (lw == 0) lw = 1; |
|
216 |
|
217 return lw; |
|
218 } |
|
219 |
|
220 QFixed QFontEngine::underlinePosition() const |
|
221 { |
|
222 return ((lineThickness() * 2) + 3) / 6; |
|
223 } |
|
224 |
|
225 HB_Font QFontEngine::harfbuzzFont() const |
|
226 { |
|
227 if (!hbFont.x_ppem) { |
|
228 QFixed emSquare = emSquareSize(); |
|
229 hbFont.x_ppem = fontDef.pixelSize; |
|
230 hbFont.y_ppem = fontDef.pixelSize * fontDef.stretch / 100; |
|
231 hbFont.x_scale = (QFixed(hbFont.x_ppem * (1 << 16)) / emSquare).value(); |
|
232 hbFont.y_scale = (QFixed(hbFont.y_ppem * (1 << 16)) / emSquare).value(); |
|
233 } |
|
234 return &hbFont; |
|
235 } |
|
236 |
|
237 HB_Face QFontEngine::harfbuzzFace() const |
|
238 { |
|
239 if (!hbFace) { |
|
240 hbFace = qHBNewFace(const_cast<QFontEngine *>(this), hb_getSFntTable); |
|
241 Q_CHECK_PTR(hbFace); |
|
242 } |
|
243 return hbFace; |
|
244 } |
|
245 |
|
246 glyph_metrics_t QFontEngine::boundingBox(glyph_t glyph, const QTransform &matrix) |
|
247 { |
|
248 glyph_metrics_t metrics = boundingBox(glyph); |
|
249 |
|
250 if (matrix.type() > QTransform::TxTranslate) { |
|
251 return metrics.transformed(matrix); |
|
252 } |
|
253 return metrics; |
|
254 } |
|
255 |
|
256 QFixed QFontEngine::xHeight() const |
|
257 { |
|
258 QGlyphLayoutArray<8> glyphs; |
|
259 int nglyphs = 7; |
|
260 QChar x((ushort)'x'); |
|
261 stringToCMap(&x, 1, &glyphs, &nglyphs, QTextEngine::GlyphIndicesOnly); |
|
262 |
|
263 glyph_metrics_t bb = const_cast<QFontEngine *>(this)->boundingBox(glyphs.glyphs[0]); |
|
264 return bb.height; |
|
265 } |
|
266 |
|
267 QFixed QFontEngine::averageCharWidth() const |
|
268 { |
|
269 QGlyphLayoutArray<8> glyphs; |
|
270 int nglyphs = 7; |
|
271 QChar x((ushort)'x'); |
|
272 stringToCMap(&x, 1, &glyphs, &nglyphs, QTextEngine::GlyphIndicesOnly); |
|
273 |
|
274 glyph_metrics_t bb = const_cast<QFontEngine *>(this)->boundingBox(glyphs.glyphs[0]); |
|
275 return bb.xoff; |
|
276 } |
|
277 |
|
278 |
|
279 void QFontEngine::getGlyphPositions(const QGlyphLayout &glyphs, const QTransform &matrix, QTextItem::RenderFlags flags, |
|
280 QVarLengthArray<glyph_t> &glyphs_out, QVarLengthArray<QFixedPoint> &positions) |
|
281 { |
|
282 QFixed xpos; |
|
283 QFixed ypos; |
|
284 |
|
285 const bool transform = matrix.m11() != 1. |
|
286 || matrix.m12() != 0. |
|
287 || matrix.m21() != 0. |
|
288 || matrix.m22() != 1.; |
|
289 if (!transform) { |
|
290 xpos = QFixed::fromReal(matrix.dx()); |
|
291 ypos = QFixed::fromReal(matrix.dy()); |
|
292 } |
|
293 |
|
294 int current = 0; |
|
295 if (flags & QTextItem::RightToLeft) { |
|
296 int i = glyphs.numGlyphs; |
|
297 int totalKashidas = 0; |
|
298 while(i--) { |
|
299 xpos += glyphs.advances_x[i] + QFixed::fromFixed(glyphs.justifications[i].space_18d6); |
|
300 ypos += glyphs.advances_y[i]; |
|
301 totalKashidas += glyphs.justifications[i].nKashidas; |
|
302 } |
|
303 positions.resize(glyphs.numGlyphs+totalKashidas); |
|
304 glyphs_out.resize(glyphs.numGlyphs+totalKashidas); |
|
305 |
|
306 i = 0; |
|
307 while(i < glyphs.numGlyphs) { |
|
308 if (glyphs.attributes[i].dontPrint) { |
|
309 ++i; |
|
310 continue; |
|
311 } |
|
312 xpos -= glyphs.advances_x[i]; |
|
313 ypos -= glyphs.advances_y[i]; |
|
314 |
|
315 QFixed gpos_x = xpos + glyphs.offsets[i].x; |
|
316 QFixed gpos_y = ypos + glyphs.offsets[i].y; |
|
317 if (transform) { |
|
318 QPointF gpos(gpos_x.toReal(), gpos_y.toReal()); |
|
319 gpos = gpos * matrix; |
|
320 gpos_x = QFixed::fromReal(gpos.x()); |
|
321 gpos_y = QFixed::fromReal(gpos.y()); |
|
322 } |
|
323 positions[current].x = gpos_x; |
|
324 positions[current].y = gpos_y; |
|
325 glyphs_out[current] = glyphs.glyphs[i]; |
|
326 ++current; |
|
327 if (glyphs.justifications[i].nKashidas) { |
|
328 QChar ch(0x640); // Kashida character |
|
329 QGlyphLayoutArray<8> g; |
|
330 int nglyphs = 7; |
|
331 stringToCMap(&ch, 1, &g, &nglyphs, 0); |
|
332 for (uint k = 0; k < glyphs.justifications[i].nKashidas; ++k) { |
|
333 xpos -= g.advances_x[0]; |
|
334 ypos -= g.advances_y[0]; |
|
335 |
|
336 QFixed gpos_x = xpos + glyphs.offsets[i].x; |
|
337 QFixed gpos_y = ypos + glyphs.offsets[i].y; |
|
338 if (transform) { |
|
339 QPointF gpos(gpos_x.toReal(), gpos_y.toReal()); |
|
340 gpos = gpos * matrix; |
|
341 gpos_x = QFixed::fromReal(gpos.x()); |
|
342 gpos_y = QFixed::fromReal(gpos.y()); |
|
343 } |
|
344 positions[current].x = gpos_x; |
|
345 positions[current].y = gpos_y; |
|
346 glyphs_out[current] = g.glyphs[0]; |
|
347 ++current; |
|
348 } |
|
349 } else { |
|
350 xpos -= QFixed::fromFixed(glyphs.justifications[i].space_18d6); |
|
351 } |
|
352 ++i; |
|
353 } |
|
354 } else { |
|
355 positions.resize(glyphs.numGlyphs); |
|
356 glyphs_out.resize(glyphs.numGlyphs); |
|
357 int i = 0; |
|
358 if (!transform) { |
|
359 while (i < glyphs.numGlyphs) { |
|
360 if (!glyphs.attributes[i].dontPrint) { |
|
361 positions[current].x = xpos + glyphs.offsets[i].x; |
|
362 positions[current].y = ypos + glyphs.offsets[i].y; |
|
363 glyphs_out[current] = glyphs.glyphs[i]; |
|
364 xpos += glyphs.advances_x[i] + QFixed::fromFixed(glyphs.justifications[i].space_18d6); |
|
365 ypos += glyphs.advances_y[i]; |
|
366 ++current; |
|
367 } |
|
368 ++i; |
|
369 } |
|
370 } else { |
|
371 positions.resize(glyphs.numGlyphs); |
|
372 glyphs_out.resize(glyphs.numGlyphs); |
|
373 int i = 0; |
|
374 while (i < glyphs.numGlyphs) { |
|
375 if (!glyphs.attributes[i].dontPrint) { |
|
376 QFixed gpos_x = xpos + glyphs.offsets[i].x; |
|
377 QFixed gpos_y = ypos + glyphs.offsets[i].y; |
|
378 QPointF gpos(gpos_x.toReal(), gpos_y.toReal()); |
|
379 gpos = gpos * matrix; |
|
380 positions[current].x = QFixed::fromReal(gpos.x()); |
|
381 positions[current].y = QFixed::fromReal(gpos.y()); |
|
382 glyphs_out[current] = glyphs.glyphs[i]; |
|
383 xpos += glyphs.advances_x[i] + QFixed::fromFixed(glyphs.justifications[i].space_18d6); |
|
384 ypos += glyphs.advances_y[i]; |
|
385 ++current; |
|
386 } |
|
387 ++i; |
|
388 } |
|
389 } |
|
390 } |
|
391 positions.resize(current); |
|
392 glyphs_out.resize(current); |
|
393 Q_ASSERT(positions.size() == glyphs_out.size()); |
|
394 } |
|
395 |
|
396 |
|
397 glyph_metrics_t QFontEngine::tightBoundingBox(const QGlyphLayout &glyphs) |
|
398 { |
|
399 glyph_metrics_t overall; |
|
400 |
|
401 QFixed ymax = 0; |
|
402 QFixed xmax = 0; |
|
403 for (int i = 0; i < glyphs.numGlyphs; i++) { |
|
404 glyph_metrics_t bb = boundingBox(glyphs.glyphs[i]); |
|
405 QFixed x = overall.xoff + glyphs.offsets[i].x + bb.x; |
|
406 QFixed y = overall.yoff + glyphs.offsets[i].y + bb.y; |
|
407 overall.x = qMin(overall.x, x); |
|
408 overall.y = qMin(overall.y, y); |
|
409 xmax = qMax(xmax, x + bb.width); |
|
410 ymax = qMax(ymax, y + bb.height); |
|
411 overall.xoff += bb.xoff; |
|
412 overall.yoff += bb.yoff; |
|
413 } |
|
414 overall.height = qMax(overall.height, ymax - overall.y); |
|
415 overall.width = xmax - overall.x; |
|
416 |
|
417 return overall; |
|
418 } |
|
419 |
|
420 |
|
421 void QFontEngine::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, |
|
422 QTextItem::RenderFlags flags) |
|
423 { |
|
424 if (!glyphs.numGlyphs) |
|
425 return; |
|
426 |
|
427 QVarLengthArray<QFixedPoint> positions; |
|
428 QVarLengthArray<glyph_t> positioned_glyphs; |
|
429 QTransform matrix = QTransform::fromTranslate(x, y); |
|
430 getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions); |
|
431 addGlyphsToPath(positioned_glyphs.data(), positions.data(), positioned_glyphs.size(), path, flags); |
|
432 } |
|
433 |
|
434 #define GRID(x, y) grid[(y)*(w+1) + (x)] |
|
435 #define SET(x, y) (*(image_data + (y)*bpl + ((x) >> 3)) & (0x80 >> ((x) & 7))) |
|
436 |
|
437 enum { EdgeRight = 0x1, |
|
438 EdgeDown = 0x2, |
|
439 EdgeLeft = 0x4, |
|
440 EdgeUp = 0x8 |
|
441 }; |
|
442 |
|
443 static void collectSingleContour(qreal x0, qreal y0, uint *grid, int x, int y, int w, int h, QPainterPath *path) |
|
444 { |
|
445 Q_UNUSED(h); |
|
446 |
|
447 path->moveTo(x + x0, y + y0); |
|
448 while (GRID(x, y)) { |
|
449 if (GRID(x, y) & EdgeRight) { |
|
450 while (GRID(x, y) & EdgeRight) { |
|
451 GRID(x, y) &= ~EdgeRight; |
|
452 ++x; |
|
453 } |
|
454 Q_ASSERT(x <= w); |
|
455 path->lineTo(x + x0, y + y0); |
|
456 continue; |
|
457 } |
|
458 if (GRID(x, y) & EdgeDown) { |
|
459 while (GRID(x, y) & EdgeDown) { |
|
460 GRID(x, y) &= ~EdgeDown; |
|
461 ++y; |
|
462 } |
|
463 Q_ASSERT(y <= h); |
|
464 path->lineTo(x + x0, y + y0); |
|
465 continue; |
|
466 } |
|
467 if (GRID(x, y) & EdgeLeft) { |
|
468 while (GRID(x, y) & EdgeLeft) { |
|
469 GRID(x, y) &= ~EdgeLeft; |
|
470 --x; |
|
471 } |
|
472 Q_ASSERT(x >= 0); |
|
473 path->lineTo(x + x0, y + y0); |
|
474 continue; |
|
475 } |
|
476 if (GRID(x, y) & EdgeUp) { |
|
477 while (GRID(x, y) & EdgeUp) { |
|
478 GRID(x, y) &= ~EdgeUp; |
|
479 --y; |
|
480 } |
|
481 Q_ASSERT(y >= 0); |
|
482 path->lineTo(x + x0, y + y0); |
|
483 continue; |
|
484 } |
|
485 } |
|
486 path->closeSubpath(); |
|
487 } |
|
488 |
|
489 void qt_addBitmapToPath(qreal x0, qreal y0, const uchar *image_data, int bpl, int w, int h, QPainterPath *path) |
|
490 { |
|
491 uint *grid = new uint[(w+1)*(h+1)]; |
|
492 // set up edges |
|
493 for (int y = 0; y <= h; ++y) { |
|
494 for (int x = 0; x <= w; ++x) { |
|
495 bool topLeft = (x == 0)|(y == 0) ? false : SET(x - 1, y - 1); |
|
496 bool topRight = (x == w)|(y == 0) ? false : SET(x, y - 1); |
|
497 bool bottomLeft = (x == 0)|(y == h) ? false : SET(x - 1, y); |
|
498 bool bottomRight = (x == w)|(y == h) ? false : SET(x, y); |
|
499 |
|
500 GRID(x, y) = 0; |
|
501 if ((!topRight) & bottomRight) |
|
502 GRID(x, y) |= EdgeRight; |
|
503 if ((!bottomRight) & bottomLeft) |
|
504 GRID(x, y) |= EdgeDown; |
|
505 if ((!bottomLeft) & topLeft) |
|
506 GRID(x, y) |= EdgeLeft; |
|
507 if ((!topLeft) & topRight) |
|
508 GRID(x, y) |= EdgeUp; |
|
509 } |
|
510 } |
|
511 |
|
512 // collect edges |
|
513 for (int y = 0; y < h; ++y) { |
|
514 for (int x = 0; x < w; ++x) { |
|
515 if (!GRID(x, y)) |
|
516 continue; |
|
517 // found start of a contour, follow it |
|
518 collectSingleContour(x0, y0, grid, x, y, w, h, path); |
|
519 } |
|
520 } |
|
521 delete [] grid; |
|
522 } |
|
523 |
|
524 #undef GRID |
|
525 #undef SET |
|
526 |
|
527 |
|
528 void QFontEngine::addBitmapFontToPath(qreal x, qreal y, const QGlyphLayout &glyphs, |
|
529 QPainterPath *path, QTextItem::RenderFlags flags) |
|
530 { |
|
531 // TODO what to do with 'flags' ?? |
|
532 Q_UNUSED(flags); |
|
533 QFixed advanceX = QFixed::fromReal(x); |
|
534 QFixed advanceY = QFixed::fromReal(y); |
|
535 for (int i=0; i < glyphs.numGlyphs; ++i) { |
|
536 glyph_metrics_t metrics = boundingBox(glyphs.glyphs[i]); |
|
537 if (metrics.width.value() == 0 || metrics.height.value() == 0) { |
|
538 advanceX += glyphs.advances_x[i]; |
|
539 advanceY += glyphs.advances_y[i]; |
|
540 continue; |
|
541 } |
|
542 const QImage alphaMask = alphaMapForGlyph(glyphs.glyphs[i]); |
|
543 |
|
544 const int w = alphaMask.width(); |
|
545 const int h = alphaMask.height(); |
|
546 const int srcBpl = alphaMask.bytesPerLine(); |
|
547 QImage bitmap; |
|
548 if (alphaMask.depth() == 1) { |
|
549 bitmap = alphaMask; |
|
550 } else { |
|
551 bitmap = QImage(w, h, QImage::Format_Mono); |
|
552 const uchar *imageData = alphaMask.bits(); |
|
553 const int destBpl = bitmap.bytesPerLine(); |
|
554 uchar *bitmapData = bitmap.bits(); |
|
555 |
|
556 for (int yi = 0; yi < h; ++yi) { |
|
557 const uchar *src = imageData + yi*srcBpl; |
|
558 uchar *dst = bitmapData + yi*destBpl; |
|
559 for (int xi = 0; xi < w; ++xi) { |
|
560 const int byte = xi / 8; |
|
561 const int bit = xi % 8; |
|
562 if (bit == 0) |
|
563 dst[byte] = 0; |
|
564 if (src[xi]) |
|
565 dst[byte] |= 128 >> bit; |
|
566 } |
|
567 } |
|
568 } |
|
569 const uchar *bitmap_data = bitmap.bits(); |
|
570 QFixedPoint offset = glyphs.offsets[i]; |
|
571 advanceX += offset.x; |
|
572 advanceY += offset.y; |
|
573 qt_addBitmapToPath((advanceX + metrics.x).toReal(), (advanceY + metrics.y).toReal(), bitmap_data, bitmap.bytesPerLine(), w, h, path); |
|
574 advanceX += glyphs.advances_x[i]; |
|
575 advanceY += glyphs.advances_y[i]; |
|
576 } |
|
577 } |
|
578 |
|
579 void QFontEngine::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nGlyphs, |
|
580 QPainterPath *path, QTextItem::RenderFlags flags) |
|
581 { |
|
582 qreal x = positions[0].x.toReal(); |
|
583 qreal y = positions[0].y.toReal(); |
|
584 QVarLengthGlyphLayoutArray g(nGlyphs); |
|
585 |
|
586 for (int i = 0; i < nGlyphs; ++i) { |
|
587 g.glyphs[i] = glyphs[i]; |
|
588 if (i < nGlyphs - 1) { |
|
589 g.advances_x[i] = positions[i+1].x - positions[i].x; |
|
590 g.advances_y[i] = positions[i+1].y - positions[i].y; |
|
591 } else { |
|
592 g.advances_x[i] = QFixed::fromReal(maxCharWidth()); |
|
593 g.advances_y[i] = 0; |
|
594 } |
|
595 } |
|
596 |
|
597 addBitmapFontToPath(x, y, g, path, flags); |
|
598 } |
|
599 |
|
600 QImage QFontEngine::alphaMapForGlyph(glyph_t glyph, const QTransform &t) |
|
601 { |
|
602 QImage i = alphaMapForGlyph(glyph); |
|
603 if (t.type() > QTransform::TxTranslate) |
|
604 i = i.transformed(t); |
|
605 Q_ASSERT(i.depth() <= 8); // To verify that transformed didn't change the format... |
|
606 return i; |
|
607 } |
|
608 |
|
609 QImage QFontEngine::alphaRGBMapForGlyph(glyph_t glyph, int /* margin */, const QTransform &t) |
|
610 { |
|
611 QImage alphaMask = alphaMapForGlyph(glyph, t); |
|
612 QImage rgbMask(alphaMask.width(), alphaMask.height(), QImage::Format_RGB32); |
|
613 |
|
614 for (int y=0; y<alphaMask.height(); ++y) { |
|
615 uint *dst = (uint *) rgbMask.scanLine(y); |
|
616 uchar *src = (uchar *) alphaMask.scanLine(y); |
|
617 for (int x=0; x<alphaMask.width(); ++x) |
|
618 dst[x] = qRgb(src[x], src[x], src[x]); |
|
619 } |
|
620 |
|
621 return rgbMask; |
|
622 } |
|
623 |
|
624 QImage QFontEngine::alphaMapForGlyph(glyph_t glyph) |
|
625 { |
|
626 glyph_metrics_t gm = boundingBox(glyph); |
|
627 int glyph_x = qFloor(gm.x.toReal()); |
|
628 int glyph_y = qFloor(gm.y.toReal()); |
|
629 int glyph_width = qCeil((gm.x + gm.width).toReal()) - glyph_x; |
|
630 int glyph_height = qCeil((gm.y + gm.height).toReal()) - glyph_y; |
|
631 |
|
632 if (glyph_width <= 0 || glyph_height <= 0) |
|
633 return QImage(); |
|
634 QFixedPoint pt; |
|
635 pt.x = 0; |
|
636 pt.y = -glyph_y; // the baseline |
|
637 QPainterPath path; |
|
638 QImage im(glyph_width + qAbs(glyph_x) + 4, glyph_height, QImage::Format_ARGB32_Premultiplied); |
|
639 im.fill(Qt::transparent); |
|
640 QPainter p(&im); |
|
641 p.setRenderHint(QPainter::Antialiasing); |
|
642 addGlyphsToPath(&glyph, &pt, 1, &path, 0); |
|
643 p.setPen(Qt::NoPen); |
|
644 p.setBrush(Qt::black); |
|
645 p.drawPath(path); |
|
646 p.end(); |
|
647 |
|
648 QImage indexed(im.width(), im.height(), QImage::Format_Indexed8); |
|
649 QVector<QRgb> colors(256); |
|
650 for (int i=0; i<256; ++i) |
|
651 colors[i] = qRgba(0, 0, 0, i); |
|
652 indexed.setColorTable(colors); |
|
653 |
|
654 for (int y=0; y<im.height(); ++y) { |
|
655 uchar *dst = (uchar *) indexed.scanLine(y); |
|
656 uint *src = (uint *) im.scanLine(y); |
|
657 for (int x=0; x<im.width(); ++x) |
|
658 dst[x] = qAlpha(src[x]); |
|
659 } |
|
660 |
|
661 return indexed; |
|
662 } |
|
663 |
|
664 void QFontEngine::removeGlyphFromCache(glyph_t) |
|
665 { |
|
666 } |
|
667 |
|
668 QFontEngine::Properties QFontEngine::properties() const |
|
669 { |
|
670 Properties p; |
|
671 #ifndef QT_NO_PRINTER |
|
672 QByteArray psname = QPdf::stripSpecialCharacters(fontDef.family.toUtf8()); |
|
673 #else |
|
674 QByteArray psname = fontDef.family.toUtf8(); |
|
675 #endif |
|
676 psname += '-'; |
|
677 psname += QByteArray::number(fontDef.style); |
|
678 psname += '-'; |
|
679 psname += QByteArray::number(fontDef.weight); |
|
680 |
|
681 p.postscriptName = psname; |
|
682 p.ascent = ascent(); |
|
683 p.descent = descent(); |
|
684 p.leading = leading(); |
|
685 p.emSquare = p.ascent; |
|
686 p.boundingBox = QRectF(0, -p.ascent.toReal(), maxCharWidth(), (p.ascent + p.descent).toReal()); |
|
687 p.italicAngle = 0; |
|
688 p.capHeight = p.ascent; |
|
689 p.lineWidth = lineThickness(); |
|
690 return p; |
|
691 } |
|
692 |
|
693 void QFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) |
|
694 { |
|
695 *metrics = boundingBox(glyph); |
|
696 QFixedPoint p; |
|
697 p.x = 0; |
|
698 p.y = 0; |
|
699 addGlyphsToPath(&glyph, &p, 1, path, QFlag(0)); |
|
700 } |
|
701 |
|
702 QByteArray QFontEngine::getSfntTable(uint tag) const |
|
703 { |
|
704 QByteArray table; |
|
705 uint len = 0; |
|
706 if (!getSfntTableData(tag, 0, &len)) |
|
707 return table; |
|
708 if (!len) |
|
709 return table; |
|
710 table.resize(len); |
|
711 if (!getSfntTableData(tag, reinterpret_cast<uchar *>(table.data()), &len)) |
|
712 return QByteArray(); |
|
713 return table; |
|
714 } |
|
715 |
|
716 void QFontEngine::expireGlyphCache() |
|
717 { |
|
718 if (m_glyphCacheQueue.count() > 10) { // hold only 10 caches in memory. |
|
719 QFontEngineGlyphCache *old = m_glyphCacheQueue.takeFirst(); |
|
720 // remove the value from either of our hashes |
|
721 for (GlyphPointerHash::iterator i = m_glyphPointerHash.begin(); i != m_glyphPointerHash.end(); ++i) { |
|
722 QList<QFontEngineGlyphCache *> list = i.value(); |
|
723 if (list.removeAll(old)) { |
|
724 if (list.isEmpty()) |
|
725 m_glyphPointerHash.remove(i.key()); |
|
726 else |
|
727 m_glyphPointerHash.insert(i.key(), list); |
|
728 break; |
|
729 } |
|
730 } |
|
731 for (GlyphIntHash::iterator i = m_glyphIntHash.begin(); i != m_glyphIntHash.end(); ++i) { |
|
732 QList<QFontEngineGlyphCache *> list = i.value(); |
|
733 if (list.removeAll(old)) { |
|
734 if (list.isEmpty()) |
|
735 m_glyphIntHash.remove(i.key()); |
|
736 else |
|
737 m_glyphIntHash.insert(i.key(), list); |
|
738 break; |
|
739 } |
|
740 } |
|
741 delete old; |
|
742 } |
|
743 } |
|
744 |
|
745 void QFontEngine::setGlyphCache(void *key, QFontEngineGlyphCache *data) |
|
746 { |
|
747 Q_ASSERT(data); |
|
748 QList<QFontEngineGlyphCache*> items = m_glyphPointerHash.value(key); |
|
749 |
|
750 for (QList<QFontEngineGlyphCache*>::iterator it = items.begin(), end = items.end(); it != end; ++it) { |
|
751 QFontEngineGlyphCache *c = *it; |
|
752 if (qtransform_equals_no_translate(c->m_transform, data->m_transform)) { |
|
753 if (c == data) |
|
754 return; |
|
755 items.removeAll(c); |
|
756 delete c; |
|
757 break; |
|
758 } |
|
759 } |
|
760 items.append(data); |
|
761 m_glyphPointerHash.insert(key, items); |
|
762 |
|
763 m_glyphCacheQueue.append(data); |
|
764 expireGlyphCache(); |
|
765 } |
|
766 |
|
767 void QFontEngine::setGlyphCache(QFontEngineGlyphCache::Type key, QFontEngineGlyphCache *data) |
|
768 { |
|
769 Q_ASSERT(data); |
|
770 QList<QFontEngineGlyphCache*> items = m_glyphIntHash.value(key); |
|
771 |
|
772 for (QList<QFontEngineGlyphCache*>::iterator it = items.begin(), end = items.end(); it != end; ++it) { |
|
773 QFontEngineGlyphCache *c = *it; |
|
774 if (qtransform_equals_no_translate(c->m_transform, data->m_transform)) { |
|
775 if (c == data) |
|
776 return; |
|
777 items.removeAll(c); |
|
778 delete c; |
|
779 break; |
|
780 } |
|
781 } |
|
782 items.append(data); |
|
783 m_glyphIntHash.insert(key, items); |
|
784 |
|
785 m_glyphCacheQueue.append(data); |
|
786 expireGlyphCache(); |
|
787 } |
|
788 |
|
789 QFontEngineGlyphCache *QFontEngine::glyphCache(void *key, const QTransform &transform) const |
|
790 { |
|
791 QList<QFontEngineGlyphCache*> items = m_glyphPointerHash.value(key); |
|
792 |
|
793 for (QList<QFontEngineGlyphCache*>::iterator it = items.begin(), end = items.end(); it != end; ++it) { |
|
794 QFontEngineGlyphCache *c = *it; |
|
795 if (qtransform_equals_no_translate(c->m_transform, transform)) { |
|
796 m_glyphCacheQueue.removeAll(c); // last used, move it up |
|
797 m_glyphCacheQueue.append(c); |
|
798 return c; |
|
799 } |
|
800 } |
|
801 return 0; |
|
802 } |
|
803 |
|
804 QFontEngineGlyphCache *QFontEngine::glyphCache(QFontEngineGlyphCache::Type key, const QTransform &transform) const |
|
805 { |
|
806 QList<QFontEngineGlyphCache*> items = m_glyphIntHash.value(key); |
|
807 |
|
808 for (QList<QFontEngineGlyphCache*>::iterator it = items.begin(), end = items.end(); it != end; ++it) { |
|
809 QFontEngineGlyphCache *c = *it; |
|
810 if (qtransform_equals_no_translate(c->m_transform, transform)) { |
|
811 m_glyphCacheQueue.removeAll(c); // last used, move it up |
|
812 m_glyphCacheQueue.append(c); |
|
813 return c; |
|
814 } |
|
815 } |
|
816 return 0; |
|
817 } |
|
818 |
|
819 #if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN) |
|
820 static inline QFixed kerning(int left, int right, const QFontEngine::KernPair *pairs, int numPairs) |
|
821 { |
|
822 uint left_right = (left << 16) + right; |
|
823 |
|
824 left = 0, right = numPairs - 1; |
|
825 while (left <= right) { |
|
826 int middle = left + ( ( right - left ) >> 1 ); |
|
827 |
|
828 if(pairs[middle].left_right == left_right) |
|
829 return pairs[middle].adjust; |
|
830 |
|
831 if (pairs[middle].left_right < left_right) |
|
832 left = middle + 1; |
|
833 else |
|
834 right = middle - 1; |
|
835 } |
|
836 return 0; |
|
837 } |
|
838 |
|
839 void QFontEngine::doKerning(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const |
|
840 { |
|
841 int numPairs = kerning_pairs.size(); |
|
842 if(!numPairs) |
|
843 return; |
|
844 |
|
845 const KernPair *pairs = kerning_pairs.constData(); |
|
846 |
|
847 if(flags & QTextEngine::DesignMetrics) { |
|
848 for(int i = 0; i < glyphs->numGlyphs - 1; ++i) |
|
849 glyphs->advances_x[i] += kerning(glyphs->glyphs[i], glyphs->glyphs[i+1] , pairs, numPairs); |
|
850 } else { |
|
851 for(int i = 0; i < glyphs->numGlyphs - 1; ++i) |
|
852 glyphs->advances_x[i] += qRound(kerning(glyphs->glyphs[i], glyphs->glyphs[i+1] , pairs, numPairs)); |
|
853 } |
|
854 } |
|
855 |
|
856 void QFontEngine::loadKerningPairs(QFixed scalingFactor) |
|
857 { |
|
858 kerning_pairs.clear(); |
|
859 |
|
860 QByteArray tab = getSfntTable(MAKE_TAG('k', 'e', 'r', 'n')); |
|
861 if (tab.isEmpty()) |
|
862 return; |
|
863 |
|
864 const uchar *table = reinterpret_cast<const uchar *>(tab.constData()); |
|
865 |
|
866 unsigned short version = qFromBigEndian<quint16>(table); |
|
867 if (version != 0) { |
|
868 // qDebug("wrong version"); |
|
869 return; |
|
870 } |
|
871 |
|
872 unsigned short numTables = qFromBigEndian<quint16>(table + 2); |
|
873 { |
|
874 int offset = 4; |
|
875 for(int i = 0; i < numTables; ++i) { |
|
876 if (offset + 6 > tab.size()) { |
|
877 // qDebug("offset out of bounds"); |
|
878 goto end; |
|
879 } |
|
880 const uchar *header = table + offset; |
|
881 |
|
882 ushort version = qFromBigEndian<quint16>(header); |
|
883 ushort length = qFromBigEndian<quint16>(header+2); |
|
884 ushort coverage = qFromBigEndian<quint16>(header+4); |
|
885 // qDebug("subtable: version=%d, coverage=%x",version, coverage); |
|
886 if(version == 0 && coverage == 0x0001) { |
|
887 if (offset + length > tab.size()) { |
|
888 // qDebug("length ouf ot bounds"); |
|
889 goto end; |
|
890 } |
|
891 const uchar *data = table + offset + 6; |
|
892 |
|
893 ushort nPairs = qFromBigEndian<quint16>(data); |
|
894 if(nPairs * 6 + 8 > length - 6) { |
|
895 // qDebug("corrupt table!"); |
|
896 // corrupt table |
|
897 goto end; |
|
898 } |
|
899 |
|
900 int off = 8; |
|
901 for(int i = 0; i < nPairs; ++i) { |
|
902 QFontEngine::KernPair p; |
|
903 p.left_right = (((uint)qFromBigEndian<quint16>(data+off)) << 16) + qFromBigEndian<quint16>(data+off+2); |
|
904 p.adjust = QFixed(((int)(short)qFromBigEndian<quint16>(data+off+4))) / scalingFactor; |
|
905 kerning_pairs.append(p); |
|
906 off += 6; |
|
907 } |
|
908 } |
|
909 offset += length; |
|
910 } |
|
911 } |
|
912 end: |
|
913 qSort(kerning_pairs); |
|
914 // for (int i = 0; i < kerning_pairs.count(); ++i) |
|
915 // qDebug() << 'i' << i << "left_right" << hex << kerning_pairs.at(i).left_right; |
|
916 } |
|
917 |
|
918 #else |
|
919 void QFontEngine::doKerning(QGlyphLayout *, QTextEngine::ShaperFlags) const |
|
920 { |
|
921 } |
|
922 #endif |
|
923 |
|
924 int QFontEngine::glyphCount() const |
|
925 { |
|
926 QByteArray maxpTable = getSfntTable(MAKE_TAG('m', 'a', 'x', 'p')); |
|
927 if (maxpTable.size() < 6) |
|
928 return 0; |
|
929 return qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(maxpTable.constData() + 4)); |
|
930 } |
|
931 |
|
932 const uchar *QFontEngine::getCMap(const uchar *table, uint tableSize, bool *isSymbolFont, int *cmapSize) |
|
933 { |
|
934 const uchar *header = table; |
|
935 if (tableSize < 4) |
|
936 return 0; |
|
937 |
|
938 const uchar *endPtr = table + tableSize; |
|
939 |
|
940 // version check |
|
941 if (qFromBigEndian<quint16>(header) != 0) |
|
942 return 0; |
|
943 |
|
944 unsigned short numTables = qFromBigEndian<quint16>(header + 2); |
|
945 const uchar *maps = table + 4; |
|
946 if (maps + 8 * numTables > endPtr) |
|
947 return 0; |
|
948 |
|
949 enum { |
|
950 Invalid, |
|
951 Symbol, |
|
952 AppleRoman, |
|
953 Unicode11, |
|
954 Unicode, |
|
955 MicrosoftUnicode, |
|
956 MicrosoftUnicodeExtended |
|
957 }; |
|
958 |
|
959 int symbolTable = -1; |
|
960 int tableToUse = -1; |
|
961 int score = Invalid; |
|
962 for (int n = 0; n < numTables; ++n) { |
|
963 const quint16 platformId = qFromBigEndian<quint16>(maps + 8 * n); |
|
964 const quint16 platformSpecificId = qFromBigEndian<quint16>(maps + 8 * n + 2); |
|
965 switch (platformId) { |
|
966 case 0: // Unicode |
|
967 if (score < Unicode && |
|
968 (platformSpecificId == 0 || |
|
969 platformSpecificId == 2 || |
|
970 platformSpecificId == 3)) { |
|
971 tableToUse = n; |
|
972 score = Unicode; |
|
973 } else if (score < Unicode11 && platformSpecificId == 1) { |
|
974 tableToUse = n; |
|
975 score = Unicode11; |
|
976 } |
|
977 break; |
|
978 case 1: // Apple |
|
979 if (score < AppleRoman && platformSpecificId == 0) { // Apple Roman |
|
980 tableToUse = n; |
|
981 score = AppleRoman; |
|
982 } |
|
983 break; |
|
984 case 3: // Microsoft |
|
985 switch (platformSpecificId) { |
|
986 case 0: |
|
987 symbolTable = n; |
|
988 if (score < Symbol) { |
|
989 tableToUse = n; |
|
990 score = Symbol; |
|
991 } |
|
992 break; |
|
993 case 1: |
|
994 if (score < MicrosoftUnicode) { |
|
995 tableToUse = n; |
|
996 score = MicrosoftUnicode; |
|
997 } |
|
998 break; |
|
999 case 0xa: |
|
1000 if (score < MicrosoftUnicodeExtended) { |
|
1001 tableToUse = n; |
|
1002 score = MicrosoftUnicodeExtended; |
|
1003 } |
|
1004 break; |
|
1005 default: |
|
1006 break; |
|
1007 } |
|
1008 default: |
|
1009 break; |
|
1010 } |
|
1011 } |
|
1012 if(tableToUse < 0) |
|
1013 return 0; |
|
1014 |
|
1015 resolveTable: |
|
1016 *isSymbolFont = (score == Symbol); |
|
1017 |
|
1018 unsigned int unicode_table = qFromBigEndian<quint32>(maps + 8*tableToUse + 4); |
|
1019 |
|
1020 if (!unicode_table || unicode_table + 8 > tableSize) |
|
1021 return 0; |
|
1022 |
|
1023 // get the header of the unicode table |
|
1024 header = table + unicode_table; |
|
1025 |
|
1026 unsigned short format = qFromBigEndian<quint16>(header); |
|
1027 unsigned int length; |
|
1028 if(format < 8) |
|
1029 length = qFromBigEndian<quint16>(header + 2); |
|
1030 else |
|
1031 length = qFromBigEndian<quint32>(header + 4); |
|
1032 |
|
1033 if (table + unicode_table + length > endPtr) |
|
1034 return 0; |
|
1035 *cmapSize = length; |
|
1036 |
|
1037 // To support symbol fonts that contain a unicode table for the symbol area |
|
1038 // we check the cmap tables and fall back to symbol font unless that would |
|
1039 // involve losing information from the unicode table |
|
1040 if (symbolTable > -1 && ((score == Unicode) || (score == Unicode11))) { |
|
1041 const uchar *selectedTable = table + unicode_table; |
|
1042 |
|
1043 // Check that none of the latin1 range are in the unicode table |
|
1044 bool unicodeTableHasLatin1 = false; |
|
1045 for (int uc=0x00; uc<0x100; ++uc) { |
|
1046 if (getTrueTypeGlyphIndex(selectedTable, uc) != 0) { |
|
1047 unicodeTableHasLatin1 = true; |
|
1048 break; |
|
1049 } |
|
1050 } |
|
1051 |
|
1052 // Check that at least one symbol char is in the unicode table |
|
1053 bool unicodeTableHasSymbols = false; |
|
1054 if (!unicodeTableHasLatin1) { |
|
1055 for (int uc=0xf000; uc<0xf100; ++uc) { |
|
1056 if (getTrueTypeGlyphIndex(selectedTable, uc) != 0) { |
|
1057 unicodeTableHasSymbols = true; |
|
1058 break; |
|
1059 } |
|
1060 } |
|
1061 } |
|
1062 |
|
1063 // Fall back to symbol table |
|
1064 if (!unicodeTableHasLatin1 && unicodeTableHasSymbols) { |
|
1065 tableToUse = symbolTable; |
|
1066 score = Symbol; |
|
1067 goto resolveTable; |
|
1068 } |
|
1069 } |
|
1070 |
|
1071 return table + unicode_table; |
|
1072 } |
|
1073 |
|
1074 quint32 QFontEngine::getTrueTypeGlyphIndex(const uchar *cmap, uint unicode) |
|
1075 { |
|
1076 unsigned short format = qFromBigEndian<quint16>(cmap); |
|
1077 if (format == 0) { |
|
1078 if (unicode < 256) |
|
1079 return (int) *(cmap+6+unicode); |
|
1080 } else if (format == 4) { |
|
1081 /* some fonts come with invalid cmap tables, where the last segment |
|
1082 specified end = start = rangeoffset = 0xffff, delta = 0x0001 |
|
1083 Since 0xffff is never a valid Unicode char anyway, we just get rid of the issue |
|
1084 by returning 0 for 0xffff |
|
1085 */ |
|
1086 if(unicode >= 0xffff) |
|
1087 return 0; |
|
1088 quint16 segCountX2 = qFromBigEndian<quint16>(cmap + 6); |
|
1089 const unsigned char *ends = cmap + 14; |
|
1090 int i = 0; |
|
1091 for (; i < segCountX2/2 && qFromBigEndian<quint16>(ends + 2*i) < unicode; i++) {} |
|
1092 |
|
1093 const unsigned char *idx = ends + segCountX2 + 2 + 2*i; |
|
1094 quint16 startIndex = qFromBigEndian<quint16>(idx); |
|
1095 |
|
1096 if (startIndex > unicode) |
|
1097 return 0; |
|
1098 |
|
1099 idx += segCountX2; |
|
1100 qint16 idDelta = (qint16)qFromBigEndian<quint16>(idx); |
|
1101 idx += segCountX2; |
|
1102 quint16 idRangeoffset_t = (quint16)qFromBigEndian<quint16>(idx); |
|
1103 |
|
1104 quint16 glyphIndex; |
|
1105 if (idRangeoffset_t) { |
|
1106 quint16 id = qFromBigEndian<quint16>(idRangeoffset_t + 2*(unicode - startIndex) + idx); |
|
1107 if (id) |
|
1108 glyphIndex = (idDelta + id) % 0x10000; |
|
1109 else |
|
1110 glyphIndex = 0; |
|
1111 } else { |
|
1112 glyphIndex = (idDelta + unicode) % 0x10000; |
|
1113 } |
|
1114 return glyphIndex; |
|
1115 } else if (format == 6) { |
|
1116 quint16 tableSize = qFromBigEndian<quint16>(cmap + 2); |
|
1117 |
|
1118 quint16 firstCode6 = qFromBigEndian<quint16>(cmap + 6); |
|
1119 if (unicode < firstCode6) |
|
1120 return 0; |
|
1121 |
|
1122 quint16 entryCount6 = qFromBigEndian<quint16>(cmap + 8); |
|
1123 if (entryCount6 * 2 + 10 > tableSize) |
|
1124 return 0; |
|
1125 |
|
1126 quint16 sentinel6 = firstCode6 + entryCount6; |
|
1127 if (unicode >= sentinel6) |
|
1128 return 0; |
|
1129 |
|
1130 quint16 entryIndex6 = unicode - firstCode6; |
|
1131 return qFromBigEndian<quint16>(cmap + 10 + (entryIndex6 * 2)); |
|
1132 } else if (format == 12) { |
|
1133 quint32 nGroups = qFromBigEndian<quint32>(cmap + 12); |
|
1134 |
|
1135 cmap += 16; // move to start of groups |
|
1136 |
|
1137 int left = 0, right = nGroups - 1; |
|
1138 while (left <= right) { |
|
1139 int middle = left + ( ( right - left ) >> 1 ); |
|
1140 |
|
1141 quint32 startCharCode = qFromBigEndian<quint32>(cmap + 12*middle); |
|
1142 if(unicode < startCharCode) |
|
1143 right = middle - 1; |
|
1144 else { |
|
1145 quint32 endCharCode = qFromBigEndian<quint32>(cmap + 12*middle + 4); |
|
1146 if(unicode <= endCharCode) |
|
1147 return qFromBigEndian<quint32>(cmap + 12*middle + 8) + unicode - startCharCode; |
|
1148 left = middle + 1; |
|
1149 } |
|
1150 } |
|
1151 } else { |
|
1152 qDebug("cmap table of format %d not implemented", format); |
|
1153 } |
|
1154 |
|
1155 return 0; |
|
1156 } |
|
1157 |
|
1158 Q_GLOBAL_STATIC_WITH_INITIALIZER(QVector<QRgb>, qt_grayPalette, { |
|
1159 x->resize(256); |
|
1160 QRgb *it = x->data(); |
|
1161 for (int i = 0; i < x->size(); ++i, ++it) |
|
1162 *it = 0xff000000 | i | (i<<8) | (i<<16); |
|
1163 }) |
|
1164 |
|
1165 const QVector<QRgb> &QFontEngine::grayPalette() |
|
1166 { |
|
1167 return *qt_grayPalette(); |
|
1168 } |
|
1169 |
|
1170 // ------------------------------------------------------------------ |
|
1171 // The box font engine |
|
1172 // ------------------------------------------------------------------ |
|
1173 |
|
1174 QFontEngineBox::QFontEngineBox(int size) |
|
1175 : _size(size) |
|
1176 { |
|
1177 cache_cost = sizeof(QFontEngineBox); |
|
1178 } |
|
1179 |
|
1180 QFontEngineBox::~QFontEngineBox() |
|
1181 { |
|
1182 } |
|
1183 |
|
1184 bool QFontEngineBox::stringToCMap(const QChar *, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags) const |
|
1185 { |
|
1186 if (*nglyphs < len) { |
|
1187 *nglyphs = len; |
|
1188 return false; |
|
1189 } |
|
1190 |
|
1191 for (int i = 0; i < len; i++) { |
|
1192 glyphs->glyphs[i] = 0; |
|
1193 glyphs->advances_x[i] = _size; |
|
1194 glyphs->advances_y[i] = 0; |
|
1195 } |
|
1196 |
|
1197 *nglyphs = len; |
|
1198 glyphs->numGlyphs = len; |
|
1199 return true; |
|
1200 } |
|
1201 |
|
1202 void QFontEngineBox::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const |
|
1203 { |
|
1204 for (int i = 0; i < glyphs->numGlyphs; i++) { |
|
1205 glyphs->advances_x[i] = _size; |
|
1206 glyphs->advances_y[i] = 0; |
|
1207 } |
|
1208 } |
|
1209 |
|
1210 void QFontEngineBox::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags) |
|
1211 { |
|
1212 if (!glyphs.numGlyphs) |
|
1213 return; |
|
1214 |
|
1215 QVarLengthArray<QFixedPoint> positions; |
|
1216 QVarLengthArray<glyph_t> positioned_glyphs; |
|
1217 QTransform matrix = QTransform::fromTranslate(x, y - _size); |
|
1218 getGlyphPositions(glyphs, matrix, flags, positioned_glyphs, positions); |
|
1219 |
|
1220 QSize s(_size - 3, _size - 3); |
|
1221 for (int k = 0; k < positions.size(); k++) |
|
1222 path->addRect(QRectF(positions[k].toPointF(), s)); |
|
1223 } |
|
1224 |
|
1225 glyph_metrics_t QFontEngineBox::boundingBox(const QGlyphLayout &glyphs) |
|
1226 { |
|
1227 glyph_metrics_t overall; |
|
1228 overall.width = _size*glyphs.numGlyphs; |
|
1229 overall.height = _size; |
|
1230 overall.xoff = overall.width; |
|
1231 return overall; |
|
1232 } |
|
1233 |
|
1234 #if defined(Q_WS_QWS) |
|
1235 void QFontEngineBox::draw(QPaintEngine *p, qreal x, qreal y, const QTextItemInt &ti) |
|
1236 { |
|
1237 if (!ti.glyphs.numGlyphs) |
|
1238 return; |
|
1239 |
|
1240 // any fixes here should probably also be done in QPaintEnginePrivate::drawBoxTextItem |
|
1241 QSize s(_size - 3, _size - 3); |
|
1242 |
|
1243 QVarLengthArray<QFixedPoint> positions; |
|
1244 QVarLengthArray<glyph_t> glyphs; |
|
1245 QTransform matrix = QTransform::fromTranslate(x, y - _size); |
|
1246 ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions); |
|
1247 if (glyphs.size() == 0) |
|
1248 return; |
|
1249 |
|
1250 |
|
1251 QPainter *painter = p->painter(); |
|
1252 painter->save(); |
|
1253 painter->setBrush(Qt::NoBrush); |
|
1254 QPen pen = painter->pen(); |
|
1255 pen.setWidthF(lineThickness().toReal()); |
|
1256 painter->setPen(pen); |
|
1257 for (int k = 0; k < positions.size(); k++) |
|
1258 painter->drawRect(QRectF(positions[k].toPointF(), s)); |
|
1259 painter->restore(); |
|
1260 } |
|
1261 #endif |
|
1262 |
|
1263 glyph_metrics_t QFontEngineBox::boundingBox(glyph_t) |
|
1264 { |
|
1265 return glyph_metrics_t(0, -_size, _size, _size, _size, 0); |
|
1266 } |
|
1267 |
|
1268 |
|
1269 |
|
1270 QFixed QFontEngineBox::ascent() const |
|
1271 { |
|
1272 return _size; |
|
1273 } |
|
1274 |
|
1275 QFixed QFontEngineBox::descent() const |
|
1276 { |
|
1277 return 0; |
|
1278 } |
|
1279 |
|
1280 QFixed QFontEngineBox::leading() const |
|
1281 { |
|
1282 QFixed l = _size * QFixed::fromReal(qreal(0.15)); |
|
1283 return l.ceil(); |
|
1284 } |
|
1285 |
|
1286 qreal QFontEngineBox::maxCharWidth() const |
|
1287 { |
|
1288 return _size; |
|
1289 } |
|
1290 |
|
1291 #ifdef Q_WS_X11 |
|
1292 int QFontEngineBox::cmap() const |
|
1293 { |
|
1294 return -1; |
|
1295 } |
|
1296 #endif |
|
1297 |
|
1298 const char *QFontEngineBox::name() const |
|
1299 { |
|
1300 return "null"; |
|
1301 } |
|
1302 |
|
1303 bool QFontEngineBox::canRender(const QChar *, int) |
|
1304 { |
|
1305 return true; |
|
1306 } |
|
1307 |
|
1308 QFontEngine::Type QFontEngineBox::type() const |
|
1309 { |
|
1310 return Box; |
|
1311 } |
|
1312 |
|
1313 QImage QFontEngineBox::alphaMapForGlyph(glyph_t) |
|
1314 { |
|
1315 QImage image(_size, _size, QImage::Format_Indexed8); |
|
1316 QVector<QRgb> colors(256); |
|
1317 for (int i=0; i<256; ++i) |
|
1318 colors[i] = qRgba(0, 0, 0, i); |
|
1319 image.setColorTable(colors); |
|
1320 image.fill(0); |
|
1321 |
|
1322 // can't use qpainter for index8; so use setPixel to draw our rectangle. |
|
1323 for (int i=2; i <= _size-3; ++i) { |
|
1324 image.setPixel(i, 2, 255); |
|
1325 image.setPixel(i, _size-3, 255); |
|
1326 image.setPixel(2, i, 255); |
|
1327 image.setPixel(_size-3, i, 255); |
|
1328 } |
|
1329 return image; |
|
1330 } |
|
1331 |
|
1332 // ------------------------------------------------------------------ |
|
1333 // Multi engine |
|
1334 // ------------------------------------------------------------------ |
|
1335 |
|
1336 static inline uchar highByte(glyph_t glyph) |
|
1337 { return glyph >> 24; } |
|
1338 |
|
1339 // strip high byte from glyph |
|
1340 static inline glyph_t stripped(glyph_t glyph) |
|
1341 { return glyph & 0x00ffffff; } |
|
1342 |
|
1343 QFontEngineMulti::QFontEngineMulti(int engineCount) |
|
1344 { |
|
1345 engines.fill(0, engineCount); |
|
1346 cache_cost = 0; |
|
1347 } |
|
1348 |
|
1349 QFontEngineMulti::~QFontEngineMulti() |
|
1350 { |
|
1351 for (int i = 0; i < engines.size(); ++i) { |
|
1352 QFontEngine *fontEngine = engines.at(i); |
|
1353 if (fontEngine) { |
|
1354 fontEngine->ref.deref(); |
|
1355 if (fontEngine->cache_count == 0 && fontEngine->ref == 0) |
|
1356 delete fontEngine; |
|
1357 } |
|
1358 } |
|
1359 } |
|
1360 |
|
1361 bool QFontEngineMulti::stringToCMap(const QChar *str, int len, |
|
1362 QGlyphLayout *glyphs, int *nglyphs, |
|
1363 QTextEngine::ShaperFlags flags) const |
|
1364 { |
|
1365 int ng = *nglyphs; |
|
1366 if (!engine(0)->stringToCMap(str, len, glyphs, &ng, flags)) |
|
1367 return false; |
|
1368 |
|
1369 int glyph_pos = 0; |
|
1370 for (int i = 0; i < len; ++i) { |
|
1371 bool surrogate = (str[i].unicode() >= 0xd800 && str[i].unicode() < 0xdc00 && i < len-1 |
|
1372 && str[i+1].unicode() >= 0xdc00 && str[i+1].unicode() < 0xe000); |
|
1373 if (glyphs->glyphs[glyph_pos] == 0) { |
|
1374 |
|
1375 QGlyphLayoutInstance tmp = glyphs->instance(glyph_pos); |
|
1376 for (int x = 1; x < engines.size(); ++x) { |
|
1377 QFontEngine *engine = engines.at(x); |
|
1378 if (!engine) { |
|
1379 const_cast<QFontEngineMulti *>(this)->loadEngine(x); |
|
1380 engine = engines.at(x); |
|
1381 } |
|
1382 Q_ASSERT(engine != 0); |
|
1383 if (engine->type() == Box) |
|
1384 continue; |
|
1385 glyphs->advances_x[glyph_pos] = glyphs->advances_y[glyph_pos] = 0; |
|
1386 glyphs->offsets[glyph_pos] = QFixedPoint(); |
|
1387 int num = 2; |
|
1388 QGlyphLayout offs = glyphs->mid(glyph_pos, num); |
|
1389 engine->stringToCMap(str + i, surrogate ? 2 : 1, &offs, &num, flags); |
|
1390 Q_ASSERT(num == 1); // surrogates only give 1 glyph |
|
1391 if (glyphs->glyphs[glyph_pos]) { |
|
1392 // set the high byte to indicate which engine the glyph came from |
|
1393 glyphs->glyphs[glyph_pos] |= (x << 24); |
|
1394 break; |
|
1395 } |
|
1396 } |
|
1397 // ensure we use metrics from the 1st font when we use the fallback image. |
|
1398 if (!glyphs->glyphs[glyph_pos]) { |
|
1399 glyphs->setInstance(glyph_pos, tmp); |
|
1400 } |
|
1401 } |
|
1402 if (surrogate) |
|
1403 ++i; |
|
1404 ++glyph_pos; |
|
1405 } |
|
1406 |
|
1407 *nglyphs = ng; |
|
1408 glyphs->numGlyphs = ng; |
|
1409 return true; |
|
1410 } |
|
1411 |
|
1412 glyph_metrics_t QFontEngineMulti::boundingBox(const QGlyphLayout &glyphs) |
|
1413 { |
|
1414 if (glyphs.numGlyphs <= 0) |
|
1415 return glyph_metrics_t(); |
|
1416 |
|
1417 glyph_metrics_t overall; |
|
1418 |
|
1419 int which = highByte(glyphs.glyphs[0]); |
|
1420 int start = 0; |
|
1421 int end, i; |
|
1422 for (end = 0; end < glyphs.numGlyphs; ++end) { |
|
1423 const int e = highByte(glyphs.glyphs[end]); |
|
1424 if (e == which) |
|
1425 continue; |
|
1426 |
|
1427 // set the high byte to zero |
|
1428 for (i = start; i < end; ++i) |
|
1429 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]); |
|
1430 |
|
1431 // merge the bounding box for this run |
|
1432 const glyph_metrics_t gm = engine(which)->boundingBox(glyphs.mid(start, end - start)); |
|
1433 |
|
1434 overall.x = qMin(overall.x, gm.x); |
|
1435 overall.y = qMin(overall.y, gm.y); |
|
1436 overall.width = overall.xoff + gm.width; |
|
1437 overall.height = qMax(overall.height + overall.y, gm.height + gm.y) - |
|
1438 qMin(overall.y, gm.y); |
|
1439 overall.xoff += gm.xoff; |
|
1440 overall.yoff += gm.yoff; |
|
1441 |
|
1442 // reset the high byte for all glyphs |
|
1443 const int hi = which << 24; |
|
1444 for (i = start; i < end; ++i) |
|
1445 glyphs.glyphs[i] = hi | glyphs.glyphs[i]; |
|
1446 |
|
1447 // change engine |
|
1448 start = end; |
|
1449 which = e; |
|
1450 } |
|
1451 |
|
1452 // set the high byte to zero |
|
1453 for (i = start; i < end; ++i) |
|
1454 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]); |
|
1455 |
|
1456 // merge the bounding box for this run |
|
1457 const glyph_metrics_t gm = engine(which)->boundingBox(glyphs.mid(start, end - start)); |
|
1458 |
|
1459 overall.x = qMin(overall.x, gm.x); |
|
1460 overall.y = qMin(overall.y, gm.y); |
|
1461 overall.width = overall.xoff + gm.width; |
|
1462 overall.height = qMax(overall.height + overall.y, gm.height + gm.y) - |
|
1463 qMin(overall.y, gm.y); |
|
1464 overall.xoff += gm.xoff; |
|
1465 overall.yoff += gm.yoff; |
|
1466 |
|
1467 // reset the high byte for all glyphs |
|
1468 const int hi = which << 24; |
|
1469 for (i = start; i < end; ++i) |
|
1470 glyphs.glyphs[i] = hi | glyphs.glyphs[i]; |
|
1471 |
|
1472 return overall; |
|
1473 } |
|
1474 |
|
1475 void QFontEngineMulti::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, |
|
1476 QPainterPath *path, QTextItem::RenderFlags flags) |
|
1477 { |
|
1478 if (glyphs.numGlyphs <= 0) |
|
1479 return; |
|
1480 |
|
1481 int which = highByte(glyphs.glyphs[0]); |
|
1482 int start = 0; |
|
1483 int end, i; |
|
1484 if (flags & QTextItem::RightToLeft) { |
|
1485 for (int gl = 0; gl < glyphs.numGlyphs; gl++) { |
|
1486 x += glyphs.advances_x[gl].toReal(); |
|
1487 y += glyphs.advances_y[gl].toReal(); |
|
1488 } |
|
1489 } |
|
1490 for (end = 0; end < glyphs.numGlyphs; ++end) { |
|
1491 const int e = highByte(glyphs.glyphs[end]); |
|
1492 if (e == which) |
|
1493 continue; |
|
1494 |
|
1495 if (flags & QTextItem::RightToLeft) { |
|
1496 for (i = start; i < end; ++i) { |
|
1497 x -= glyphs.advances_x[i].toReal(); |
|
1498 y -= glyphs.advances_y[i].toReal(); |
|
1499 } |
|
1500 } |
|
1501 |
|
1502 // set the high byte to zero |
|
1503 for (i = start; i < end; ++i) |
|
1504 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]); |
|
1505 engine(which)->addOutlineToPath(x, y, glyphs.mid(start, end - start), path, flags); |
|
1506 // reset the high byte for all glyphs and update x and y |
|
1507 const int hi = which << 24; |
|
1508 for (i = start; i < end; ++i) |
|
1509 glyphs.glyphs[i] = hi | glyphs.glyphs[i]; |
|
1510 |
|
1511 if (!(flags & QTextItem::RightToLeft)) { |
|
1512 for (i = start; i < end; ++i) { |
|
1513 x += glyphs.advances_x[i].toReal(); |
|
1514 y += glyphs.advances_y[i].toReal(); |
|
1515 } |
|
1516 } |
|
1517 |
|
1518 // change engine |
|
1519 start = end; |
|
1520 which = e; |
|
1521 } |
|
1522 |
|
1523 if (flags & QTextItem::RightToLeft) { |
|
1524 for (i = start; i < end; ++i) { |
|
1525 x -= glyphs.advances_x[i].toReal(); |
|
1526 y -= glyphs.advances_y[i].toReal(); |
|
1527 } |
|
1528 } |
|
1529 |
|
1530 // set the high byte to zero |
|
1531 for (i = start; i < end; ++i) |
|
1532 glyphs.glyphs[i] = stripped(glyphs.glyphs[i]); |
|
1533 |
|
1534 engine(which)->addOutlineToPath(x, y, glyphs.mid(start, end - start), path, flags); |
|
1535 |
|
1536 // reset the high byte for all glyphs |
|
1537 const int hi = which << 24; |
|
1538 for (i = start; i < end; ++i) |
|
1539 glyphs.glyphs[i] = hi | glyphs.glyphs[i]; |
|
1540 } |
|
1541 |
|
1542 void QFontEngineMulti::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const |
|
1543 { |
|
1544 if (glyphs->numGlyphs <= 0) |
|
1545 return; |
|
1546 |
|
1547 int which = highByte(glyphs->glyphs[0]); |
|
1548 int start = 0; |
|
1549 int end, i; |
|
1550 for (end = 0; end < glyphs->numGlyphs; ++end) { |
|
1551 const int e = highByte(glyphs->glyphs[end]); |
|
1552 if (e == which) |
|
1553 continue; |
|
1554 |
|
1555 // set the high byte to zero |
|
1556 for (i = start; i < end; ++i) |
|
1557 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]); |
|
1558 |
|
1559 QGlyphLayout offs = glyphs->mid(start, end - start); |
|
1560 engine(which)->recalcAdvances(&offs, flags); |
|
1561 |
|
1562 // reset the high byte for all glyphs and update x and y |
|
1563 const int hi = which << 24; |
|
1564 for (i = start; i < end; ++i) |
|
1565 glyphs->glyphs[i] = hi | glyphs->glyphs[i]; |
|
1566 |
|
1567 // change engine |
|
1568 start = end; |
|
1569 which = e; |
|
1570 } |
|
1571 |
|
1572 // set the high byte to zero |
|
1573 for (i = start; i < end; ++i) |
|
1574 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]); |
|
1575 |
|
1576 QGlyphLayout offs = glyphs->mid(start, end - start); |
|
1577 engine(which)->recalcAdvances(&offs, flags); |
|
1578 |
|
1579 // reset the high byte for all glyphs |
|
1580 const int hi = which << 24; |
|
1581 for (i = start; i < end; ++i) |
|
1582 glyphs->glyphs[i] = hi | glyphs->glyphs[i]; |
|
1583 } |
|
1584 |
|
1585 void QFontEngineMulti::doKerning(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const |
|
1586 { |
|
1587 if (glyphs->numGlyphs <= 0) |
|
1588 return; |
|
1589 |
|
1590 int which = highByte(glyphs->glyphs[0]); |
|
1591 int start = 0; |
|
1592 int end, i; |
|
1593 for (end = 0; end < glyphs->numGlyphs; ++end) { |
|
1594 const int e = highByte(glyphs->glyphs[end]); |
|
1595 if (e == which) |
|
1596 continue; |
|
1597 |
|
1598 // set the high byte to zero |
|
1599 for (i = start; i < end; ++i) |
|
1600 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]); |
|
1601 |
|
1602 QGlyphLayout offs = glyphs->mid(start, end - start); |
|
1603 engine(which)->doKerning(&offs, flags); |
|
1604 |
|
1605 // reset the high byte for all glyphs and update x and y |
|
1606 const int hi = which << 24; |
|
1607 for (i = start; i < end; ++i) |
|
1608 glyphs->glyphs[i] = hi | glyphs->glyphs[i]; |
|
1609 |
|
1610 // change engine |
|
1611 start = end; |
|
1612 which = e; |
|
1613 } |
|
1614 |
|
1615 // set the high byte to zero |
|
1616 for (i = start; i < end; ++i) |
|
1617 glyphs->glyphs[i] = stripped(glyphs->glyphs[i]); |
|
1618 |
|
1619 QGlyphLayout offs = glyphs->mid(start, end - start); |
|
1620 engine(which)->doKerning(&offs, flags); |
|
1621 |
|
1622 // reset the high byte for all glyphs |
|
1623 const int hi = which << 24; |
|
1624 for (i = start; i < end; ++i) |
|
1625 glyphs->glyphs[i] = hi | glyphs->glyphs[i]; |
|
1626 } |
|
1627 |
|
1628 glyph_metrics_t QFontEngineMulti::boundingBox(glyph_t glyph) |
|
1629 { |
|
1630 const int which = highByte(glyph); |
|
1631 Q_ASSERT(which < engines.size()); |
|
1632 return engine(which)->boundingBox(stripped(glyph)); |
|
1633 } |
|
1634 |
|
1635 QFixed QFontEngineMulti::ascent() const |
|
1636 { return engine(0)->ascent(); } |
|
1637 |
|
1638 QFixed QFontEngineMulti::descent() const |
|
1639 { return engine(0)->descent(); } |
|
1640 |
|
1641 QFixed QFontEngineMulti::leading() const |
|
1642 { |
|
1643 return engine(0)->leading(); |
|
1644 } |
|
1645 |
|
1646 QFixed QFontEngineMulti::xHeight() const |
|
1647 { |
|
1648 return engine(0)->xHeight(); |
|
1649 } |
|
1650 |
|
1651 QFixed QFontEngineMulti::averageCharWidth() const |
|
1652 { |
|
1653 return engine(0)->averageCharWidth(); |
|
1654 } |
|
1655 |
|
1656 QFixed QFontEngineMulti::lineThickness() const |
|
1657 { |
|
1658 return engine(0)->lineThickness(); |
|
1659 } |
|
1660 |
|
1661 QFixed QFontEngineMulti::underlinePosition() const |
|
1662 { |
|
1663 return engine(0)->underlinePosition(); |
|
1664 } |
|
1665 |
|
1666 qreal QFontEngineMulti::maxCharWidth() const |
|
1667 { |
|
1668 return engine(0)->maxCharWidth(); |
|
1669 } |
|
1670 |
|
1671 qreal QFontEngineMulti::minLeftBearing() const |
|
1672 { |
|
1673 return engine(0)->minLeftBearing(); |
|
1674 } |
|
1675 |
|
1676 qreal QFontEngineMulti::minRightBearing() const |
|
1677 { |
|
1678 return engine(0)->minRightBearing(); |
|
1679 } |
|
1680 |
|
1681 bool QFontEngineMulti::canRender(const QChar *string, int len) |
|
1682 { |
|
1683 if (engine(0)->canRender(string, len)) |
|
1684 return true; |
|
1685 |
|
1686 QVarLengthGlyphLayoutArray glyphs(len); |
|
1687 int nglyphs = len; |
|
1688 if (stringToCMap(string, len, &glyphs, &nglyphs, QTextEngine::GlyphIndicesOnly) == false) { |
|
1689 glyphs.resize(nglyphs); |
|
1690 stringToCMap(string, len, &glyphs, &nglyphs, QTextEngine::GlyphIndicesOnly); |
|
1691 } |
|
1692 |
|
1693 bool allExist = true; |
|
1694 for (int i = 0; i < nglyphs; i++) { |
|
1695 if (!glyphs.glyphs[i]) { |
|
1696 allExist = false; |
|
1697 break; |
|
1698 } |
|
1699 } |
|
1700 |
|
1701 return allExist; |
|
1702 } |
|
1703 |
|
1704 QImage QFontEngineMulti::alphaMapForGlyph(glyph_t) |
|
1705 { |
|
1706 Q_ASSERT(false); |
|
1707 return QImage(); |
|
1708 } |
|
1709 |
|
1710 |
|
1711 QT_END_NAMESPACE |