Coverage Report

Created: 2025-08-25 06:48

/src/ogre/OgreMain/src/OgreImage.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
-----------------------------------------------------------------------------
3
This source file is part of OGRE
4
(Object-oriented Graphics Rendering Engine)
5
For the latest info, see http://www.ogre3d.org/
6
7
Copyright (c) 2000-2014 Torus Knot Software Ltd
8
9
Permission is hereby granted, free of charge, to any person obtaining a copy
10
of this software and associated documentation files (the "Software"), to deal
11
in the Software without restriction, including without limitation the rights
12
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
copies of the Software, and to permit persons to whom the Software is
14
furnished to do so, subject to the following conditions:
15
16
The above copyright notice and this permission notice shall be included in
17
all copies or substantial portions of the Software.
18
19
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
THE SOFTWARE.
26
-----------------------------------------------------------------------------
27
*/
28
#include "OgreStableHeaders.h"
29
#include "OgreImage.h"
30
#include "OgreImageCodec.h"
31
#include "OgreImageResampler.h"
32
33
namespace Ogre {
34
    //-----------------------------------------------------------------------------
35
    Image::Image(PixelFormat format, uint32 width, uint32 height, uint32 depth, uchar* buffer, bool autoDelete)
36
2.67k
        : mWidth(0),
37
2.67k
        mHeight(0),
38
2.67k
        mDepth(0),
39
2.67k
        mNumMipmaps(0),
40
2.67k
        mBufSize(0),
41
2.67k
        mFlags(0),
42
2.67k
        mFormat(format),
43
        mBuffer( NULL ),
44
2.67k
        mAutoDelete( true )
45
2.67k
    {
46
2.67k
        if (format == PF_UNKNOWN)
47
2.67k
            return;
48
49
0
        size_t size = calculateSize(0, 1,  width, height, depth, mFormat);
50
51
0
        if (size == 0)
52
0
            return;
53
54
0
        if (!buffer)
55
0
            buffer = (uchar*)malloc(size);
56
0
        loadDynamicImage(buffer, width, height, depth, format, autoDelete);
57
0
    }
58
59
    void Image::create(PixelFormat format, uint32 width, uint32 height, uint32 depth, uint32 numFaces,
60
                       uint32 numMipMaps)
61
0
    {
62
0
        size_t size = calculateSize(numMipMaps, numFaces, width, height, depth, format);
63
0
        if (!mAutoDelete || !mBuffer || mBufSize != size)
64
0
        {
65
0
            freeMemory();
66
0
            mBuffer = (uchar*)malloc(size); // allocate
67
0
        }
68
69
        // make sure freeMemory() does nothing, we set this true immediately after
70
0
        mAutoDelete = false;
71
0
        loadDynamicImage(mBuffer, width, height, depth, format, true, numFaces, numMipMaps);
72
0
    }
73
74
    //-----------------------------------------------------------------------------
75
    Image::Image( const Image &img )
76
        : mBuffer( NULL ),
77
0
        mAutoDelete( true )
78
0
    {
79
        // call assignment operator
80
0
        *this = img;
81
0
    }
82
83
    //-----------------------------------------------------------------------------
84
    Image::~Image()
85
2.67k
    {
86
2.67k
        freeMemory();
87
2.67k
    }
88
    //---------------------------------------------------------------------
89
    void Image::freeMemory()
90
6.38k
    {
91
        //Only delete if this was not a dynamic image (meaning app holds & destroys buffer)
92
6.38k
        if( mBuffer && mAutoDelete )
93
1.03k
        {
94
1.03k
            free(mBuffer);
95
1.03k
            mBuffer = NULL;
96
1.03k
        }
97
98
6.38k
    }
99
100
    //-----------------------------------------------------------------------------
101
    Image& Image::operator=(const Image& img)
102
0
    {
103
0
        if (this == &img)
104
0
            return *this;
105
106
        // Only create & copy when other data was owning
107
0
        if (img.mBuffer && img.mAutoDelete)
108
0
        {
109
0
            create(img.mFormat, img.mWidth, img.mHeight, img.mDepth, img.getNumFaces(), img.mNumMipmaps);
110
0
            memcpy(mBuffer, img.mBuffer, mBufSize);
111
0
        }
112
0
        else
113
0
        {
114
0
            loadDynamicImage(img.mBuffer, img.mWidth, img.mHeight, img.mDepth, img.mFormat, false,
115
0
                             img.getNumFaces(), img.mNumMipmaps);
116
0
        }
117
118
0
        return *this;
119
0
    }
120
121
    void Image::setTo(const ColourValue& col)
122
0
    {
123
0
        OgreAssert(mBuffer, "No image data loaded");
124
0
        if(col == ColourValue::ZERO)
125
0
        {
126
0
            memset(mBuffer, 0, getSize());
127
0
            return;
128
0
        }
129
130
0
        uchar rawCol[4 * sizeof(float)]; // max packed size currently is 4*float
131
0
        PixelUtil::packColour(col, mFormat, rawCol);
132
0
        for(size_t p = 0; p < mBufSize; p += mPixelSize)
133
0
        {
134
0
            memcpy(mBuffer + p, rawCol, mPixelSize);
135
0
        }
136
0
    }
137
138
    //-----------------------------------------------------------------------------
139
    Image & Image::flipAroundY()
140
0
    {
141
0
        OgreAssert(mBuffer, "No image data loaded");
142
0
        mNumMipmaps = 0; // Image operations lose precomputed mipmaps
143
144
0
        uint32 y;
145
0
        switch (mPixelSize)
146
0
        {
147
0
        case 1:
148
0
            for (y = 0; y < mHeight; y++)
149
0
            {
150
0
                std::reverse(mBuffer + mWidth * y, mBuffer + mWidth * (y + 1));
151
0
            }
152
0
            break;
153
154
0
        case 2:
155
0
            for (y = 0; y < mHeight; y++)
156
0
            {
157
0
                std::reverse((ushort*)mBuffer + mWidth * y, (ushort*)mBuffer + mWidth * (y + 1));
158
0
            }
159
0
            break;
160
161
0
        case 3:
162
0
            typedef uchar uchar3[3];
163
0
            for (y = 0; y < mHeight; y++)
164
0
            {
165
0
                std::reverse((uchar3*)mBuffer + mWidth * y, (uchar3*)mBuffer + mWidth * (y + 1));
166
0
            }
167
0
            break;
168
169
0
        case 4:
170
0
            for (y = 0; y < mHeight; y++)
171
0
            {
172
0
                std::reverse((uint*)mBuffer + mWidth * y, (uint*)mBuffer + mWidth * (y + 1));
173
0
            }
174
0
            break;
175
176
0
        default:
177
0
            OGRE_EXCEPT( 
178
0
                Exception::ERR_INTERNAL_ERROR,
179
0
                "Unknown pixel depth",
180
0
                "Image::flipAroundY" );
181
0
            break;
182
0
        }
183
184
0
        return *this;
185
186
0
    }
187
188
    //-----------------------------------------------------------------------------
189
    Image & Image::flipAroundX()
190
0
    {
191
0
        OgreAssert(mBuffer, "No image data loaded");
192
        
193
0
        mNumMipmaps = 0; // Image operations lose precomputed mipmaps
194
0
        PixelUtil::bulkPixelVerticalFlip(getPixelBox());
195
196
0
        return *this;
197
0
    }
198
199
    //-----------------------------------------------------------------------------
200
    Image& Image::loadDynamicImage(uchar* pData, uint32 uWidth, uint32 uHeight, uint32 depth,
201
                                   PixelFormat eFormat, bool autoDelete, uint32 numFaces, uint32 numMipMaps)
202
1.03k
    {
203
204
1.03k
        freeMemory();
205
        // Set image metadata
206
1.03k
        mWidth = uWidth;
207
1.03k
        mHeight = uHeight;
208
1.03k
        mDepth = depth;
209
1.03k
        mFormat = eFormat;
210
1.03k
        mPixelSize = static_cast<uchar>(PixelUtil::getNumElemBytes( mFormat ));
211
1.03k
        mNumMipmaps = numMipMaps;
212
1.03k
        mFlags = 0;
213
        // Set flags
214
1.03k
        if (PixelUtil::isCompressed(eFormat))
215
0
            mFlags |= IF_COMPRESSED;
216
1.03k
        if (mDepth != 1)
217
0
            mFlags |= IF_3D_TEXTURE;
218
1.03k
        if(numFaces == 6)
219
0
            mFlags |= IF_CUBEMAP;
220
1.03k
        OgreAssert(numFaces == 6 || numFaces == 1, "Invalid number of faces");
221
222
1.03k
        mBufSize = calculateSize(numMipMaps, numFaces, uWidth, uHeight, depth, eFormat);
223
1.03k
        mBuffer = pData;
224
1.03k
        mAutoDelete = autoDelete;
225
226
1.03k
        return *this;
227
1.03k
    }
228
229
    //-----------------------------------------------------------------------------
230
    Image& Image::loadRawData(const DataStreamPtr& stream, uint32 uWidth, uint32 uHeight, uint32 uDepth,
231
                              PixelFormat eFormat, uint32 numFaces, uint32 numMipMaps)
232
0
    {
233
234
0
        size_t size = calculateSize(numMipMaps, numFaces, uWidth, uHeight, uDepth, eFormat);
235
0
        OgreAssert(size == stream->size(), "Wrong stream size");
236
237
0
        uchar *buffer = OGRE_ALLOC_T(uchar, size, MEMCATEGORY_GENERAL);
238
0
        stream->read(buffer, size);
239
240
0
        return loadDynamicImage(buffer,
241
0
            uWidth, uHeight, uDepth,
242
0
            eFormat, true, numFaces, numMipMaps);
243
244
0
    }
245
    //-----------------------------------------------------------------------------
246
    Image & Image::load(const String& strFileName, const String& group)
247
0
    {
248
249
0
        String strExt;
250
251
0
        size_t pos = strFileName.find_last_of('.');
252
0
        if( pos != String::npos && pos < (strFileName.length() - 1))
253
0
        {
254
0
            strExt = strFileName.substr(pos+1);
255
0
        }
256
257
0
        DataStreamPtr encoded = ResourceGroupManager::getSingleton().openResource(strFileName, group);
258
0
        return load(encoded, strExt);
259
260
0
    }
261
    //-----------------------------------------------------------------------------
262
    void Image::save(const String& filename)
263
1.03k
    {
264
1.03k
        OgreAssert(mBuffer, "No image data loaded");
265
266
1.03k
        String base, ext;
267
1.03k
        StringUtil::splitBaseFilename(filename, base, ext);
268
269
        // getCodec throws when no codec is found
270
1.03k
        Codec::getCodec(ext)->encodeToFile(this, filename);
271
1.03k
    }
272
    //---------------------------------------------------------------------
273
    DataStreamPtr Image::encode(const String& formatextension)
274
0
    {
275
0
        OgreAssert(mBuffer, "No image data loaded");
276
        // getCodec throws when no codec is found
277
0
        return Codec::getCodec(formatextension)->encode(this);
278
0
    }
279
    //-----------------------------------------------------------------------------
280
    Image & Image::load(const DataStreamPtr& stream, String type )
281
2.67k
    {
282
2.67k
        freeMemory();
283
284
2.67k
        if (type.empty())
285
0
        {
286
0
            String base, ext;
287
0
            StringUtil::splitBaseFilename(stream->getName(), base, ext);
288
0
            if (!ext.empty())
289
0
                type = ext;
290
0
        }
291
292
2.67k
        Codec * pCodec = 0;
293
2.67k
        if (!type.empty())
294
2.67k
        {
295
            // use named codec
296
2.67k
            pCodec = Codec::getCodec(type);
297
2.67k
        }
298
0
        else
299
0
        {
300
            // derive from magic number
301
            // read the first 32 bytes or file size, if less
302
0
            size_t magicLen = std::min(stream->size(), (size_t)32);
303
0
            char magicBuf[32];
304
0
            stream->read(magicBuf, magicLen);
305
            // return to start
306
0
            stream->seek(0);
307
0
            pCodec = Codec::getCodec(magicBuf, magicLen);
308
309
0
            if (!pCodec)
310
0
                OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
311
0
                            "Unable to load image: Image format is unknown. Unable to identify codec. "
312
0
                            "Check it or specify format explicitly.");
313
0
        }
314
315
2.67k
        pCodec->decode(stream, this);
316
317
        // compute the pixel size
318
2.67k
        mPixelSize = static_cast<uchar>(PixelUtil::getNumElemBytes( mFormat ));
319
        // make sure we delete
320
2.67k
        mAutoDelete = true;
321
322
2.67k
        return *this;
323
2.67k
    }
