TextureData.cc 14.3 KB
Newer Older
Robert Menzel's avatar
Robert Menzel committed
1 2 3 4 5 6 7 8 9
/***********************************************************************
 * Copyright 2011-2013 Computer Graphics Group RWTH Aachen University. *
 * All rights reserved.                                                *
 * Distributed under the terms of the MIT License (see LICENSE.TXT).   *
 **********************************************************************/

#include <ACGL/OpenGL/Data/TextureData.hh>
#include <ACGL/Utils/Memory.hh>

Janis Born's avatar
Janis Born committed
10 11
namespace ACGL {
namespace OpenGL {
Robert Menzel's avatar
Robert Menzel committed
12

13 14
GLsizei  TextureData::getPackAlignment() const
{
15 16
    size_t dataAlignment = Utils::pointerAlignment( mData );
    size_t rowAlignment  = Utils::pointerAlignment( mData + getBytesPerScanline() );
Robert Menzel's avatar
Robert Menzel committed
17 18 19 20 21 22 23

    return std::min( dataAlignment, rowAlignment ); //minimum of the data and the begining of the second row
}


GLsizei  TextureData::getNumberOfChannels() const
{
24 25 26 27 28 29 30
    return ACGL::OpenGL::getNumberOfChannels( mFormat );
}


GLenum TextureData::getRecommendedInternalFormat() const
{
    return recommendedInternalFormat(mFormat, mColorSpace);
Robert Menzel's avatar
Robert Menzel committed
31
}
32 33


34
void TextureData::flipVertically()
35 36 37 38
{
    size_t scanlineInBytes = getBytesPerScanline();
    GLubyte *tmpScanLine = new GLubyte[ scanlineInBytes ];

39
    for (GLsizei line = 0; line < mHeight/2; ++line) {
40
        size_t topLine    = line;
41 42 43
        size_t bottomLine = mHeight - line - 1;
        void *topLinePtr    = mData + topLine*scanlineInBytes;
        void *bottomLinePtr = mData + bottomLine*scanlineInBytes;
44 45 46 47 48 49 50 51 52 53 54
        memcpy( tmpScanLine,   topLinePtr,    scanlineInBytes ); // top    -> tmp
        memcpy( topLinePtr,    bottomLinePtr, scanlineInBytes ); // bottom -> top
        memcpy( bottomLinePtr, tmpScanLine,   scanlineInBytes ); // tmp    -> bottom
    }

    delete[] tmpScanLine;
}


size_t TextureData::getBytesPerScanline() const
{
55 56 57 58
    // if uncompressed -> texel size is a multiple of 8
    // if compressed   -> mWidth is a multiple of 4
    // so this function will work for bitsizes of 2,4,8,16 etc. -> 1 bit per pixel might fail
    return (mWidth*getTexelSizeInBits())/8 + mPaddingBytesPerRow;
59 60
}

61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
size_t TextureData::getSizeInBytes() const
{
    size_t s = getBytesPerScanline(); // correct even for compressed data
    if (mHeight > 0) s *= mHeight;
    if (mDepth  > 0) s *= mDepth;
    return s;
}

bool TextureData::dataIsCompressed() const
{
    if (mFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || mFormat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT) {
        // BC 1 aka DXT 1
        return true;
    } else if (mFormat == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT) {
        // BC 2 aka DXT 3
        return true;
    } else if (mFormat == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) {
        // BC 3 aka DXT 5
        return true;
    } else if (mFormat == GL_COMPRESSED_RED_RGTC1 || mFormat == GL_COMPRESSED_SIGNED_RED_RGTC1) {
        // BC 4
        return true;
    //} else if (mFormat == GL_COMPRESSED_RED_GREEN_RGTC2 || mFormat == GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2) {
    //    // BC 5
    //    return true;
    //} else if (mFormat == GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT || mFormat == GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT) {
    //    // BC 6H
    //    return true;
    //} else if (mFormat == GL_COMPRESSED_RGBA_BPTC_UNORM || mFormat == GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM) {
    //    // BC 7
    //    return true;
    } else {
        return false;
    }
}

size_t TextureData::getTexelSizeInBits() const
{
    if (mFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || mFormat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT) {
        // BC 1 aka DXT 1
        return 4;
    } else if (mFormat == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT) {
        // BC 2 aka DXT 3
        return 8;
    } else if (mFormat == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) {
        // BC 3 aka DXT 5
        return 8;
    } else if (mFormat == GL_COMPRESSED_RED_RGTC1 || mFormat == GL_COMPRESSED_SIGNED_RED_RGTC1) {
        // BC 4
        return 8;
    //} else if (mFormat == GL_COMPRESSED_RED_GREEN_RGTC2 || mFormat == GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2) {
    //    // BC 5
    //    return 8;
    //} else if (mFormat == GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT || mFormat == GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT) {
    //    // BC 6H
    //    return 8;
    //} else if (mFormat == GL_COMPRESSED_RGBA_BPTC_UNORM || mFormat == GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM) {
    //    // BC 7
    //    return 8;
    } else {
        return ( getNumberOfChannels()*getGLTypeSize(mType) )*8;
    }
}
124 125 126 127 128 129 130 131

glm::vec4 TextureData::getTexel( glm::uvec2 _texCoord )
{
    // clamp negative to 0:
    _texCoord.x = std::max( 0u, _texCoord.x );
    _texCoord.y = std::max( 0u, _texCoord.y );

    // clamp values larger than the texture size to the maximum:
132 133
    _texCoord.x = std::min( _texCoord.x, (unsigned int) getWidth()  );
    _texCoord.y = std::min( _texCoord.y, (unsigned int) getHeight() );
134 135 136 137 138 139 140 141 142

    // the byte offset into pData of the desired texel:
    size_t texelOffset  = _texCoord.y * getBytesPerScanline();
    texelOffset        += _texCoord.x * getNumberOfChannels()*getGLTypeSize(mType);

    // default values:
    glm::vec4 result = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);

