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 "WebGLContextGroup.h"
33#include "WebGLFramebuffer.h"
34#include "WebGLRenderingContext.h"
35
36namespace WebCore {
37
38PassRefPtr<WebGLTexture> WebGLTexture::create(WebGLRenderingContext* ctx)
39{
40    return adoptRef(new WebGLTexture(ctx));
41}
42
43WebGLTexture::WebGLTexture(WebGLRenderingContext* ctx)
44    : WebGLSharedObject(ctx)
45    , m_target(0)
46    , m_minFilter(GraphicsContext3D::NEAREST_MIPMAP_LINEAR)
47    , m_magFilter(GraphicsContext3D::LINEAR)
48    , m_wrapS(GraphicsContext3D::REPEAT)
49    , m_wrapT(GraphicsContext3D::REPEAT)
50    , m_isNPOT(false)
51    , m_isComplete(false)
52    , m_needToUseBlackTexture(false)
53{
54    setObject(ctx->graphicsContext3D()->createTexture());
55}
56
57WebGLTexture::~WebGLTexture()
58{
59    deleteObject(0);
60}
61
62void 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 GraphicsContext3D::TEXTURE_2D:
71        m_target = target;
72        m_info.resize(1);
73        m_info[0].resize(maxLevel);
74        break;
75    case GraphicsContext3D::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
84void WebGLTexture::setParameteri(GC3Denum pname, GC3Dint param)
85{
86    if (!object() || !m_target)
87        return;
88    switch (pname) {
89    case GraphicsContext3D::TEXTURE_MIN_FILTER:
90        switch (param) {
91        case GraphicsContext3D::NEAREST:
92        case GraphicsContext3D::LINEAR:
93        case GraphicsContext3D::NEAREST_MIPMAP_NEAREST:
94        case GraphicsContext3D::LINEAR_MIPMAP_NEAREST:
95        case GraphicsContext3D::NEAREST_MIPMAP_LINEAR:
96        case GraphicsContext3D::LINEAR_MIPMAP_LINEAR:
97            m_minFilter = param;
98            break;
99        }
100        break;
101    case GraphicsContext3D::TEXTURE_MAG_FILTER:
102        switch (param) {
103        case GraphicsContext3D::NEAREST:
104        case GraphicsContext3D::LINEAR:
105            m_magFilter = param;
106            break;
107        }
108        break;
109    case GraphicsContext3D::TEXTURE_WRAP_S:
110        switch (param) {
111        case GraphicsContext3D::CLAMP_TO_EDGE:
112        case GraphicsContext3D::MIRRORED_REPEAT:
113        case GraphicsContext3D::REPEAT:
114            m_wrapS = param;
115            break;
116        }
117        break;
118    case GraphicsContext3D::TEXTURE_WRAP_T:
119        switch (param) {
120        case GraphicsContext3D::CLAMP_TO_EDGE:
121        case GraphicsContext3D::MIRRORED_REPEAT:
122        case GraphicsContext3D::REPEAT:
123            m_wrapT = param;
124            break;
125        }
126        break;
127    default:
128        return;
129    }
130    update();
131}
132
133void 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
141void 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
154void 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
178GC3Denum 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
186GC3Denum 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
194GC3Dsizei 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
202GC3Dsizei 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
210bool 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
218bool 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
228bool WebGLTexture::isNPOT() const
229{
230    if (!object())
231        return false;
232    return m_isNPOT;
233}
234
235bool WebGLTexture::needToUseBlackTexture() const
236{
237    if (!object())
238        return false;
239    return m_needToUseBlackTexture;
240}
241
242void WebGLTexture::deleteObjectImpl(GraphicsContext3D* context3d, Platform3DObject object)
243{
244    context3d->deleteTexture(object);
245}
246
247int WebGLTexture::mapTargetToIndex(GC3Denum target) const
248{
249    if (m_target == GraphicsContext3D::TEXTURE_2D) {
250        if (target == GraphicsContext3D::TEXTURE_2D)
251            return 0;
252    } else if (m_target == GraphicsContext3D::TEXTURE_CUBE_MAP) {
253        switch (target) {
254        case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X:
255            return 0;
256        case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X:
257            return 1;
258        case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Y:
259            return 2;
260        case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y:
261            return 3;
262        case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z:
263            return 4;
264        case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z:
265            return 5;
266        }
267    }
268    return -1;
269}
270
271bool WebGLTexture::canGenerateMipmaps()
272{
273    if (isNPOT())
274        return false;
275    const LevelInfo& first = m_info[0][0];
276    for (size_t ii = 0; ii < m_info.size(); ++ii) {
277        const LevelInfo& info = m_info[ii][0];
278        if (!info.valid
279            || info.width != first.width || info.height != first.height
280            || info.internalFormat != first.internalFormat || info.type != first.type)
281            return false;
282    }
283    return true;
284}
285
286GC3Dint WebGLTexture::computeLevelCount(GC3Dsizei width, GC3Dsizei height)
287{
288    // return 1 + log2Floor(std::max(width, height));
289    GC3Dsizei n = std::max(width, height);
290    if (n <= 0)
291        return 0;
292    GC3Dint log = 0;
293    GC3Dsizei value = n;
294    for (int ii = 4; ii >= 0; --ii) {
295        int shift = (1 << ii);
296        GC3Dsizei x = (value >> shift);
297        if (x) {
298            value = x;
299            log += shift;
300        }
301    }
302    ASSERT(value == 1);
303    return log + 1;
304}
305
306void WebGLTexture::update()
307{
308    m_isNPOT = false;
309    for (size_t ii = 0; ii < m_info.size(); ++ii) {
310        if (isNPOT(m_info[ii][0].width, m_info[ii][0].height)) {
311            m_isNPOT = true;
312            break;
313        }
314    }
315    m_isComplete = true;
316    const LevelInfo& first = m_info[0][0];
317    GC3Dint levelCount = computeLevelCount(first.width, first.height);
318    if (levelCount < 1)
319        m_isComplete = false;
320    else {
321        for (size_t ii = 0; ii < m_info.size() && m_isComplete; ++ii) {
322            const LevelInfo& info0 = m_info[ii][0];
323            if (!info0.valid
324                || info0.width != first.width || info0.height != first.height
325                || info0.internalFormat != first.internalFormat || info0.type != first.type) {
326                m_isComplete = false;
327                break;
328            }
329            GC3Dsizei width = info0.width;
330            GC3Dsizei height = info0.height;
331            for (GC3Dint level = 1; level < levelCount; ++level) {
332                width = std::max(1, width >> 1);
333                height = std::max(1, height >> 1);
334                const LevelInfo& info = m_info[ii][level];
335                if (!info.valid
336                    || info.width != width || info.height != height
337                    || info.internalFormat != info0.internalFormat || info.type != info0.type) {
338                    m_isComplete = false;
339                    break;
340                }
341
342            }
343        }
344    }
345
346    m_needToUseBlackTexture = false;
347    // NPOT
348    if (m_isNPOT && ((m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::LINEAR)
349                     || m_wrapS != GraphicsContext3D::CLAMP_TO_EDGE || m_wrapT != GraphicsContext3D::CLAMP_TO_EDGE))
350        m_needToUseBlackTexture = true;
351    // Completeness
352    if (!m_isComplete && m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::LINEAR)
353        m_needToUseBlackTexture = true;
354}
355
356const WebGLTexture::LevelInfo* WebGLTexture::getLevelInfo(GC3Denum target, GC3Dint level) const
357{
358    if (!object() || !m_target)
359        return 0;
360    int targetIndex = mapTargetToIndex(target);
361    if (targetIndex < 0 || targetIndex >= static_cast<int>(m_info.size()))
362        return 0;
363    if (level < 0 || level >= static_cast<GC3Dint>(m_info[targetIndex].size()))
364        return 0;
365    return &(m_info[targetIndex][level]);
366}
367
368}
369
370#endif // ENABLE(WEBGL)
371