324
    //---------------------------------------------------------------------
325
    String Image::getFileExtFromMagic(const DataStreamPtr stream)
326
0
    {
327
        // read the first 32 bytes or file size, if less
328
0
        size_t magicLen = std::min(stream->size(), (size_t)32);
329
0
        char magicBuf[32];
330
0
        stream->read(magicBuf, magicLen);
331
        // return to start
332
0
        stream->seek(0);
333
0
        Codec* pCodec = Codec::getCodec(magicBuf, magicLen);
334
335
0
        if(pCodec)
336
0
            return pCodec->getType();
337
0
        else
338
0
            return BLANKSTRING;
339
340
0
    }
341
342
    //-----------------------------------------------------------------------------
343
    bool Image::getHasAlpha(void) const
344
0
    {
345
0
        return PixelUtil::getFlags(mFormat) & PFF_HASALPHA;
346
0
    }
347
    //-----------------------------------------------------------------------------
348
    void Image::applyGamma( uchar *buffer, Real gamma, size_t size, uchar bpp )
349
0
    {
350
0
        if( gamma == 1.0f )
351
0
            return;
352
353
0
        OgreAssert( bpp == 24 || bpp == 32, "");
354
355
0
        uint stride = bpp >> 3;
356
        
357
0
        uchar gammaramp[256];
358
0
        const Real exponent = 1.0f / gamma;
359
0
        for(int i = 0; i < 256; i++) {
360
0
            gammaramp[i] = static_cast<uchar>(Math::Pow(i/255.0f, exponent)*255+0.5f);
361
0
        }
362
363
0
        for( size_t i = 0, j = size / stride; i < j; i++, buffer += stride )
364
0
        {
365
0
            buffer[0] = gammaramp[buffer[0]];
366
0
            buffer[1] = gammaramp[buffer[1]];
367
0
            buffer[2] = gammaramp[buffer[2]];
368
0
        }
369
0
    }