    if ( mType == GL_BYTE ) {
143
        GLbyte *data = (GLbyte *) (mData+texelOffset);
144 145 146 147 148 149
        result.r = data[0]/128.0f; // to -1..1
        if ( getNumberOfChannels() > 1 ) result.g = data[1]/128.0f;
        if ( getNumberOfChannels() > 2 ) result.b = data[2]/128.0f;
        if ( getNumberOfChannels() > 3 ) result.a = data[3]/128.0f;

    } else if ( mType == GL_UNSIGNED_BYTE ) {
150
        GLubyte *data = (GLubyte *) (mData+texelOffset);
151 152 153 154 155 156
        result.r = data[0]/255.0f; // to 0..1
        if ( getNumberOfChannels() > 1 ) result.g = data[1]/255.0f;
        if ( getNumberOfChannels() > 2 ) result.b = data[2]/255.0f;
        if ( getNumberOfChannels() > 3 ) result.a = data[3]/255.0f;

    } else if ( mType == GL_SHORT ) {
157
        GLshort *data = (GLshort *) (mData+texelOffset);
158 159 160 161 162 163
        result.r = data[0]/32768.0f; // to -1..1
        if ( getNumberOfChannels() > 1 ) result.g = data[1]/32768.0f;
        if ( getNumberOfChannels() > 2 ) result.b = data[2]/32768.0f;
        if ( getNumberOfChannels() > 3 ) result.a = data[3]/32768.0f;

    } else if ( mType == GL_UNSIGNED_SHORT ) {
164
        GLushort *data = (GLushort *) (mData+texelOffset);
165 166 167 168 169 170
        result.r = data[0]/65535.0f; // to 0..1
        if ( getNumberOfChannels() > 1 ) result.g = data[1]/65535.0f;
        if ( getNumberOfChannels() > 2 ) result.b = data[2]/65535.0f;
        if ( getNumberOfChannels() > 3 ) result.a = data[3]/65535.0f;

    } else if ( mType == GL_INT ) {
171
        GLint *data = (GLint *) (mData+texelOffset);
172 173 174 175 176 177
        result.r = data[0]/2147483648.0f; // to -1..1
        if ( getNumberOfChannels() > 1 ) result.g = data[1]/2147483648.0f;
        if ( getNumberOfChannels() > 2 ) result.b = data[2]/2147483648.0f;
        if ( getNumberOfChannels() > 3 ) result.a = data[3]/2147483648.0f;

    } else if ( mType == GL_UNSIGNED_INT ) {
178
        GLuint *data = (GLuint *) (mData+texelOffset);
179 180 181 182 183 184
        result.r = data[0]/4294967295.0f; // to 0..1
        if ( getNumberOfChannels() > 1 ) result.g = data[1]/4294967295.0f;
        if ( getNumberOfChannels() > 2 ) result.b = data[2]/4294967295.0f;
        if ( getNumberOfChannels() > 3 ) result.a = data[3]/4294967295.0f;

    } else if ( mType == GL_FLOAT ) {
185
        GLfloat *data = (GLfloat *) (mData+texelOffset);
186 187 188 189 190 191
        result.r = data[0];
        if ( getNumberOfChannels() > 1 ) result.g = data[1];
        if ( getNumberOfChannels() > 2 ) result.b = data[2];
        if ( getNumberOfChannels() > 3 ) result.a = data[3];

    } else if ( mType == GL_DOUBLE ) {
192
        GLdouble *data = (GLdouble *) (mData+texelOffset);
193 194 195 196 197 198 199 200 201 202 203 204 205 206
        result.r = (float) data[0];
        if ( getNumberOfChannels() > 1 ) result.g = (float) data[1];
        if ( getNumberOfChannels() > 2 ) result.b = (float) data[2];
        if ( getNumberOfChannels() > 3 ) result.a = (float) data[3];

    } else {
        ACGL::Utils::error() << "datatype " << mType << " not supported for getTexel! Return (0,0,0,1)." << std::endl;
    }

