Make QRegion not need to be friends with QVector
[profile/ivi/qtbase.git] / src / gui / painting / qtextureglyphcache.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <qmath.h>
43
44 #include "qtextureglyphcache_p.h"
45 #include "private/qfontengine_p.h"
46 #include "private/qnumeric_p.h"
47 #include "private/qnativeimage_p.h"
48
49 QT_BEGIN_NAMESPACE
50
51 // #define CACHE_DEBUG
52
53 // returns the highest number closest to v, which is a power of 2
54 // NB! assumes 32 bit ints
55 static inline int qt_next_power_of_two(int v)
56 {
57     v--;
58     v |= v >> 1;
59     v |= v >> 2;
60     v |= v >> 4;
61     v |= v >> 8;
62     v |= v >> 16;
63     ++v;
64     return v;
65 }
66
67 int QTextureGlyphCache::calculateSubPixelPositionCount(glyph_t glyph) const
68 {
69     // Test 12 different subpixel positions since it factors into 3*4 so it gives
70     // the coverage we need.
71
72     QList<QImage> images;
73     for (int i=0; i<12; ++i) {
74         QImage img = textureMapForGlyph(glyph, QFixed::fromReal(i / 12.0));
75
76         if (images.isEmpty()) {
77             QPainterPath path;
78             QFixedPoint point;
79             m_current_fontengine->addGlyphsToPath(&glyph, &point, 1, &path, QTextItem::RenderFlags());
80
81             // Glyph is space, return 0 to indicate that we need to keep trying
82             if (path.isEmpty())
83                 break;
84
85             images.append(img);
86         } else {
87             bool found = false;
88             for (int j=0; j<images.size(); ++j) {
89                 if (images.at(j) == img) {
90                     found = true;
91                     break;
92                 }
93             }
94             if (!found)
95                 images.append(img);
96         }
97     }
98
99     return images.size();
100 }
101
102 bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const glyph_t *glyphs,
103                                                 const QFixedPoint *positions)
104 {
105 #ifdef CACHE_DEBUG
106     printf("Populating with %d glyphs\n", numGlyphs);
107     qDebug() << " -> current transformation: " << m_transform;
108 #endif
109
110     m_current_fontengine = fontEngine;
111     const int margin = m_current_fontengine->glyphMargin(m_type);
112     const int paddingDoubled = glyphPadding() * 2;
113
114     bool supportsSubPixelPositions = fontEngine->supportsSubPixelPositions();
115     if (fontEngine->m_subPixelPositionCount == 0) {
116         if (!supportsSubPixelPositions) {
117             fontEngine->m_subPixelPositionCount = 1;
118         } else {
119             int i = 0;
120             while (fontEngine->m_subPixelPositionCount == 0 && i < numGlyphs)
121                 fontEngine->m_subPixelPositionCount = calculateSubPixelPositionCount(glyphs[i++]);
122         }
123     }
124
125     QHash<GlyphAndSubPixelPosition, Coord> listItemCoordinates;
126     int rowHeight = 0;
127
128     QFontEngine::GlyphFormat format;
129     switch (m_type) {
130     case Raster_A8: format = QFontEngine::Format_A8; break;
131     case Raster_RGBMask: format = QFontEngine::Format_A32; break;
132     default: format = QFontEngine::Format_Mono; break;
133     }
134
135     // check each glyph for its metrics and get the required rowHeight.
136     for (int i=0; i < numGlyphs; ++i) {
137         const glyph_t glyph = glyphs[i];
138
139         QFixed subPixelPosition;
140         if (supportsSubPixelPositions) {
141             QFixed x = positions != 0 ? positions[i].x : QFixed();
142             subPixelPosition = fontEngine->subPixelPositionForX(x);
143         }
144
145         if (coords.contains(GlyphAndSubPixelPosition(glyph, subPixelPosition)))
146             continue;
147         if (listItemCoordinates.contains(GlyphAndSubPixelPosition(glyph, subPixelPosition)))
148             continue;
149         glyph_metrics_t metrics = fontEngine->alphaMapBoundingBox(glyph, subPixelPosition, m_transform, format);
150
151 #ifdef CACHE_DEBUG
152         printf("(%4x): w=%.2f, h=%.2f, xoff=%.2f, yoff=%.2f, x=%.2f, y=%.2f\n",
153                glyph,
154                metrics.width.toReal(),
155                metrics.height.toReal(),
156                metrics.xoff.toReal(),
157                metrics.yoff.toReal(),
158                metrics.x.toReal(),
159                metrics.y.toReal());
160 #endif        
161         GlyphAndSubPixelPosition key(glyph, subPixelPosition);
162         int glyph_width = metrics.width.ceil().toInt();
163         int glyph_height = metrics.height.ceil().toInt();
164         if (glyph_height == 0 || glyph_width == 0) {
165             // Avoid multiple calls to boundingBox() for non-printable characters
166             Coord c = { 0, 0, 0, 0, 0, 0 };
167             coords.insert(key, c);
168             continue;
169         }
170         glyph_width += margin * 2 + 4;
171         glyph_height += margin * 2 + 4;
172         // align to 8-bit boundary
173         if (m_type == QFontEngineGlyphCache::Raster_Mono)
174             glyph_width = (glyph_width+7)&~7;
175
176         Coord c = { 0, 0, // will be filled in later
177                     glyph_width,
178                     glyph_height, // texture coords
179                     metrics.x.truncate(),
180                     -metrics.y.truncate() }; // baseline for horizontal scripts
181
182         listItemCoordinates.insert(key, c);
183         rowHeight = qMax(rowHeight, glyph_height);
184     }
185     if (listItemCoordinates.isEmpty())
186         return true;
187
188     rowHeight += margin * 2 + paddingDoubled;
189
190     if (m_w == 0) {
191         if (fontEngine->maxCharWidth() <= QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH)
192             m_w = QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH;
193         else
194             m_w = qt_next_power_of_two(fontEngine->maxCharWidth());
195     }
196
197     // now actually use the coords and paint the wanted glyps into cache.
198     QHash<GlyphAndSubPixelPosition, Coord>::iterator iter = listItemCoordinates.begin();
199     int requiredWidth = m_w;
200     while (iter != listItemCoordinates.end()) {
201         Coord c = iter.value();
202
203         m_currentRowHeight = qMax(m_currentRowHeight, c.h + margin * 2);
204
205         if (m_cx + c.w > requiredWidth) {
206             int new_width = requiredWidth*2;
207             while (new_width < m_cx + c.w)
208                 new_width *= 2;
209             if (new_width <= maxTextureWidth()) {
210                 requiredWidth = new_width;
211             } else {
212                 // no room on the current line, start new glyph strip
213                 m_cx = 0;
214                 m_cy += m_currentRowHeight + paddingDoubled;
215                 m_currentRowHeight = c.h + margin * 2; // New row
216             }
217         }
218
219         if (maxTextureHeight() > 0 && m_cy + c.h > maxTextureHeight()) {
220             // We can't make a cache of the required size, so we bail out
221             return false;
222         }
223
224         c.x = m_cx;
225         c.y = m_cy;
226
227         coords.insert(iter.key(), c);
228         m_pendingGlyphs.insert(iter.key(), c);
229
230         m_cx += c.w + paddingDoubled;
231         ++iter;
232     }
233     return true;
234
235 }
236
237 void QTextureGlyphCache::fillInPendingGlyphs()
238 {
239     if (m_pendingGlyphs.isEmpty())
240         return;
241
242     int requiredHeight = m_h;
243     int requiredWidth = m_w; // Use a minimum size to avoid a lot of initial reallocations
244     {
245         QHash<GlyphAndSubPixelPosition, Coord>::iterator iter = m_pendingGlyphs.begin();
246         while (iter != m_pendingGlyphs.end()) {
247             Coord c = iter.value();
248             requiredHeight = qMax(requiredHeight, c.y + c.h);
249             requiredWidth = qMax(requiredWidth, c.x + c.w);
250             ++iter;
251         }
252     }
253
254     if (isNull() || requiredHeight > m_h || requiredWidth > m_w) {
255         if (isNull())
256             createCache(qt_next_power_of_two(requiredWidth), qt_next_power_of_two(requiredHeight));
257         else
258             resizeCache(qt_next_power_of_two(requiredWidth), qt_next_power_of_two(requiredHeight));
259     }
260
261     {
262         QHash<GlyphAndSubPixelPosition, Coord>::iterator iter = m_pendingGlyphs.begin();
263         while (iter != m_pendingGlyphs.end()) {
264             GlyphAndSubPixelPosition key = iter.key();
265             fillTexture(iter.value(), key.glyph, key.subPixelPosition);
266
267             ++iter;
268         }
269     }
270
271     m_pendingGlyphs.clear();
272 }
273
274 QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g, QFixed subPixelPosition) const
275 {
276     if (m_type == QFontEngineGlyphCache::Raster_RGBMask)
277         return m_current_fontengine->alphaRGBMapForGlyph(g, subPixelPosition, m_transform);
278     else
279         return m_current_fontengine->alphaMapForGlyph(g, subPixelPosition, m_transform);
280
281     return QImage();
282 }
283
284 /************************************************************************
285  * QImageTextureGlyphCache
286  */
287
288 void QImageTextureGlyphCache::resizeTextureData(int width, int height)
289 {
290     m_image = m_image.copy(0, 0, width, height);
291 }
292
293 void QImageTextureGlyphCache::createTextureData(int width, int height)
294 {
295     switch (m_type) {
296     case QFontEngineGlyphCache::Raster_Mono:
297         m_image = QImage(width, height, QImage::Format_Mono);
298         break;
299     case QFontEngineGlyphCache::Raster_A8: {
300         m_image = QImage(width, height, QImage::Format_Indexed8);
301         m_image.fill(0);
302         QVector<QRgb> colors(256);
303         QRgb *it = colors.data();
304         for (int i=0; i<256; ++i, ++it)
305             *it = 0xff000000 | i | (i<<8) | (i<<16);
306         m_image.setColorTable(colors);
307         break;   }
308     case QFontEngineGlyphCache::Raster_RGBMask:
309         m_image = QImage(width, height, QImage::Format_RGB32);
310         break;
311     }
312 }
313
314 void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g, QFixed subPixelPosition)
315 {
316     QImage mask = textureMapForGlyph(g, subPixelPosition);
317
318 #ifdef CACHE_DEBUG
319     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());
320     if (mask.width() > c.w || mask.height() > c.h) {
321         printf("   ERROR; mask is bigger than reserved space! %dx%d instead of %dx%d\n", mask.width(), mask.height(), c.w,c.h);
322         return;
323     }
324 #endif
325
326     if (m_type == QFontEngineGlyphCache::Raster_RGBMask) {        
327         QImage ref(m_image.bits() + (c.x * 4 + c.y * m_image.bytesPerLine()),
328                    qMax(mask.width(), c.w), qMax(mask.height(), c.h), m_image.bytesPerLine(),
329                    m_image.format());
330         QPainter p(&ref);
331         p.setCompositionMode(QPainter::CompositionMode_Source);
332         p.fillRect(0, 0, c.w, c.h, QColor(0,0,0,0)); // TODO optimize this
333         p.drawImage(0, 0, mask);
334         p.end();
335     } else if (m_type == QFontEngineGlyphCache::Raster_Mono) {
336         if (mask.depth() > 1) {
337             // TODO optimize this
338             mask = mask.alphaChannel();
339             mask.invertPixels();
340             mask = mask.convertToFormat(QImage::Format_Mono);
341         }
342
343         int mw = qMin(mask.width(), c.w);
344         int mh = qMin(mask.height(), c.h);
345         uchar *d = m_image.bits();
346         int dbpl = m_image.bytesPerLine();
347
348         for (int y = 0; y < c.h; ++y) {
349             uchar *dest = d + (c.y + y) *dbpl + c.x/8;
350
351             if (y < mh) {
352                 uchar *src = mask.scanLine(y);
353                 for (int x = 0; x < c.w/8; ++x) {
354                     if (x < (mw+7)/8)
355                         dest[x] = src[x];
356                     else
357                         dest[x] = 0;
358                 }
359             } else {
360                 for (int x = 0; x < c.w/8; ++x)
361                     dest[x] = 0;
362             }
363         }
364     } else { // A8
365         int mw = qMin(mask.width(), c.w);
366         int mh = qMin(mask.height(), c.h);
367         uchar *d = m_image.bits();
368         int dbpl = m_image.bytesPerLine();
369
370         if (mask.depth() == 1) {
371             for (int y = 0; y < c.h; ++y) {
372                 uchar *dest = d + (c.y + y) *dbpl + c.x;
373                 if (y < mh) {
374                     uchar *src = (uchar *) mask.scanLine(y);
375                     for (int x = 0; x < c.w; ++x) {
376                         if (x < mw)
377                             dest[x] = (src[x >> 3] & (1 << (7 - (x & 7)))) > 0 ? 255 : 0;
378                     }
379                 }
380             }
381         } else if (mask.depth() == 8) {
382             for (int y = 0; y < c.h; ++y) {
383                 uchar *dest = d + (c.y + y) *dbpl + c.x;
384                 if (y < mh) {
385                     uchar *src = (uchar *) mask.scanLine(y);
386                     for (int x = 0; x < c.w; ++x) {
387                         if (x < mw)
388                             dest[x] = src[x];
389                     }
390                 }
391             }
392         }
393     }
394
395 #ifdef CACHE_DEBUG
396 //     QPainter p(&m_image);
397 //     p.drawLine(
398     int margin = m_current_fontengine ? m_current_fontengine->glyphMargin(m_type) : 0;
399     QPoint base(c.x + margin, c.y + margin + c.baseLineY-1);
400     if (m_image.rect().contains(base))
401         m_image.setPixel(base, 255);
402     m_image.save(QString::fromLatin1("cache-%1.png").arg(qint64(this)));
403 #endif
404 }
405
406 QT_END_NAMESPACE