| /* |
| * Copyright (C) 2009 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| |
| #if ENABLE(WEBGL) |
| |
| #include "WebGLTexture.h" |
| |
| #include "WebGLContextGroup.h" |
| #include "WebGLFramebuffer.h" |
| #include "WebGLRenderingContextBase.h" |
| |
| namespace WebCore { |
| |
| Ref<WebGLTexture> WebGLTexture::create(WebGLRenderingContextBase& ctx) |
| { |
| return adoptRef(*new WebGLTexture(ctx)); |
| } |
| |
| WebGLTexture::WebGLTexture(WebGLRenderingContextBase& ctx) |
| : WebGLSharedObject(ctx) |
| , m_target(0) |
| , m_minFilter(GraphicsContext3D::NEAREST_MIPMAP_LINEAR) |
| , m_magFilter(GraphicsContext3D::LINEAR) |
| , m_wrapS(GraphicsContext3D::REPEAT) |
| , m_wrapT(GraphicsContext3D::REPEAT) |
| , m_isNPOT(false) |
| , m_isComplete(false) |
| , m_needToUseBlackTexture(false) |
| , m_isCompressed(false) |
| , m_isFloatType(false) |
| , m_isHalfFloatType(false) |
| , m_isForWebGL1(ctx.isWebGL1()) |
| { |
| setObject(ctx.graphicsContext3D()->createTexture()); |
| } |
| |
| WebGLTexture::~WebGLTexture() |
| { |
| deleteObject(0); |
| } |
| |
| void WebGLTexture::setTarget(GC3Denum target, GC3Dint maxLevel) |
| { |
| if (!object()) |
| return; |
| // Target is finalized the first time bindTexture() is called. |
| if (m_target) |
| return; |
| switch (target) { |
| case GraphicsContext3D::TEXTURE_2D: |
| m_target = target; |
| m_info.resize(1); |
| m_info[0].resize(maxLevel); |
| break; |
| case GraphicsContext3D::TEXTURE_CUBE_MAP: |
| m_target = target; |
| m_info.resize(6); |
| for (int ii = 0; ii < 6; ++ii) |
| m_info[ii].resize(maxLevel); |
| break; |
| } |
| } |
| |
| void WebGLTexture::setParameteri(GC3Denum pname, GC3Dint param) |
| { |
| if (!object() || !m_target) |
| return; |
| switch (pname) { |
| case GraphicsContext3D::TEXTURE_MIN_FILTER: |
| switch (param) { |
| case GraphicsContext3D::NEAREST: |
| case GraphicsContext3D::LINEAR: |
| case GraphicsContext3D::NEAREST_MIPMAP_NEAREST: |
| case GraphicsContext3D::LINEAR_MIPMAP_NEAREST: |
| case GraphicsContext3D::NEAREST_MIPMAP_LINEAR: |
| case GraphicsContext3D::LINEAR_MIPMAP_LINEAR: |
| m_minFilter = param; |
| break; |
| } |
| break; |
| case GraphicsContext3D::TEXTURE_MAG_FILTER: |
| switch (param) { |
| case GraphicsContext3D::NEAREST: |
| case GraphicsContext3D::LINEAR: |
| m_magFilter = param; |
| break; |
| } |
| break; |
| case GraphicsContext3D::TEXTURE_WRAP_S: |
| switch (param) { |
| case GraphicsContext3D::CLAMP_TO_EDGE: |
| case GraphicsContext3D::MIRRORED_REPEAT: |
| case GraphicsContext3D::REPEAT: |
| m_wrapS = param; |
| break; |
| } |
| break; |
| case GraphicsContext3D::TEXTURE_WRAP_T: |
| switch (param) { |
| case GraphicsContext3D::CLAMP_TO_EDGE: |
| case GraphicsContext3D::MIRRORED_REPEAT: |
| case GraphicsContext3D::REPEAT: |
| m_wrapT = param; |
| break; |
| } |
| break; |
| default: |
| return; |
| } |
| update(); |
| } |
| |
| void WebGLTexture::setParameterf(GC3Denum pname, GC3Dfloat param) |
| { |
| if (!object() || !m_target) |
| return; |
| GC3Dint iparam = static_cast<GC3Dint>(param); |
| setParameteri(pname, iparam); |
| } |
| |
| void WebGLTexture::setLevelInfo(GC3Denum target, GC3Dint level, GC3Denum internalFormat, GC3Dsizei width, GC3Dsizei height, GC3Denum type) |
| { |
| if (!object() || !m_target) |
| return; |
| // We assume level, internalFormat, width, height, and type have all been |
| // validated already. |
| int index = mapTargetToIndex(target); |
| if (index < 0) |
| return; |
| m_info[index][level].setInfo(internalFormat, width, height, type); |
| update(); |
| } |
| |
| void WebGLTexture::generateMipmapLevelInfo() |
| { |
| if (!object() || !m_target) |
| return; |
| if (!canGenerateMipmaps()) |
| return; |
| if (!m_isComplete) { |
| for (size_t ii = 0; ii < m_info.size(); ++ii) { |
| const LevelInfo& info0 = m_info[ii][0]; |
| GC3Dsizei width = info0.width; |
| GC3Dsizei height = info0.height; |
| GC3Dint levelCount = computeLevelCount(width, height); |
| for (GC3Dint level = 1; level < levelCount; ++level) { |
| width = std::max(1, width >> 1); |
| height = std::max(1, height >> 1); |
| LevelInfo& info = m_info[ii][level]; |
| info.setInfo(info0.internalFormat, width, height, info0.type); |
| } |
| } |
| m_isComplete = true; |
| } |
| m_needToUseBlackTexture = false; |
| } |
| |
| GC3Denum WebGLTexture::getInternalFormat(GC3Denum target, GC3Dint level) const |
| { |
| const LevelInfo* info = getLevelInfo(target, level); |
| if (!info) |
| return 0; |
| return info->internalFormat; |
| } |
| |
| GC3Denum WebGLTexture::getType(GC3Denum target, GC3Dint level) const |
| { |
| ASSERT(m_isForWebGL1); |
| const LevelInfo* info = getLevelInfo(target, level); |
| if (!info) |
| return 0; |
| return info->type; |
| } |
| |
| GC3Dsizei WebGLTexture::getWidth(GC3Denum target, GC3Dint level) const |
| { |
| const LevelInfo* info = getLevelInfo(target, level); |
| if (!info) |
| return 0; |
| return info->width; |
| } |
| |
| GC3Dsizei WebGLTexture::getHeight(GC3Denum target, GC3Dint level) const |
| { |
| const LevelInfo* info = getLevelInfo(target, level); |
| if (!info) |
| return 0; |
| return info->height; |
| } |
| |
| bool WebGLTexture::isValid(GC3Denum target, GC3Dint level) const |
| { |
| const LevelInfo* info = getLevelInfo(target, level); |
| if (!info) |
| return 0; |
| return info->valid; |
| } |
| |
| void WebGLTexture::markInvalid(GC3Denum target, GC3Dint level) |
| { |
| int index = mapTargetToIndex(target); |
| if (index < 0) |
| return; |
| m_info[index][level].valid = false; |
| update(); |
| } |
| |
| bool WebGLTexture::isNPOT(GC3Dsizei width, GC3Dsizei height) |
| { |
| ASSERT(width >= 0 && height >= 0); |
| if (!width || !height) |
| return false; |
| if ((width & (width - 1)) || (height & (height - 1))) |
| return true; |
| return false; |
| } |
| |
| bool WebGLTexture::isNPOT() const |
| { |
| if (!object()) |
| return false; |
| return m_isNPOT; |
| } |
| |
| bool WebGLTexture::needToUseBlackTexture(TextureExtensionFlag extensions) const |
| { |
| if (!object()) |
| return false; |
| if (m_needToUseBlackTexture) |
| return true; |
| if (m_magFilter == GraphicsContext3D::NEAREST && (m_minFilter == GraphicsContext3D::NEAREST || m_minFilter == GraphicsContext3D::NEAREST_MIPMAP_NEAREST)) |
| return false; |
| if (m_isForWebGL1 && m_isHalfFloatType && !(extensions & TextureExtensionHalfFloatLinearEnabled)) |
| return true; |
| if (m_isFloatType && !(extensions & TextureExtensionFloatLinearEnabled)) |
| return true; |
| return false; |
| } |
| |
| bool WebGLTexture::isCompressed() const |
| { |
| if (!object()) |
| return false; |
| return m_isCompressed; |
| } |
| |
| void WebGLTexture::setCompressed() |
| { |
| ASSERT(object()); |
| m_isCompressed = true; |
| } |
| |
| void WebGLTexture::deleteObjectImpl(GraphicsContext3D* context3d, Platform3DObject object) |
| { |
| context3d->deleteTexture(object); |
| } |
| |
| int WebGLTexture::mapTargetToIndex(GC3Denum target) const |
| { |
| if (m_target == GraphicsContext3D::TEXTURE_2D) { |
| if (target == GraphicsContext3D::TEXTURE_2D) |
| return 0; |
| } else if (m_target == GraphicsContext3D::TEXTURE_CUBE_MAP) { |
| switch (target) { |
| case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X: |
| return 0; |
| case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X: |
| return 1; |
| case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Y: |
| return 2; |
| case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y: |
| return 3; |
| case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z: |
| return 4; |
| case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z: |
| return 5; |
| } |
| } |
| return -1; |
| } |
| |
| bool WebGLTexture::canGenerateMipmaps() |
| { |
| if (isNPOT()) |
| return false; |
| const LevelInfo& first = m_info[0][0]; |
| for (size_t ii = 0; ii < m_info.size(); ++ii) { |
| const LevelInfo& info = m_info[ii][0]; |
| if (!info.valid |
| || info.width != first.width || info.height != first.height |
| || info.internalFormat != first.internalFormat || (m_isForWebGL1 && info.type != first.type)) |
| return false; |
| } |
| return true; |
| } |
| |
| GC3Dint WebGLTexture::computeLevelCount(GC3Dsizei width, GC3Dsizei height) |
| { |
| // return 1 + log2Floor(std::max(width, height)); |
| GC3Dsizei n = std::max(width, height); |
| if (n <= 0) |
| return 0; |
| GC3Dint log = 0; |
| GC3Dsizei value = n; |
| for (int ii = 4; ii >= 0; --ii) { |
| int shift = (1 << ii); |
| GC3Dsizei x = (value >> shift); |
| if (x) { |
| value = x; |
| log += shift; |
| } |
| } |
| ASSERT(value == 1); |
| return log + 1; |
| } |
| |
| static bool internalFormatIsFloatType(GC3Denum internalFormat) |
| { |
| switch (internalFormat) { |
| case GraphicsContext3D::R32F: |
| case GraphicsContext3D::RG32F: |
| case GraphicsContext3D::RGB32F: |
| case GraphicsContext3D::RGBA32F: |
| case GraphicsContext3D::DEPTH_COMPONENT32F: |
| case GraphicsContext3D::DEPTH32F_STENCIL8: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| static bool internalFormatIsHalfFloatType(GC3Denum internalFormat) |
| { |
| switch (internalFormat) { |
| case GraphicsContext3D::R16F: |
| case GraphicsContext3D::RG16F: |
| case GraphicsContext3D::R11F_G11F_B10F: |
| case GraphicsContext3D::RGB9_E5: |
| case GraphicsContext3D::RGB16F: |
| case GraphicsContext3D::RGBA16F: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| void WebGLTexture::update() |
| { |
| m_isNPOT = false; |
| for (size_t ii = 0; ii < m_info.size(); ++ii) { |
| if (isNPOT(m_info[ii][0].width, m_info[ii][0].height)) { |
| m_isNPOT = true; |
| break; |
| } |
| } |
| m_isComplete = true; |
| const LevelInfo& first = m_info[0][0]; |
| GC3Dint levelCount = computeLevelCount(first.width, first.height); |
| if (levelCount < 1) |
| m_isComplete = false; |
| else { |
| for (size_t ii = 0; ii < m_info.size() && m_isComplete; ++ii) { |
| const LevelInfo& info0 = m_info[ii][0]; |
| if (!info0.valid |
| || info0.width != first.width || info0.height != first.height |
| || info0.internalFormat != first.internalFormat || (m_isForWebGL1 && info0.type != first.type)) { |
| m_isComplete = false; |
| break; |
| } |
| GC3Dsizei width = info0.width; |
| GC3Dsizei height = info0.height; |
| for (GC3Dint level = 1; level < levelCount; ++level) { |
| width = std::max(1, width >> 1); |
| height = std::max(1, height >> 1); |
| const LevelInfo& info = m_info[ii][level]; |
| if (!info.valid |
| || info.width != width || info.height != height |
| || info.internalFormat != info0.internalFormat || (m_isForWebGL1 && info.type != info0.type)) { |
| m_isComplete = false; |
| break; |
| } |
| |
| } |
| } |
| } |
| |
| m_isFloatType = false; |
| if (m_isForWebGL1) { |
| if (m_isComplete) { |
| if (m_isForWebGL1) |
| m_isFloatType = m_info[0][0].type == GraphicsContext3D::FLOAT; |
| else |
| m_isFloatType = internalFormatIsFloatType(m_info[0][0].internalFormat); |
| } else { |
| for (size_t ii = 0; ii < m_info.size(); ++ii) { |
| if ((m_isForWebGL1 && m_info[ii][0].type == GraphicsContext3D::FLOAT) |
| || (!m_isForWebGL1 && internalFormatIsFloatType(m_info[ii][0].internalFormat))) { |
| m_isFloatType = true; |
| break; |
| } |
| } |
| } |
| } |
| |
| m_isHalfFloatType = false; |
| if (m_isForWebGL1) { |
| if (m_isComplete) { |
| if (m_isForWebGL1) |
| m_isHalfFloatType = internalFormatIsHalfFloatType(m_info[0][0].internalFormat); |
| else |
| m_isHalfFloatType = m_info[0][0].type == GraphicsContext3D::HALF_FLOAT_OES; |
| } else { |
| for (size_t ii = 0; ii < m_info.size(); ++ii) { |
| if ((m_isForWebGL1 && m_info[ii][0].type == GraphicsContext3D::HALF_FLOAT_OES) |
| || (!m_isForWebGL1 && internalFormatIsHalfFloatType(m_info[ii][0].internalFormat))) { |
| m_isHalfFloatType = true; |
| break; |
| } |
| } |
| } |
| } |
| |
| m_needToUseBlackTexture = false; |
| // NPOT |
| if (m_isNPOT && ((m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::LINEAR) |
| || m_wrapS != GraphicsContext3D::CLAMP_TO_EDGE || m_wrapT != GraphicsContext3D::CLAMP_TO_EDGE)) |
| m_needToUseBlackTexture = true; |
| // Completeness |
| if (!m_isComplete && m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::LINEAR) |
| m_needToUseBlackTexture = true; |
| } |
| |
| const WebGLTexture::LevelInfo* WebGLTexture::getLevelInfo(GC3Denum target, GC3Dint level) const |
| { |
| if (!object() || !m_target) |
| return 0; |
| int targetIndex = mapTargetToIndex(target); |
| if (targetIndex < 0 || targetIndex >= static_cast<int>(m_info.size())) |
| return 0; |
| if (level < 0 || level >= static_cast<GC3Dint>(m_info[targetIndex].size())) |
| return 0; |
| return &(m_info[targetIndex][level]); |
| } |
| |
| } |
| |
| #endif // ENABLE(WEBGL) |