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