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/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