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 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 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 "WebGLContextGroup.h" 33#include "WebGLFramebuffer.h" 34#include "WebGLRenderingContext.h" 35 36namespace WebCore { 37 38PassRefPtr<WebGLTexture> WebGLTexture::create(WebGLRenderingContext* ctx) 39{ 40 return adoptRef(new WebGLTexture(ctx)); 41} 42 43WebGLTexture::WebGLTexture(WebGLRenderingContext* ctx) 44 : WebGLSharedObject(ctx) 45 , m_target(0) 46 , m_minFilter(GraphicsContext3D::NEAREST_MIPMAP_LINEAR) 47 , m_magFilter(GraphicsContext3D::LINEAR) 48 , m_wrapS(GraphicsContext3D::REPEAT) 49 , m_wrapT(GraphicsContext3D::REPEAT) 50 , m_isNPOT(false) 51 , m_isComplete(false) 52 , m_needToUseBlackTexture(false) 53 , m_isCompressed(false) 54 , m_isFloatType(false) 55 , m_isHalfFloatType(false) 56{ 57 setObject(ctx->graphicsContext3D()->createTexture()); 58} 59 60WebGLTexture::~WebGLTexture() 61{ 62 deleteObject(0); 63} 64 65void WebGLTexture::setTarget(GC3Denum target, GC3Dint maxLevel) 66{ 67 if (!object()) 68 return; 69 // Target is finalized the first time bindTexture() is called. 70 if (m_target) 71 return; 72 switch (target) { 73 case GraphicsContext3D::TEXTURE_2D: 74 m_target = target; 75 m_info.resize(1); 76 m_info[0].resize(maxLevel); 77 break; 78 case GraphicsContext3D::TEXTURE_CUBE_MAP: 79 m_target = target; 80 m_info.resize(6); 81 for (int ii = 0; ii < 6; ++ii) 82 m_info[ii].resize(maxLevel); 83 break; 84 } 85} 86 87void WebGLTexture::setParameteri(GC3Denum pname, GC3Dint param) 88{ 89 if (!object() || !m_target) 90 return; 91 switch (pname) { 92 case GraphicsContext3D::TEXTURE_MIN_FILTER: 93 switch (param) { 94 case GraphicsContext3D::NEAREST: 95 case GraphicsContext3D::LINEAR: 96 case GraphicsContext3D::NEAREST_MIPMAP_NEAREST: 97 case GraphicsContext3D::LINEAR_MIPMAP_NEAREST: 98 case GraphicsContext3D::NEAREST_MIPMAP_LINEAR: 99 case GraphicsContext3D::LINEAR_MIPMAP_LINEAR: 100 m_minFilter = param; 101 break; 102 } 103 break; 104 case GraphicsContext3D::TEXTURE_MAG_FILTER: 105 switch (param) { 106 case GraphicsContext3D::NEAREST: 107 case GraphicsContext3D::LINEAR: 108 m_magFilter = param; 109 break; 110 } 111 break; 112 case GraphicsContext3D::TEXTURE_WRAP_S: 113 switch (param) { 114 case GraphicsContext3D::CLAMP_TO_EDGE: 115 case GraphicsContext3D::MIRRORED_REPEAT: 116 case GraphicsContext3D::REPEAT: 117 m_wrapS = param; 118 break; 119 } 120 break; 121 case GraphicsContext3D::TEXTURE_WRAP_T: 122 switch (param) { 123 case GraphicsContext3D::CLAMP_TO_EDGE: 124 case GraphicsContext3D::MIRRORED_REPEAT: 125 case GraphicsContext3D::REPEAT: 126 m_wrapT = param; 127 break; 128 } 129 break; 130 default: 131 return; 132 } 133 update(); 134} 135 136void WebGLTexture::setParameterf(GC3Denum pname, GC3Dfloat param) 137{ 138 if (!object() || !m_target) 139 return; 140 GC3Dint iparam = static_cast<GC3Dint>(param); 141 setParameteri(pname, iparam); 142} 143 144void WebGLTexture::setLevelInfo(GC3Denum target, GC3Dint level, GC3Denum internalFormat, GC3Dsizei width, GC3Dsizei height, GC3Denum type) 145{ 146 if (!object() || !m_target) 147 return; 148 // We assume level, internalFormat, width, height, and type have all been 149 // validated already. 150 int index = mapTargetToIndex(target); 151 if (index < 0) 152 return; 153 m_info[index][level].setInfo(internalFormat, width, height, type); 154 update(); 155} 156 157void WebGLTexture::generateMipmapLevelInfo() 158{ 159 if (!object() || !m_target) 160 return; 161 if (!canGenerateMipmaps()) 162 return; 163 if (!m_isComplete) { 164 for (size_t ii = 0; ii < m_info.size(); ++ii) { 165 const LevelInfo& info0 = m_info[ii][0]; 166 GC3Dsizei width = info0.width; 167 GC3Dsizei height = info0.height; 168 GC3Dint levelCount = computeLevelCount(width, height); 169 for (GC3Dint level = 1; level < levelCount; ++level) { 170 width = std::max(1, width >> 1); 171 height = std::max(1, height >> 1); 172 LevelInfo& info = m_info[ii][level]; 173 info.setInfo(info0.internalFormat, width, height, info0.type); 174 } 175 } 176 m_isComplete = true; 177 } 178 m_needToUseBlackTexture = false; 179} 180 181GC3Denum WebGLTexture::getInternalFormat(GC3Denum target, GC3Dint level) const 182{ 183 const LevelInfo* info = getLevelInfo(target, level); 184 if (!info) 185 return 0; 186 return info->internalFormat; 187} 188 189GC3Denum WebGLTexture::getType(GC3Denum target, GC3Dint level) const 190{ 191 const LevelInfo* info = getLevelInfo(target, level); 192 if (!info) 193 return 0; 194 return info->type; 195} 196 197GC3Dsizei WebGLTexture::getWidth(GC3Denum target, GC3Dint level) const 198{ 199 const LevelInfo* info = getLevelInfo(target, level); 200 if (!info) 201 return 0; 202 return info->width; 203} 204 205GC3Dsizei WebGLTexture::getHeight(GC3Denum target, GC3Dint level) const 206{ 207 const LevelInfo* info = getLevelInfo(target, level); 208 if (!info) 209 return 0; 210 return info->height; 211} 212 213bool WebGLTexture::isValid(GC3Denum target, GC3Dint level) const 214{ 215 const LevelInfo* info = getLevelInfo(target, level); 216 if (!info) 217 return 0; 218 return info->valid; 219} 220 221void WebGLTexture::markInvalid(GC3Denum target, GC3Dint level) 222{ 223 int index = mapTargetToIndex(target); 224 if (index < 0) 225 return; 226 m_info[index][level].valid = false; 227 update(); 228} 229 230bool WebGLTexture::isNPOT(GC3Dsizei width, GC3Dsizei height) 231{ 232 ASSERT(width >= 0 && height >= 0); 233 if (!width || !height) 234 return false; 235 if ((width & (width - 1)) || (height & (height - 1))) 236 return true; 237 return false; 238} 239 240bool WebGLTexture::isNPOT() const 241{ 242 if (!object()) 243 return false; 244 return m_isNPOT; 245} 246 247bool WebGLTexture::needToUseBlackTexture(TextureExtensionFlag extensions) const 248{ 249 if (!object()) 250 return false; 251 if (m_needToUseBlackTexture) 252 return true; 253 if ((m_isFloatType && !(extensions & TextureExtensionFloatLinearEnabled)) || (m_isHalfFloatType && !(extensions & TextureExtensionHalfFloatLinearEnabled))) { 254 if (m_magFilter != GraphicsContext3D::NEAREST || (m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::NEAREST_MIPMAP_NEAREST)) 255 return true; 256 } 257 return false; 258} 259 260bool WebGLTexture::isCompressed() const 261{ 262 if (!object()) 263 return false; 264 return m_isCompressed; 265} 266 267void WebGLTexture::setCompressed() 268{ 269 ASSERT(object()); 270 m_isCompressed = true; 271} 272 273void WebGLTexture::deleteObjectImpl(GraphicsContext3D* context3d, Platform3DObject object) 274{ 275 context3d->deleteTexture(object); 276} 277 278int WebGLTexture::mapTargetToIndex(GC3Denum target) const 279{ 280 if (m_target == GraphicsContext3D::TEXTURE_2D) { 281 if (target == GraphicsContext3D::TEXTURE_2D) 282 return 0; 283 } else if (m_target == GraphicsContext3D::TEXTURE_CUBE_MAP) { 284 switch (target) { 285 case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X: 286 return 0; 287 case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X: 288 return 1; 289 case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Y: 290 return 2; 291 case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y: 292 return 3; 293 case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z: 294 return 4; 295 case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z: 296 return 5; 297 } 298 } 299 return -1; 300} 301 302bool WebGLTexture::canGenerateMipmaps() 303{ 304 if (isNPOT()) 305 return false; 306 const LevelInfo& first = m_info[0][0]; 307 for (size_t ii = 0; ii < m_info.size(); ++ii) { 308 const LevelInfo& info = m_info[ii][0]; 309 if (!info.valid 310 || info.width != first.width || info.height != first.height 311 || info.internalFormat != first.internalFormat || info.type != first.type) 312 return false; 313 } 314 return true; 315} 316 317GC3Dint WebGLTexture::computeLevelCount(GC3Dsizei width, GC3Dsizei height) 318{ 319 // return 1 + log2Floor(std::max(width, height)); 320 GC3Dsizei n = std::max(width, height); 321 if (n <= 0) 322 return 0; 323 GC3Dint log = 0; 324 GC3Dsizei value = n; 325 for (int ii = 4; ii >= 0; --ii) { 326 int shift = (1 << ii); 327 GC3Dsizei x = (value >> shift); 328 if (x) { 329 value = x; 330 log += shift; 331 } 332 } 333 ASSERT(value == 1); 334 return log + 1; 335} 336 337void WebGLTexture::update() 338{ 339 m_isNPOT = false; 340 for (size_t ii = 0; ii < m_info.size(); ++ii) { 341 if (isNPOT(m_info[ii][0].width, m_info[ii][0].height)) { 342 m_isNPOT = true; 343 break; 344 } 345 } 346 m_isComplete = true; 347 const LevelInfo& first = m_info[0][0]; 348 GC3Dint levelCount = computeLevelCount(first.width, first.height); 349 if (levelCount < 1) 350 m_isComplete = false; 351 else { 352 for (size_t ii = 0; ii < m_info.size() && m_isComplete; ++ii) { 353 const LevelInfo& info0 = m_info[ii][0]; 354 if (!info0.valid 355 || info0.width != first.width || info0.height != first.height 356 || info0.internalFormat != first.internalFormat || info0.type != first.type) { 357 m_isComplete = false; 358 break; 359 } 360 GC3Dsizei width = info0.width; 361 GC3Dsizei height = info0.height; 362 for (GC3Dint level = 1; level < levelCount; ++level) { 363 width = std::max(1, width >> 1); 364 height = std::max(1, height >> 1); 365 const LevelInfo& info = m_info[ii][level]; 366 if (!info.valid 367 || info.width != width || info.height != height 368 || info.internalFormat != info0.internalFormat || info.type != info0.type) { 369 m_isComplete = false; 370 break; 371 } 372 373 } 374 } 375 } 376 377 m_isFloatType = false; 378 if (m_isComplete) 379 m_isFloatType = m_info[0][0].type == GraphicsContext3D::FLOAT; 380 else { 381 for (size_t ii = 0; ii < m_info.size(); ++ii) { 382 if (m_info[ii][0].type == GraphicsContext3D::FLOAT) { 383 m_isFloatType = true; 384 break; 385 } 386 } 387 } 388 389 m_isHalfFloatType = false; 390 if (m_isComplete) 391 m_isHalfFloatType = m_info[0][0].type == GraphicsContext3D::HALF_FLOAT_OES; 392 else { 393 for (size_t ii = 0; ii < m_info.size(); ++ii) { 394 if (m_info[ii][0].type == GraphicsContext3D::HALF_FLOAT_OES) { 395 m_isHalfFloatType = true; 396 break; 397 } 398 } 399 } 400 401 m_needToUseBlackTexture = false; 402 // NPOT 403 if (m_isNPOT && ((m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::LINEAR) 404 || m_wrapS != GraphicsContext3D::CLAMP_TO_EDGE || m_wrapT != GraphicsContext3D::CLAMP_TO_EDGE)) 405 m_needToUseBlackTexture = true; 406 // Completeness 407 if (!m_isComplete && m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::LINEAR) 408 m_needToUseBlackTexture = true; 409} 410 411const WebGLTexture::LevelInfo* WebGLTexture::getLevelInfo(GC3Denum target, GC3Dint level) const 412{ 413 if (!object() || !m_target) 414 return 0; 415 int targetIndex = mapTargetToIndex(target); 416 if (targetIndex < 0 || targetIndex >= static_cast<int>(m_info.size())) 417 return 0; 418 if (level < 0 || level >= static_cast<GC3Dint>(m_info[targetIndex].size())) 419 return 0; 420 return &(m_info[targetIndex][level]); 421} 422 423} 424 425#endif // ENABLE(WEBGL) 426