370
    //-----------------------------------------------------------------------------
371
    void Image::resize(ushort width, ushort height, Filter filter)
372
0
    {
373
0
        OgreAssert(mAutoDelete, "resizing dynamic images is not supported");
374
0
        OgreAssert(mDepth == 1, "only 2D formats supported");
375
376
        // reassign buffer to temp image, make sure auto-delete is true
377
0
        Image temp(mFormat, mWidth, mHeight, 1, mBuffer, true);
378
379
        // do not delete[] mBuffer!  temp will destroy it
380
0
        mBuffer = 0;
381
382
        // set new dimensions, allocate new buffer
383
0
        create(mFormat, width, height); // Loses precomputed mipmaps
384
385
        // scale the image from temp into our resized buffer
386
0
        Image::scale(temp.getPixelBox(), getPixelBox(), filter);
387
0
    }
388
    //-----------------------------------------------------------------------
389
    void Image::scale(const PixelBox &src, const PixelBox &scaled, Filter filter) 
390
0
    {
391
0
        assert(PixelUtil::isAccessible(src.format));
392
0
        assert(PixelUtil::isAccessible(scaled.format));
393
0
        Image buf; // For auto-delete
394
        // Assume no intermediate buffer needed
395
0
        PixelBox temp = scaled;
396
0
        switch (filter) 
397
0
        {
398
0
        default:
399
0
        case FILTER_NEAREST:
400
0
            if(src.format != scaled.format)
401
0
            {
402
                // Allocate temporary buffer of destination size in source format 
403
0
                buf.create(src.format, scaled.getWidth(), scaled.getHeight(), scaled.getDepth());
404
0
                temp = buf.getPixelBox();
405
0
            }
406
            // super-optimized: no conversion
407
0
            switch (PixelUtil::getNumElemBytes(src.format)) 
408
0
            {
409
0
            case 1: NearestResampler<1>::scale(src, temp); break;
410
0
            case 2: NearestResampler<2>::scale(src, temp); break;
411
0
            case 3: NearestResampler<3>::scale(src, temp); break;
412
0
            case 4: NearestResampler<4>::scale(src, temp); break;
413
0
            case 6: NearestResampler<6>::scale(src, temp); break;
414
0
            case 8: NearestResampler<8>::scale(src, temp); break;
415
0
            case 12: NearestResampler<12>::scale(src, temp); break;
416
0
            case 16: NearestResampler<16>::scale(src, temp); break;
417
0
            default:
418
                // never reached
419
0
                assert(false);
420
0
            }
421
0
            if(temp.data != scaled.data)
422
0
            {
423
                // Blit temp buffer
424
0
                PixelUtil::bulkPixelConversion(temp, scaled);
425
0
            }
426
0
            break;
427
428
0
        case FILTER_BILINEAR:
429
0
            switch (src.format) 
430
0
            {
431
0
            case PF_L8: case PF_R8: case PF_A8: case PF_BYTE_LA:
432
0
            case PF_R8G8B8: case PF_B8G8R8:
433
0
            case PF_R8G8B8A8: case PF_B8G8R8A8:
434
0
            case PF_A8B8G8R8: case PF_A8R8G8B8:
435
0
            case PF_X8B8G8R8: case PF_X8R8G8B8:
436
0
                if(src.format != scaled.format)
437
0
                {
438
                    // Allocate temp buffer of destination size in source format 
439
0
                    buf.create(src.format, scaled.getWidth(), scaled.getHeight(), scaled.getDepth());
440
0
                    temp = buf.getPixelBox();
441
0
                }
442
                // super-optimized: byte-oriented math, no conversion
443
0
                switch (PixelUtil::getNumElemBytes(src.format)) 
444
0
                {
445
0
                case 1: LinearResampler_Byte<1>::scale(src, temp); break;
446
0
                case 2: LinearResampler_Byte<2>::scale(src, temp); break;
447
0
                case 3: LinearResampler_Byte<3>::scale(src, temp); break;
448
0
                case 4: LinearResampler_Byte<4>::scale(src, temp); break;
449
0
                default:
450
                    // never reached
451
0
                    assert(false);
452
0
                }
453
0
                if(temp.data != scaled.data)
454
0
                {
455
                    // Blit temp buffer
456
0
                    PixelUtil::bulkPixelConversion(temp, scaled);
457
0
                }
458
0
                break;
459
0
            case PF_FLOAT32_RGB:
460
0
            case PF_FLOAT32_RGBA:
461
0
                if (scaled.format == PF_FLOAT32_RGB || scaled.format == PF_FLOAT32_RGBA)
462
0
                {
463
                    // float32 to float32, avoid unpack/repack overhead
464
0
                    LinearResampler_Float32::scale(src, scaled);
465
0
                    break;
466
0
                }
467
                // else, fall through
468
0
            default:
469
                // non-optimized: floating-point math, performs conversion but always works
470
0
                LinearResampler::scale(src, scaled);
471
0
            }
472
0
            break;
473
0
        }
474
0
    }
