qtextureglyphcache.cpp

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

Generated by Squish Coco Non-Commercial 4.3.0-BETA-master-30-08-2018-4cb69e9