diff options
Diffstat (limited to 'src/gui/text/qfontengine_qws.cpp')
-rw-r--r-- | src/gui/text/qfontengine_qws.cpp | 625 |
1 files changed, 625 insertions, 0 deletions
diff --git a/src/gui/text/qfontengine_qws.cpp b/src/gui/text/qfontengine_qws.cpp new file mode 100644 index 0000000..d776329 --- /dev/null +++ b/src/gui/text/qfontengine_qws.cpp @@ -0,0 +1,625 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 <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; +}; + + +QFontEngineQPF1::QFontEngineQPF1(const QFontDef&, const QString &fn) +{ + cache_cost = 1; + + int f = ::open( QFile::encodeName(fn), O_RDONLY ); + 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) + 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()); + ::close(f); + + d = new QFontEngineQPF1Data; + 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() +{ + 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); + } else { + image = QImage(glyph->metrics->width, glyph->metrics->height, QImage::Format_Indexed8); + for (int j=0; j<256; ++j) + image.setColor(j, 0xff000000 | j | (j<<8) | (j<<16)); + } + 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 |