15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)/*
25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Copyright (C) 2009 Apple Inc. All rights reserved.
35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Redistribution and use in source and binary forms, with or without
55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * modification, are permitted provided that the following conditions
65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * are met:
75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 1. Redistributions of source code must retain the above copyright
85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *    notice, this list of conditions and the following disclaimer.
95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 2. Redistributions in binary form must reproduce the above copyright
105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *    notice, this list of conditions and the following disclaimer in the
115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *    documentation and/or other materials provided with the distribution.
125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2302772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) */
255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "config.h"
275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "core/html/canvas/WebGLTexture.h"
295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
30d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)#include "core/html/canvas/WebGLRenderingContextBase.h"
315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
32c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)namespace blink {
335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
34c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)PassRefPtrWillBeRawPtr<WebGLTexture> WebGLTexture::create(WebGLRenderingContextBase* ctx)
355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
36c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)    return adoptRefWillBeNoop(new WebGLTexture(ctx));
375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
39d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)WebGLTexture::WebGLTexture(WebGLRenderingContextBase* ctx)
405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    : WebGLSharedObject(ctx)
415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_target(0)
4209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    , m_minFilter(GL_NEAREST_MIPMAP_LINEAR)
4309380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    , m_magFilter(GL_LINEAR)
4409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    , m_wrapS(GL_REPEAT)
4509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    , m_wrapT(GL_REPEAT)
465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_isNPOT(false)
47521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)    , m_isCubeComplete(false)
485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_isComplete(false)
495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_needToUseBlackTexture(false)
5093ac45cfc74041c8ae536ce58a9534d46db2024eTorne (Richard Coles)    , m_isFloatType(false)
5193ac45cfc74041c8ae536ce58a9534d46db2024eTorne (Richard Coles)    , m_isHalfFloatType(false)
525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
53a9984bf9ddc3cf73fdae3f29134a2bab379e7029Ben Murdoch    setObject(ctx->webContext()->createTexture());
545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)WebGLTexture::~WebGLTexture()
575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
58c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)    // Always perform detach here to ensure that platform object
59c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)    // deletion happens with Oilpan enabled. It keeps the code regular
60c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)    // to do it with or without Oilpan enabled.
61c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)    //
62c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)    // See comment in WebGLBuffer's destructor for additional
63c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)    // information on why this is done for WebGLSharedObject-derived
64c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)    // objects.
65c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)    detachAndDeleteObject();
665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
6809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)void WebGLTexture::setTarget(GLenum target, GLint maxLevel)
695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!object())
715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // Target is finalized the first time bindTexture() is called.
735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_target)
745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    switch (target) {
7609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    case GL_TEXTURE_2D:
775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        m_target = target;
785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        m_info.resize(1);
795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        m_info[0].resize(maxLevel);
805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        break;
8109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    case GL_TEXTURE_CUBE_MAP:
825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        m_target = target;
835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        m_info.resize(6);
845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for (int ii = 0; ii < 6; ++ii)
855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            m_info[ii].resize(maxLevel);
865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        break;
875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
9009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)void WebGLTexture::setParameteri(GLenum pname, GLint param)
915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!object() || !m_target)
935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    switch (pname) {
9509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    case GL_TEXTURE_MIN_FILTER:
965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        switch (param) {
9709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_NEAREST:
9809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_LINEAR:
9909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_NEAREST_MIPMAP_NEAREST:
10009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_LINEAR_MIPMAP_NEAREST:
10109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_NEAREST_MIPMAP_LINEAR:
10209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_LINEAR_MIPMAP_LINEAR:
1035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            m_minFilter = param;
1045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            break;
1055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
1065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        break;
10709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    case GL_TEXTURE_MAG_FILTER:
1085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        switch (param) {
10909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_NEAREST:
11009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_LINEAR:
1115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            m_magFilter = param;
1125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            break;
1135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
1145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        break;
11509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    case GL_TEXTURE_WRAP_S:
1165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        switch (param) {
11709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_CLAMP_TO_EDGE:
11809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_MIRRORED_REPEAT:
11909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_REPEAT:
1205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            m_wrapS = param;
1215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            break;
1225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
1235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        break;
12409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    case GL_TEXTURE_WRAP_T:
1255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        switch (param) {
12609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_CLAMP_TO_EDGE:
12709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_MIRRORED_REPEAT:
12809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_REPEAT:
1295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            m_wrapT = param;
1305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            break;
1315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
1325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        break;
1335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    default:
1345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
1355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    update();
1375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
13909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)void WebGLTexture::setParameterf(GLenum pname, GLfloat param)
1405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
1415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!object() || !m_target)
1425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
14309380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    GLint iparam = static_cast<GLint>(param);
1445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    setParameteri(pname, iparam);
1455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
14709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)void WebGLTexture::setLevelInfo(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum type)
1485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
1495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!object() || !m_target)
1505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
1515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // We assume level, internalFormat, width, height, and type have all been
1525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // validated already.
1535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    int index = mapTargetToIndex(target);
1545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (index < 0)
1555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
1565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_info[index][level].setInfo(internalFormat, width, height, type);
1575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    update();
1585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void WebGLTexture::generateMipmapLevelInfo()
1615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
1625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!object() || !m_target)
1635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
1645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!canGenerateMipmaps())
1655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return;
1665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!m_isComplete) {
1675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for (size_t ii = 0; ii < m_info.size(); ++ii) {
1685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            const LevelInfo& info0 = m_info[ii][0];
16909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)            GLsizei width = info0.width;
17009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)            GLsizei height = info0.height;
17109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)            GLint levelCount = computeLevelCount(width, height);
17209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)            for (GLint level = 1; level < levelCount; ++level) {
1735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                width = std::max(1, width >> 1);
1745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                height = std::max(1, height >> 1);
1755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                LevelInfo& info = m_info[ii][level];
1765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                info.setInfo(info0.internalFormat, width, height, info0.type);
1775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            }
1785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
1795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        m_isComplete = true;
1805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_needToUseBlackTexture = false;
1825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
18409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)GLenum WebGLTexture::getInternalFormat(GLenum target, GLint level) const
1855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
1865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    const LevelInfo* info = getLevelInfo(target, level);
1875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!info)
1885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return 0;
1895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return info->internalFormat;
1905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
19209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)GLenum WebGLTexture::getType(GLenum target, GLint level) const
1935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
1945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    const LevelInfo* info = getLevelInfo(target, level);
1955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!info)
1965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return 0;
1975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return info->type;
1985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
20009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)GLsizei WebGLTexture::getWidth(GLenum target, GLint level) const
2015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
2025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    const LevelInfo* info = getLevelInfo(target, level);
2035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!info)
2045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return 0;
2055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return info->width;
2065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
2075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
20809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)GLsizei WebGLTexture::getHeight(GLenum target, GLint level) const
2095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
2105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    const LevelInfo* info = getLevelInfo(target, level);
2115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!info)
2125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return 0;
2135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return info->height;
2145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
2155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
21609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)bool WebGLTexture::isValid(GLenum target, GLint level) const
217926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles){
218926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)    const LevelInfo* info = getLevelInfo(target, level);
219926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)    if (!info)
220926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)        return 0;
221926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)    return info->valid;
222926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)}
223926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)
22409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)bool WebGLTexture::isNPOT(GLsizei width, GLsizei height)
2255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
2265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    ASSERT(width >= 0 && height >= 0);
2275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!width || !height)
2285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return false;
2295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if ((width & (width - 1)) || (height & (height - 1)))
2305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return true;
2315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return false;
2325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
2335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)bool WebGLTexture::isNPOT() const
2355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
2365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!object())
2375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return false;
2385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return m_isNPOT;
2395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
2405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
24193ac45cfc74041c8ae536ce58a9534d46db2024eTorne (Richard Coles)bool WebGLTexture::needToUseBlackTexture(TextureExtensionFlag flag) const
2425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
2435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!object())
2445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return false;
24593ac45cfc74041c8ae536ce58a9534d46db2024eTorne (Richard Coles)    if (m_needToUseBlackTexture)
24693ac45cfc74041c8ae536ce58a9534d46db2024eTorne (Richard Coles)        return true;
24793ac45cfc74041c8ae536ce58a9534d46db2024eTorne (Richard Coles)    if ((m_isFloatType && !(flag & TextureFloatLinearExtensionEnabled)) || (m_isHalfFloatType && !(flag && TextureHalfFloatLinearExtensionEnabled))) {
24809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        if (m_magFilter != GL_NEAREST || (m_minFilter != GL_NEAREST && m_minFilter != GL_NEAREST_MIPMAP_NEAREST))
24993ac45cfc74041c8ae536ce58a9534d46db2024eTorne (Richard Coles)            return true;
25093ac45cfc74041c8ae536ce58a9534d46db2024eTorne (Richard Coles)    }
25193ac45cfc74041c8ae536ce58a9534d46db2024eTorne (Richard Coles)    return false;
2525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
2535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
25409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)void WebGLTexture::deleteObjectImpl(blink::WebGraphicsContext3D* context3d, Platform3DObject object)
2555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
2565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    context3d->deleteTexture(object);
2575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
2585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
25909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)int WebGLTexture::mapTargetToIndex(GLenum target) const
2605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
26109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    if (m_target == GL_TEXTURE_2D) {
26209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        if (target == GL_TEXTURE_2D)
2635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return 0;
26409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    } else if (m_target == GL_TEXTURE_CUBE_MAP) {
2655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        switch (target) {
26609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
2675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return 0;
26809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
2695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return 1;
27009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
2715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return 2;
27209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
2735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return 3;
27409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
2755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return 4;
27609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
2775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return 5;
2785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
2795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
2805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return -1;
2815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
2825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)bool WebGLTexture::canGenerateMipmaps()
2845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
2855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (isNPOT())
2865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return false;
2875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    const LevelInfo& first = m_info[0][0];
2885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    for (size_t ii = 0; ii < m_info.size(); ++ii) {
2895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        const LevelInfo& info = m_info[ii][0];
2905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (!info.valid
2915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            || info.width != first.width || info.height != first.height
292521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)            || info.internalFormat != first.internalFormat || info.type != first.type
293521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)            || (m_info.size() > 1 && !m_isCubeComplete))
2945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return false;
2955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
2965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return true;
2975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
2985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
29909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)GLint WebGLTexture::computeLevelCount(GLsizei width, GLsizei height)
3005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
3015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // return 1 + log2Floor(std::max(width, height));
30209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    GLsizei n = std::max(width, height);
3035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (n <= 0)
3045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return 0;
30509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    GLint log = 0;
30609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    GLsizei value = n;
3075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    for (int ii = 4; ii >= 0; --ii) {
3085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        int shift = (1 << ii);
30909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        GLsizei x = (value >> shift);
3105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (x) {
3115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            value = x;
3125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            log += shift;
3135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
3145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
3155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    ASSERT(value == 1);
3165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return log + 1;
3175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
3185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void WebGLTexture::update()
3205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
3215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_isNPOT = false;
3225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    for (size_t ii = 0; ii < m_info.size(); ++ii) {
3235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (isNPOT(m_info[ii][0].width, m_info[ii][0].height)) {
3245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            m_isNPOT = true;
3255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            break;
3265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
3275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
3285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_isComplete = true;
329521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)    m_isCubeComplete = true;
3305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    const LevelInfo& first = m_info[0][0];
33109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    GLint levelCount = computeLevelCount(first.width, first.height);
3325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (levelCount < 1)
3335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        m_isComplete = false;
3345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    else {
3355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for (size_t ii = 0; ii < m_info.size() && m_isComplete; ++ii) {
3365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            const LevelInfo& info0 = m_info[ii][0];
3375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if (!info0.valid
3385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                || info0.width != first.width || info0.height != first.height
339521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)                || info0.internalFormat != first.internalFormat || info0.type != first.type
340521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)                || (m_info.size() > 1 && info0.width != info0.height)) {
341521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)                if (m_info.size() > 1)
342521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)                    m_isCubeComplete = false;
3435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                m_isComplete = false;
3445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                break;
3455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            }
34609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)            GLsizei width = info0.width;
34709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)            GLsizei height = info0.height;
34809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)            for (GLint level = 1; level < levelCount; ++level) {
3495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                width = std::max(1, width >> 1);
3505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                height = std::max(1, height >> 1);
3515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                const LevelInfo& info = m_info[ii][level];
3525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                if (!info.valid
3535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    || info.width != width || info.height != height
3545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    || info.internalFormat != info0.internalFormat || info.type != info0.type) {
3555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    m_isComplete = false;
3565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    break;
3575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                }
3585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            }
3605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
3615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
36209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    m_isFloatType = m_info[0][0].type == GL_FLOAT;
36309380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    m_isHalfFloatType = m_info[0][0].type == GL_HALF_FLOAT_OES;
3645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_needToUseBlackTexture = false;
3665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // NPOT
36709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    if (m_isNPOT && ((m_minFilter != GL_NEAREST && m_minFilter != GL_LINEAR)
36809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        || m_wrapS != GL_CLAMP_TO_EDGE || m_wrapT != GL_CLAMP_TO_EDGE))
3695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        m_needToUseBlackTexture = true;
370521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)    // If it is a Cube texture, check Cube Completeness first
371521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)    if (m_info.size() > 1 && !m_isCubeComplete)
372521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)        m_needToUseBlackTexture = true;
3735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // Completeness
37409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    if (!m_isComplete && m_minFilter != GL_NEAREST && m_minFilter != GL_LINEAR)
3755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        m_needToUseBlackTexture = true;
3765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
3775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
37809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)const WebGLTexture::LevelInfo* WebGLTexture::getLevelInfo(GLenum target, GLint level) const
3795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
3805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!object() || !m_target)
3815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return 0;
3825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    int targetIndex = mapTargetToIndex(target);
3835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (targetIndex < 0 || targetIndex >= static_cast<int>(m_info.size()))
3845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return 0;
38509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)    if (level < 0 || level >= static_cast<GLint>(m_info[targetIndex].size()))
3865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return 0;
3875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return &(m_info[targetIndex][level]);
3885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
3895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
391