aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph/qsgrhitextureglyphcache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/scenegraph/qsgrhitextureglyphcache.cpp')
-rw-r--r--src/quick/scenegraph/qsgrhitextureglyphcache.cpp269
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