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