Merge branch 'qtquick2' of scm.dev.nokia.troll.no:qt/qtbase-staging into qtquick2
[profile/ivi/qtbase.git] / src / gui / text / qfontenginedirectwrite.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 ** 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 #ifndef QT_NO_DIRECTWRITE
43
44 #include "qfontenginedirectwrite_p.h"
45
46 #include <qendian.h>
47 #include <dwrite.h>
48 #include <private/qnativeimage_p.h>
49
50 #include <d2d1.h>
51
52 QT_BEGIN_NAMESPACE
53
54 // Convert from design units to logical pixels
55 #define DESIGN_TO_LOGICAL(DESIGN_UNIT_VALUE) \
56     QFixed::fromReal((qreal(DESIGN_UNIT_VALUE) / qreal(m_unitsPerEm)) * fontDef.pixelSize)
57
58 namespace {
59
60     class GeometrySink: public IDWriteGeometrySink
61     {
62     public:
63         GeometrySink(QPainterPath *path) : m_path(path), m_refCount(0)
64         {
65             Q_ASSERT(m_path != 0);
66         }
67
68         IFACEMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *beziers, UINT bezierCount);
69         IFACEMETHOD_(void, AddLines)(const D2D1_POINT_2F *points, UINT pointCount);
70         IFACEMETHOD_(void, BeginFigure)(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin);
71         IFACEMETHOD(Close)();
72         IFACEMETHOD_(void, EndFigure)(D2D1_FIGURE_END figureEnd);
73         IFACEMETHOD_(void, SetFillMode)(D2D1_FILL_MODE fillMode);
74         IFACEMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT vertexFlags);
75
76         IFACEMETHOD_(unsigned long, AddRef)();
77         IFACEMETHOD_(unsigned long, Release)();
78         IFACEMETHOD(QueryInterface)(IID const &riid, void **ppvObject);
79
80     private:
81         inline static QPointF fromD2D1_POINT_2F(const D2D1_POINT_2F &inp)
82         {
83             return QPointF(inp.x, inp.y);
84         }
85
86         unsigned long m_refCount;
87         QPointF m_startPoint;
88         QPainterPath *m_path;
89     };
90
91     void GeometrySink::AddBeziers(const D2D1_BEZIER_SEGMENT *beziers,
92                                   UINT bezierCount)
93     {
94         for (uint i=0; i<bezierCount; ++i) {
95             QPointF c1 = fromD2D1_POINT_2F(beziers[i].point1);
96             QPointF c2 = fromD2D1_POINT_2F(beziers[i].point2);
97             QPointF p2 = fromD2D1_POINT_2F(beziers[i].point3);
98
99             m_path->cubicTo(c1, c2, p2);
100         }
101     }
102
103     void GeometrySink::AddLines(const D2D1_POINT_2F *points, UINT pointsCount)
104     {
105         for (uint i=0; i<pointsCount; ++i)
106             m_path->lineTo(fromD2D1_POINT_2F(points[i]));
107     }
108
109     void GeometrySink::BeginFigure(D2D1_POINT_2F startPoint,
110                                    D2D1_FIGURE_BEGIN /*figureBegin*/)
111     {
112         m_startPoint = fromD2D1_POINT_2F(startPoint);
113         m_path->moveTo(m_startPoint);
114     }
115
116     IFACEMETHODIMP GeometrySink::Close()
117     {
118         return E_NOTIMPL;
119     }
120
121     void GeometrySink::EndFigure(D2D1_FIGURE_END figureEnd)
122     {
123         if (figureEnd == D2D1_FIGURE_END_CLOSED)
124             m_path->closeSubpath();
125     }
126
127     void GeometrySink::SetFillMode(D2D1_FILL_MODE fillMode)
128     {
129         m_path->setFillRule(fillMode == D2D1_FILL_MODE_ALTERNATE
130                             ? Qt::OddEvenFill
131                             : Qt::WindingFill);
132     }
133
134     void GeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT /*vertexFlags*/)
135     {
136         /* Not implemented */
137     }
138
139     IFACEMETHODIMP_(unsigned long) GeometrySink::AddRef()
140     {
141         return InterlockedIncrement(&m_refCount);
142     }
143
144     IFACEMETHODIMP_(unsigned long) GeometrySink::Release()
145     {
146         unsigned long newCount = InterlockedDecrement(&m_refCount);
147         if (newCount == 0)
148         {
149             delete this;
150             return 0;
151         }
152
153         return newCount;
154     }
155
156     IFACEMETHODIMP GeometrySink::QueryInterface(IID const &riid, void **ppvObject)
157     {
158         if (__uuidof(IDWriteGeometrySink) == riid) {
159             *ppvObject = this;
160         } else if (__uuidof(IUnknown) == riid) {
161             *ppvObject = this;
162         } else {
163             *ppvObject = NULL;
164             return E_FAIL;
165         }
166
167         AddRef();
168         return S_OK;
169     }
170
171 }
172
173 QFontEngineDirectWrite::QFontEngineDirectWrite(IDWriteFactory *directWriteFactory,
174                                                IDWriteFontFace *directWriteFontFace,
175                                                qreal pixelSize)
176     : m_directWriteFontFace(directWriteFontFace)
177     , m_directWriteFactory(directWriteFactory)
178     , m_directWriteBitmapRenderTarget(0)
179     , m_lineThickness(-1)
180     , m_unitsPerEm(-1)
181     , m_ascent(-1)
182     , m_descent(-1)
183     , m_xHeight(-1)
184     , m_lineGap(-1)
185 {
186     m_directWriteFactory->AddRef();
187     m_directWriteFontFace->AddRef();
188
189     fontDef.pixelSize = pixelSize;
190     collectMetrics();
191 }
192
193 QFontEngineDirectWrite::~QFontEngineDirectWrite()
194 {
195     m_directWriteFactory->Release();
196     m_directWriteFontFace->Release();
197
198     if (m_directWriteBitmapRenderTarget != 0)
199         m_directWriteBitmapRenderTarget->Release();
200 }
201
202 void QFontEngineDirectWrite::collectMetrics()
203 {
204     if (m_directWriteFontFace != 0) {
205         DWRITE_FONT_METRICS metrics;
206
207         m_directWriteFontFace->GetMetrics(&metrics);
208         m_unitsPerEm = metrics.designUnitsPerEm;
209
210         m_lineThickness = DESIGN_TO_LOGICAL(metrics.underlineThickness);
211         m_ascent = DESIGN_TO_LOGICAL(metrics.ascent);
212         m_descent = DESIGN_TO_LOGICAL(metrics.descent);
213         m_xHeight = DESIGN_TO_LOGICAL(metrics.xHeight);
214         m_lineGap = DESIGN_TO_LOGICAL(metrics.lineGap);
215     }
216 }
217
218 QFixed QFontEngineDirectWrite::lineThickness() const
219 {
220     if (m_lineThickness > 0)
221         return m_lineThickness;
222     else
223         return QFontEngine::lineThickness();
224 }
225
226 bool QFontEngineDirectWrite::getSfntTableData(uint tag, uchar *buffer, uint *length) const
227 {
228     if (m_directWriteFontFace) {
229         DWORD t = qbswap<quint32>(tag);
230
231         const void *tableData = 0;
232         void *tableContext = 0;
233         UINT32 tableSize;
234         BOOL exists;
235         HRESULT hr = m_directWriteFontFace->TryGetFontTable(
236                     t, &tableData, &tableSize, &tableContext, &exists
237                     );
238
239         if (SUCCEEDED(hr)) {
240             if (!exists)
241                 return false;
242
243             if (buffer == 0) {
244                 *length = tableSize;
245                 return true;
246             } else if (*length < tableSize) {
247                 return false;
248             }
249
250             qMemCopy(buffer, tableData, tableSize);
251             m_directWriteFontFace->ReleaseFontTable(tableContext);
252
253             return true;
254         } else {
255             qErrnoWarning("QFontEngineDirectWrite::getSfntTableData: TryGetFontTable failed");
256         }
257     }
258
259     return false;
260 }
261
262 QFixed QFontEngineDirectWrite::emSquareSize() const
263 {
264     if (m_unitsPerEm > 0)
265         return m_unitsPerEm;
266     else
267         return QFontEngine::emSquareSize();
268 }
269
270 inline unsigned int getChar(const QChar *str, int &i, const int len)
271 {
272     unsigned int uc = str[i].unicode();
273     if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) {
274         uint low = str[i+1].unicode();
275        if (low >= 0xdc00 && low < 0xe000) {
276             uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000;
277             ++i;
278         }
279     }
280     return uc;
281 }
282
283 bool QFontEngineDirectWrite::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs,
284                                           int *nglyphs, QTextEngine::ShaperFlags flags) const
285 {
286     if (m_directWriteFontFace != 0) {
287         QVarLengthArray<UINT32> codePoints(len);
288         for (int i=0; i<len; ++i) {
289             codePoints[i] = getChar(str, i, len);
290             if (flags & QTextEngine::RightToLeft)
291                 codePoints[i] = QChar::mirroredChar(codePoints[i]);
292         }
293
294         QVarLengthArray<UINT16> glyphIndices(len);
295         HRESULT hr = m_directWriteFontFace->GetGlyphIndicesW(codePoints.data(),
296                                                              len,
297                                                              glyphIndices.data());
298
299         if (SUCCEEDED(hr)) {
300             for (int i=0; i<len; ++i)
301                 glyphs->glyphs[i] = glyphIndices[i];
302
303             *nglyphs = len;
304
305             if (!(flags & QTextEngine::GlyphIndicesOnly))
306                 recalcAdvances(glyphs, 0);
307
308             return true;
309         } else {
310             qErrnoWarning("QFontEngineDirectWrite::stringToCMap: GetGlyphIndicesW failed");
311         }
312     }
313
314     return false;
315 }
316
317 void QFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const
318 {
319     if (m_directWriteFontFace == 0)
320         return;
321
322     QVarLengthArray<UINT16> glyphIndices(glyphs->numGlyphs);
323
324     // ### Caching?
325     for(int i=0; i<glyphs->numGlyphs; i++)
326         glyphIndices[i] = UINT16(glyphs->glyphs[i]);
327
328     QVarLengthArray<DWRITE_GLYPH_METRICS> glyphMetrics(glyphIndices.size());
329     HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(glyphIndices.data(),
330                                                               glyphIndices.size(),
331                                                               glyphMetrics.data());
332     if (SUCCEEDED(hr)) {
333         for (int i=0; i<glyphs->numGlyphs; ++i) {
334             glyphs->advances_x[i] = DESIGN_TO_LOGICAL(glyphMetrics[i].advanceWidth);
335             if (fontDef.styleStrategy & QFont::ForceIntegerMetrics)
336                 glyphs->advances_x[i] = glyphs->advances_x[i].round();
337             glyphs->advances_y[i] = 0;
338         }
339     } else {
340         qErrnoWarning("QFontEngineDirectWrite::recalcAdvances: GetDesignGlyphMetrics failed");
341     }
342 }
343
344 void QFontEngineDirectWrite::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs,
345                                              QPainterPath *path, QTextItem::RenderFlags flags)
346 {
347     if (m_directWriteFontFace == 0)
348         return;
349
350     QVarLengthArray<UINT16> glyphIndices(nglyphs);
351     QVarLengthArray<DWRITE_GLYPH_OFFSET> glyphOffsets(nglyphs);
352     QVarLengthArray<FLOAT> glyphAdvances(nglyphs);
353
354     for (int i=0; i<nglyphs; ++i) {
355         glyphIndices[i] = glyphs[i];
356         glyphOffsets[i].advanceOffset  = positions[i].x.toReal();
357         glyphOffsets[i].ascenderOffset = -positions[i].y.toReal();
358         glyphAdvances[i] = 0.0;
359     }
360
361     GeometrySink geometrySink(path);
362     HRESULT hr = m_directWriteFontFace->GetGlyphRunOutline(
363                 fontDef.pixelSize,
364                 glyphIndices.data(),
365                 glyphAdvances.data(),
366                 glyphOffsets.data(),
367                 nglyphs,
368                 false,
369                 flags & QTextItem::RightToLeft,
370                 &geometrySink
371                 );
372
373     if (FAILED(hr))
374         qErrnoWarning("QFontEngineDirectWrite::addGlyphsToPath: GetGlyphRunOutline failed");
375 }
376
377 glyph_metrics_t QFontEngineDirectWrite::boundingBox(const QGlyphLayout &glyphs)
378 {
379     if (glyphs.numGlyphs == 0)
380         return glyph_metrics_t();
381
382     bool round = fontDef.styleStrategy & QFont::ForceIntegerMetrics;
383
384     QFixed w = 0;
385     for (int i = 0; i < glyphs.numGlyphs; ++i) {
386         w += round ? glyphs.effectiveAdvance(i).round() : glyphs.effectiveAdvance(i);
387
388     }
389
390     return glyph_metrics_t(0, -m_ascent, w - lastRightBearing(glyphs), m_ascent + m_descent, w, 0);
391 }
392
393 glyph_metrics_t QFontEngineDirectWrite::boundingBox(glyph_t g)
394 {
395     if (m_directWriteFontFace == 0)
396         return glyph_metrics_t();
397
398     UINT16 glyphIndex = g;
399
400     DWRITE_GLYPH_METRICS glyphMetrics;
401     HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(&glyphIndex, 1, &glyphMetrics);
402     if (SUCCEEDED(hr)) {
403         QFixed advanceWidth = DESIGN_TO_LOGICAL(glyphMetrics.advanceWidth);
404         QFixed leftSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.leftSideBearing);
405         QFixed rightSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.rightSideBearing);
406         QFixed advanceHeight = DESIGN_TO_LOGICAL(glyphMetrics.advanceHeight);
407         QFixed verticalOriginY = DESIGN_TO_LOGICAL(glyphMetrics.verticalOriginY);
408
409         if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) {
410             advanceWidth = advanceWidth.round();
411             advanceHeight = advanceHeight.round();
412         }
413
414         QFixed width = advanceWidth - leftSideBearing - rightSideBearing;
415
416         return glyph_metrics_t(-leftSideBearing, -verticalOriginY,
417                                width, m_ascent + m_descent,
418                                advanceWidth, advanceHeight);
419     } else {
420         qErrnoWarning("QFontEngineDirectWrite::boundingBox: GetDesignGlyphMetrics failed");
421     }
422
423     return glyph_metrics_t();
424 }
425
426 QFixed QFontEngineDirectWrite::ascent() const
427 {
428     return fontDef.styleStrategy & QFont::ForceIntegerMetrics
429             ? m_ascent.round()
430             : m_ascent;
431 }
432
433 QFixed QFontEngineDirectWrite::descent() const
434 {
435     return fontDef.styleStrategy & QFont::ForceIntegerMetrics
436            ? (m_descent - 1).round()
437            : (m_descent - 1);
438 }
439
440 QFixed QFontEngineDirectWrite::leading() const
441 {
442     return fontDef.styleStrategy & QFont::ForceIntegerMetrics
443            ? m_lineGap.round()
444            : m_lineGap;
445 }
446
447 QFixed QFontEngineDirectWrite::xHeight() const
448 {
449     return fontDef.styleStrategy & QFont::ForceIntegerMetrics
450            ? m_xHeight.round()
451            : m_xHeight;
452 }
453
454 qreal QFontEngineDirectWrite::maxCharWidth() const
455 {
456     // ###
457     return 0;
458 }
459
460 extern uint qt_pow_gamma[256];
461
462 QImage QFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition)
463 {
464     QImage im = imageForGlyph(glyph, subPixelPosition, 0, QTransform());
465
466     QImage indexed(im.width(), im.height(), QImage::Format_Indexed8);
467     QVector<QRgb> colors(256);
468     for (int i=0; i<256; ++i)
469         colors[i] = qRgba(0, 0, 0, i);
470     indexed.setColorTable(colors);
471
472     for (int y=0; y<im.height(); ++y) {
473         uint *src = (uint*) im.scanLine(y);
474         uchar *dst = indexed.scanLine(y);
475         for (int x=0; x<im.width(); ++x) {
476             *dst = 255 - (qt_pow_gamma[qGray(0xffffffff - *src)] * 255. / 2047.);
477             ++dst;
478             ++src;
479         }
480     }
481
482     return indexed;
483 }
484
485 bool QFontEngineDirectWrite::supportsSubPixelPositions() const
486 {
487     return true;
488 }
489
490 QImage QFontEngineDirectWrite::imageForGlyph(glyph_t t,
491                                              QFixed subPixelPosition,
492                                              int margin,
493                                              const QTransform &xform)
494 {
495     glyph_metrics_t metrics = QFontEngine::boundingBox(t, xform);
496     int width = (metrics.width + margin * 2 + 4).ceil().toInt() ;
497     int height = (metrics.height + margin * 2 + 4).ceil().toInt();
498
499     UINT16 glyphIndex = t;
500     FLOAT glyphAdvance = metrics.xoff.toReal();
501
502     DWRITE_GLYPH_OFFSET glyphOffset;
503     glyphOffset.advanceOffset = 0;
504     glyphOffset.ascenderOffset = 0;
505
506     DWRITE_GLYPH_RUN glyphRun;
507     glyphRun.fontFace = m_directWriteFontFace;
508     glyphRun.fontEmSize = fontDef.pixelSize;
509     glyphRun.glyphCount = 1;
510     glyphRun.glyphIndices = &glyphIndex;
511     glyphRun.glyphAdvances = &glyphAdvance;
512     glyphRun.isSideways = false;
513     glyphRun.bidiLevel = 0;
514     glyphRun.glyphOffsets = &glyphOffset;
515
516     QFixed x = margin - metrics.x.round() + subPixelPosition;
517     QFixed y = margin - metrics.y.floor();
518
519     DWRITE_MATRIX transform;
520     transform.dx = x.toReal();
521     transform.dy = y.toReal();
522     transform.m11 = xform.m11();
523     transform.m12 = xform.m12();
524     transform.m21 = xform.m21();
525     transform.m22 = xform.m22();
526
527     IDWriteGlyphRunAnalysis *glyphAnalysis = NULL;
528     HRESULT hr = m_directWriteFactory->CreateGlyphRunAnalysis(
529                 &glyphRun,
530                 1.0f,
531                 &transform,
532                 DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC,
533                 DWRITE_MEASURING_MODE_NATURAL,
534                 0.0, 0.0,
535                 &glyphAnalysis
536                 );
537
538     if (SUCCEEDED(hr)) {
539         RECT rect;
540         rect.left = 0;
541         rect.top = 0;
542         rect.right = width;
543         rect.bottom = height;
544
545         int size = width * height * 3;
546         BYTE *alphaValues = new BYTE[size];
547         qMemSet(alphaValues, size, 0);
548
549         hr = glyphAnalysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1,
550                                                &rect,
551                                                alphaValues,
552                                                size);
553
554         if (SUCCEEDED(hr)) {
555             QImage img(width, height, QImage::Format_RGB32);
556             img.fill(0xffffffff);
557
558             for (int y=0; y<height; ++y) {
559                 uint *dest = reinterpret_cast<uint *>(img.scanLine(y));
560                 BYTE *src = alphaValues + width * 3 * y;
561
562                 for (int x=0; x<width; ++x) {
563                     dest[x] = *(src) << 16
564                             | *(src + 1) << 8
565                             | *(src + 2);
566
567                     src += 3;
568                 }
569             }
570
571             delete[] alphaValues;
572             glyphAnalysis->Release();
573
574             return img;
575         } else {
576             delete[] alphaValues;
577             glyphAnalysis->Release();
578
579             qErrnoWarning("QFontEngineDirectWrite::imageForGlyph: CreateAlphaTexture failed");
580         }
581
582     } else {
583         qErrnoWarning("QFontEngineDirectWrite::imageForGlyph: CreateGlyphRunAnalysis failed");
584     }
585
586     return QImage();
587 }
588
589 QImage QFontEngineDirectWrite::alphaRGBMapForGlyph(glyph_t t,
590                                                    QFixed subPixelPosition,
591                                                    int margin,
592                                                    const QTransform &xform)
593 {
594     QImage mask = imageForGlyph(t, subPixelPosition, margin, xform);
595     return mask.depth() == 32
596            ? mask
597            : mask.convertToFormat(QImage::Format_RGB32);
598 }
599
600 const char *QFontEngineDirectWrite::name() const
601 {
602     return 0;
603 }
604
605 bool QFontEngineDirectWrite::canRender(const QChar *string, int len)
606 {
607     QVarLengthArray<UINT32> codePoints(len);
608     int actualLength = 0;
609     for (int i=0; i<len; ++i, actualLength++)
610         codePoints[actualLength] = getChar(string, i, len);
611
612     QVarLengthArray<UINT16> glyphIndices(actualLength);
613     HRESULT hr = m_directWriteFontFace->GetGlyphIndices(codePoints.data(), actualLength,
614                                                         glyphIndices.data());
615     if (FAILED(hr)) {
616         qErrnoWarning(hr, "QFontEngineDirectWrite::canRender: GetGlyphIndices failed");
617         return false;
618     } else {
619         for (int i=0; i<glyphIndices.size(); ++i) {
620             if (glyphIndices.at(i) == 0)
621                 return false;
622         }
623
624         return true;
625     }
626 }
627
628 QFontEngine::Type QFontEngineDirectWrite::type() const
629 {
630     return QFontEngine::DirectWrite;
631 }
632
633 QFontEngine *QFontEngineDirectWrite::cloneWithSize(qreal pixelSize) const
634 {
635     QFontEngine *fontEngine = new QFontEngineDirectWrite(m_directWriteFactory, m_directWriteFontFace,
636                                                          pixelSize);
637
638     fontEngine->fontDef = fontDef;
639     fontEngine->fontDef.pixelSize = pixelSize;
640
641     return fontEngine;
642 }
643
644 QT_END_NAMESPACE
645
646 #endif // QT_NO_DIRECTWRITE