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