1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtCanvas3D 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 "teximage3d_p.h"
41#include "canvas3dcommon_p.h"
42
43#include <QJSValueIterator>
44#include <QtQml/QQmlEngine>
45
46QT_BEGIN_NAMESPACE
47
48QT_CANVAS3D_BEGIN_NAMESPACE
49
50class EngineToImageFactoryMap : public QMap<QQmlEngine *, CanvasTextureImageFactory *>
51{
52 bool isDeleting = false;
53public:
54 ~EngineToImageFactoryMap() {
55 isDeleting = true;
56 qDeleteAll(c: *this);
57 }
58 void remove(QQmlEngine *e) {
59 if (isDeleting)
60 return;
61 QMap<QQmlEngine *, CanvasTextureImageFactory *>::remove(akey: e);
62 }
63};
64
65static EngineToImageFactoryMap m_qmlEngineToImageFactoryMap;
66static ulong m_texId = 0;
67
68CanvasTextureImageFactory::CanvasTextureImageFactory(QQmlEngine *engine, QObject *parent) :
69 QObject(parent)
70{
71 m_qmlEngine = engine;
72
73 QObject::connect(sender: engine, signal: &QObject::destroyed,
74 receiver: this, slot: &CanvasTextureImageFactory::deleteLater);
75}
76
77CanvasTextureImageFactory::~CanvasTextureImageFactory()
78{
79 m_qmlEngineToImageFactoryMap.remove(e: m_qmlEngine);
80}
81
82/*!
83 * \qmltype TextureImageFactory
84 * \since QtCanvas3D 1.0
85 * \inqmlmodule QtCanvas3D
86 * \brief Create TextureImage elements.
87 * \deprecated
88 *
89 * \b{Deprecated in Qt 5.12.}
90 * This static QML type is used for creating TextureImage instances by calling the
91 * TextureImageFactory::newTexImage() function.
92 *
93 * \sa TextureImage
94 */
95QObject *CanvasTextureImageFactory::texture_image_factory_provider(QQmlEngine *engine,
96 QJSEngine *scriptEngine)
97{
98 Q_UNUSED(scriptEngine)
99 return factory(engine);
100}
101
102CanvasTextureImageFactory *CanvasTextureImageFactory::factory(QQmlEngine *engine)
103{
104 if (m_qmlEngineToImageFactoryMap.contains(akey: engine))
105 return m_qmlEngineToImageFactoryMap[engine];
106
107 CanvasTextureImageFactory *factory = new CanvasTextureImageFactory(engine);
108 m_qmlEngineToImageFactoryMap[engine] = factory;
109 return factory;
110}
111
112void CanvasTextureImageFactory::handleImageLoadingStarted(CanvasTextureImage *image)
113{
114 if (m_loadingImagesList.contains(t: image))
115 return;
116
117 m_loadingImagesList << image;
118}
119
120void CanvasTextureImageFactory::notifyLoadedImages()
121{
122 if (!m_loadingImagesList.size())
123 return;
124
125 auto hasLoadingFinishedOrFailed = [](CanvasTextureImage *image) -> bool {
126 if (image->imageState() == CanvasTextureImage::LOADING_FINISHED) {
127 image->emitImageLoaded();
128 return true;
129 } else if (image->imageState() == CanvasTextureImage::LOADING_ERROR) {
130 image->emitImageLoadingError();
131 return true;
132 }
133 return false;
134 };
135
136 m_loadingImagesList.erase(afirst: std::remove_if(first: m_loadingImagesList.begin(), last: m_loadingImagesList.end(),
137 pred: hasLoadingFinishedOrFailed),
138 alast: m_loadingImagesList.end());
139}
140
141/*!
142 * \qmlmethod TextureImage TextureImageFactory::newTexImage()
143 * \b{Deprecated in Qt 5.12.}
144 * Returns a new empty TextureImage.
145 */
146QJSValue CanvasTextureImageFactory::newTexImage()
147{
148 CanvasTextureImage *newImg = new CanvasTextureImage(this, m_qmlEngine);
149 return m_qmlEngine->newQObject(object: newImg);
150}
151
152void CanvasTextureImageFactory::handleImageDestroyed(CanvasTextureImage *image)
153{
154 m_loadingImagesList.removeOne(t: image);
155}
156
157/*!
158 * \qmltype TextureImage
159 * \since QtCanvas3D 1.0
160 * \inqmlmodule QtCanvas3D
161 * \brief Contains a texture image.
162 * \deprecated
163 *
164 * \b{Deprecated in Qt 5.12.}
165 * An uncreatable QML type that contains a texture image created by calling
166 * TextureImageFactory::newTexImage() and settings the \c src of the image.
167 *
168 * \sa TextureImageFactory
169 */
170CanvasTextureImage::CanvasTextureImage(CanvasTextureImageFactory *parent, QQmlEngine *engine) :
171 CanvasAbstractObject(0, 0),
172 m_engine(engine),
173 m_networkAccessManager(m_engine->networkAccessManager()),
174 m_networkReply(0),
175 m_state(INITIALIZED),
176 m_errorString(""),
177 m_pixelCache(0),
178 m_pixelCacheFormat(CanvasContext::NONE),
179 m_pixelCacheFlipY(false),
180 m_parentFactory(parent)
181{
182}
183
184CanvasTextureImage::CanvasTextureImage(const QImage &source,
185 int width, int height,
186 CanvasTextureImageFactory *parent,
187 QQmlEngine *engine) :
188 CanvasAbstractObject(0, 0),
189 m_engine(engine),
190 m_networkAccessManager(m_engine->networkAccessManager()),
191 m_networkReply(0),
192 m_state(INITIALIZED),
193 m_errorString(""),
194 m_pixelCache(0),
195 m_pixelCacheFormat(CanvasContext::NONE),
196 m_pixelCacheFlipY(false),
197 m_parentFactory(parent)
198{
199 m_image = source.scaled(w: width, h: height);
200 setImageState(LOADING_FINISHED);
201}
202
203void CanvasTextureImage::cleanupNetworkReply()
204{
205 if (m_networkReply) {
206 QObject::disconnect(sender: m_networkReply, signal: &QNetworkReply::finished,
207 receiver: this, slot: &CanvasTextureImage::handleReply);
208 m_networkReply->abort();
209 m_networkReply->deleteLater();
210 m_networkReply = 0;
211 }
212}
213
214CanvasTextureImage::~CanvasTextureImage()
215{
216 if (!m_parentFactory.isNull())
217 m_parentFactory->handleImageDestroyed(image: this);
218 cleanupNetworkReply();
219 delete[] m_pixelCache;
220}
221
222/*!
223 * \qmlproperty url TextureImage::src()
224 * \b{Deprecated in Qt 5.12.}
225 * Contains the url source where the image data is loaded from.
226 */
227const QUrl &CanvasTextureImage::src() const
228{
229 return m_source;
230}
231
232void CanvasTextureImage::setSrc(const QUrl &url)
233{
234 if (url == m_source)
235 return;
236
237 m_source = url;
238 emit srcChanged(source: m_source);
239
240 load();
241}
242
243/*!
244 * \qmlmethod int TextureImage::id()
245 * \b{Deprecated in Qt 5.12.}
246 * Contains the object id.
247 */
248ulong CanvasTextureImage::id()
249{
250 return m_texId++;
251}
252
253void CanvasTextureImage::emitImageLoaded()
254{
255 emit imageLoaded(image: this);
256}
257
258void CanvasTextureImage::emitImageLoadingError()
259{
260 emit imageLoadingFailed(image: this);
261}
262
263void CanvasTextureImage::load()
264{
265 if (m_source.isEmpty()) {
266 QByteArray array;
267 m_image.loadFromData(data: array);
268 m_glImage = m_image.convertToFormat(f: QImage::Format_RGBA8888);
269 setImageState(LOADING_FINISHED);
270 return;
271 }
272
273 if (m_state == LOADING)
274 return;
275
276 setImageState(LOADING);
277 if (!m_parentFactory.isNull())
278 m_parentFactory->handleImageLoadingStarted(image: this);
279 emit imageLoadingStarted(image: this);
280
281 QNetworkRequest request(m_source);
282 m_networkReply = m_networkAccessManager->get(request);
283 QObject::connect(sender: m_networkReply, signal: &QNetworkReply::finished,
284 receiver: this, slot: &CanvasTextureImage::handleReply);
285}
286
287/*!
288 * \qmlproperty string TextureImage::errorString()
289 * \b{Deprecated in Qt 5.12.}
290 * Contains the error string if an error happened during image creation.
291 */
292QString CanvasTextureImage::errorString() const
293{
294 return m_errorString;
295}
296
297void CanvasTextureImage::handleReply()
298{
299 if (m_networkReply) {
300 if (m_networkReply->error() != QNetworkReply::NoError) {
301 m_errorString = m_networkReply->errorString();
302 emit errorStringChanged(errorString: m_errorString);
303 setImageState(LOADING_ERROR);
304 } else {
305 m_image.loadFromData(data: m_networkReply->readAll());
306 setImageState(LOADING_FINISHED);
307 }
308 cleanupNetworkReply();
309 }
310}
311
312QImage &CanvasTextureImage::getImage()
313{
314 return m_image;
315}
316
317QVariant *CanvasTextureImage::anything() const
318{
319 return m_anyValue;
320}
321
322void CanvasTextureImage::setAnything(QVariant *value)
323{
324 if (m_anyValue == value)
325 return;
326 m_anyValue = value;
327 emit anythingChanged(value);
328}
329
330/*!
331 * \qmlproperty TextureImageState TextureImage::imageState()
332 * \b{Deprecated in Qt 5.12.}
333 * Contains the texture image state. It is one of \c{TextureImage.INITIALIZED},
334 * \c{TextureImage.LOAD_PENDING}, \c{TextureImage.LOADING}, \c{TextureImage.LOADING_FINISHED} or
335 * \c{TextureImage.LOADING_ERROR}.
336 */
337CanvasTextureImage::TextureImageState CanvasTextureImage::imageState() const
338{
339 return m_state;
340}
341
342void CanvasTextureImage::setImageState(TextureImageState state)
343{
344 if (state == m_state)
345 return;
346 m_state = state;
347 emit imageStateChanged(state);
348}
349
350/*!
351 * \qmlproperty int TextureImage::width()
352 * \b{Deprecated in Qt 5.12.}
353 * Contains the texture image width.
354 */
355int CanvasTextureImage::width() const
356{
357 if (m_state != LOADING_FINISHED)
358 return 0;
359
360 return m_image.width();
361}
362
363/*!
364 * \qmlproperty int TextureImage::height()
365 * \b{Deprecated in Qt 5.12.}
366 * Contains the texture image height.
367 */
368int CanvasTextureImage::height() const
369{
370 if (m_state != LOADING_FINISHED)
371 return 0;
372
373 return m_image.height();
374}
375
376uchar *CanvasTextureImage::convertToFormat(CanvasContext::glEnums format,
377 bool flipY, bool premultipliedAlpha)
378{
379 if (m_pixelCacheFormat == format && m_pixelCacheFlipY == flipY)
380 return m_pixelCache;
381
382 // Destroy the pixel cache
383 delete[] m_pixelCache;
384 m_pixelCache = 0;
385 m_pixelCacheFormat = CanvasContext::NONE;
386
387 // Flip the image if needed
388 if (m_pixelCacheFlipY != flipY) {
389 m_image = m_image.mirrored(horizontally: false, vertically: true);
390 m_pixelCacheFlipY = flipY;
391 }
392
393 if (premultipliedAlpha)
394 m_glImage = m_image.convertToFormat(f: QImage::Format_RGBA8888_Premultiplied);
395 else
396 m_glImage = m_image.convertToFormat(f: QImage::Format_RGBA8888);
397
398 // Get latest data for the conversion
399 uchar *origPixels = m_glImage.bits();
400 int width = m_glImage.width();
401 int height = m_glImage.height();
402
403 // Handle format conversions if needed
404 switch (format) {
405 case CanvasContext::UNSIGNED_BYTE: {
406 return origPixels;
407 break;
408 }
409 case CanvasContext::UNSIGNED_SHORT_5_6_5: {
410 ushort *pixels = new ushort[width * height];
411 ushort pixel;
412 for (int y = 0; y < height; y++) {
413 for (int x = 0; x < width; x++) {
414 int srcIdx = y * width * 4 + x * 4;
415 pixel = ((origPixels[srcIdx++] >> 3) & 0x1F) << 11;
416 pixel |= ((origPixels[srcIdx++] >> 2) & 0x3F) << 5;
417 pixel |= ((origPixels[srcIdx++] >> 3) & 0x1F) << 0;
418 pixels[y * width + x] = pixel;
419 }
420 }
421 m_pixelCacheFormat = CanvasContext::UNSIGNED_SHORT_5_6_5;
422 m_pixelCache = (uchar *)pixels;
423 return m_pixelCache;
424 }
425 case CanvasContext::UNSIGNED_SHORT_4_4_4_4: {
426 ushort *pixels = new ushort[width * height];
427 ushort pixel;
428 for (int y = 0; y < height; y++) {
429 for (int x = 0; x < width; x++) {
430 int srcIdx = y * width * 4 + x * 4;
431 pixel = ((origPixels[srcIdx++] >> 4) & 0x0F) << 12;
432 pixel |= ((origPixels[srcIdx++] >> 4) & 0x0F) << 8;
433 pixel |= ((origPixels[srcIdx++] >> 4) & 0x0F) << 4;
434 pixel |= ((origPixels[srcIdx++] >> 4) & 0x0F) << 0;
435 pixels[y * width + x] = pixel;
436 }
437 }
438 m_pixelCacheFormat = CanvasContext::UNSIGNED_SHORT_4_4_4_4;
439 m_pixelCache = (uchar *)pixels;
440 return m_pixelCache;
441 }
442 case CanvasContext::UNSIGNED_SHORT_5_5_5_1: {
443 ushort *pixels = new ushort[width * height];
444 ushort pixel;
445 for (int y = 0; y < height; y++) {
446 for (int x = 0; x < width; x++) {
447 int srcIdx = y * width * 4 + x * 4;
448 pixel = ((origPixels[srcIdx++] >> 3) & 0x1F) << 11;
449 pixel |= ((origPixels[srcIdx++] >> 3) & 0x1F) << 6;
450 pixel |= ((origPixels[srcIdx++] >> 3) & 0x1F) << 1;
451 pixel |= ((origPixels[srcIdx++] >> 7) & 0x01) << 0;
452 pixels[y * width + x] = pixel;
453 }
454 }
455 m_pixelCacheFormat = CanvasContext::UNSIGNED_SHORT_5_5_5_1;
456 m_pixelCache = (uchar *)pixels;
457 return m_pixelCache;
458 }
459 default: {
460 qDebug() << "TexImage3D::" << __FUNCTION__ << ":INVALID_ENUM Invalid type enum";
461 break;
462 }
463 }
464
465 return 0;
466}
467
468/*!
469 * \qmlmethod TextureImage TextureImage::create()
470 * \b{Deprecated in Qt 5.12.}
471 * Returns a new texture image.
472 */
473QJSValue CanvasTextureImage::create()
474{
475 return m_engine->newQObject(object: new CanvasTextureImage(m_parentFactory, m_engine));
476}
477
478/*!
479 * \qmlmethod TextureImage TextureImage::resize(int width, int height)
480 * \b{Deprecated in Qt 5.12.}
481 * Returns a copy of the texture image resized to the given \a width and \a height.
482 */
483QJSValue CanvasTextureImage::resize(int width, int height)
484{
485 if (m_state != LOADING_FINISHED)
486 return 0;
487
488 return m_engine->newQObject(object: new CanvasTextureImage(m_image,
489 width, height,
490 m_parentFactory, m_engine));
491}
492
493QDebug operator<<(QDebug dbg, const CanvasTextureImage *texImage)
494{
495 if (texImage)
496 dbg.nospace() << "TexImage3D("<< ((void*) texImage) << texImage->name() << ")";
497 else
498 dbg.nospace() << "TexImage3D("<< ((void*) texImage) <<")";
499 return dbg.maybeSpace();
500}
501
502QT_CANVAS3D_END_NAMESPACE
503QT_END_NAMESPACE
504

source code of qtcanvas3d/src/imports/qtcanvas3d/teximage3d.cpp