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 #include "core/html/canvas/WebGLTexture.h"
29
30 #include "core/html/canvas/WebGLRenderingContextBase.h"
31
32 namespace WebCore {
33
create(WebGLRenderingContextBase * ctx)34 PassRefPtr<WebGLTexture> WebGLTexture::create(WebGLRenderingContextBase* ctx)
35 {
36 return adoptRef(new WebGLTexture(ctx));
37 }
38
WebGLTexture(WebGLRenderingContextBase * ctx)39 WebGLTexture::WebGLTexture(WebGLRenderingContextBase* ctx)
40 : WebGLSharedObject(ctx)
41 , m_target(0)
42 , m_minFilter(GL_NEAREST_MIPMAP_LINEAR)
43 , m_magFilter(GL_LINEAR)
44 , m_wrapS(GL_REPEAT)
45 , m_wrapT(GL_REPEAT)
46 , m_isNPOT(false)
47 , m_isCubeComplete(false)
48 , m_isComplete(false)
49 , m_needToUseBlackTexture(false)
50 , m_isFloatType(false)
51 , m_isHalfFloatType(false)
52 {
53 ScriptWrappable::init(this);
54 setObject(ctx->webContext()->createTexture());
55 }
56
~WebGLTexture()57 WebGLTexture::~WebGLTexture()
58 {
59 deleteObject(0);
60 }
61
setTarget(GLenum target,GLint maxLevel)62 void WebGLTexture::setTarget(GLenum target, GLint maxLevel)
63 {
64 if (!object())
65 return;
66 // Target is finalized the first time bindTexture() is called.
67 if (m_target)
68 return;
69 switch (target) {
70 case GL_TEXTURE_2D:
71 m_target = target;
72 m_info.resize(1);
73 m_info[0].resize(maxLevel);
74 break;
75 case GL_TEXTURE_CUBE_MAP:
76 m_target = target;
77 m_info.resize(6);
78 for (int ii = 0; ii < 6; ++ii)
79 m_info[ii].resize(maxLevel);
80 break;
81 }
82 }
83
setParameteri(GLenum pname,GLint param)84 void WebGLTexture::setParameteri(GLenum pname, GLint param)
85 {
86 if (!object() || !m_target)
87 return;
88 switch (pname) {
89 case GL_TEXTURE_MIN_FILTER:
90 switch (param) {
91 case GL_NEAREST:
92 case GL_LINEAR:
93 case GL_NEAREST_MIPMAP_NEAREST:
94 case GL_LINEAR_MIPMAP_NEAREST:
95 case GL_NEAREST_MIPMAP_LINEAR:
96 case GL_LINEAR_MIPMAP_LINEAR:
97 m_minFilter = param;
98 break;
99 }
100 break;
101 case GL_TEXTURE_MAG_FILTER:
102 switch (param) {
103 case GL_NEAREST:
104 case GL_LINEAR:
105 m_magFilter = param;
106 break;
107 }
108 break;
109 case GL_TEXTURE_WRAP_S:
110 switch (param) {
111 case GL_CLAMP_TO_EDGE:
112 case GL_MIRRORED_REPEAT:
113 case GL_REPEAT:
114 m_wrapS = param;
115 break;
116 }
117 break;
118 case GL_TEXTURE_WRAP_T:
119 switch (param) {
120 case GL_CLAMP_TO_EDGE:
121 case GL_MIRRORED_REPEAT:
122 case GL_REPEAT:
123 m_wrapT = param;
124 break;
125 }
126 break;
127 default:
128 return;
129 }
130 update();
131 }
132
setParameterf(GLenum pname,GLfloat param)133 void WebGLTexture::setParameterf(GLenum pname, GLfloat param)
134 {
135 if (!object() || !m_target)
136 return;
137 GLint iparam = static_cast<GLint>(param);
138 setParameteri(pname, iparam);
139 }
140
setLevelInfo(GLenum target,GLint level,GLenum internalFormat,GLsizei width,GLsizei height,GLenum type)141 void WebGLTexture::setLevelInfo(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum type)
142 {
143 if (!object() || !m_target)
144 return;
145 // We assume level, internalFormat, width, height, and type have all been
146 // validated already.
147 int index = mapTargetToIndex(target);
148 if (index < 0)
149 return;
150 m_info[index][level].setInfo(internalFormat, width, height, type);
151 update();
152 }
153
generateMipmapLevelInfo()154 void WebGLTexture::generateMipmapLevelInfo()
155 {
156 if (!object() || !m_target)
157 return;
158 if (!canGenerateMipmaps())
159 return;
160 if (!m_isComplete) {
161 for (size_t ii = 0; ii < m_info.size(); ++ii) {
162 const LevelInfo& info0 = m_info[ii][0];
163 GLsizei width = info0.width;
164 GLsizei height = info0.height;
165 GLint levelCount = computeLevelCount(width, height);
166 for (GLint level = 1; level < levelCount; ++level) {
167 width = std::max(1, width >> 1);
168 height = std::max(1, height >> 1);
169 LevelInfo& info = m_info[ii][level];
170 info.setInfo(info0.internalFormat, width, height, info0.type);
171 }
172 }
173 m_isComplete = true;
174 }
175 m_needToUseBlackTexture = false;
176 }
177
getInternalFormat(GLenum target,GLint level) const178 GLenum WebGLTexture::getInternalFormat(GLenum target, GLint level) const
179 {
180 const LevelInfo* info = getLevelInfo(target, level);
181 if (!info)
182 return 0;
183 return info->internalFormat;
184 }
185
getType(GLenum target,GLint level) const186 GLenum WebGLTexture::getType(GLenum target, GLint level) const
187 {
188 const LevelInfo* info = getLevelInfo(target, level);
189 if (!info)
190 return 0;
191 return info->type;
192 }
193
getWidth(GLenum target,GLint level) const194 GLsizei WebGLTexture::getWidth(GLenum target, GLint level) const
195 {
196 const LevelInfo* info = getLevelInfo(target, level);
197 if (!info)
198 return 0;
199 return info->width;
200 }
201
getHeight(GLenum target,GLint level) const202 GLsizei WebGLTexture::getHeight(GLenum target, GLint level) const
203 {
204 const LevelInfo* info = getLevelInfo(target, level);
205 if (!info)
206 return 0;
207 return info->height;
208 }
209
isValid(GLenum target,GLint level) const210 bool WebGLTexture::isValid(GLenum target, GLint level) const
211 {
212 const LevelInfo* info = getLevelInfo(target, level);
213 if (!info)
214 return 0;
215 return info->valid;
216 }
217
isNPOT(GLsizei width,GLsizei height)218 bool WebGLTexture::isNPOT(GLsizei width, GLsizei height)
219 {
220 ASSERT(width >= 0 && height >= 0);
221 if (!width || !height)
222 return false;
223 if ((width & (width - 1)) || (height & (height - 1)))
224 return true;
225 return false;
226 }
227
isNPOT() const228 bool WebGLTexture::isNPOT() const
229 {
230 if (!object())
231 return false;
232 return m_isNPOT;
233 }
234
needToUseBlackTexture(TextureExtensionFlag flag) const235 bool WebGLTexture::needToUseBlackTexture(TextureExtensionFlag flag) const
236 {
237 if (!object())
238 return false;
239 if (m_needToUseBlackTexture)
240 return true;
241 if ((m_isFloatType && !(flag & TextureFloatLinearExtensionEnabled)) || (m_isHalfFloatType && !(flag && TextureHalfFloatLinearExtensionEnabled))) {
242 if (m_magFilter != GL_NEAREST || (m_minFilter != GL_NEAREST && m_minFilter != GL_NEAREST_MIPMAP_NEAREST))
243 return true;
244 }
245 return false;
246 }
247
deleteObjectImpl(blink::WebGraphicsContext3D * context3d,Platform3DObject object)248 void WebGLTexture::deleteObjectImpl(blink::WebGraphicsContext3D* context3d, Platform3DObject object)
249 {
250 context3d->deleteTexture(object);
251 }
252
mapTargetToIndex(GLenum target) const253 int WebGLTexture::mapTargetToIndex(GLenum target) const
254 {
255 if (m_target == GL_TEXTURE_2D) {
256 if (target == GL_TEXTURE_2D)
257 return 0;
258 } else if (m_target == GL_TEXTURE_CUBE_MAP) {
259 switch (target) {
260 case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
261 return 0;
262 case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
263 return 1;
264 case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
265 return 2;
266 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
267 return 3;
268 case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
269 return 4;
270 case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
271 return 5;
272 }
273 }
274 return -1;
275 }
276
canGenerateMipmaps()277 bool WebGLTexture::canGenerateMipmaps()
278 {
279 if (isNPOT())
280 return false;
281 const LevelInfo& first = m_info[0][0];
282 for (size_t ii = 0; ii < m_info.size(); ++ii) {
283 const LevelInfo& info = m_info[ii][0];
284 if (!info.valid
285 || info.width != first.width || info.height != first.height
286 || info.internalFormat != first.internalFormat || info.type != first.type
287 || (m_info.size() > 1 && !m_isCubeComplete))
288 return false;
289 }
290 return true;
291 }
292
computeLevelCount(GLsizei width,GLsizei height)293 GLint WebGLTexture::computeLevelCount(GLsizei width, GLsizei height)
294 {
295 // return 1 + log2Floor(std::max(width, height));
296 GLsizei n = std::max(width, height);
297 if (n <= 0)
298 return 0;
299 GLint log = 0;
300 GLsizei value = n;
301 for (int ii = 4; ii >= 0; --ii) {
302 int shift = (1 << ii);
303 GLsizei x = (value >> shift);
304 if (x) {
305 value = x;
306 log += shift;
307 }
308 }
309 ASSERT(value == 1);
310 return log + 1;
311 }
312
update()313 void WebGLTexture::update()
314 {
315 m_isNPOT = false;
316 for (size_t ii = 0; ii < m_info.size(); ++ii) {
317 if (isNPOT(m_info[ii][0].width, m_info[ii][0].height)) {
318 m_isNPOT = true;
319 break;
320 }
321 }
322 m_isComplete = true;
323 m_isCubeComplete = true;
324 const LevelInfo& first = m_info[0][0];
325 GLint levelCount = computeLevelCount(first.width, first.height);
326 if (levelCount < 1)
327 m_isComplete = false;
328 else {
329 for (size_t ii = 0; ii < m_info.size() && m_isComplete; ++ii) {
330 const LevelInfo& info0 = m_info[ii][0];
331 if (!info0.valid
332 || info0.width != first.width || info0.height != first.height
333 || info0.internalFormat != first.internalFormat || info0.type != first.type
334 || (m_info.size() > 1 && info0.width != info0.height)) {
335 if (m_info.size() > 1)
336 m_isCubeComplete = false;
337 m_isComplete = false;
338 break;
339 }
340 GLsizei width = info0.width;
341 GLsizei height = info0.height;
342 for (GLint level = 1; level < levelCount; ++level) {
343 width = std::max(1, width >> 1);
344 height = std::max(1, height >> 1);
345 const LevelInfo& info = m_info[ii][level];
346 if (!info.valid
347 || info.width != width || info.height != height
348 || info.internalFormat != info0.internalFormat || info.type != info0.type) {
349 m_isComplete = false;
350 break;
351 }
352
353 }
354 }
355 }
356 m_isFloatType = m_info[0][0].type == GL_FLOAT;
357 m_isHalfFloatType = m_info[0][0].type == GL_HALF_FLOAT_OES;
358
359 m_needToUseBlackTexture = false;
360 // NPOT
361 if (m_isNPOT && ((m_minFilter != GL_NEAREST && m_minFilter != GL_LINEAR)
362 || m_wrapS != GL_CLAMP_TO_EDGE || m_wrapT != GL_CLAMP_TO_EDGE))
363 m_needToUseBlackTexture = true;
364 // If it is a Cube texture, check Cube Completeness first
365 if (m_info.size() > 1 && !m_isCubeComplete)
366 m_needToUseBlackTexture = true;
367 // Completeness
368 if (!m_isComplete && m_minFilter != GL_NEAREST && m_minFilter != GL_LINEAR)
369 m_needToUseBlackTexture = true;
370 }
371
getLevelInfo(GLenum target,GLint level) const372 const WebGLTexture::LevelInfo* WebGLTexture::getLevelInfo(GLenum target, GLint level) const
373 {
374 if (!object() || !m_target)
375 return 0;
376 int targetIndex = mapTargetToIndex(target);
377 if (targetIndex < 0 || targetIndex >= static_cast<int>(m_info.size()))
378 return 0;
379 if (level < 0 || level >= static_cast<GLint>(m_info[targetIndex].size()))
380 return 0;
381 return &(m_info[targetIndex][level]);
382 }
383
384 }
385