1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick 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 The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qsgrhitextureglyphcache_p.h"
41#include <qrgb.h>
42#include <private/qdrawhelper_p.h>
43
44QT_BEGIN_NAMESPACE
45
46QSGRhiTextureGlyphCache::QSGRhiTextureGlyphCache(QRhi *rhi, QFontEngine::GlyphFormat format, const QTransform &matrix,
47 const QColor &color)
48 : QImageTextureGlyphCache(format, matrix, color),
49 m_rhi(rhi)
50{
51 // Some OpenGL implementations, for instance macOS, have issues with
52 // GL_ALPHA render targets. Similarly, BGRA may be problematic on GLES 2.0.
53 // So stick with plain image uploads on GL.
54 m_resizeWithTextureCopy = m_rhi->backend() != QRhi::OpenGLES2;
55}
56
57QSGRhiTextureGlyphCache::~QSGRhiTextureGlyphCache()
58{
59 if (m_resourceUpdates)
60 m_resourceUpdates->release();
61
62 delete m_texture;
63
64 // should be empty, but just in case
65 qDeleteAll(c: m_pendingDispose);
66}
67
68QRhiTexture *QSGRhiTextureGlyphCache::createEmptyTexture(QRhiTexture::Format format)
69{
70 QRhiTexture *t = m_rhi->newTexture(format, pixelSize: m_size, sampleCount: 1, flags: QRhiTexture::UsedAsTransferSource);
71 if (!t->build()) {
72 qWarning(msg: "Failed to build new glyph cache texture of size %dx%d", m_size.width(), m_size.height());
73 return nullptr;
74 }
75
76 if (!m_resourceUpdates)
77 m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
78
79 // The new texture must be cleared to 0 always, this cannot be avoided
80 // otherwise artifacts will occur around the glyphs.
81 QByteArray data;
82 if (format == QRhiTexture::RED_OR_ALPHA8)
83 data.fill(c: 0, size: m_size.width() * m_size.height());
84 else
85 data.fill(c: 0, size: m_size.width() * m_size.height() * 4);
86 QRhiTextureSubresourceUploadDescription subresDesc(data.constData(), data.size());
87 subresDesc.setSourceSize(m_size);
88 m_resourceUpdates->uploadTexture(tex: t, desc: QRhiTextureUploadEntry(0, 0, subresDesc));
89
90 return t;
91}
92
93void QSGRhiTextureGlyphCache::createTextureData(int width, int height)
94{
95 width = qMax(a: 128, b: width);
96 height = qMax(a: 32, b: height);
97
98 if (!m_resizeWithTextureCopy)
99 QImageTextureGlyphCache::createTextureData(width, height);
100
101 m_size = QSize(width, height);
102}
103
104void QSGRhiTextureGlyphCache::resizeTextureData(int width, int height)
105{
106 width = qMax(a: 128, b: width);
107 height = qMax(a: 32, b: height);
108
109 if (m_size.width() >= width && m_size.height() >= height)
110 return;
111
112 m_size = QSize(width, height);
113
114 if (m_texture) {
115 QRhiTexture *t = createEmptyTexture(format: m_texture->format());
116 if (!t)
117 return;
118
119 if (!m_resourceUpdates)
120 m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
121
122 if (m_resizeWithTextureCopy) {
123 m_resourceUpdates->copyTexture(dst: t, src: m_texture);
124 } else {
125 QImageTextureGlyphCache::resizeTextureData(width, height);
126 QImage img = image();
127 prepareGlyphImage(img: &img);
128 QRhiTextureSubresourceUploadDescription subresDesc(img);
129 const QSize oldSize = m_texture->pixelSize();
130 subresDesc.setSourceSize(QSize(qMin(a: oldSize.width(), b: width), qMin(a: oldSize.height(), b: height)));
131 m_resourceUpdates->uploadTexture(tex: t, desc: QRhiTextureUploadEntry(0, 0, subresDesc));
132 }
133
134 m_pendingDispose.insert(value: m_texture);
135 m_texture = t;
136 }
137}
138
139void QSGRhiTextureGlyphCache::beginFillTexture()
140{
141 Q_ASSERT(m_uploads.isEmpty());
142}
143
144void QSGRhiTextureGlyphCache::prepareGlyphImage(QImage *img)
145{
146 const int maskWidth = img->width();
147 const int maskHeight = img->height();
148#if Q_BYTE_ORDER != Q_BIG_ENDIAN
149 const bool supportsBgra = m_rhi->isTextureFormatSupported(format: QRhiTexture::BGRA8);
150#endif
151 m_bgra = false;
152
153 if (img->format() == QImage::Format_Mono) {
154 *img = img->convertToFormat(f: QImage::Format_Grayscale8);
155 } else if (img->depth() == 32) {
156 if (img->format() == QImage::Format_RGB32 || img->format() == QImage::Format_ARGB32_Premultiplied) {
157 // We need to make the alpha component equal to the average of the RGB values.
158 // This is needed when drawing sub-pixel antialiased text on translucent targets.
159 for (int y = 0; y < maskHeight; ++y) {
160 QRgb *src = (QRgb *) img->scanLine(y);
161 for (int x = 0; x < maskWidth; ++x) {
162 int r = qRed(rgb: src[x]);
163 int g = qGreen(rgb: src[x]);
164 int b = qBlue(rgb: src[x]);
165 int avg;
166 if (img->format() == QImage::Format_RGB32)
167 avg = (r + g + b + 1) / 3; // "+1" for rounding.
168 else // Format_ARGB_Premultiplied
169 avg = qAlpha(rgb: src[x]);
170
171 src[x] = qRgba(r, g, b, a: avg);
172#if Q_BYTE_ORDER != Q_BIG_ENDIAN
173 if (supportsBgra) {
174 m_bgra = true;
175 } else {
176 // swizzle the bits to accommodate for the RGBA upload.
177 src[x] = ARGB2RGBA(x: src[x]);
178 m_bgra = false;
179 }
180#endif
181 }
182 }
183 }
184 }
185}
186
187void QSGRhiTextureGlyphCache::fillTexture(const Coord &c, glyph_t glyph, QFixed subPixelPosition)
188{
189 QRhiTextureSubresourceUploadDescription subresDesc;
190 QImage mask;
191
192 if (!m_resizeWithTextureCopy) {
193 QImageTextureGlyphCache::fillTexture(c, glyph, subPixelPosition);
194 mask = image();
195 subresDesc.setSourceTopLeft(QPoint(c.x, c.y));
196 subresDesc.setSourceSize(QSize(c.w, c.h));
197 } else {
198 mask = textureMapForGlyph(g: glyph, subPixelPosition);
199 }
200
201 prepareGlyphImage(img: &mask);
202
203 subresDesc.setImage(mask);
204 subresDesc.setDestinationTopLeft(QPoint(c.x, c.y));
205 m_uploads.append(t: QRhiTextureUploadEntry(0, 0, subresDesc));
206}
207
208void QSGRhiTextureGlyphCache::endFillTexture()
209{
210 if (m_uploads.isEmpty())
211 return;
212
213 if (!m_texture) {
214 QRhiTexture::Format texFormat;
215 if (m_format == QFontEngine::Format_A32 || m_format == QFontEngine::Format_ARGB)
216 texFormat = m_bgra ? QRhiTexture::BGRA8 : QRhiTexture::RGBA8;
217 else // should be R8, but there is the OpenGL ES 2.0 nonsense
218 texFormat = QRhiTexture::RED_OR_ALPHA8;
219
220 m_texture = createEmptyTexture(format: texFormat);
221 if (!m_texture)
222 return;
223 }
224
225 if (!m_resourceUpdates)
226 m_resourceUpdates = m_rhi->nextResourceUpdateBatch();
227
228 QRhiTextureUploadDescription desc;
229 desc.setEntries(first: m_uploads.cbegin(), last: m_uploads.cend());
230 m_resourceUpdates->uploadTexture(tex: m_texture, desc);
231 m_uploads.clear();
232}
233
234int QSGRhiTextureGlyphCache::glyphPadding() const
235{
236 return 1;
237}
238
239int QSGRhiTextureGlyphCache::maxTextureWidth() const
240{
241 return m_rhi->resourceLimit(limit: QRhi::TextureSizeMax);
242}
243
244int QSGRhiTextureGlyphCache::maxTextureHeight() const
245{
246 if (!m_resizeWithTextureCopy)
247 return qMin(a: 1024, b: m_rhi->resourceLimit(limit: QRhi::TextureSizeMax));
248
249 return m_rhi->resourceLimit(limit: QRhi::TextureSizeMax);
250}
251
252void QSGRhiTextureGlyphCache::commitResourceUpdates(QRhiResourceUpdateBatch *mergeInto)
253{
254 if (m_resourceUpdates) {
255 mergeInto->merge(other: m_resourceUpdates);
256 m_resourceUpdates->release();
257 m_resourceUpdates = nullptr;
258 }
259
260 // now let's assume the resource updates will be committed in this frame
261 for (QRhiTexture *t : m_pendingDispose)
262 t->releaseAndDestroyLater(); // will be releaseAndDestroyed after the frame is submitted -> safe
263
264 m_pendingDispose.clear();
265}
266
267bool QSGRhiTextureGlyphCache::eightBitFormatIsAlphaSwizzled() const
268{
269 // return true when the shaders for 8-bit formats need .a instead of .r
270 // when sampling the texture
271 return !m_rhi->isFeatureSupported(feature: QRhi::RedOrAlpha8IsRed);
272}
273
274QT_END_NAMESPACE
275

source code of qtdeclarative/src/quick/scenegraph/qsgrhitextureglyphcache.cpp