src/gui/painting/qtextureglyphcache.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtGui module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include <qmath.h>
       
    43 
       
    44 #include "qtextureglyphcache_p.h"
       
    45 
       
    46 #include "private/qnumeric_p.h"
       
    47 #include "private/qnativeimage_p.h"
       
    48 #include "private/qfontengine_ft_p.h"
       
    49 
       
    50 QT_BEGIN_NAMESPACE
       
    51 
       
    52 // #define CACHE_DEBUG
       
    53 
       
    54 void QTextureGlyphCache::populate(const QTextItemInt &ti,
       
    55                                   const QVarLengthArray<glyph_t> &glyphs,
       
    56                                   const QVarLengthArray<QFixedPoint> &)
       
    57 {
       
    58 #ifdef CACHE_DEBUG
       
    59     printf("Populating with '%s'\n", QString::fromRawData(ti.chars, ti.num_chars).toLatin1().data());
       
    60     qDebug() << " -> current transformation: " << m_transform;
       
    61 #endif
       
    62 
       
    63     m_current_textitem = &ti;
       
    64     const int margin = glyphMargin();
       
    65 
       
    66     QHash<glyph_t, Coord> listItemCoordinates;
       
    67     int rowHeight = 0;
       
    68 
       
    69     // check each glyph for its metrics and get the required rowHeight.
       
    70     for (int i=0; i < glyphs.size(); ++i) {
       
    71         const glyph_t glyph = glyphs[i];
       
    72         if (coords.contains(glyph))
       
    73             continue;
       
    74         if (listItemCoordinates.contains(glyph))
       
    75             continue;
       
    76         glyph_metrics_t metrics = ti.fontEngine->boundingBox(glyph, m_transform);
       
    77 
       
    78 #ifdef CACHE_DEBUG
       
    79         printf("'%c' (%4x): w=%.2f, h=%.2f, xoff=%.2f, yoff=%.2f, x=%.2f, y=%.2f, ti.ascent=%.2f, ti.descent=%.2f\n",
       
    80                ti.chars[i].toLatin1(),
       
    81                glyph,
       
    82                metrics.width.toReal(),
       
    83                metrics.height.toReal(),
       
    84                metrics.xoff.toReal(),
       
    85                metrics.yoff.toReal(),
       
    86                metrics.x.toReal(),
       
    87                metrics.y.toReal(),
       
    88                ti.ascent.toReal(),
       
    89                ti.descent.toReal());
       
    90 #endif
       
    91         int glyph_width = metrics.width.ceil().toInt();
       
    92         int glyph_height = metrics.height.ceil().toInt();
       
    93         if (glyph_height == 0 || glyph_width == 0)
       
    94             continue;
       
    95         glyph_width += margin * 2 + 4;
       
    96         glyph_height += margin * 2 + 4;
       
    97         // align to 8-bit boundary
       
    98         if (m_type == QFontEngineGlyphCache::Raster_Mono)
       
    99             glyph_width = (glyph_width+7)&~7;
       
   100 
       
   101         Coord c = { 0, 0, // will be filled in later
       
   102                     glyph_width,
       
   103                     glyph_height, // texture coords
       
   104                     metrics.x.truncate(),
       
   105                     -metrics.y.truncate() }; // baseline for horizontal scripts
       
   106 
       
   107         listItemCoordinates.insert(glyph, c);
       
   108         rowHeight = qMax(rowHeight, glyph_height);
       
   109     }
       
   110     if (listItemCoordinates.isEmpty())
       
   111         return;
       
   112 
       
   113     rowHeight += margin * 2;
       
   114     if (isNull())
       
   115         createCache(256, rowHeight);
       
   116 
       
   117     // now actually use the coords and paint the wanted glyps into cache.
       
   118     QHash<glyph_t, Coord>::iterator iter = listItemCoordinates.begin();
       
   119     while (iter != listItemCoordinates.end()) {
       
   120         Coord c = iter.value();
       
   121 
       
   122         if (m_cx + c.w > m_w) {
       
   123             // no room on the current line, start new glyph strip
       
   124             m_cx = 0;
       
   125             m_cy = m_h;
       
   126         }
       
   127         if (m_cy + c.h > m_h) {
       
   128             int new_height;
       
   129             if (m_cx == 0) { // add a whole row
       
   130                 new_height = m_h + rowHeight;
       
   131                 m_cy = m_h;
       
   132             } else { // just extend row
       
   133                 new_height = m_cy + rowHeight;
       
   134             }
       
   135             // if no room in the current texture - realloc a larger texture
       
   136             resizeTextureData(m_w, new_height);
       
   137             m_h = new_height;
       
   138         }
       
   139 
       
   140         c.x = m_cx;
       
   141         c.y = m_cy;
       
   142 
       
   143         fillTexture(c, iter.key());
       
   144         coords.insert(iter.key(), c);
       
   145 
       
   146         if (m_cx + c.w > m_w) {
       
   147             m_cx = 0;
       
   148             m_cy += rowHeight;
       
   149         } else {
       
   150             // for the Mono case, glyph_width is 8-bit aligned,
       
   151             // and therefore so will m_cx
       
   152             m_cx += c.w;
       
   153         }
       
   154         ++iter;
       
   155     }
       
   156 
       
   157 
       
   158 }
       
   159 
       
   160 QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g) const
       
   161 {
       
   162 #if defined(Q_WS_X11)
       
   163     if (m_transform.type() > QTransform::TxTranslate) {
       
   164         QFontEngineFT::GlyphFormat format = QFontEngineFT::Format_None;
       
   165         QImage::Format imageFormat = QImage::Format_Invalid;
       
   166         switch (m_type) {
       
   167         case Raster_RGBMask:
       
   168             format = QFontEngineFT::Format_A32;
       
   169             imageFormat = QImage::Format_RGB32;
       
   170             break;
       
   171         case Raster_A8:
       
   172             format = QFontEngineFT::Format_A8;
       
   173             imageFormat = QImage::Format_Indexed8;
       
   174             break;
       
   175         case Raster_Mono:
       
   176             format = QFontEngineFT::Format_Mono;
       
   177             imageFormat = QImage::Format_Mono;
       
   178             break;
       
   179         };
       
   180 
       
   181         QFontEngineFT *ft = static_cast<QFontEngineFT*> (m_current_textitem->fontEngine);
       
   182         QFontEngineFT::QGlyphSet *gset = ft->loadTransformedGlyphSet(m_transform);
       
   183 
       
   184         if (gset && ft->loadGlyphs(gset, &g, 1, format)) {
       
   185             QFontEngineFT::Glyph *glyph = gset->glyph_data.value(g);
       
   186             const int bytesPerLine = (format == QFontEngineFT::Format_Mono ? ((glyph->width + 31) & ~31) >> 3
       
   187                                : (glyph->width + 3) & ~3);
       
   188             return QImage(glyph->data, glyph->width, glyph->height, bytesPerLine, imageFormat);
       
   189         }
       
   190     } else
       
   191 #endif
       
   192     if (m_type == QFontEngineGlyphCache::Raster_RGBMask)
       
   193         return m_current_textitem->fontEngine->alphaRGBMapForGlyph(g, glyphMargin(), m_transform);
       
   194     else
       
   195         return m_current_textitem->fontEngine->alphaMapForGlyph(g, m_transform);
       
   196 
       
   197     return QImage();
       
   198 }
       
   199 
       
   200 /************************************************************************
       
   201  * QImageTextureGlyphCache
       
   202  */
       
   203 
       
   204 void QImageTextureGlyphCache::resizeTextureData(int width, int height)
       
   205 {
       
   206     m_image = m_image.copy(0, 0, width, height);
       
   207 }
       
   208 
       
   209 void QImageTextureGlyphCache::createTextureData(int width, int height)
       
   210 {
       
   211     switch (m_type) {
       
   212     case QFontEngineGlyphCache::Raster_Mono:
       
   213         m_image = QImage(width, height, QImage::Format_Mono);
       
   214         break;
       
   215     case QFontEngineGlyphCache::Raster_A8: {
       
   216         m_image = QImage(width, height, QImage::Format_Indexed8);
       
   217         m_image.fill(0);
       
   218         QVector<QRgb> colors(256);
       
   219         QRgb *it = colors.data();
       
   220         for (int i=0; i<256; ++i, ++it)
       
   221             *it = 0xff000000 | i | (i<<8) | (i<<16);
       
   222         m_image.setColorTable(colors);
       
   223         break;   }
       
   224     case QFontEngineGlyphCache::Raster_RGBMask:
       
   225         m_image = QImage(width, height, QImage::Format_RGB32);
       
   226         break;
       
   227     }
       
   228 }
       
   229 
       
   230 int QImageTextureGlyphCache::glyphMargin() const
       
   231 {
       
   232 #if defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA)
       
   233     return 0;
       
   234 #else
       
   235     return m_type == QFontEngineGlyphCache::Raster_RGBMask ? 2 : 0;
       
   236 #endif
       
   237 }
       
   238 
       
   239 void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g)
       
   240 {
       
   241     QImage mask = textureMapForGlyph(g);
       
   242 
       
   243 #ifdef CACHE_DEBUG
       
   244     printf("fillTexture of %dx%d at %d,%d in the cache of %dx%d\n", c.w, c.h, c.x, c.y, m_image.width(), m_image.height());
       
   245     if (mask.width() > c.w || mask.height() > c.h) {
       
   246         printf("   ERROR; mask is bigger than reserved space! %dx%d instead of %dx%d\n", mask.width(), mask.height(), c.w,c.h);
       
   247         return;
       
   248     }
       
   249 #endif
       
   250 
       
   251     if (m_type == QFontEngineGlyphCache::Raster_RGBMask) {
       
   252         QPainter p(&m_image);
       
   253         p.setCompositionMode(QPainter::CompositionMode_Source);
       
   254         p.fillRect(c.x, c.y, c.w, c.h, QColor(0,0,0,0)); // TODO optimize this
       
   255         p.drawImage(c.x, c.y, mask);
       
   256         p.end();
       
   257     } else if (m_type == QFontEngineGlyphCache::Raster_Mono) {
       
   258         if (mask.depth() > 1) {
       
   259             // TODO optimize this
       
   260             mask = mask.alphaChannel();
       
   261             mask.invertPixels();
       
   262             mask = mask.convertToFormat(QImage::Format_Mono);
       
   263         }
       
   264 
       
   265         int mw = qMin(mask.width(), c.w);
       
   266         int mh = qMin(mask.height(), c.h);
       
   267         uchar *d = m_image.bits();
       
   268         int dbpl = m_image.bytesPerLine();
       
   269 
       
   270         for (int y = 0; y < c.h; ++y) {
       
   271             uchar *dest = d + (c.y + y) *dbpl + c.x/8;
       
   272 
       
   273             if (y < mh) {
       
   274                 uchar *src = mask.scanLine(y);
       
   275                 for (int x = 0; x < c.w/8; ++x) {
       
   276                     if (x < (mw+7)/8)
       
   277                         dest[x] = src[x];
       
   278                     else
       
   279                         dest[x] = 0;
       
   280                 }
       
   281             } else {
       
   282                 for (int x = 0; x < c.w/8; ++x)
       
   283                     dest[x] = 0;
       
   284             }
       
   285         }
       
   286     } else { // A8
       
   287         int mw = qMin(mask.width(), c.w);
       
   288         int mh = qMin(mask.height(), c.h);
       
   289         uchar *d = m_image.bits();
       
   290         int dbpl = m_image.bytesPerLine();
       
   291 
       
   292         if (mask.depth() == 1) {
       
   293             for (int y = 0; y < c.h; ++y) {
       
   294                 uchar *dest = d + (c.y + y) *dbpl + c.x;
       
   295                 if (y < mh) {
       
   296                     uchar *src = (uchar *) mask.scanLine(y);
       
   297                     for (int x = 0; x < c.w; ++x) {
       
   298                         if (x < mw)
       
   299                             dest[x] = (src[x >> 3] & (1 << (7 - (x & 7)))) > 0 ? 255 : 0;
       
   300                     }
       
   301                 }
       
   302             }
       
   303         } else if (mask.depth() == 8) {
       
   304             for (int y = 0; y < c.h; ++y) {
       
   305                 uchar *dest = d + (c.y + y) *dbpl + c.x;
       
   306                 if (y < mh) {
       
   307                     uchar *src = (uchar *) mask.scanLine(y);
       
   308                     for (int x = 0; x < c.w; ++x) {
       
   309                         if (x < mw)
       
   310                             dest[x] = src[x];
       
   311                     }
       
   312                 }
       
   313             }
       
   314         }
       
   315     }
       
   316 
       
   317 #ifdef CACHE_DEBUG
       
   318 //     QPainter p(&m_image);
       
   319 //     p.drawLine(
       
   320     QPoint base(c.x + glyphMargin(), c.y + glyphMargin() + c.baseLineY-1);
       
   321     if (m_image.rect().contains(base))
       
   322         m_image.setPixel(base, 255);
       
   323     m_image.save(QString::fromLatin1("cache-%1-%2-%3.png")
       
   324                  .arg(m_current_textitem->font().family())
       
   325                  .arg(m_current_textitem->font().pointSize())
       
   326                  .arg(m_transform.type()));
       
   327 #endif
       
   328 }
       
   329 
       
   330 QT_END_NAMESPACE