Qt 4.8
qtextureglyphcache.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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 
51 
52 // #define CACHE_DEBUG
53 
54 // returns the highest number closest to v, which is a power of 2
55 // NB! assumes 32 bit ints
56 static inline int qt_next_power_of_two(int v)
57 {
58  v--;
59  v |= v >> 1;
60  v |= v >> 2;
61  v |= v >> 4;
62  v |= v >> 8;
63  v |= v >> 16;
64  ++v;
65  return v;
66 }
67 
69 {
70  // Test 12 different subpixel positions since it factors into 3*4 so it gives
71  // the coverage we need.
72 
73  QList<QImage> images;
74  for (int i=0; i<12; ++i) {
75  QImage img = textureMapForGlyph(glyph, QFixed::fromReal(i / 12.0));
76 
77  if (images.isEmpty()) {
78  QPainterPath path;
79  QFixedPoint point;
80  m_current_fontengine->addGlyphsToPath(&glyph, &point, 1, &path, QTextItem::RenderFlags());
81 
82  // Glyph is space, return 0 to indicate that we need to keep trying
83  if (path.isEmpty())
84  break;
85 
86  images.append(img);
87  } else {
88  bool found = false;
89  for (int j=0; j<images.size(); ++j) {
90  if (images.at(j) == img) {
91  found = true;
92  break;
93  }
94  }
95  if (!found)
96  images.append(img);
97  }
98  }
99 
100  return images.size();
101 }
102 
104 {
105  if (m_subPixelPositionCount <= 1)
106  return QFixed();
107 
108  QFixed subPixelPosition;
109  if (x != 0) {
110  subPixelPosition = x - x.floor();
111  QFixed fraction = (subPixelPosition / QFixed::fromReal(1.0 / m_subPixelPositionCount)).floor();
112 
113  // Compensate for precision loss in fixed point to make sure we are always drawing at a subpixel position over
114  // the lower boundary for the selected rasterization by adding 1/64.
115  subPixelPosition = fraction / QFixed(m_subPixelPositionCount) + QFixed::fromReal(0.015625);
116  }
117  return subPixelPosition;
118 }
119 
120 bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const glyph_t *glyphs,
121  const QFixedPoint *positions)
122 {
123 #ifdef CACHE_DEBUG
124  printf("Populating with %d glyphs\n", numGlyphs);
125  qDebug() << " -> current transformation: " << m_transform;
126 #endif
127 
128  m_current_fontengine = fontEngine;
129  const int margin = glyphMargin();
130  const int paddingDoubled = glyphPadding() * 2;
131 
132  bool supportsSubPixelPositions = fontEngine->supportsSubPixelPositions();
133  if (m_subPixelPositionCount == 0) {
134  if (!supportsSubPixelPositions) {
136  } else {
137 #if !defined(Q_WS_X11)
138  int i = 0;
139  while (m_subPixelPositionCount == 0 && i < numGlyphs)
141 #else
143 #endif
144  }
145  }
146 
147  QHash<GlyphAndSubPixelPosition, Coord> listItemCoordinates;
148  int rowHeight = 0;
149 
151  switch (m_type) {
152  case Raster_A8: format = QFontEngine::Format_A8; break;
153  case Raster_RGBMask: format = QFontEngine::Format_A32; break;
154  default: format = QFontEngine::Format_Mono; break;
155  }
156 
157  // check each glyph for its metrics and get the required rowHeight.
158  for (int i=0; i < numGlyphs; ++i) {
159  const glyph_t glyph = glyphs[i];
160 
161  QFixed subPixelPosition;
162  if (supportsSubPixelPositions) {
163  QFixed x = positions != 0 ? positions[i].x : QFixed();
164  subPixelPosition = subPixelPositionForX(x);
165  }
166 
167  if (coords.contains(GlyphAndSubPixelPosition(glyph, subPixelPosition)))
168  continue;
169  if (listItemCoordinates.contains(GlyphAndSubPixelPosition(glyph, subPixelPosition)))
170  continue;
171  glyph_metrics_t metrics = fontEngine->alphaMapBoundingBox(glyph, subPixelPosition, m_transform, format);
172 
173 #ifdef CACHE_DEBUG
174  printf("(%4x): w=%.2f, h=%.2f, xoff=%.2f, yoff=%.2f, x=%.2f, y=%.2f\n",
175  glyph,
176  metrics.width.toReal(),
177  metrics.height.toReal(),
178  metrics.xoff.toReal(),
179  metrics.yoff.toReal(),
180  metrics.x.toReal(),
181  metrics.y.toReal());
182 #endif
183  GlyphAndSubPixelPosition key(glyph, subPixelPosition);
184  int glyph_width = metrics.width.ceil().toInt();
185  int glyph_height = metrics.height.ceil().toInt();
186  if (glyph_height == 0 || glyph_width == 0) {
187  // Avoid multiple calls to boundingBox() for non-printable characters
188  Coord c = { 0, 0, 0, 0, 0, 0 };
189  coords.insert(key, c);
190  continue;
191  }
192  glyph_width += margin * 2 + 4;
193  glyph_height += margin * 2 + 4;
194  // align to 8-bit boundary
196  glyph_width = (glyph_width+7)&~7;
197 
198  Coord c = { 0, 0, // will be filled in later
199  glyph_width,
200  glyph_height, // texture coords
201  metrics.x.truncate(),
202  -metrics.y.truncate() }; // baseline for horizontal scripts
203 
204  listItemCoordinates.insert(key, c);
205  rowHeight = qMax(rowHeight, glyph_height);
206  }
207  if (listItemCoordinates.isEmpty())
208  return true;
209 
210  rowHeight += margin * 2 + paddingDoubled;
211 
212  if (m_w == 0) {
215  else
216  m_w = qt_next_power_of_two(fontEngine->maxCharWidth());
217  }
218 
219  // now actually use the coords and paint the wanted glyps into cache.
220  QHash<GlyphAndSubPixelPosition, Coord>::iterator iter = listItemCoordinates.begin();
221  int requiredWidth = m_w;
222  while (iter != listItemCoordinates.end()) {
223  Coord c = iter.value();
224 
225  m_currentRowHeight = qMax(m_currentRowHeight, c.h + margin * 2);
226 
227  if (m_cx + c.w > requiredWidth) {
228  int new_width = requiredWidth*2;
229  while (new_width < m_cx + c.w)
230  new_width *= 2;
231  if (new_width <= maxTextureWidth()) {
232  requiredWidth = new_width;
233  } else {
234  // no room on the current line, start new glyph strip
235  m_cx = 0;
236  m_cy += m_currentRowHeight + paddingDoubled;
237  m_currentRowHeight = c.h + margin * 2; // New row
238  }
239  }
240 
241  if (maxTextureHeight() > 0 && m_cy + c.h > maxTextureHeight()) {
242  // We can't make a cache of the required size, so we bail out
243  return false;
244  }
245 
246  c.x = m_cx;
247  c.y = m_cy;
248 
249  coords.insert(iter.key(), c);
250  m_pendingGlyphs.insert(iter.key(), c);
251 
252  m_cx += c.w + paddingDoubled;
253  ++iter;
254  }
255  return true;
256 
257 }
258 
260 {
261  if (m_pendingGlyphs.isEmpty())
262  return;
263 
264  int requiredHeight = m_h;
265  int requiredWidth = m_w; // Use a minimum size to avoid a lot of initial reallocations
266  {
268  while (iter != m_pendingGlyphs.end()) {
269  Coord c = iter.value();
270  requiredHeight = qMax(requiredHeight, c.y + c.h);
271  requiredWidth = qMax(requiredWidth, c.x + c.w);
272  ++iter;
273  }
274  }
275 
276  if (isNull() || requiredHeight > m_h || requiredWidth > m_w) {
277  if (isNull())
278  createCache(qt_next_power_of_two(requiredWidth), qt_next_power_of_two(requiredHeight));
279  else
280  resizeCache(qt_next_power_of_two(requiredWidth), qt_next_power_of_two(requiredHeight));
281  }
282 
283  {
285  while (iter != m_pendingGlyphs.end()) {
287  fillTexture(iter.value(), key.glyph, key.subPixelPosition);
288 
289  ++iter;
290  }
291  }
292 
293  m_pendingGlyphs.clear();
294 }
295 
297 {
298 #if defined(Q_WS_X11)
302  switch (m_type) {
303  case Raster_A8:
304  format = QFontEngineFT::Format_A8;
305  imageFormat = QImage::Format_Indexed8;
306  break;
307  case Raster_Mono:
309  imageFormat = QImage::Format_Mono;
310  break;
311  case Raster_RGBMask:
312  // impossible condition (see the if-clause above)
313  // this option is here only to silence a compiler warning
314  break;
315  };
316 
317  QFontEngineFT *ft = static_cast<QFontEngineFT*> (m_current_fontengine);
320  positions[0].x = subPixelPosition;
321 
322  if (gset && ft->loadGlyphs(gset, &g, 1, positions, format)) {
323  QFontEngineFT::Glyph *glyph = gset->getGlyph(g, subPixelPosition);
324  const int bytesPerLine = (format == QFontEngineFT::Format_Mono ? ((glyph->width + 31) & ~31) >> 3
325  : (glyph->width + 3) & ~3);
326  return QImage(glyph->data, glyph->width, glyph->height, bytesPerLine, imageFormat);
327  }
328  } else
329 #endif
331  return m_current_fontengine->alphaRGBMapForGlyph(g, subPixelPosition, glyphMargin(), m_transform);
332  else
333  return m_current_fontengine->alphaMapForGlyph(g, subPixelPosition, m_transform);
334 
335  return QImage();
336 }
337 
338 /************************************************************************
339  * QImageTextureGlyphCache
340  */
341 
343 {
344  m_image = m_image.copy(0, 0, width, height);
345 }
346 
348 {
349  switch (m_type) {
351  m_image = QImage(width, height, QImage::Format_Mono);
352  break;
354  m_image = QImage(width, height, QImage::Format_Indexed8);
355  m_image.fill(0);
356  QVector<QRgb> colors(256);
357  QRgb *it = colors.data();
358  for (int i=0; i<256; ++i, ++it)
359  *it = 0xff000000 | i | (i<<8) | (i<<16);
360  m_image.setColorTable(colors);
361  break; }
363  m_image = QImage(width, height, QImage::Format_RGB32);
364  break;
365  }
366 }
367 
369 {
370 #if (defined(Q_WS_MAC) && defined(QT_MAC_USE_COCOA)) || defined(Q_WS_X11)
371  return 0;
372 #else
374 #endif
375 }
376 
377 void QImageTextureGlyphCache::fillTexture(const Coord &c, glyph_t g, QFixed subPixelPosition)
378 {
379  QImage mask = textureMapForGlyph(g, subPixelPosition);
380 
381 #ifdef CACHE_DEBUG
382  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());
383  if (mask.width() > c.w || mask.height() > c.h) {
384  printf(" ERROR; mask is bigger than reserved space! %dx%d instead of %dx%d\n", mask.width(), mask.height(), c.w,c.h);
385  return;
386  }
387 #endif
388 
390  QImage ref(m_image.bits() + (c.x * 4 + c.y * m_image.bytesPerLine()),
391  qMax(mask.width(), c.w), qMax(mask.height(), c.h), m_image.bytesPerLine(),
392  m_image.format());
393  QPainter p(&ref);
394  p.setCompositionMode(QPainter::CompositionMode_Source);
395  p.fillRect(0, 0, c.w, c.h, QColor(0,0,0,0)); // TODO optimize this
396  p.drawImage(0, 0, mask);
397  p.end();
399  if (mask.depth() > 1) {
400  // TODO optimize this
401  mask = mask.alphaChannel();
402  mask.invertPixels();
404  }
405 
406  int mw = qMin(mask.width(), c.w);
407  int mh = qMin(mask.height(), c.h);
408  uchar *d = m_image.bits();
409  int dbpl = m_image.bytesPerLine();
410 
411  for (int y = 0; y < c.h; ++y) {
412  uchar *dest = d + (c.y + y) *dbpl + c.x/8;
413 
414  if (y < mh) {
415  uchar *src = mask.scanLine(y);
416  for (int x = 0; x < c.w/8; ++x) {
417  if (x < (mw+7)/8)
418  dest[x] = src[x];
419  else
420  dest[x] = 0;
421  }
422  } else {
423  for (int x = 0; x < c.w/8; ++x)
424  dest[x] = 0;
425  }
426  }
427  } else { // A8
428  int mw = qMin(mask.width(), c.w);
429  int mh = qMin(mask.height(), c.h);
430  uchar *d = m_image.bits();
431  int dbpl = m_image.bytesPerLine();
432 
433  if (mask.depth() == 1) {
434  for (int y = 0; y < c.h; ++y) {
435  uchar *dest = d + (c.y + y) *dbpl + c.x;
436  if (y < mh) {
437  uchar *src = (uchar *) mask.scanLine(y);
438  for (int x = 0; x < c.w; ++x) {
439  if (x < mw)
440  dest[x] = (src[x >> 3] & (1 << (7 - (x & 7)))) > 0 ? 255 : 0;
441  }
442  }
443  }
444  } else if (mask.depth() == 8) {
445  for (int y = 0; y < c.h; ++y) {
446  uchar *dest = d + (c.y + y) *dbpl + c.x;
447  if (y < mh) {
448  uchar *src = (uchar *) mask.scanLine(y);
449  for (int x = 0; x < c.w; ++x) {
450  if (x < mw)
451  dest[x] = src[x];
452  }
453  }
454  }
455  }
456  }
457 
458 #ifdef CACHE_DEBUG
459 // QPainter p(&m_image);
460 // p.drawLine(
461  QPoint base(c.x + glyphMargin(), c.y + glyphMargin() + c.baseLineY-1);
462  if (m_image.rect().contains(base))
463  m_image.setPixel(base, 255);
464  m_image.save(QString::fromLatin1("cache-%1.png").arg(qint64(this)));
465 #endif
466 }
467 
The QPainter class performs low-level painting on widgets and other paint devices.
Definition: qpainter.h:86
QAtomicInt ref
Definition: qshareddata.h:59
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition: qcolor.h:67
double d
Definition: qnumeric_p.h:62
virtual int maxTextureWidth() const
bool isEmpty() const
Returns true if either there are no elements in this path, or if the only element is a MoveToElement;...
Definition: qpainterpath.h:392
Format
The following image formats are available in Qt.
Definition: qimage.h:91
unsigned int QRgb
Definition: qrgb.h:53
virtual QImage alphaMapForGlyph(glyph_t)
unsigned char c[8]
Definition: qnumeric_p.h:62
Q_DECL_CONSTEXPR const T & qMin(const T &a, const T &b)
Definition: qglobal.h:1215
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
QHash< GlyphAndSubPixelPosition, Coord > coords
#define it(className, varName)
The QPainterPath class provides a container for painting operations, enabling graphical shapes to be ...
Definition: qpainterpath.h:67
QFixed subPixelPositionForX(QFixed x) const
#define QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH
virtual void fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition)
virtual void createTextureData(int width, int height)
T & value() const
Returns a modifiable reference to the current item&#39;s value.
Definition: qhash.h:348
The QHash class is a template class that provides a hash-table-based dictionary.
Definition: qdatastream.h:66
static QFixed fromReal(qreal r)
Definition: qfixed_p.h:70
QImage textureMapForGlyph(glyph_t g, QFixed subPixelPosition) const
TransformationType type() const
Returns the transformation type of this matrix.
virtual int maxTextureHeight() const
static const uint base
Definition: qurl.cpp:268
virtual Type type() const =0
bool contains(const Key &key) const
Returns true if the hash contains an item with the key; otherwise returns false.
Definition: qhash.h:872
QFontEngineGlyphCache::Type m_type
virtual QImage alphaRGBMapForGlyph(glyph_t, QFixed subPixelPosition, int margin, const QTransform &t)
Glyph * getGlyph(glyph_t index, QFixed subPixelPosition=0) const
Q_DECL_CONSTEXPR const T & qMax(const T &a, const T &b)
Definition: qglobal.h:1217
bool isEmpty() const
Returns true if the list contains no items; otherwise returns false.
Definition: qlist.h:152
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition: qhash.h:753
virtual qreal maxCharWidth() const =0
Q_CORE_EXPORT void qDebug(const char *,...)
unsigned char uchar
Definition: qglobal.h:994
void append(const T &t)
Inserts value at the end of the list.
Definition: qlist.h:507
QFixed floor() const
Definition: qfixed_p.h:81
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
virtual void resizeTextureData(int width, int height)
virtual int glyphPadding() const
static const QCssKnownValue positions[NumKnownPositionModes - 1]
Definition: qcssparser.cpp:329
const T & at(int i) const
Returns the item at index position i in the list.
Definition: qlist.h:468
bool isEmpty() const
Returns true if the hash contains no items; otherwise returns false.
Definition: qhash.h:297
int toInt() const
Definition: qfixed_p.h:76
The QImage class provides a hardware-independent image representation that allows direct access to th...
Definition: qimage.h:87
int depth() const
Returns the depth of the image.
Definition: qimage.cpp:1620
__int64 qint64
Definition: qglobal.h:942
const Key & key() const
Returns the current item&#39;s key as a const reference.
Definition: qhash.h:347
bool loadGlyphs(QGlyphSet *gs, const glyph_t *glyphs, int num_glyphs, const QFixedPoint *positions, GlyphFormat format=Format_Render)
QImage alphaChannel() const
Returns the alpha channel of the image as a new grayscale QImage in which each pixel&#39;s red...
Definition: qimage.cpp:6430
virtual void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, QPainterPath *path, QTextItem::RenderFlags flags)
QFixed x
Definition: qfixed_p.h:190
void resizeCache(int width, int height)
int width() const
Returns the width of the image.
Definition: qimage.cpp:1557
QImage convertToFormat(Format f, Qt::ImageConversionFlags flags=Qt::AutoColor) const Q_REQUIRED_RESULT
Returns a copy of the image in the given format.
Definition: qimage.cpp:3966
int truncate() const
Definition: qfixed_p.h:79
virtual int glyphMargin() const
virtual glyph_metrics_t alphaMapBoundingBox(glyph_t glyph, QFixed, const QTransform &matrix, GlyphFormat)
QFontEngine * m_current_fontengine
iterator end()
Returns an STL-style iterator pointing to the imaginary item after the last item in the hash...
Definition: qhash.h:467
int calculateSubPixelPositionCount(glyph_t) const
static QString fromLatin1(const char *, int size=-1)
Returns a QString initialized with the first size characters of the Latin-1 string str...
Definition: qstring.cpp:4188
bool populate(QFontEngine *fontEngine, int numGlyphs, const glyph_t *glyphs, const QFixedPoint *positions)
int key
The QPoint class defines a point in the plane using integer precision.
Definition: qpoint.h:53
The QHash::iterator class provides an STL-style non-const iterator for QHash and QMultiHash.
Definition: qhash.h:330
int size() const
Returns the number of items in the list.
Definition: qlist.h:137
virtual void fillTexture(const Coord &coord, glyph_t glyph, QFixed subPixelPosition)=0
void createCache(int width, int height)
if(void) toggleToolbarShown
virtual int glyphMargin() const
iterator begin()
Returns an STL-style iterator pointing to the first item in the hash.
Definition: qhash.h:464
qreal toReal() const
Definition: qfixed_p.h:77
int height() const
Returns the height of the image.
Definition: qimage.cpp:1572
QGlyphSet * loadTransformedGlyphSet(const QTransform &matrix)
T * data()
Returns a pointer to the data stored in the vector.
Definition: qvector.h:152
void invertPixels(InvertMode=InvertRgb)
Inverts all pixel values in the image.
Definition: qimage.cpp:2179
static int qt_next_power_of_two(int v)
QHash< GlyphAndSubPixelPosition, Coord > m_pendingGlyphs
QFixed ceil() const
Definition: qfixed_p.h:82
unsigned int glyph_t
uchar * scanLine(int)
Returns a pointer to the pixel data at the scanline with index i.
Definition: qimage.cpp:1886
virtual bool supportsSubPixelPositions() const