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