    return result;
}

void TextureData::setTexel( glm::uvec2 _texCoord, glm::vec4 _color )
{
207
    assert( mData && "can't set texels if there is no data store, define data using setData()" );
208 209 210 211 212
    // clamp negative to 0:
    _texCoord.x = std::max( 0u, _texCoord.x );
    _texCoord.y = std::max( 0u, _texCoord.y );

    // clamp values larger than the texture size to the maximum:
213 214
    _texCoord.x = std::min( _texCoord.x, (unsigned int) getWidth()  );
    _texCoord.y = std::min( _texCoord.y, (unsigned int) getHeight() );
215 216 217 218 219 220

    // the byte offset into pData of the desired texel:
    size_t texelOffset  = _texCoord.y * getBytesPerScanline();
    texelOffset        += _texCoord.x * getNumberOfChannels()*getGLTypeSize(mType);

    if ( mType == GL_BYTE ) {
221
        GLbyte *data = (GLbyte *) (mData+texelOffset);
222 223 224 225 226 227 228 229 230 231

        glm::ivec4 color = glm::ivec4( _color * glm::vec4(128.0f) );
        color = glm::clamp( color, glm::ivec4(-128), glm::ivec4(127) );

        data[0] = color.r;
        if ( getNumberOfChannels() > 1 ) data[1] = color.g;
        if ( getNumberOfChannels() > 2 ) data[2] = color.b;
        if ( getNumberOfChannels() > 3 ) data[3] = color.a;

    } else if ( mType == GL_UNSIGNED_BYTE ) {
232
        GLubyte *data = (GLubyte *) (mData+texelOffset);
233 234 235 236 237 238 239 240 241 242

        glm::ivec4 color = glm::ivec4( _color * glm::vec4(255.0f) );
        color = glm::clamp( color, glm::ivec4(0), glm::ivec4(255) );

        data[0] = color.r;
        if ( getNumberOfChannels() > 1 ) data[1] = color.g;
        if ( getNumberOfChannels() > 2 ) data[2] = color.b;
        if ( getNumberOfChannels() > 3 ) data[3] = color.a;

    } else if ( mType == GL_SHORT ) {
243
        GLshort *data = (GLshort *) (mData+texelOffset);
244 245 246 247 248 249 250 251 252 253

        glm::ivec4 color = glm::ivec4( _color * glm::vec4(32768.0f) );
        color = glm::clamp( color, glm::ivec4(-32768), glm::ivec4(32767) );

        data[0] = color.r;
        if ( getNumberOfChannels() > 1 ) data[1] = color.g;
        if ( getNumberOfChannels() > 2 ) data[2] = color.b;
        if ( getNumberOfChannels() > 3 ) data[3] = color.a;

    } else if ( mType == GL_UNSIGNED_SHORT ) {
254
        GLushort *data = (GLushort *) (mData+texelOffset);
255 256 257 258 259 260 261 262 263 264

        glm::ivec4 color = glm::ivec4( _color * glm::vec4(65535.0f) );
        color = glm::clamp( color, glm::ivec4(0), glm::ivec4(65535) );

        data[0] = color.r;
        if ( getNumberOfChannels() > 1 ) data[1] = color.g;
        if ( getNumberOfChannels() > 2 ) data[2] = color.b;
        if ( getNumberOfChannels() > 3 ) data[3] = color.a;

    } else if ( mType == GL_INT ) {
265
        GLint *data = (GLint *) (mData+texelOffset);
266 267

        glm::ivec4 color = glm::ivec4( _color * glm::vec4(2147483648.0f) );
268
        color = glm::clamp( color, glm::ivec4(std::numeric_limits<int>::min()), glm::ivec4(2147483647) );
269 270 271 272 273 274 275

        data[0] = color.r;
        if ( getNumberOfChannels() > 1 ) data[1] = color.g;
        if ( getNumberOfChannels() > 2 ) data[2] = color.b;
        if ( getNumberOfChannels() > 3 ) data[3] = color.a;

    } else if ( mType == GL_UNSIGNED_INT ) {
276
        GLuint *data = (GLuint *) (mData+texelOffset);
277 278 279 280 281 282 283 284 285 286

        glm::ivec4 color = glm::ivec4( _color * glm::vec4(4294967295.0f) );
        color = glm::clamp( color, glm::ivec4(0), glm::ivec4(4294967295) );

        data[0] = color.r;
        if ( getNumberOfChannels() > 1 ) data[1] = color.g;
        if ( getNumberOfChannels() > 2 ) data[2] = color.b;
        if ( getNumberOfChannels() > 3 ) data[3] = color.a;

    } else if ( mType == GL_FLOAT ) {
287
        GLfloat *data = (GLfloat *) (mData+texelOffset);
288 289 290 291 292 293 294

        data[0] = _color.r;
        if ( getNumberOfChannels() > 1 ) data[1] = _color.g;
        if ( getNumberOfChannels() > 2 ) data[2] = _color.b;
        if ( getNumberOfChannels() > 3 ) data[3] = _color.a;

    } else if ( mType == GL_DOUBLE ) {
295
        GLdouble *data = (GLdouble *) (mData+texelOffset);
296 297 298 299 300 301 302 303 304

        data[0] = (float) _color.r;
        if ( getNumberOfChannels() > 1 ) data[1] = (float) _color.g;
        if ( getNumberOfChannels() > 2 ) data[2] = (float) _color.b;
        if ( getNumberOfChannels() > 3 ) data[3] = (float) _color.a;

    } else {
        ACGL::Utils::error() << "datatype " << mType << " not supported for setTexel!" << std::endl;
    }
305 306
}

Janis Born's avatar
Janis Born committed
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
float grayscaleMixdown(float _r, float _g, float _b)
{
    return 0.299f * _r + 0.587f * _g + 0.114f * _b;
}

glm::vec4 convertTexelNumChannels(glm::vec4 _texel, GLsizei _from, GLsizei _to)
{
    if (_from == _to) {
        return _texel;
    }
    else if (_from == 1) {
        switch (_to) {
            case 2: return {_texel.r, 1.0, 0.0, 0.0};
            case 3: return {_texel.r, _texel.r, _texel.r, 0.0};
            case 4: return {_texel.r, _texel.r, _texel.r, 1.0};
        }
    }
    else if (_from == 2) {
        switch (_to) {
            case 1: return {_texel.r, 0.0, 0.0, 0.0};
            case 3: return {_texel.r, _texel.r, _texel.r, _texel.g};
            case 4: return {_texel.r, _texel.r, _texel.r, _texel.g};
        }
    }
    else if (_from == 3) {
        switch (_to) {
            case 1: return {grayscaleMixdown(_texel.r, _texel.g, _texel.b), 0.0, 0.0, 0.0};
            case 2: return {grayscaleMixdown(_texel.r, _texel.g, _texel.b), 1.0, 0.0, 0.0};
            case 4: return {_texel.r, _texel.r, _texel.r, 1.0};
        }
    }
    else if (_from == 4) {
        switch (_to) {
            case 1: return {grayscaleMixdown(_texel.r, _texel.g, _texel.b), 0.0, 0.0, 0.0};
            case 2: return {grayscaleMixdown(_texel.r, _texel.g, _texel.b), 1.0, 0.0, 0.0};
            case 3: return {_texel.r, _texel.r, _texel.r, 0.0};
        }
    }
    return _texel;
}

void convertTextureData(const SharedTextureData& _from, const SharedTextureData& _to)
{
    assert(_from);
    assert(_to);
    if (!_from->getData()) {
        ACGL::Utils::error() << "Cannot convert TextureData: source TextureData contains no data" << std::endl;
        return;
    }

    // Setup target texture dimensions
    _to->setWidth(_from->getWidth());
    _to->setHeight(_from->getHeight());
    _to->setDepth(_from->getDepth());

    // Allocate new memory
    _to->deleteData();
    GLubyte* data = new GLubyte[_to->getSizeInBytes()];
    _to->setData(data);

    // Transfer pixels
    for (GLsizei y = 0; y < _to->getHeight(); ++y) {
        for (GLsizei x = 0; x < _to->getWidth(); ++x) {
            auto texel = convertTexelNumChannels(_from->getTexel({x, y}), _from->getNumberOfChannels(), _to->getNumberOfChannels());
            _to->setTexel({x, y}, texel);
        }
    }
}

} // namespace OpenGL
} // namespace ACGL