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 demonstration applications of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:BSD$ |
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 | ** BSD License Usage |
18 | ** Alternatively, you may use this file under the terms of the BSD license |
19 | ** as follows: |
20 | ** |
21 | ** "Redistribution and use in source and binary forms, with or without |
22 | ** modification, are permitted provided that the following conditions are |
23 | ** met: |
24 | ** * Redistributions of source code must retain the above copyright |
25 | ** notice, this list of conditions and the following disclaimer. |
26 | ** * Redistributions in binary form must reproduce the above copyright |
27 | ** notice, this list of conditions and the following disclaimer in |
28 | ** the documentation and/or other materials provided with the |
29 | ** distribution. |
30 | ** * Neither the name of The Qt Company Ltd nor the names of its |
31 | ** contributors may be used to endorse or promote products derived |
32 | ** from this software without specific prior written permission. |
33 | ** |
34 | ** |
35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
46 | ** |
47 | ** $QT_END_LICENSE$ |
48 | ** |
49 | ****************************************************************************/ |
50 | |
51 | #include "etcprovider.h" |
52 | |
53 | #include <QFile> |
54 | #include <QDebug> |
55 | #include <qopenglfunctions.h> |
56 | #include <qqmlfile.h> |
57 | |
58 | //#define ETC_DEBUG |
59 | |
60 | #ifndef GL_ETC1_RGB8_OES |
61 | #define GL_ETC1_RGB8_OES 0x8d64 |
62 | #endif |
63 | |
64 | typedef struct { |
65 | char [6]; |
66 | unsigned short ; |
67 | /* NB: Beware endianness issues here. */ |
68 | unsigned char ; |
69 | unsigned char ; |
70 | unsigned char ; |
71 | unsigned char ; |
72 | unsigned char ; |
73 | unsigned char ; |
74 | unsigned char ; |
75 | unsigned char ; |
76 | } ; |
77 | |
78 | unsigned short (ETCHeader *) |
79 | { |
80 | return (pHeader->iWidthMSB << 8) | pHeader->iWidthLSB; |
81 | } |
82 | |
83 | unsigned short (ETCHeader *) |
84 | { |
85 | return (pHeader->iHeightMSB << 8) | pHeader->iHeightLSB; |
86 | } |
87 | |
88 | unsigned short (ETCHeader *) |
89 | { |
90 | return (pHeader->iPaddedWidthMSB << 8) | pHeader->iPaddedWidthLSB; |
91 | } |
92 | |
93 | unsigned short (ETCHeader *) |
94 | { |
95 | return (pHeader->iPaddedHeightMSB << 8) | pHeader->iPaddedHeightLSB; |
96 | } |
97 | |
98 | EtcTexture::EtcTexture() |
99 | : m_texture_id(0), m_uploaded(false) |
100 | { |
101 | initializeOpenGLFunctions(); |
102 | } |
103 | |
104 | EtcTexture::~EtcTexture() |
105 | { |
106 | if (m_texture_id) |
107 | glDeleteTextures(n: 1, textures: &m_texture_id); |
108 | } |
109 | |
110 | int EtcTexture::textureId() const |
111 | { |
112 | if (m_texture_id == 0) { |
113 | EtcTexture *texture = const_cast<EtcTexture*>(this); |
114 | texture->glGenTextures(n: 1, textures: &texture->m_texture_id); |
115 | } |
116 | return m_texture_id; |
117 | } |
118 | |
119 | void EtcTexture::bind() |
120 | { |
121 | if (m_uploaded && m_texture_id) { |
122 | glBindTexture(GL_TEXTURE_2D, texture: m_texture_id); |
123 | return; |
124 | } |
125 | |
126 | if (m_texture_id == 0) |
127 | glGenTextures(n: 1, textures: &m_texture_id); |
128 | glBindTexture(GL_TEXTURE_2D, texture: m_texture_id); |
129 | |
130 | #ifdef ETC_DEBUG |
131 | qDebug() << "glCompressedTexImage2D, width: " << m_size.width() << "height" << m_size.height() << |
132 | "paddedWidth: " << m_paddedSize.width() << "paddedHeight: " << m_paddedSize.height(); |
133 | #endif |
134 | |
135 | #ifndef QT_NO_DEBUG |
136 | while (glGetError() != GL_NO_ERROR) { } |
137 | #endif |
138 | |
139 | QOpenGLContext *ctx = QOpenGLContext::currentContext(); |
140 | Q_ASSERT(ctx != nullptr); |
141 | ctx->functions()->glCompressedTexImage2D(GL_TEXTURE_2D, level: 0, GL_ETC1_RGB8_OES, |
142 | width: m_size.width(), height: m_size.height(), border: 0, |
143 | imageSize: (m_paddedSize.width() * m_paddedSize.height()) >> 1, |
144 | data: m_data.data() + 16); |
145 | |
146 | #ifndef QT_NO_DEBUG |
147 | // Gracefully fail in case of an error... |
148 | GLuint error = glGetError(); |
149 | if (error != GL_NO_ERROR) { |
150 | qDebug () << "glCompressedTexImage2D for compressed texture failed, error: " << error; |
151 | glBindTexture(GL_TEXTURE_2D, texture: 0); |
152 | glDeleteTextures(n: 1, textures: &m_texture_id); |
153 | m_texture_id = 0; |
154 | return; |
155 | } |
156 | #endif |
157 | |
158 | m_uploaded = true; |
159 | updateBindOptions(force: true); |
160 | } |
161 | |
162 | class QEtcTextureFactory : public QQuickTextureFactory |
163 | { |
164 | public: |
165 | QByteArray m_data; |
166 | QSize m_size; |
167 | QSize m_paddedSize; |
168 | |
169 | QSize textureSize() const override { return m_size; } |
170 | int textureByteCount() const override { return m_data.size(); } |
171 | |
172 | QSGTexture *createTexture(QQuickWindow *) const override { |
173 | EtcTexture *texture = new EtcTexture; |
174 | texture->m_data = m_data; |
175 | texture->m_size = m_size; |
176 | texture->m_paddedSize = m_paddedSize; |
177 | return texture; |
178 | } |
179 | }; |
180 | |
181 | QQuickTextureFactory *EtcProvider::requestTexture(const QString &id, QSize *size, const QSize &requestedSize) |
182 | { |
183 | Q_UNUSED(requestedSize); |
184 | QEtcTextureFactory *ret = nullptr; |
185 | |
186 | size->setHeight(0); |
187 | size->setWidth(0); |
188 | |
189 | QUrl url = QUrl(id); |
190 | if (url.isRelative() && !m_baseUrl.isEmpty()) |
191 | url = m_baseUrl.resolved(relative: url); |
192 | QString path = QQmlFile::urlToLocalFileOrQrc(url); |
193 | |
194 | QFile file(path); |
195 | #ifdef ETC_DEBUG |
196 | qDebug() << "requestTexture opening file: " << path; |
197 | #endif |
198 | if (file.open(flags: QIODevice::ReadOnly)) { |
199 | ret = new QEtcTextureFactory; |
200 | ret->m_data = file.readAll(); |
201 | if (!ret->m_data.isEmpty()) { |
202 | ETCHeader * = nullptr; |
203 | pETCHeader = (ETCHeader *)ret->m_data.data(); |
204 | size->setHeight(getHeight(pHeader: pETCHeader)); |
205 | size->setWidth(getWidth(pHeader: pETCHeader)); |
206 | ret->m_size = *size; |
207 | ret->m_paddedSize.setHeight(getPaddedHeight(pHeader: pETCHeader)); |
208 | ret->m_paddedSize.setWidth(getPaddedWidth(pHeader: pETCHeader)); |
209 | } |
210 | else { |
211 | delete ret; |
212 | ret = nullptr; |
213 | } |
214 | } |
215 | |
216 | #ifdef ETC_DEBUG |
217 | if (ret) |
218 | qDebug() << "requestTexture returning: " << ret->m_data.length() << ", bytes; width: " << size->width() << ", height: " << size->height(); |
219 | else |
220 | qDebug () << "File not found." ; |
221 | #endif |
222 | |
223 | return ret; |
224 | } |
225 | |
226 | void EtcProvider::setBaseUrl(const QUrl &base) |
227 | { |
228 | m_baseUrl = base; |
229 | } |
230 | |