1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
44 #include "qtextureglyphcache_p.h"
45 #include "private/qfontengine_p.h"
46 #include "private/qnumeric_p.h"
47 #include "private/qnativeimage_p.h"
51 // #define CACHE_DEBUG
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)
67 int QTextureGlyphCache::calculateSubPixelPositionCount(glyph_t glyph) const
69 // Test 12 different subpixel positions since it factors into 3*4 so it gives
70 // the coverage we need.
73 for (int i=0; i<12; ++i) {
74 QImage img = textureMapForGlyph(glyph, QFixed::fromReal(i / 12.0));
76 if (images.isEmpty()) {
79 m_current_fontengine->addGlyphsToPath(&glyph, &point, 1, &path, QTextItem::RenderFlags());
81 // Glyph is space, return 0 to indicate that we need to keep trying
88 for (int j=0; j<images.size(); ++j) {
89 if (images.at(j) == img) {
102 bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const glyph_t *glyphs,
103 const QFixedPoint *positions)
106 printf("Populating with %d glyphs\n", numGlyphs);
107 qDebug() << " -> current transformation: " << m_transform;
110 m_current_fontengine = fontEngine;
111 const int margin = m_current_fontengine->glyphMargin(m_type);
112 const int paddingDoubled = glyphPadding() * 2;
114 bool supportsSubPixelPositions = fontEngine->supportsSubPixelPositions();
115 if (fontEngine->m_subPixelPositionCount == 0) {
116 if (!supportsSubPixelPositions) {
117 fontEngine->m_subPixelPositionCount = 1;
120 while (fontEngine->m_subPixelPositionCount == 0 && i < numGlyphs)
121 fontEngine->m_subPixelPositionCount = calculateSubPixelPositionCount(glyphs[i++]);
125 QHash<GlyphAndSubPixelPosition, Coord> listItemCoordinates;
128 QFontEngine::GlyphFormat format;
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;
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];
139 QFixed subPixelPosition;
140 if (supportsSubPixelPositions) {
141 QFixed x = positions != 0 ? positions[i].x : QFixed();
142 subPixelPosition = fontEngine->subPixelPositionForX(x);
145 if (coords.contains(GlyphAndSubPixelPosition(glyph, subPixelPosition)))
147 if (listItemCoordinates.contains(GlyphAndSubPixelPosition(glyph, subPixelPosition)))
149 glyph_metrics_t metrics = fontEngine->alphaMapBoundingBox(glyph, subPixelPosition, m_transform, format);
152 printf("(%4x): w=%.2f, h=%.2f, xoff=%.2f, yoff=%.2f, x=%.2f, y=%.2f\n",
154 metrics.width.toReal(),
155 metrics.height.toReal(),
156 metrics.xoff.toReal(),
157 metrics.yoff.toReal(),
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);
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;
176 Coord c = { 0, 0, // will be filled in later
178 glyph_height, // texture coords
179 metrics.x.truncate(),
180 -metrics.y.truncate() }; // baseline for horizontal scripts
182 listItemCoordinates.insert(key, c);
183 rowHeight = qMax(rowHeight, glyph_height);
185 if (listItemCoordinates.isEmpty())
188 rowHeight += margin * 2 + paddingDoubled;
191 if (fontEngine->maxCharWidth() <= QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH)
192 m_w = QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH;
194 m_w = qt_next_power_of_two(fontEngine->maxCharWidth());
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();
203 m_currentRowHeight = qMax(m_currentRowHeight, c.h + margin * 2);
205 if (m_cx + c.w > requiredWidth) {
206 int new_width = requiredWidth*2;
207 while (new_width < m_cx + c.w)
209 if (new_width <= maxTextureWidth()) {
210 requiredWidth = new_width;
212 // no room on the current line, start new glyph strip
214 m_cy += m_currentRowHeight + paddingDoubled;
215 m_currentRowHeight = c.h + margin * 2; // New row
219 if (maxTextureHeight() > 0 && m_cy + c.h > maxTextureHeight()) {
220 // We can't make a cache of the required size, so we bail out
227 coords.insert(iter.key(), c);
228 m_pendingGlyphs.insert(iter.key(), c);
230 m_cx += c.w + paddingDoubled;
237 void QTextureGlyphCache::fillInPendingGlyphs()
239 if (m_pendingGlyphs.isEmpty())
242 int requiredHeight = m_h;
243 int requiredWidth = m_w; // Use a minimum size to avoid a lot of initial reallocations
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);
254 if (isNull() || requiredHeight > m_h || requiredWidth > m_w) {
256 createCache(qt_next_power_of_two(requiredWidth), qt_next_power_of_two(requiredHeight));
258 resizeCache(qt_next_power_of_two(requiredWidth), qt_next_power_of_two(requiredHeight));
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);
271 m_pendingGlyphs.clear();
274 QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g, QFixed subPixelPosition) const
276 if (m_type == QFontEngineGlyphCache::Raster_RGBMask)
277 return m_current_fontengine->alphaRGBMapForGlyph(g, subPixelPosition, m_transform);
279 return m_current_fontengine->alphaMapForGlyph(g, subPixelPosition, m_transform);
284 /************************************************************************
285 * QImageTextureGlyphCache
288 void QImageTextureGlyphCache::resizeTextureData(int width, int height)
290 m_image = m_image.copy(0, 0, width, height);
293 void QImageTextureGlyphCache::createTextureData(int width, int height)
296 case QFontEngineGlyphCache::Raster_Mono:
297 m_image = QImage(width, height, QImage::Format_Mono);
299 case QFontEngineGlyphCache::Raster_A8: {
300 m_image = QImage(width, height, QImage::Format_Indexed8);
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);
308 case QFontEngineGlyphCache::Raster_RGBMask:
309 m_image = QImage(width, height, QImage::Format_RGB32);
314 void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g, QFixed subPixelPosition)
316 QImage mask = textureMapForGlyph(g, subPixelPosition);
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);
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(),
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);
335 } else if (m_type == QFontEngineGlyphCache::Raster_Mono) {
336 if (mask.depth() > 1) {
337 // TODO optimize this
338 mask = mask.alphaChannel();
340 mask = mask.convertToFormat(QImage::Format_Mono);
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();
348 for (int y = 0; y < c.h; ++y) {
349 uchar *dest = d + (c.y + y) *dbpl + c.x/8;
352 uchar *src = mask.scanLine(y);
353 for (int x = 0; x < c.w/8; ++x) {
360 for (int x = 0; x < c.w/8; ++x)
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();
370 if (mask.depth() == 1) {
371 for (int y = 0; y < c.h; ++y) {
372 uchar *dest = d + (c.y + y) *dbpl + c.x;
374 uchar *src = (uchar *) mask.scanLine(y);
375 for (int x = 0; x < c.w; ++x) {
377 dest[x] = (src[x >> 3] & (1 << (7 - (x & 7)))) > 0 ? 255 : 0;
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;
385 uchar *src = (uchar *) mask.scanLine(y);
386 for (int x = 0; x < c.w; ++x) {
396 // QPainter p(&m_image);
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)));