475
476
    //-----------------------------------------------------------------------------    
477
478
    ColourValue Image::getColourAt(uint32 x, uint32 y, uint32 z) const
479
0
    {
480
0
        ColourValue rval;
481
0
        PixelUtil::unpackColour(&rval, mFormat, getData(x, y, z));
482
0
        return rval;
483
0
    }
484
485
    //-----------------------------------------------------------------------------    
486
    
487
    void Image::setColourAt(ColourValue const &cv, uint32 x, uint32 y, uint32 z)
488
0
    {
489
0
        PixelUtil::packColour(cv, mFormat, getData(x, y, z));
490
0
    }
491
492
    //-----------------------------------------------------------------------------    
493
494
    PixelBox Image::getPixelBox(uint32 face, uint32 mipmap) const
495
0
    {
496
        // Image data is arranged as:
497
        // face 0, top level (mip 0)
498
        // face 0, mip 1
499
        // face 0, mip 2
500
        // face 1, top level (mip 0)
501
        // face 1, mip 1
502
        // face 1, mip 2
503
        // etc
504
0
        OgreAssert(mipmap <= getNumMipmaps(), "out of range");
505
0
        OgreAssert(face < getNumFaces(), "out of range");
506
        // Calculate mipmap offset and size
507
0
        uint8 *offset = mBuffer;
508
        // Base offset is number of full faces
509
0
        uint32 width = getWidth(), height=getHeight(), depth=getDepth();
510
0
        uint32 numMips = getNumMipmaps();
511
512
        // Figure out the offsets 
513
0
        size_t fullFaceSize = 0;
514
0
        size_t finalFaceSize = 0;
515
0
        uint32 finalWidth = 0, finalHeight = 0, finalDepth = 0;
516
0
        for(uint32 mip=0; mip <= numMips; ++mip)
517
0
        {
518
0
            if (mip == mipmap)
519
0
            {
520
0
                finalFaceSize = fullFaceSize;
521
0
                finalWidth = width;
522
0
                finalHeight = height;
523
0
                finalDepth = depth;
524
0
            }
525
0
            fullFaceSize += PixelUtil::getMemorySize(width, height, depth, getFormat());
526
527
            /// Half size in each dimension
528
0
            if(width!=1) width /= 2;
529
0
            if(height!=1) height /= 2;
530
0
            if(depth!=1) depth /= 2;
531
0
        }
532
        // Advance pointer by number of full faces, plus mip offset into
533
0
        offset += face * fullFaceSize;
534
0
        offset += finalFaceSize;
535
        // Return subface as pixelbox
536
0
        PixelBox src(finalWidth, finalHeight, finalDepth, getFormat(), offset);
537
0
        return src;
538
0
    }
