TextureData.cc 14.4 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
	return (GLsizei) std::min(dataAlignment, rowAlignment); //minimum of the data and the begining of the second row
Robert Menzel's avatar
Robert Menzel committed
19
20
21
22
23
}


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
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) {
319
320
321
            case 2: return glm::vec4( _texel.r, 1.0, 0.0, 0.0 );
            case 3: return glm::vec4( _texel.r, _texel.r, _texel.r, 0.0 );
            case 4: return glm::vec4( _texel.r, _texel.r, _texel.r, 1.0 );
Janis Born's avatar
Janis Born committed
322
323
324
325
        }
    }
    else if (_from == 2) {
        switch (_to) {
326
327
328
            case 1: return glm::vec4( _texel.r, 0.0, 0.0, 0.0 );
            case 3: return glm::vec4( _texel.r, _texel.r, _texel.r, _texel.g );
            case 4: return glm::vec4( _texel.r, _texel.r, _texel.r, _texel.g );
Janis Born's avatar
Janis Born committed
329
330
331
332
        }
    }
    else if (_from == 3) {
        switch (_to) {
333
334
335
            case 1: return glm::vec4( grayscaleMixdown(_texel.r, _texel.g, _texel.b), 0.0, 0.0, 0.0 );
            case 2: return glm::vec4( grayscaleMixdown(_texel.r, _texel.g, _texel.b), 1.0, 0.0, 0.0 );
            case 4: return glm::vec4( _texel.r, _texel.r, _texel.r, 1.0 );
Janis Born's avatar
Janis Born committed
336
337
338
339
        }
    }
    else if (_from == 4) {
        switch (_to) {
340
341
342
            case 1: return glm::vec4( grayscaleMixdown(_texel.r, _texel.g, _texel.b), 0.0, 0.0, 0.0 );
            case 2: return glm::vec4( grayscaleMixdown(_texel.r, _texel.g, _texel.b), 1.0, 0.0, 0.0 );
            case 3: return glm::vec4( _texel.r, _texel.r, _texel.r, 0.0 );
Janis Born's avatar
Janis Born committed
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
        }
    }
    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) {
370
371
            auto texel = convertTexelNumChannels(_from->getTexel(glm::uvec2(x, y)), _from->getNumberOfChannels(), _to->getNumberOfChannels());
            _to->setTexel(glm::uvec2(x, y), texel);
Janis Born's avatar
Janis Born committed
372
373
374
375
376
377
        }
    }
}

} // namespace OpenGL
} // namespace ACGL