e8e8bf8245d03400723e72766f203972e43b182f
[WebKit-https.git] / Source / WebCore / html / canvas / WebGLTexture.cpp
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
37 PassRefPtr<WebGLTexture> WebGLTexture::create(WebGLRenderingContext* ctx)
38 {
39     return adoptRef(new WebGLTexture(ctx));
40 }
41
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
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
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
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
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
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
172 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
180 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
188 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
196 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
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
214 bool WebGLTexture::isNPOT() const
215 {
216     if (!object())
217         return false;
218     return m_isNPOT;
219 }
220
221 bool WebGLTexture::needToUseBlackTexture() const
222 {
223     if (!object())
224         return false;
225     return m_needToUseBlackTexture;
226 }
227
228 void WebGLTexture::deleteObjectImpl(Platform3DObject object)
229 {
230     context()->graphicsContext3D()->deleteTexture(object);
231 }
232
233 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
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
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
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
342 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)