1 /*
2 * Copyright (C) 2009 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27
28 #if ENABLE(WEBGL)
29
30 #include "WebGLTexture.h"
31
32 #include "WebGLFramebuffer.h"
33 #include "WebGLRenderingContext.h"
34
35 namespace WebCore {
36
create(WebGLRenderingContext * ctx)37 PassRefPtr<WebGLTexture> WebGLTexture::create(WebGLRenderingContext* ctx)
38 {
39 return adoptRef(new WebGLTexture(ctx));
40 }
41
WebGLTexture(WebGLRenderingContext * ctx)42 WebGLTexture::WebGLTexture(WebGLRenderingContext* ctx)
43 : WebGLObject(ctx)
44 , m_target(0)
45 , m_minFilter(GraphicsContext3D::NEAREST_MIPMAP_LINEAR)
46 , m_magFilter(GraphicsContext3D::LINEAR)
47 , m_wrapS(GraphicsContext3D::REPEAT)
48 , m_wrapT(GraphicsContext3D::REPEAT)
49 , m_isNPOT(false)
50 , m_isComplete(false)
51 , m_needToUseBlackTexture(false)
52 {
53 setObject(context()->graphicsContext3D()->createTexture());
54 }
55
setTarget(GC3Denum target,GC3Dint maxLevel)56 void WebGLTexture::setTarget(GC3Denum target, GC3Dint maxLevel)
57 {
58 if (!object())
59 return;
60 // Target is finalized the first time bindTexture() is called.
61 if (m_target)
62 return;
63 switch (target) {
64 case GraphicsContext3D::TEXTURE_2D:
65 m_target = target;
66 m_info.resize(1);
67 m_info[0].resize(maxLevel);
68 break;
69 case GraphicsContext3D::TEXTURE_CUBE_MAP:
70 m_target = target;
71 m_info.resize(6);
72 for (int ii = 0; ii < 6; ++ii)
73 m_info[ii].resize(maxLevel);
74 break;
75 }
76 }
77
setParameteri(GC3Denum pname,GC3Dint param)78 void WebGLTexture::setParameteri(GC3Denum pname, GC3Dint param)
79 {
80 if (!object() || !m_target)
81 return;
82 switch (pname) {
83 case GraphicsContext3D::TEXTURE_MIN_FILTER:
84 switch (param) {
85 case GraphicsContext3D::NEAREST:
86 case GraphicsContext3D::LINEAR:
87 case GraphicsContext3D::NEAREST_MIPMAP_NEAREST:
88 case GraphicsContext3D::LINEAR_MIPMAP_NEAREST:
89 case GraphicsContext3D::NEAREST_MIPMAP_LINEAR:
90 case GraphicsContext3D::LINEAR_MIPMAP_LINEAR:
91 m_minFilter = param;
92 break;
93 }
94 break;
95 case GraphicsContext3D::TEXTURE_MAG_FILTER:
96 switch (param) {
97 case GraphicsContext3D::NEAREST:
98 case GraphicsContext3D::LINEAR:
99 m_magFilter = param;
100 break;
101 }
102 break;
103 case GraphicsContext3D::TEXTURE_WRAP_S:
104 switch (param) {
105 case GraphicsContext3D::CLAMP_TO_EDGE:
106 case GraphicsContext3D::MIRRORED_REPEAT:
107 case GraphicsContext3D::REPEAT:
108 m_wrapS = param;
109 break;
110 }
111 break;
112 case GraphicsContext3D::TEXTURE_WRAP_T:
113 switch (param) {
114 case GraphicsContext3D::CLAMP_TO_EDGE:
115 case GraphicsContext3D::MIRRORED_REPEAT:
116 case GraphicsContext3D::REPEAT:
117 m_wrapT = param;
118 break;
119 }
120 break;
121 default:
122 return;
123 }
124 update();
125 }
126
setParameterf(GC3Denum pname,GC3Dfloat param)127 void WebGLTexture::setParameterf(GC3Denum pname, GC3Dfloat param)
128 {
129 if (!object() || !m_target)
130 return;
131 GC3Dint iparam = static_cast<GC3Dint>(param);
132 setParameteri(pname, iparam);
133 }
134
setLevelInfo(GC3Denum target,GC3Dint level,GC3Denum internalFormat,GC3Dsizei width,GC3Dsizei height,GC3Denum type)135 void WebGLTexture::setLevelInfo(GC3Denum target, GC3Dint level, GC3Denum internalFormat, GC3Dsizei width, GC3Dsizei height, GC3Denum type)
136 {
137 if (!object() || !m_target)
138 return;
139 // We assume level, internalFormat, width, height, and type have all been
140 // validated already.
141 int index = mapTargetToIndex(target);
142 if (index < 0)
143 return;
144 m_info[index][level].setInfo(internalFormat, width, height, type);
145 update();
146 }
147
generateMipmapLevelInfo()148 void WebGLTexture::generateMipmapLevelInfo()
149 {
150 if (!object() || !m_target)
151 return;
152 if (!canGenerateMipmaps())
153 return;
154 if (!m_isComplete) {
155 for (size_t ii = 0; ii < m_info.size(); ++ii) {
156 const LevelInfo& info0 = m_info[ii][0];
157 GC3Dsizei width = info0.width;
158 GC3Dsizei height = info0.height;
159 GC3Dint levelCount = computeLevelCount(width, height);
160 for (GC3Dint level = 1; level < levelCount; ++level) {
161 width = std::max(1, width >> 1);
162 height = std::max(1, height >> 1);
163 LevelInfo& info = m_info[ii][level];
164 info.setInfo(info0.internalFormat, width, height, info0.type);
165 }
166 }
167 m_isComplete = true;
168 }
169 m_needToUseBlackTexture = false;
170 }
171
getInternalFormat(GC3Denum target,GC3Dint level) const172 GC3Denum WebGLTexture::getInternalFormat(GC3Denum target, GC3Dint level) const
173 {
174 const LevelInfo* info = getLevelInfo(target, level);
175 if (!info)
176 return 0;
177 return info->internalFormat;
178 }
179
getType(GC3Denum target,GC3Dint level) const180 GC3Denum WebGLTexture::getType(GC3Denum target, GC3Dint level) const
181 {
182 const LevelInfo* info = getLevelInfo(target, level);
183 if (!info)
184 return 0;
185 return info->type;
186 }
187
getWidth(GC3Denum target,GC3Dint level) const188 GC3Dsizei WebGLTexture::getWidth(GC3Denum target, GC3Dint level) const
189 {
190 const LevelInfo* info = getLevelInfo(target, level);
191 if (!info)
192 return 0;
193 return info->width;
194 }
195
getHeight(GC3Denum target,GC3Dint level) const196 GC3Dsizei WebGLTexture::getHeight(GC3Denum target, GC3Dint level) const
197 {
198 const LevelInfo* info = getLevelInfo(target, level);
199 if (!info)
200 return 0;
201 return info->height;
202 }
203
isNPOT(GC3Dsizei width,GC3Dsizei height)204 bool WebGLTexture::isNPOT(GC3Dsizei width, GC3Dsizei height)
205 {
206 ASSERT(width >= 0 && height >= 0);
207 if (!width || !height)
208 return false;
209 if ((width & (width - 1)) || (height & (height - 1)))
210 return true;
211 return false;
212 }
213
isNPOT() const214 bool WebGLTexture::isNPOT() const
215 {
216 if (!object())
217 return false;
218 return m_isNPOT;
219 }
220
needToUseBlackTexture() const221 bool WebGLTexture::needToUseBlackTexture() const
222 {
223 if (!object())
224 return false;
225 return m_needToUseBlackTexture;
226 }
227
deleteObjectImpl(Platform3DObject object)228 void WebGLTexture::deleteObjectImpl(Platform3DObject object)
229 {
230 context()->graphicsContext3D()->deleteTexture(object);
231 }
232
mapTargetToIndex(GC3Denum target) const233 int WebGLTexture::mapTargetToIndex(GC3Denum target) const
234 {
235 if (m_target == GraphicsContext3D::TEXTURE_2D) {
236 if (target == GraphicsContext3D::TEXTURE_2D)
237 return 0;
238 } else if (m_target == GraphicsContext3D::TEXTURE_CUBE_MAP) {
239 switch (target) {
240 case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X:
241 return 0;
242 case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X:
243 return 1;
244 case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Y:
245 return 2;
246 case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y:
247 return 3;
248 case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z:
249 return 4;
250 case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z:
251 return 5;
252 }
253 }
254 return -1;
255 }
256
canGenerateMipmaps()257 bool WebGLTexture::canGenerateMipmaps()
258 {
259 if (isNPOT())
260 return false;
261 const LevelInfo& first = m_info[0][0];
262 for (size_t ii = 0; ii < m_info.size(); ++ii) {
263 const LevelInfo& info = m_info[ii][0];
264 if (!info.valid
265 || info.width != first.width || info.height != first.height
266 || info.internalFormat != first.internalFormat || info.type != first.type)
267 return false;
268 }
269 return true;
270 }
271
computeLevelCount(GC3Dsizei width,GC3Dsizei height)272 GC3Dint WebGLTexture::computeLevelCount(GC3Dsizei width, GC3Dsizei height)
273 {
274 // return 1 + log2Floor(std::max(width, height));
275 GC3Dsizei n = std::max(width, height);
276 if (n <= 0)
277 return 0;
278 GC3Dint log = 0;
279 GC3Dsizei value = n;
280 for (int ii = 4; ii >= 0; --ii) {
281 int shift = (1 << ii);
282 GC3Dsizei x = (value >> shift);
283 if (x) {
284 value = x;
285 log += shift;
286 }
287 }
288 ASSERT(value == 1);
289 return log + 1;
290 }
291
update()292 void WebGLTexture::update()
293 {
294 m_isNPOT = false;
295 for (size_t ii = 0; ii < m_info.size(); ++ii) {
296 if (isNPOT(m_info[ii][0].width, m_info[ii][0].height)) {
297 m_isNPOT = true;
298 break;
299 }
300 }
301 m_isComplete = true;
302 const LevelInfo& first = m_info[0][0];
303 GC3Dint levelCount = computeLevelCount(first.width, first.height);
304 if (levelCount < 1)
305 m_isComplete = false;
306 else {
307 for (size_t ii = 0; ii < m_info.size() && m_isComplete; ++ii) {
308 const LevelInfo& info0 = m_info[ii][0];
309 if (!info0.valid
310 || info0.width != first.width || info0.height != first.height
311 || info0.internalFormat != first.internalFormat || info0.type != first.type) {
312 m_isComplete = false;
313 break;
314 }
315 GC3Dsizei width = info0.width;
316 GC3Dsizei height = info0.height;
317 for (GC3Dint level = 1; level < levelCount; ++level) {
318 width = std::max(1, width >> 1);
319 height = std::max(1, height >> 1);
320 const LevelInfo& info = m_info[ii][level];
321 if (!info.valid
322 || info.width != width || info.height != height
323 || info.internalFormat != info0.internalFormat || info.type != info0.type) {
324 m_isComplete = false;
325 break;
326 }
327
328 }
329 }
330 }
331
332 m_needToUseBlackTexture = false;
333 // NPOT
334 if (m_isNPOT && ((m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::LINEAR)
335 || m_wrapS != GraphicsContext3D::CLAMP_TO_EDGE || m_wrapT != GraphicsContext3D::CLAMP_TO_EDGE))
336 m_needToUseBlackTexture = true;
337 // Completeness
338 if (!m_isComplete && m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::LINEAR)
339 m_needToUseBlackTexture = true;
340 }
341
getLevelInfo(GC3Denum target,GC3Dint level) const342 const WebGLTexture::LevelInfo* WebGLTexture::getLevelInfo(GC3Denum target, GC3Dint level) const
343 {
344 if (!object() || !m_target)
345 return 0;
346 int targetIndex = mapTargetToIndex(target);
347 if (targetIndex < 0 || targetIndex >= static_cast<int>(m_info.size()))
348 return 0;
349 if (level < 0 || level >= static_cast<GC3Dint>(m_info[targetIndex].size()))
350 return 0;
351 return &(m_info[targetIndex][level]);
352 }
353
354 }
355
356 #endif // ENABLE(WEBGL)
357