GLuint QOpenGLTextureCache::bindTexture(QOpenGLContext *context, const QImage &image, BindOptions options) { if (image.isNull()) return 0; QMutexLocker locker(&m_mutex); qint64 key = image.cacheKey(); // A QPainter is active on the image - take the safe route and replace the texture. if (!image.paintingActive()) { QOpenGLCachedTexture *entry = m_cache.object(key); if (entry && entry->options() == options) { context->functions()->glBindTexture(GL_TEXTURE_2D, entry->id()); return entry->id(); } } QImage img = image; if (!context->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextures)) { // Scale the pixmap if needed. GL textures needs to have the // dimensions 2^n+2(border) x 2^m+2(border), unless we're using GL // 2.0 or use the GL_TEXTURE_RECTANGLE texture target int tx_w = qNextPowerOfTwo(image.width() - 1); int tx_h = qNextPowerOfTwo(image.height() - 1); if (tx_w != image.width() || tx_h != image.height()) { img = img.scaled(tx_w, tx_h); } } GLuint id = bindTexture(context, key, img, options); if (id > 0) QImagePixmapCleanupHooks::enableCleanupHooks(image); return id; }
void QTextureGlyphCache::fillInPendingGlyphs() { if (!hasPendingGlyphs()) return; int requiredHeight = m_h; int requiredWidth = m_w; // Use a minimum size to avoid a lot of initial reallocations { QHash<GlyphAndSubPixelPosition, Coord>::iterator iter = m_pendingGlyphs.begin(); while (iter != m_pendingGlyphs.end()) { Coord c = iter.value(); requiredHeight = qMax(requiredHeight, c.y + c.h); requiredWidth = qMax(requiredWidth, c.x + c.w); ++iter; } } if (isNull() || requiredHeight > m_h || requiredWidth > m_w) { if (isNull()) createCache(qNextPowerOfTwo(requiredWidth - 1), qNextPowerOfTwo(requiredHeight - 1)); else resizeCache(qNextPowerOfTwo(requiredWidth - 1), qNextPowerOfTwo(requiredHeight - 1)); } { QHash<GlyphAndSubPixelPosition, Coord>::iterator iter = m_pendingGlyphs.begin(); while (iter != m_pendingGlyphs.end()) { GlyphAndSubPixelPosition key = iter.key(); fillTexture(iter.value(), key.glyph, key.subPixelPosition); ++iter; } } m_pendingGlyphs.clear(); }
void MainWindow::roundSBToPowerOfTwo(int value) { QSpinBox *sb = (QSpinBox *)sender(); sb->blockSignals(true); int prevValue = (sb == ui->rangeMinSB) ? rangeMinSBPrevValue : rangeMaxSBPrevValue; int nextValue = value; if (prevValue < value) nextValue = qNextPowerOfTwo(value); else if(prevValue > value) nextValue = qNextPowerOfTwo(value) >> 1; if (sb == ui->rangeMinSB) rangeMinSBPrevValue = nextValue; else rangeMaxSBPrevValue = nextValue; sb->setValue(nextValue); sb->blockSignals(false); }
bool QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const glyph_t *glyphs, const QFixedPoint *positions) { #ifdef CACHE_DEBUG printf("Populating with %d glyphs\n", numGlyphs); qDebug() << " -> current transformation: " << m_transform; #endif m_current_fontengine = fontEngine; const int padding = glyphPadding(); const int paddingDoubled = padding * 2; bool supportsSubPixelPositions = fontEngine->supportsSubPixelPositions(); if (fontEngine->m_subPixelPositionCount == 0) { if (!supportsSubPixelPositions) { fontEngine->m_subPixelPositionCount = 1; } else { int i = 0; while (fontEngine->m_subPixelPositionCount == 0 && i < numGlyphs) fontEngine->m_subPixelPositionCount = calculateSubPixelPositionCount(glyphs[i++]); } } if (m_cx == 0 && m_cy == 0) { m_cx = padding; m_cy = padding; } QHash<GlyphAndSubPixelPosition, Coord> listItemCoordinates; int rowHeight = 0; // check each glyph for its metrics and get the required rowHeight. for (int i=0; i < numGlyphs; ++i) { const glyph_t glyph = glyphs[i]; QFixed subPixelPosition; if (supportsSubPixelPositions) { QFixed x = positions != 0 ? positions[i].x : QFixed(); subPixelPosition = fontEngine->subPixelPositionForX(x); } if (coords.contains(GlyphAndSubPixelPosition(glyph, subPixelPosition))) continue; if (listItemCoordinates.contains(GlyphAndSubPixelPosition(glyph, subPixelPosition))) continue; glyph_metrics_t metrics = fontEngine->alphaMapBoundingBox(glyph, subPixelPosition, m_transform, m_format); #ifdef CACHE_DEBUG printf("(%4x): w=%.2f, h=%.2f, xoff=%.2f, yoff=%.2f, x=%.2f, y=%.2f\n", glyph, metrics.width.toReal(), metrics.height.toReal(), metrics.xoff.toReal(), metrics.yoff.toReal(), metrics.x.toReal(), metrics.y.toReal()); #endif GlyphAndSubPixelPosition key(glyph, subPixelPosition); int glyph_width = metrics.width.ceil().toInt(); int glyph_height = metrics.height.ceil().toInt(); if (glyph_height == 0 || glyph_width == 0) { // Avoid multiple calls to boundingBox() for non-printable characters Coord c = { 0, 0, 0, 0, 0, 0 }; coords.insert(key, c); continue; } // align to 8-bit boundary if (m_format == QFontEngine::Format_Mono) glyph_width = (glyph_width+7)&~7; Coord c = { 0, 0, // will be filled in later glyph_width, glyph_height, // texture coords metrics.x.truncate(), -metrics.y.truncate() }; // baseline for horizontal scripts listItemCoordinates.insert(key, c); rowHeight = qMax(rowHeight, glyph_height); } if (listItemCoordinates.isEmpty()) return true; rowHeight += paddingDoubled; if (m_w == 0) { if (fontEngine->maxCharWidth() <= QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH) m_w = QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH; else m_w = qNextPowerOfTwo(qCeil(fontEngine->maxCharWidth()) - 1); } // now actually use the coords and paint the wanted glyps into cache. QHash<GlyphAndSubPixelPosition, Coord>::iterator iter = listItemCoordinates.begin(); int requiredWidth = m_w; while (iter != listItemCoordinates.end()) { Coord c = iter.value(); m_currentRowHeight = qMax(m_currentRowHeight, c.h); if (m_cx + c.w + padding > requiredWidth) { int new_width = requiredWidth*2; while (new_width < m_cx + c.w + padding) new_width *= 2; if (new_width <= maxTextureWidth()) { requiredWidth = new_width; } else { // no room on the current line, start new glyph strip m_cx = padding; m_cy += m_currentRowHeight + paddingDoubled; m_currentRowHeight = c.h; // New row } } if (maxTextureHeight() > 0 && m_cy + c.h + padding > maxTextureHeight()) { // We can't make a cache of the required size, so we bail out return false; } c.x = m_cx; c.y = m_cy; coords.insert(iter.key(), c); m_pendingGlyphs.insert(iter.key(), c); m_cx += c.w + paddingDoubled; ++iter; } return true; }
void MainWindow::startBench() { int rangeMin = ui->rangeMinSB->value(); int rangeMax = ui->rangeMaxSB->value(); Q_ASSERT(IS_POWER_OF_TWO(rangeMin)); Q_ASSERT(IS_POWER_OF_TWO(rangeMax)); int sizeCount = (int)(log2(rangeMax) - log2(rangeMin) + 1); if (sizeCount <= 0) return; int iterations = ui->benchIterRB->value(); float fourierCount = iterations * (sizeCount + 2); float progressStep = 100.0 / fourierCount; float progressCounter; FT::FTType algorithm = (FT::FTType)ui->benchFtCombo->currentIndex(); QString input = ui->benchInputLine->text(); Q_ASSERT(FImage::isRectCode(input)); ui->benchResultView->clear(); progressCounter = 0.0; m_progress->setValue(progressCounter); for (int size = rangeMin; size <= rangeMax; size = qNextPowerOfTwo(size)) { QVector<int> results; FImage rectangle = FImage::rectangle(input, QSize(size, size)); FT *fourierWarmUp = FT::createFT(algorithm, &rectangle); fourierWarmUp->bench(); delete fourierWarmUp; progressCounter += progressStep; m_progress->setValue(progressCounter); for (int i = 0; i < iterations; ++i) { FT *fourier = FT::createFT(algorithm, &rectangle); results.append(fourier->bench()); delete fourier; progressCounter += progressStep; m_progress->setValue(progressCounter); } int result = 0; if (ui->benchMinRB->isChecked()) result = *std::min_element(results.begin(), results.end()); else if (ui->benchMaxRB->isChecked()) result = *std::max_element(results.begin(), results.end()); else if (ui->benchMeanRB->isChecked()) { float sum = 0.0; Q_FOREACH (int r, results) sum += (float)r; result = qRound(sum / (float)results.count()); } QStringList benchSum; benchSum.append(QStringLiteral("%1").arg(rectangle.id()).leftJustified(28, ' ')); benchSum.append(QString::number(size).rightJustified(4, ' ')); benchSum.append(QStringLiteral("%1 ms").arg(QString::number(result).rightJustified(4, ' '))); QStringList resultList; Q_FOREACH (int r, results) resultList.append(QString::number(r).rightJustified(4, ' ')); ui->benchResultView->append(QStringLiteral("%1\t%2").arg(benchSum.join(" ")).arg(resultList.join(" "))); progressCounter += progressStep; m_progress->setValue(progressCounter); }