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) 357