diff options
Diffstat (limited to 'src/quick/scenegraph/qsgrhitextureglyphcache.cpp')
-rw-r--r-- | src/quick/scenegraph/qsgrhitextureglyphcache.cpp | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/src/quick/scenegraph/qsgrhitextureglyphcache.cpp b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp new file mode 100644 index 0000000000..f181d101c6 --- /dev/null +++ b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp @@ -0,0 +1,269 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgrhitextureglyphcache_p.h" +#include <qrgb.h> +#include <private/qdrawhelper_p.h> + +QT_BEGIN_NAMESPACE + +QSGRhiTextureGlyphCache::QSGRhiTextureGlyphCache(QRhi *rhi, QFontEngine::GlyphFormat format, const QTransform &matrix) + : QImageTextureGlyphCache(format, matrix), + m_rhi(rhi) +{ + // Some OpenGL implementations, for instance macOS, have issues with + // GL_ALPHA render targets. Similarly, BGRA may be problematic on GLES 2.0. + // So stick with plain image uploads on GL. + m_resizeWithTextureCopy = m_rhi->backend() != QRhi::OpenGLES2; +} + +QSGRhiTextureGlyphCache::~QSGRhiTextureGlyphCache() +{ + if (m_resourceUpdates) + m_resourceUpdates->release(); + + delete m_texture; + + // should be empty, but just in case + qDeleteAll(m_pendingDispose); +} + +QRhiTexture *QSGRhiTextureGlyphCache::createEmptyTexture(QRhiTexture::Format format) +{ + QRhiTexture *t = m_rhi->newTexture(format, m_size, 1, QRhiTexture::UsedAsTransferSource); + if (!t->build()) { + qWarning("Failed to build new glyph cache texture of size %dx%d", m_size.width(), m_size.height()); + return nullptr; + } + + if (!m_resourceUpdates) + m_resourceUpdates = m_rhi->nextResourceUpdateBatch(); + + // The new texture must be cleared to 0 always, this cannot be avoided + // otherwise artifacts will occur around the glyphs. + QByteArray data; + if (format == QRhiTexture::RED_OR_ALPHA8) + data.fill(0, m_size.width() * m_size.height()); + else + data.fill(0, m_size.width() * m_size.height() * 4); + QRhiTextureSubresourceUploadDescription subresDesc(data.constData(), data.size()); + subresDesc.setSourceSize(m_size); + m_resourceUpdates->uploadTexture(t, QRhiTextureUploadEntry(0, 0, subresDesc)); + + return t; +} + +void QSGRhiTextureGlyphCache::createTextureData(int width, int height) +{ + width = qMax(128, width); + height = qMax(32, height); + + if (!m_resizeWithTextureCopy) + QImageTextureGlyphCache::createTextureData(width, height); + + m_size = QSize(width, height); +} + +void QSGRhiTextureGlyphCache::resizeTextureData(int width, int height) +{ + width = qMax(128, width); + height = qMax(32, height); + + if (m_size.width() >= width && m_size.height() >= height) + return; + + m_size = QSize(width, height); + + if (m_texture) { + QRhiTexture *t = createEmptyTexture(m_texture->format()); + if (!t) + return; + + if (!m_resourceUpdates) + m_resourceUpdates = m_rhi->nextResourceUpdateBatch(); + + if (m_resizeWithTextureCopy) { + m_resourceUpdates->copyTexture(t, m_texture); + } else { + QImageTextureGlyphCache::resizeTextureData(width, height); + QImage img = image(); + prepareGlyphImage(&img); + QRhiTextureSubresourceUploadDescription subresDesc(img); + const QSize oldSize = m_texture->pixelSize(); + subresDesc.setSourceSize(QSize(qMin(oldSize.width(), width), qMin(oldSize.height(), height))); + m_resourceUpdates->uploadTexture(t, QRhiTextureUploadEntry(0, 0, subresDesc)); + } + + m_pendingDispose.insert(m_texture); + m_texture = t; + } +} + +void QSGRhiTextureGlyphCache::beginFillTexture() +{ + Q_ASSERT(m_uploads.isEmpty()); +} + +void QSGRhiTextureGlyphCache::prepareGlyphImage(QImage *img) +{ + const int maskWidth = img->width(); + const int maskHeight = img->height(); + const bool supportsBgra = m_rhi->isTextureFormatSupported(QRhiTexture::BGRA8); + m_bgra = false; + + if (img->format() == QImage::Format_Mono) { + *img = img->convertToFormat(QImage::Format_Grayscale8); + } else if (img->depth() == 32) { + if (img->format() == QImage::Format_RGB32 || img->format() == QImage::Format_ARGB32_Premultiplied) { + // We need to make the alpha component equal to the average of the RGB values. + // This is needed when drawing sub-pixel antialiased text on translucent targets. + for (int y = 0; y < maskHeight; ++y) { + QRgb *src = (QRgb *) img->scanLine(y); + for (int x = 0; x < maskWidth; ++x) { + int r = qRed(src[x]); + int g = qGreen(src[x]); + int b = qBlue(src[x]); + int avg; + if (img->format() == QImage::Format_RGB32) + avg = (r + g + b + 1) / 3; // "+1" for rounding. + else // Format_ARGB_Premultiplied + avg = qAlpha(src[x]); + + src[x] = qRgba(r, g, b, avg); +#if Q_BYTE_ORDER != Q_BIG_ENDIAN + if (supportsBgra) { + m_bgra = true; + } else { + // swizzle the bits to accommodate for the RGBA upload. + src[x] = ARGB2RGBA(src[x]); + m_bgra = false; + } +#endif + } + } + } + } +} + +void QSGRhiTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition) +{ + QRhiTextureSubresourceUploadDescription subresDesc; + QImage mask; + + if (!m_resizeWithTextureCopy) { + QImageTextureGlyphCache::fillTexture(c, glyph, subPixelPosition); + mask = image(); + subresDesc.setSourceTopLeft(QPoint(c.x, c.y)); + subresDesc.setSourceSize(QSize(c.w, c.h)); + } else { + mask = textureMapForGlyph(glyph, subPixelPosition); + } + + prepareGlyphImage(&mask); + + subresDesc.setImage(mask); + subresDesc.setDestinationTopLeft(QPoint(c.x, c.y)); + m_uploads.append(QRhiTextureUploadEntry(0, 0, subresDesc)); +} + +void QSGRhiTextureGlyphCache::endFillTexture() +{ + if (m_uploads.isEmpty()) + return; + + if (!m_texture) { + QRhiTexture::Format texFormat; + if (m_format == QFontEngine::Format_A32 || m_format == QFontEngine::Format_ARGB) + texFormat = m_bgra ? QRhiTexture::BGRA8 : QRhiTexture::RGBA8; + else // should be R8, but there is the OpenGL ES 2.0 nonsense + texFormat = QRhiTexture::RED_OR_ALPHA8; + + m_texture = createEmptyTexture(texFormat); + if (!m_texture) + return; + } + + if (!m_resourceUpdates) + m_resourceUpdates = m_rhi->nextResourceUpdateBatch(); + + m_resourceUpdates->uploadTexture(m_texture, m_uploads); + m_uploads.clear(); +} + +int QSGRhiTextureGlyphCache::glyphPadding() const +{ + return 1; +} + +int QSGRhiTextureGlyphCache::maxTextureWidth() const +{ + return m_rhi->resourceLimit(QRhi::TextureSizeMax); +} + +int QSGRhiTextureGlyphCache::maxTextureHeight() const +{ + if (!m_resizeWithTextureCopy) + return qMin(1024, m_rhi->resourceLimit(QRhi::TextureSizeMax)); + + return m_rhi->resourceLimit(QRhi::TextureSizeMax); +} + +void QSGRhiTextureGlyphCache::commitResourceUpdates(QRhiResourceUpdateBatch *mergeInto) +{ + if (m_resourceUpdates) { + mergeInto->merge(m_resourceUpdates); + m_resourceUpdates->release(); + m_resourceUpdates = nullptr; + } + + // now let's assume the resource updates will be committed in this frame + for (QRhiTexture *t : m_pendingDispose) + t->releaseAndDestroyLater(); // will be releaseAndDestroyed after the frame is submitted -> safe + + m_pendingDispose.clear(); +} + +bool QSGRhiTextureGlyphCache::eightBitFormatIsAlphaSwizzled() const +{ + // return true when the shaders for 8-bit formats need .a instead of .r + // when sampling the texture + return !m_rhi->isFeatureSupported(QRhi::RedOrAlpha8IsRed); +} + +QT_END_NAMESPACE |