539
    //-----------------------------------------------------------------------------
540
    size_t Image::calculateSize(uint32 mipmaps, uint32 faces, uint32 width, uint32 height, uint32 depth,
541
                                PixelFormat format)
542
1.03k
    {
543
1.03k
        size_t size = 0;
544
2.06k
        for(uint32 mip=0; mip<=mipmaps; ++mip)
545
1.03k
        {
546
1.03k
            size += PixelUtil::getMemorySize(width, height, depth, format)*faces; 
547
1.03k
            if(width!=1) width /= 2;
548
1.03k
            if(height!=1) height /= 2;
549
1.03k
            if(depth!=1) depth /= 2;
550
1.03k
        }
551
1.03k
        return size;
552
1.03k
    }
553
    //---------------------------------------------------------------------
554
    Image & Image::loadTwoImagesAsRGBA(const String& rgbFilename, const String& alphaFilename,
555
        const String& groupName, PixelFormat fmt)
556
0
    {
557
0
        Image rgb, alpha;
558
559
0
        rgb.load(rgbFilename, groupName);
560
0
        alpha.load(alphaFilename, groupName);
561
562
0
        return combineTwoImagesAsRGBA(rgb, alpha, fmt);
563
564
0
    }
565
    //---------------------------------------------------------------------
566
    Image& Image::loadTwoImagesAsRGBA(const DataStreamPtr& rgbStream,
567
                                      const DataStreamPtr& alphaStream, PixelFormat fmt,
568
                                      const String& rgbType, const String& alphaType)
