src/gui/text/qfontengine_qws.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/text/qfontengine_qws.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,634 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfontengine_p.h"
+#include <private/qunicodetables_p.h>
+#include <qwsdisplay_qws.h>
+#include <qvarlengtharray.h>
+#include <private/qpainter_p.h>
+#include <private/qpaintengine_raster_p.h>
+#include <private/qpdf_p.h>
+#include "qtextengine_p.h"
+#include "private/qcore_unix_p.h" // overrides QT_OPEN
+
+#include <qdebug.h>
+
+
+#ifndef QT_NO_QWS_QPF
+
+#include "qfile.h"
+#include "qdir.h"
+
+#define QT_USE_MMAP
+#include <stdlib.h>
+
+#ifdef QT_USE_MMAP
+// for mmap
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#  if defined(QT_LINUXBASE) && !defined(MAP_FILE)
+     // LSB 3.2 does not define MAP_FILE
+#    define MAP_FILE 0
+#  endif
+
+#endif
+
+#endif // QT_NO_QWS_QPF
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_QWS_QPF
+QT_BEGIN_INCLUDE_NAMESPACE
+#include "qplatformdefs.h"
+QT_END_INCLUDE_NAMESPACE
+
+static inline unsigned int getChar(const QChar *str, int &i, const int len)
+{
+    unsigned int uc = str[i].unicode();
+    if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) {
+        uint low = str[i+1].unicode();
+       if (low >= 0xdc00 && low < 0xe000) {
+            uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000;
+            ++i;
+        }
+    }
+    return uc;
+}
+
+#define FM_SMOOTH 1
+
+
+class Q_PACKED QPFGlyphMetrics {
+
+public:
+    quint8 linestep;
+    quint8 width;
+    quint8 height;
+    quint8 flags;
+
+    qint8 bearingx;      // Difference from pen position to glyph's left bbox
+    quint8 advance;       // Difference between pen positions
+    qint8 bearingy;      // Used for putting characters on baseline
+
+    qint8 reserved;      // Do not use
+
+    // Flags:
+    // RendererOwnsData - the renderer is responsible for glyph data
+    //                    memory deletion otherwise QPFGlyphTree must
+    //                    delete [] the data when the glyph is deleted.
+    enum Flags { RendererOwnsData=0x01 };
+};
+
+class QPFGlyph {
+public:
+    QPFGlyph() { metrics=0; data=0; }
+    QPFGlyph(QPFGlyphMetrics* m, uchar* d) :
+	metrics(m), data(d) { }
+    ~QPFGlyph() {}
+
+    QPFGlyphMetrics* metrics;
+    uchar* data;
+};
+
+struct Q_PACKED QPFFontMetrics{
+    qint8 ascent,descent;
+    qint8 leftbearing,rightbearing;
+    quint8 maxwidth;
+    qint8 leading;
+    quint8 flags;
+    quint8 underlinepos;
+    quint8 underlinewidth;
+    quint8 reserved3;
+};
+
+
+class QPFGlyphTree {
+public:
+    /* reads in a tree like this:
+
+       A-Z
+       /   \
+       0-9   a-z
+
+       etc.
+
+    */
+    glyph_t min,max;
+    QPFGlyphTree* less;
+    QPFGlyphTree* more;
+    QPFGlyph* glyph;
+public:
+#ifdef QT_USE_MMAP
+    QPFGlyphTree(uchar*& data)
+    {
+        read(data);
+    }
+#else
+    QPFGlyphTree(QIODevice& f)
+    {
+        read(f);
+    }
+#endif
+
+    ~QPFGlyphTree()
+    {
+        // NOTE: does not delete glyph[*].metrics or .data.
+        //       the caller does this (only they know who owns
+        //       the data).  See clear().
+        delete less;
+        delete more;
+        delete [] glyph;
+    }
+
+    bool inFont(glyph_t g) const
+    {
+        if ( g < min ) {
+            if ( !less )
+                return false;
+            return less->inFont(g);
+        } else if ( g > max ) {
+            if ( !more )
+                return false;
+            return more->inFont(g);
+        }
+        return true;
+    }
+
+    QPFGlyph* get(glyph_t g)
+    {
+        if ( g < min ) {
+            if ( !less )
+                return 0;
+            return less->get(g);
+        } else if ( g > max ) {
+            if ( !more )
+                return 0;
+            return more->get(g);
+        }
+        return &glyph[g - min];
+    }
+    int totalChars() const
+    {
+        if ( !this ) return 0;
+        return max-min+1 + less->totalChars() + more->totalChars();
+    }
+    int weight() const
+    {
+        if ( !this ) return 0;
+        return 1 + less->weight() + more->weight();
+    }
+
+    void dump(int indent=0)
+    {
+        for (int i=0; i<indent; i++) printf(" ");
+        printf("%d..%d",min,max);
+        //if ( indent == 0 )
+        printf(" (total %d)",totalChars());
+        printf("\n");
+        if ( less ) less->dump(indent+1);
+        if ( more ) more->dump(indent+1);
+    }
+
+private:
+    QPFGlyphTree()
+    {
+    }
+
+#ifdef QT_USE_MMAP
+    void read(uchar*& data)
+    {
+        // All node data first
+        readNode(data);
+        // Then all non-video data
+        readMetrics(data);
+        // Then all video data
+        readData(data);
+    }
+#else
+    void read(QIODevice& f)
+    {
+        // All node data first
+        readNode(f);
+        // Then all non-video data
+        readMetrics(f);
+        // Then all video data
+        readData(f);
+    }
+#endif
+
+#ifdef QT_USE_MMAP
+    void readNode(uchar*& data)
+    {
+        uchar rw = *data++;
+        uchar cl = *data++;
+        min = (rw << 8) | cl;
+        rw = *data++;
+        cl = *data++;
+        max = (rw << 8) | cl;
+        int flags = *data++;
+        if ( flags & 1 )
+            less = new QPFGlyphTree;
+        else
+            less = 0;
+        if ( flags & 2 )
+            more = new QPFGlyphTree;
+        else
+            more = 0;
+        int n = max-min+1;
+        glyph = new QPFGlyph[n];
+
+        if ( less )
+            less->readNode(data);
+        if ( more )
+            more->readNode(data);
+    }
+#else
+    void readNode(QIODevice& f)
+    {
+        uchar rw = f.getch();
+        uchar cl = f.getch();
+        min = (rw << 8) | cl;
+        rw = f.getch();
+        cl = f.getch();
+        max = (rw << 8) | cl;
+        int flags = f.getch();
+        if ( flags & 1 )
+            less = new QPFGlyphTree;
+        else
+            less = 0;
+        if ( flags & 2 )
+            more = new QPFGlyphTree;
+        else
+            more = 0;
+        int n = max-min+1;
+        glyph = new QPFGlyph[n];
+
+        if ( less )
+            less->readNode(f);
+        if ( more )
+            more->readNode(f);
+    }
+#endif
+
+#ifdef QT_USE_MMAP
+    void readMetrics(uchar*& data)
+    {
+        int n = max-min+1;
+        for (int i=0; i<n; i++) {
+            glyph[i].metrics = (QPFGlyphMetrics*)data;
+            data += sizeof(QPFGlyphMetrics);
+        }
+        if ( less )
+            less->readMetrics(data);
+        if ( more )
+            more->readMetrics(data);
+    }
+#else
+    void readMetrics(QIODevice& f)
+    {
+        int n = max-min+1;
+        for (int i=0; i<n; i++) {
+            glyph[i].metrics = new QPFGlyphMetrics;
+            f.readBlock((char*)glyph[i].metrics, sizeof(QPFGlyphMetrics));
+        }
+        if ( less )
+            less->readMetrics(f);
+        if ( more )
+            more->readMetrics(f);
+    }
+#endif
+
+#ifdef QT_USE_MMAP
+    void readData(uchar*& data)
+    {
+        int n = max-min+1;
+        for (int i=0; i<n; i++) {
+            QSize s( glyph[i].metrics->width, glyph[i].metrics->height );
+            //######### s = qt_screen->mapToDevice( s );
+            uint datasize = glyph[i].metrics->linestep * s.height();
+            glyph[i].data = data; data += datasize;
+        }
+        if ( less )
+            less->readData(data);
+        if ( more )
+            more->readData(data);
+    }
+#else
+    void readData(QIODevice& f)
+    {
+        int n = max-min+1;
+        for (int i=0; i<n; i++) {
+            QSize s( glyph[i].metrics->width, glyph[i].metrics->height );
+            //############### s = qt_screen->mapToDevice( s );
+            uint datasize = glyph[i].metrics->linestep * s.height();
+            glyph[i].data = new uchar[datasize]; // ### deleted?
+            f.readBlock((char*)glyph[i].data, datasize);
+        }
+        if ( less )
+            less->readData(f);
+        if ( more )
+            more->readData(f);
+    }
+#endif
+
+};
+
+class QFontEngineQPF1Data
+{
+public:
+    QPFFontMetrics fm;
+    QPFGlyphTree *tree;
+    void *mmapStart;
+    size_t mmapLength;
+};
+
+
+QFontEngineQPF1::QFontEngineQPF1(const QFontDef&, const QString &fn)
+{
+    cache_cost = 1;
+
+    int f = QT_OPEN( QFile::encodeName(fn), O_RDONLY, 0);
+    Q_ASSERT(f>=0);
+    QT_STATBUF st;
+    if ( QT_FSTAT( f, &st ) )
+        qFatal("Failed to stat %s",QFile::encodeName(fn).data());
+    uchar* data = (uchar*)mmap( 0, // any address
+                                st.st_size, // whole file
+                                PROT_READ, // read-only memory
+#if !defined(Q_OS_SOLARIS) && !defined(Q_OS_QNX4) && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_VXWORKS)
+                                MAP_FILE | MAP_PRIVATE, // swap-backed map from file
+#else
+                                MAP_PRIVATE,
+#endif
+                                f, 0 ); // from offset 0 of f
+#if defined(Q_OS_QNX4) && !defined(MAP_FAILED)
+#define MAP_FAILED ((void *)-1)
+#endif
+    if ( !data || data == (uchar*)MAP_FAILED )
+        qFatal("Failed to mmap %s",QFile::encodeName(fn).data());
+    QT_CLOSE(f);
+
+    d = new QFontEngineQPF1Data;
+    d->mmapStart = data;
+    d->mmapLength = st.st_size;
+    memcpy(reinterpret_cast<char*>(&d->fm),data,sizeof(d->fm));
+
+    data += sizeof(d->fm);
+    d->tree = new QPFGlyphTree(data);
+    glyphFormat = (d->fm.flags & FM_SMOOTH) ? QFontEngineGlyphCache::Raster_A8
+                  : QFontEngineGlyphCache::Raster_Mono;
+#if 0
+    qDebug() << "font file" << fn
+             << "ascent" << d->fm.ascent << "descent" << d->fm.descent
+             << "leftbearing" << d->fm.leftbearing
+             << "rightbearing" << d->fm.rightbearing
+             << "maxwidth" << d->fm.maxwidth
+             << "leading" << d->fm.leading
+             << "flags" << d->fm.flags
+             << "underlinepos" << d->fm.underlinepos
+             << "underlinewidth" << d->fm.underlinewidth;
+#endif
+}
+
+QFontEngineQPF1::~QFontEngineQPF1()
+{
+    if (d->mmapStart)
+        munmap(d->mmapStart, d->mmapLength);
+    delete d->tree;
+    delete d;
+}
+
+
+bool QFontEngineQPF1::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const
+{
+    if(*nglyphs < len) {
+        *nglyphs = len;
+        return false;
+    }
+    *nglyphs = 0;
+
+    bool mirrored = flags & QTextEngine::RightToLeft;
+    for(int i = 0; i < len; i++) {
+        unsigned int uc = getChar(str, i, len);
+        if (mirrored)
+            uc = QChar::mirroredChar(uc);
+        glyphs->glyphs[*nglyphs] = uc < 0x10000 ? uc : 0;
+        ++*nglyphs;
+    }
+
+    glyphs->numGlyphs = *nglyphs;
+
+    if (flags & QTextEngine::GlyphIndicesOnly)
+        return true;
+
+    recalcAdvances(glyphs, flags);
+
+    return true;
+}
+
+void QFontEngineQPF1::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const
+{
+    for(int i = 0; i < glyphs->numGlyphs; i++) {
+        QPFGlyph *glyph = d->tree->get(glyphs->glyphs[i]);
+
+        glyphs->advances_x[i] = glyph ? glyph->metrics->advance : 0;
+        glyphs->advances_y[i] = 0;
+
+        if (!glyph)
+            glyphs->glyphs[i] = 0;
+    }
+}
+
+void QFontEngineQPF1::draw(QPaintEngine *p, qreal _x, qreal _y, const QTextItemInt &si)
+{
+    QPaintEngineState *pState = p->state;
+    QRasterPaintEngine *paintEngine = static_cast<QRasterPaintEngine*>(p);
+
+    QTransform matrix = pState->transform();
+    matrix.translate(_x, _y);
+    QFixed x = QFixed::fromReal(matrix.dx());
+    QFixed y = QFixed::fromReal(matrix.dy());
+
+    QVarLengthArray<QFixedPoint> positions;
+    QVarLengthArray<glyph_t> glyphs;
+    getGlyphPositions(si.glyphs, matrix, si.flags, glyphs, positions);
+    if (glyphs.size() == 0)
+        return;
+
+    int depth = (d->fm.flags & FM_SMOOTH) ? 8 : 1;
+    for(int i = 0; i < glyphs.size(); i++) {
+        const QPFGlyph *glyph = d->tree->get(glyphs[i]);
+        if (!glyph)
+            continue;
+
+        int bpl = glyph->metrics->linestep;
+
+        if(glyph->data)
+            paintEngine->alphaPenBlt(glyph->data, bpl, depth,
+                                     qRound(positions[i].x) + glyph->metrics->bearingx,
+                                     qRound(positions[i].y) - glyph->metrics->bearingy,
+                                     glyph->metrics->width,glyph->metrics->height);
+    }
+}
+
+
+QImage QFontEngineQPF1::alphaMapForGlyph(glyph_t g)
+{
+    const QPFGlyph *glyph = d->tree->get(g);
+    if (!glyph)
+	return QImage();
+
+    int mono = !(d->fm.flags & FM_SMOOTH);
+
+    const uchar *bits = glyph->data;//((const uchar *) glyph);
+
+    QImage image;
+    if (mono) {
+        image = QImage((glyph->metrics->width+7)&~7, glyph->metrics->height, QImage::Format_Mono);
+        image.setColor(0, qRgba(0, 0, 0, 0));
+        image.setColor(1, qRgba(0, 0, 0, 255));
+    } else {
+        image = QImage(glyph->metrics->width, glyph->metrics->height, QImage::Format_Indexed8);
+        for (int j=0; j<256; ++j)
+            image.setColor(j, qRgba(0, 0, 0, j));
+    }
+    for (int i=0; i<glyph->metrics->height; ++i) {
+        memcpy(image.scanLine(i), bits, glyph->metrics->linestep);
+        bits += glyph->metrics->linestep;
+    }
+    return image;
+}
+
+
+
+void QFontEngineQPF1::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, QPainterPath *path, QTextItem::RenderFlags flags)
+{
+    addBitmapFontToPath(x, y, glyphs, path, flags);
+}
+
+glyph_metrics_t QFontEngineQPF1::boundingBox(const QGlyphLayout &glyphs)
+{
+   if (glyphs.numGlyphs == 0)
+        return glyph_metrics_t();
+
+    QFixed w = 0;
+    for (int i = 0; i < glyphs.numGlyphs; ++i)
+        w += glyphs.effectiveAdvance(i);
+    return glyph_metrics_t(0, -ascent(), w, ascent()+descent()+1, w, 0);
+}
+
+glyph_metrics_t QFontEngineQPF1::boundingBox(glyph_t glyph)
+{
+    const QPFGlyph *g = d->tree->get(glyph);
+    if (!g)
+        return glyph_metrics_t();
+    Q_ASSERT(g);
+    return glyph_metrics_t(g->metrics->bearingx, -g->metrics->bearingy,
+                            g->metrics->width, g->metrics->height,
+                            g->metrics->advance, 0);
+}
+
+QFixed QFontEngineQPF1::ascent() const
+{
+    return d->fm.ascent;
+}
+
+QFixed QFontEngineQPF1::descent() const
+{
+    return d->fm.descent;
+}
+
+QFixed QFontEngineQPF1::leading() const
+{
+    return d->fm.leading;
+}
+
+qreal QFontEngineQPF1::maxCharWidth() const
+{
+    return d->fm.maxwidth;
+}
+/*
+const char *QFontEngineQPF1::name() const
+{
+    return "qt";
+}
+*/
+bool QFontEngineQPF1::canRender(const QChar *str, int len)
+{
+    for(int i = 0; i < len; i++)
+        if (!d->tree->inFont(str[i].unicode()))
+            return false;
+    return true;
+}
+
+QFontEngine::Type QFontEngineQPF1::type() const
+{
+    return QPF1;
+}
+
+qreal QFontEngineQPF1::minLeftBearing() const
+{
+    return d->fm.leftbearing;
+}
+
+qreal QFontEngineQPF1::minRightBearing() const
+{
+    return d->fm.rightbearing;
+}
+
+QFixed QFontEngineQPF1::underlinePosition() const
+{
+    return d->fm.underlinepos;
+}
+
+QFixed QFontEngineQPF1::lineThickness() const
+{
+    return d->fm.underlinewidth;
+}
+
+#endif //QT_NO_QWS_QPF
+
+QT_END_NAMESPACE