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/WebGLRenderingContext.h" 31 32 namespace WebCore { 33 34 PassRefPtr<WebGLTexture> WebGLTexture::create(WebGLRenderingContext* ctx) 35 { 36 return adoptRef(new WebGLTexture(ctx)); 37 } 38 39 WebGLTexture::WebGLTexture(WebGLRenderingContext* 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->graphicsContext3D()->createTexture()); 55 } 56 57 WebGLTexture::~WebGLTexture() 58 { 59 deleteObject(0); 60 } 61 62 void WebGLTexture::setTarget(GC3Denum target, GC3Dint 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 84 void WebGLTexture::setParameteri(GC3Denum pname, GC3Dint 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 133 void WebGLTexture::setParameterf(GC3Denum pname, GC3Dfloat param) 134 { 135 if (!object() || !m_target) 136 return; 137 GC3Dint iparam = static_cast<GC3Dint>(param); 138 setParameteri(pname, iparam); 139 } 140 141 void WebGLTexture::setLevelInfo(GC3Denum target, GC3Dint level, GC3Denum internalFormat, GC3Dsizei width, GC3Dsizei height, GC3Denum 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 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 GC3Dsizei width = info0.width; 164 GC3Dsizei height = info0.height; 165 GC3Dint levelCount = computeLevelCount(width, height); 166 for (GC3Dint 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 178 GC3Denum WebGLTexture::getInternalFormat(GC3Denum target, GC3Dint level) const 179 { 180 const LevelInfo* info = getLevelInfo(target, level); 181 if (!info) 182 return 0; 183 return info->internalFormat; 184 } 185 186 GC3Denum WebGLTexture::getType(GC3Denum target, GC3Dint level) const 187 { 188 const LevelInfo* info = getLevelInfo(target, level); 189 if (!info) 190 return 0; 191 return info->type; 192 } 193 194 GC3Dsizei WebGLTexture::getWidth(GC3Denum target, GC3Dint level) const 195 { 196 const LevelInfo* info = getLevelInfo(target, level); 197 if (!info) 198 return 0; 199 return info->width; 200 } 201 202 GC3Dsizei WebGLTexture::getHeight(GC3Denum target, GC3Dint level) const 203 { 204 const LevelInfo* info = getLevelInfo(target, level); 205 if (!info) 206 return 0; 207 return info->height; 208 } 209 210 bool WebGLTexture::isValid(GC3Denum target, GC3Dint level) const 211 { 212 const LevelInfo* info = getLevelInfo(target, level); 213 if (!info) 214 return 0; 215 return info->valid; 216 } 217 218 bool WebGLTexture::isNPOT(GC3Dsizei width, GC3Dsizei 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 228 bool WebGLTexture::isNPOT() const 229 { 230 if (!object()) 231 return false; 232 return m_isNPOT; 233 } 234 235 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 248 void WebGLTexture::deleteObjectImpl(GraphicsContext3D* context3d, Platform3DObject object) 249 { 250 context3d->deleteTexture(object); 251 } 252 253 int WebGLTexture::mapTargetToIndex(GC3Denum 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 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 293 GC3Dint WebGLTexture::computeLevelCount(GC3Dsizei width, GC3Dsizei height) 294 { 295 // return 1 + log2Floor(std::max(width, height)); 296 GC3Dsizei n = std::max(width, height); 297 if (n <= 0) 298 return 0; 299 GC3Dint log = 0; 300 GC3Dsizei value = n; 301 for (int ii = 4; ii >= 0; --ii) { 302 int shift = (1 << ii); 303 GC3Dsizei x = (value >> shift); 304 if (x) { 305 value = x; 306 log += shift; 307 } 308 } 309 ASSERT(value == 1); 310 return log + 1; 311 } 312 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 GC3Dint 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 GC3Dsizei width = info0.width; 341 GC3Dsizei height = info0.height; 342 for (GC3Dint 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 372 const WebGLTexture::LevelInfo* WebGLTexture::getLevelInfo(GC3Denum target, GC3Dint 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<GC3Dint>(m_info[targetIndex].size())) 380 return 0; 381 return &(m_info[targetIndex][level]); 382 } 383 384 } 385