Home | History | Annotate | Download | only in canvas
      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