569
0
    {
570
0
        Image rgb, alpha;
571
572
0
        rgb.load(rgbStream, rgbType);
573
0
        alpha.load(alphaStream, alphaType);
574
575
0
        return combineTwoImagesAsRGBA(rgb, alpha, fmt);
576
577
0
    }
578
    //---------------------------------------------------------------------
579
    Image & Image::combineTwoImagesAsRGBA(const Image& rgb, const Image& alpha, PixelFormat fmt)
580
0
    {
581
        // the images should be the same size, have the same number of mipmaps
582
0
        OgreAssert(rgb.getWidth() == alpha.getWidth() && rgb.getHeight() == alpha.getHeight() &&
583
0
                       rgb.getDepth() == alpha.getDepth(),
584
0
                   "Images must be the same dimensions");
585
0
        OgreAssert(rgb.getNumMipmaps() == alpha.getNumMipmaps() && rgb.getNumFaces() == alpha.getNumFaces(),
586
0
                   "Images must have the same number of surfaces");
587
588
        // Format check
589
0
        OgreAssert(PixelUtil::getComponentCount(fmt) == 4, "Target format must have 4 components");
590
591
0
        OgreAssert(!(PixelUtil::isCompressed(fmt) || PixelUtil::isCompressed(rgb.getFormat()) ||
592
0
                     PixelUtil::isCompressed(alpha.getFormat())),
593
0
                   "Compressed formats are not supported in this method");
594
595
0
        uint32 numFaces = rgb.getNumFaces();
596
0
        create(fmt, rgb.getWidth(), rgb.getHeight(), rgb.getDepth(), numFaces, rgb.getNumMipmaps());
597
598
0
        for (uint32 face = 0; face < numFaces; ++face)
599
0
        {
600
0
            for (uint8 mip = 0; mip <= mNumMipmaps; ++mip)
601
0
            {
602
                // convert the RGB first
603
0
                PixelBox srcRGB = rgb.getPixelBox(face, mip);
604
0
                PixelBox dst = getPixelBox(face, mip);
605
0
                PixelUtil::bulkPixelConversion(srcRGB, dst);
606
607
                // now selectively add the alpha
608
0
                PixelBox srcAlpha = alpha.getPixelBox(face, mip);
609
0
                uchar* psrcAlpha = srcAlpha.data;
610
0
                uchar* pdst = dst.data;
611
0
                for (uint32 d = 0; d < mDepth; ++d)
612
0
                {
613
0
                    for (uint32 y = 0; y < mHeight; ++y)
614
0
                    {
615
0
                        for (uint32 x = 0; x < mWidth; ++x)
616
0
                        {
617
0
                            ColourValue colRGBA, colA;
618
                            // read RGB back from dest to save having another pointer
619
0
                            PixelUtil::unpackColour(&colRGBA, mFormat, pdst);
620
0
                            PixelUtil::unpackColour(&colA, alpha.getFormat(), psrcAlpha);
621
622
                            // combine RGB from alpha source texture
623
0
                            colRGBA.a = (colA.r + colA.g + colA.b) / 3.0f;
624
625
0
                            PixelUtil::packColour(colRGBA, mFormat, pdst);
626
                            
627
0
                            psrcAlpha += PixelUtil::getNumElemBytes(alpha.getFormat());
628
0
                            pdst += PixelUtil::getNumElemBytes(mFormat);
629
0
                        }
630
0
                    }
631
0
                }
632
0
            }
633
0
        }
634
635
0
        return *this;
636
0
    }
637
    //---------------------------------------------------------------------
638
639
    
640
}