1 | // Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qpaintedtextureimage.h" |
5 | #include "qpaintedtextureimage_p.h" |
6 | |
7 | #include <QtGui/qpainter.h> |
8 | #include <QtGui/qimage.h> |
9 | |
10 | QT_BEGIN_NAMESPACE |
11 | |
12 | namespace Qt3DRender { |
13 | |
14 | /*! |
15 | \class Qt3DRender::QPaintedTextureImage |
16 | \inmodule Qt3DRender |
17 | \since 5.8 |
18 | \brief A QAbstractTextureImage that can be written through a QPainter. |
19 | |
20 | A QPaintedTextureImage provides a way to specify a texture image |
21 | (and thus an OpenGL texture) through a QPainter. The width and height of the |
22 | texture image can be specified through the width and height or size |
23 | properties. |
24 | |
25 | A QPaintedTextureImage must be subclassed and the virtual paint() function |
26 | implemented. Each time update() is called on the QPaintedTextureImage, |
27 | the paint() function is invoked and the resulting image is uploaded. |
28 | |
29 | The QPaintedTextureImage must be attached to some QAbstractTexture. |
30 | */ |
31 | |
32 | |
33 | |
34 | QPaintedTextureImagePrivate::QPaintedTextureImagePrivate() |
35 | : m_imageSize(256,256) |
36 | , m_devicePixelRatio(1.0) |
37 | , m_generation(0) |
38 | { |
39 | } |
40 | |
41 | QPaintedTextureImagePrivate::~QPaintedTextureImagePrivate() |
42 | { |
43 | } |
44 | |
45 | void QPaintedTextureImagePrivate::repaint() |
46 | { |
47 | // create or re-allocate QImage with current size |
48 | if (m_image.isNull() |
49 | || m_image->size() != m_imageSize |
50 | || m_image->devicePixelRatio() != m_devicePixelRatio) |
51 | { |
52 | m_image.reset(other: new QImage(m_imageSize, QImage::Format_RGBA8888)); |
53 | m_image->setDevicePixelRatio(m_devicePixelRatio); |
54 | m_image->fill(color: Qt::transparent); |
55 | } |
56 | |
57 | QPainter painter(m_image.data()); |
58 | q_func()->paint(painter: &painter); |
59 | painter.end(); |
60 | |
61 | ++m_generation; |
62 | m_currentGenerator = QSharedPointer<QPaintedTextureImageDataGenerator>::create(arguments&: *m_image.data(), arguments&: m_generation, arguments: q_func()->id()); |
63 | q_func()->notifyDataGeneratorChanged(); |
64 | } |
65 | |
66 | QPaintedTextureImage::QPaintedTextureImage(Qt3DCore::QNode *parent) |
67 | : QAbstractTextureImage(*new QPaintedTextureImagePrivate, parent) |
68 | { |
69 | } |
70 | |
71 | QPaintedTextureImage::~QPaintedTextureImage() |
72 | { |
73 | } |
74 | |
75 | /*! |
76 | \property QPaintedTextureImage::width |
77 | |
78 | This property holds the width of the texture image. |
79 | The width must be greater than or equal to 1. |
80 | */ |
81 | int QPaintedTextureImage::width() const |
82 | { |
83 | Q_D(const QPaintedTextureImage); |
84 | return d->m_imageSize.width(); |
85 | } |
86 | |
87 | /*! |
88 | \property QPaintedTextureImage::height |
89 | |
90 | This property holds the height of the texture image. |
91 | The height must be greater than or equal to 1. |
92 | */ |
93 | int QPaintedTextureImage::height() const |
94 | { |
95 | Q_D(const QPaintedTextureImage); |
96 | return d->m_imageSize.height(); |
97 | } |
98 | |
99 | /*! |
100 | \property QPaintedTextureImage::size |
101 | |
102 | This property holds the size of the texture image. |
103 | |
104 | \sa height, width |
105 | |
106 | */ |
107 | QSize QPaintedTextureImage::size() const |
108 | { |
109 | Q_D(const QPaintedTextureImage); |
110 | return d->m_imageSize; |
111 | } |
112 | |
113 | /*! |
114 | Sets the width (\a w) of the texture image. Triggers an update, if the size changes. |
115 | */ |
116 | void QPaintedTextureImage::setWidth(int w) |
117 | { |
118 | if (w < 1) { |
119 | qWarning() << "QPaintedTextureImage: Attempting to set invalid width" << w << ". Will be ignored" ; |
120 | return; |
121 | } |
122 | setSize(QSize(w, height())); |
123 | } |
124 | |
125 | /*! |
126 | Sets the height (\a h) of the texture image. Triggers an update, if the size changes. |
127 | */ |
128 | void QPaintedTextureImage::setHeight(int h) |
129 | { |
130 | if (h < 1) { |
131 | qWarning() << "QPaintedTextureImage: Attempting to set invalid height" << h << ". Will be ignored" ; |
132 | return; |
133 | } |
134 | setSize(QSize(width(), h)); |
135 | } |
136 | |
137 | /*! |
138 | Sets the width and height of the texture image. Triggers an update, if the \a size changes. |
139 | */ |
140 | void QPaintedTextureImage::setSize(QSize size) |
141 | { |
142 | Q_D(QPaintedTextureImage); |
143 | |
144 | if (d->m_imageSize != size) { |
145 | if (size.isEmpty()) { |
146 | qWarning() << "QPaintedTextureImage: Attempting to set invalid size" << size << ". Will be ignored" ; |
147 | return; |
148 | } |
149 | |
150 | const bool changeW = d->m_imageSize.width() != size.width(); |
151 | const bool changeH = d->m_imageSize.height() != size.height(); |
152 | |
153 | d->m_imageSize = size; |
154 | |
155 | if (changeW) |
156 | Q_EMIT widthChanged(w: d->m_imageSize.height()); |
157 | if (changeH) |
158 | Q_EMIT heightChanged(w: d->m_imageSize.height()); |
159 | |
160 | Q_EMIT sizeChanged(size: d->m_imageSize); |
161 | |
162 | d->repaint(); |
163 | } |
164 | } |
165 | |
166 | /*! |
167 | Immediately triggers the painted texture's paint() function, |
168 | which in turn uploads the new image to the GPU. If you are |
169 | making multiple changes to a painted texture, consider waiting |
170 | until all changes are complete before calling update, in order |
171 | to minimize the number of repaints required. |
172 | |
173 | Parameter \a rect is currently unused. |
174 | */ |
175 | void QPaintedTextureImage::update(const QRect &rect) |
176 | { |
177 | Q_UNUSED(rect); |
178 | Q_D(QPaintedTextureImage); |
179 | |
180 | d->repaint(); |
181 | } |
182 | |
183 | /*! |
184 | \fn Qt3DRender::QPaintedTextureImage::paint(QPainter *painter) |
185 | |
186 | Paints the texture image with the specified QPainter object \a painter. |
187 | |
188 | QPainter considers the top-left corner of an image as its origin, while OpenGL considers |
189 | the bottom-left corner of a texture as its origin. An easy way to account for this difference |
190 | is to set a custom viewport on the painter before doing any other painting: |
191 | |
192 | \code |
193 | painter->setViewport(0, height(), width(), -height()); |
194 | ... |
195 | \endcode |
196 | */ |
197 | QTextureImageDataGeneratorPtr QPaintedTextureImage::dataGenerator() const |
198 | { |
199 | Q_D(const QPaintedTextureImage); |
200 | return d->m_currentGenerator; |
201 | } |
202 | |
203 | |
204 | QPaintedTextureImageDataGenerator::QPaintedTextureImageDataGenerator(const QImage &image, int gen, Qt3DCore::QNodeId texId) |
205 | : m_image(image) // pixels are implicitly shared, no copying |
206 | , m_generation(gen) |
207 | , m_paintedTextureImageId(texId) |
208 | { |
209 | } |
210 | |
211 | QPaintedTextureImageDataGenerator::~QPaintedTextureImageDataGenerator() |
212 | { |
213 | } |
214 | |
215 | QTextureImageDataPtr QPaintedTextureImageDataGenerator::operator ()() |
216 | { |
217 | QTextureImageDataPtr textureData = QTextureImageDataPtr::create(); |
218 | textureData->setImage(m_image); |
219 | return textureData; |
220 | } |
221 | |
222 | bool QPaintedTextureImageDataGenerator::operator ==(const QTextureImageDataGenerator &other) const |
223 | { |
224 | const QPaintedTextureImageDataGenerator *otherFunctor = functor_cast<QPaintedTextureImageDataGenerator>(other: &other); |
225 | return (otherFunctor != nullptr && otherFunctor->m_generation == m_generation && otherFunctor->m_paintedTextureImageId == m_paintedTextureImageId); |
226 | } |
227 | |
228 | } // namespace Qt3DRender |
229 | |
230 | QT_END_NAMESPACE |
231 | |
232 | #include "moc_qpaintedtextureimage.cpp" |
233 | |
234 | |