/src/gui/painting/qtextureglyphcache.cpp

https://bitbucket.org/ultra_iter/qt-vtl · C++ · 468 lines · 347 code · 59 blank · 62 comment · 96 complexity · 579f2366e785808ee26af202d7b11a97 MD5 · raw file

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