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 {
~ImageCodec()34     ImageCodec::~ImageCodec() {
35     }
36 
37     //-----------------------------------------------------------------------------
Image()38     Image::Image()
39         : mWidth(0),
40         mHeight(0),
41         mDepth(0),
42         mBufSize(0),
43         mNumMipmaps(0),
44         mFlags(0),
45         mFormat(PF_UNKNOWN),
46         mBuffer( NULL ),
47         mAutoDelete( true )
48     {
49     }
50 
51     //-----------------------------------------------------------------------------
Image(const Image & img)52     Image::Image( const Image &img )
53         : mBuffer( NULL ),
54         mAutoDelete( true )
55     {
56         // call assignment operator
57         *this = img;
58     }
59 
60     //-----------------------------------------------------------------------------
~Image()61     Image::~Image()
62     {
63         freeMemory();
64     }
65     //---------------------------------------------------------------------
freeMemory()66     void Image::freeMemory()
67     {
68         //Only delete if this was not a dynamic image (meaning app holds & destroys buffer)
69         if( mBuffer && mAutoDelete )
70         {
71             OGRE_FREE(mBuffer, MEMCATEGORY_GENERAL);
72             mBuffer = NULL;
73         }
74 
75     }
76 
77     //-----------------------------------------------------------------------------
operator =(const Image & img)78     Image & Image::operator = ( const Image &img )
79     {
80         freeMemory();
81         mWidth = img.mWidth;
82         mHeight = img.mHeight;
83         mDepth = img.mDepth;
84         mFormat = img.mFormat;
85         mBufSize = img.mBufSize;
86         mFlags = img.mFlags;
87         mPixelSize = img.mPixelSize;
88         mNumMipmaps = img.mNumMipmaps;
89         mAutoDelete = img.mAutoDelete;
90         //Only create/copy when previous data was not dynamic data
91         if( img.mBuffer && mAutoDelete )
92         {
93             mBuffer = OGRE_ALLOC_T(uchar, mBufSize, MEMCATEGORY_GENERAL);
94             memcpy( mBuffer, img.mBuffer, mBufSize );
95         }
96         else
97         {
98             mBuffer = img.mBuffer;
99         }
100 
101         return *this;
102     }
103 
104     //-----------------------------------------------------------------------------
flipAroundY()105     Image & Image::flipAroundY()
106     {
107         if( !mBuffer )
108         {
109             OGRE_EXCEPT(
110                 Exception::ERR_INTERNAL_ERROR,
111                 "Can not flip an uninitialised texture",
112                 "Image::flipAroundY" );
113         }
114 
115         mNumMipmaps = 0; // Image operations lose precomputed mipmaps
116 
117         ushort y;
118         switch (mPixelSize)
119         {
120         case 1:
121             for (y = 0; y < mHeight; y++)
122             {
123                 std::reverse(mBuffer + mWidth * y, mBuffer + mWidth * (y + 1));
124             }
125             break;
126 
127         case 2:
128             for (y = 0; y < mHeight; y++)
129             {
130                 std::reverse((ushort*)mBuffer + mWidth * y, (ushort*)mBuffer + mWidth * (y + 1));
131             }
132             break;
133 
134         case 3:
135             typedef uchar uchar3[3];
136             for (y = 0; y < mHeight; y++)
137             {
138                 std::reverse((uchar3*)mBuffer + mWidth * y, (uchar3*)mBuffer + mWidth * (y + 1));
139             }
140             break;
141 
142         case 4:
143             for (y = 0; y < mHeight; y++)
144             {
145                 std::reverse((uint*)mBuffer + mWidth * y, (uint*)mBuffer + mWidth * (y + 1));
146             }
147             break;
148 
149         default:
150             OGRE_EXCEPT(
151                 Exception::ERR_INTERNAL_ERROR,
152                 "Unknown pixel depth",
153                 "Image::flipAroundY" );
154             break;
155         }
156 
157         return *this;
158 
159     }
160 
161     //-----------------------------------------------------------------------------
flipAroundX()162     Image & Image::flipAroundX()
163     {
164         if( !mBuffer )
165         {
166             OGRE_EXCEPT(
167                 Exception::ERR_INTERNAL_ERROR,
168                 "Can not flip an uninitialised texture",
169                 "Image::flipAroundX" );
170         }
171 
172         mNumMipmaps = 0; // Image operations lose precomputed mipmaps
173         PixelUtil::bulkPixelVerticalFlip(getPixelBox());
174 
175         return *this;
176     }
177 
178     //-----------------------------------------------------------------------------
loadDynamicImage(uchar * pData,uint32 uWidth,uint32 uHeight,uint32 depth,PixelFormat eFormat,bool autoDelete,size_t numFaces,uint32 numMipMaps)179     Image& Image::loadDynamicImage( uchar* pData, uint32 uWidth, uint32 uHeight,
180         uint32 depth,
181         PixelFormat eFormat, bool autoDelete,
182         size_t numFaces, uint32 numMipMaps)
183     {
184 
185         freeMemory();
186         // Set image metadata
187         mWidth = uWidth;
188         mHeight = uHeight;
189         mDepth = depth;
190         mFormat = eFormat;
191         mPixelSize = static_cast<uchar>(PixelUtil::getNumElemBytes( mFormat ));
192         mNumMipmaps = numMipMaps;
193         mFlags = 0;
194         // Set flags
195         if (PixelUtil::isCompressed(eFormat))
196             mFlags |= IF_COMPRESSED;
197         if (mDepth != 1)
198             mFlags |= IF_3D_TEXTURE;
199         if(numFaces == 6)
200             mFlags |= IF_CUBEMAP;
201         if(numFaces != 6 && numFaces != 1)
202             OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
203             "Number of faces currently must be 6 or 1.",
204             "Image::loadDynamicImage");
205 
206         mBufSize = calculateSize(numMipMaps, numFaces, uWidth, uHeight, depth, eFormat);
207         mBuffer = pData;
208         mAutoDelete = autoDelete;
209 
210         return *this;
211 
212     }
213 
214     //-----------------------------------------------------------------------------
loadRawData(const DataStreamPtr & stream,uint32 uWidth,uint32 uHeight,uint32 uDepth,PixelFormat eFormat,size_t numFaces,uint32 numMipMaps)215     Image & Image::loadRawData(
216         const DataStreamPtr& stream,
217         uint32 uWidth, uint32 uHeight, uint32 uDepth,
218         PixelFormat eFormat,
219         size_t numFaces, uint32 numMipMaps)
220     {
221 
222         size_t size = calculateSize(numMipMaps, numFaces, uWidth, uHeight, uDepth, eFormat);
223         if (size != stream->size())
224         {
225             OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
226                 "Stream size does not match calculated image size",
227                 "Image::loadRawData");
228         }
229 
230         uchar *buffer = OGRE_ALLOC_T(uchar, size, MEMCATEGORY_GENERAL);
231         stream->read(buffer, size);
232 
233         return loadDynamicImage(buffer,
234             uWidth, uHeight, uDepth,
235             eFormat, true, numFaces, numMipMaps);
236 
237     }
238     //-----------------------------------------------------------------------------
load(const String & strFileName,const String & group)239     Image & Image::load(const String& strFileName, const String& group)
240     {
241 
242         String strExt;
243 
244         size_t pos = strFileName.find_last_of('.');
245         if( pos != String::npos && pos < (strFileName.length() - 1))
246         {
247             strExt = strFileName.substr(pos+1);
248         }
249 
250         DataStreamPtr encoded = ResourceGroupManager::getSingleton().openResource(strFileName, group);
251         return load(encoded, strExt);
252 
253     }
254     //-----------------------------------------------------------------------------
save(const String & filename)255     void Image::save(const String& filename)
256     {
257         if( !mBuffer )
258         {
259             OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "No image data loaded",
260                 "Image::save");
261         }
262 
263         String strExt;
264         size_t pos = filename.find_last_of('.');
265         if( pos == String::npos )
266             OGRE_EXCEPT(
267             Exception::ERR_INVALIDPARAMS,
268             "Unable to save image file '" + filename + "' - invalid extension.",
269             "Image::save" );
270 
271         while( pos != filename.length() - 1 )
272             strExt += filename[++pos];
273 
274         Codec * pCodec = Codec::getCodec(strExt);
275         if( !pCodec )
276             OGRE_EXCEPT(
277             Exception::ERR_INVALIDPARAMS,
278             "Unable to save image file '" + filename + "' - invalid extension.",
279             "Image::save" );
280 
281         ImageCodec::ImageData* imgData = OGRE_NEW ImageCodec::ImageData();
282         imgData->format = mFormat;
283         imgData->height = mHeight;
284         imgData->width = mWidth;
285         imgData->depth = mDepth;
286         imgData->size = mBufSize;
287 		imgData->num_mipmaps = mNumMipmaps;
288         // Wrap in CodecDataPtr, this will delete
289         Codec::CodecDataPtr codeDataPtr(imgData);
290         // Wrap memory, be sure not to delete when stream destroyed
291         MemoryDataStreamPtr wrapper(OGRE_NEW MemoryDataStream(mBuffer, mBufSize, false));
292 
293         pCodec->encodeToFile(wrapper, filename, codeDataPtr);
294     }
295     //---------------------------------------------------------------------
encode(const String & formatextension)296     DataStreamPtr Image::encode(const String& formatextension)
297     {
298         if( !mBuffer )
299         {
300             OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "No image data loaded",
301                 "Image::encode");
302         }
303 
304         Codec * pCodec = Codec::getCodec(formatextension);
305         if( !pCodec )
306             OGRE_EXCEPT(
307             Exception::ERR_INVALIDPARAMS,
308             "Unable to encode image data as '" + formatextension + "' - invalid extension.",
309             "Image::encode" );
310 
311         ImageCodec::ImageData* imgData = OGRE_NEW ImageCodec::ImageData();
312         imgData->format = mFormat;
313         imgData->height = mHeight;
314         imgData->width = mWidth;
315         imgData->depth = mDepth;
316         // Wrap in CodecDataPtr, this will delete
317         Codec::CodecDataPtr codeDataPtr(imgData);
318         // Wrap memory, be sure not to delete when stream destroyed
319         MemoryDataStreamPtr wrapper(OGRE_NEW MemoryDataStream(mBuffer, mBufSize, false));
320 
321         return pCodec->encode(wrapper, codeDataPtr);
322     }
323     //-----------------------------------------------------------------------------
load(const DataStreamPtr & stream,const String & type)324     Image & Image::load(const DataStreamPtr& stream, const String& type )
325     {
326         freeMemory();
327 
328         Codec * pCodec = 0;
329         if (!type.empty())
330         {
331             // use named codec
332             pCodec = Codec::getCodec(type);
333         }
334         else
335         {
336             // derive from magic number
337             // read the first 32 bytes or file size, if less
338             size_t magicLen = std::min(stream->size(), (size_t)32);
339             char magicBuf[32];
340             stream->read(magicBuf, magicLen);
341             // return to start
342             stream->seek(0);
343             pCodec = Codec::getCodec(magicBuf, magicLen);
344 
345       if( !pCodec )
346         OGRE_EXCEPT(
347         Exception::ERR_INVALIDPARAMS,
348         "Unable to load image: Image format is unknown. Unable to identify codec. "
349         "Check it or specify format explicitly.",
350         "Image::load" );
351         }
352 
353         Codec::DecodeResult res = pCodec->decode(stream);
354 
355         ImageCodec::ImageData* pData =
356             static_cast<ImageCodec::ImageData*>(res.second.get());
357 
358         mWidth = pData->width;
359         mHeight = pData->height;
360         mDepth = pData->depth;
361         mBufSize = pData->size;
362         mNumMipmaps = pData->num_mipmaps;
363         mFlags = pData->flags;
364 
365         // Get the format and compute the pixel size
366         mFormat = pData->format;
367         mPixelSize = static_cast<uchar>(PixelUtil::getNumElemBytes( mFormat ));
368         // Just use internal buffer of returned memory stream
369         mBuffer = res.first->getPtr();
370         // Make sure stream does not delete
371         res.first->setFreeOnClose(false);
372         // make sure we delete
373         mAutoDelete = true;
374 
375         return *this;
376     }
377     //---------------------------------------------------------------------
getFileExtFromMagic(const DataStreamPtr stream)378     String Image::getFileExtFromMagic(const DataStreamPtr stream)
379     {
380         // read the first 32 bytes or file size, if less
381         size_t magicLen = std::min(stream->size(), (size_t)32);
382         char magicBuf[32];
383         stream->read(magicBuf, magicLen);
384         // return to start
385         stream->seek(0);
386         Codec* pCodec = Codec::getCodec(magicBuf, magicLen);
387 
388         if(pCodec)
389             return pCodec->getType();
390         else
391             return BLANKSTRING;
392 
393     }
394     //-----------------------------------------------------------------------------
getData()395     uchar* Image::getData()
396     {
397         return mBuffer;
398     }
399 
400     //-----------------------------------------------------------------------------
getData() const401     const uchar* Image::getData() const
402     {
403         assert( mBuffer );
404         return mBuffer;
405     }
406 
407     //-----------------------------------------------------------------------------
getSize() const408     size_t Image::getSize() const
409     {
410         return mBufSize;
411     }
412 
413     //-----------------------------------------------------------------------------
getNumMipmaps() const414     uint32 Image::getNumMipmaps() const
415     {
416         return mNumMipmaps;
417     }
418 
419     //-----------------------------------------------------------------------------
hasFlag(const ImageFlags imgFlag) const420     bool Image::hasFlag(const ImageFlags imgFlag) const
421     {
422         return (mFlags & imgFlag) != 0;
423     }
424 
425     //-----------------------------------------------------------------------------
getDepth() const426     uint32 Image::getDepth() const
427     {
428         return mDepth;
429     }
430     //-----------------------------------------------------------------------------
getWidth() const431     uint32 Image::getWidth() const
432     {
433         return mWidth;
434     }
435 
436     //-----------------------------------------------------------------------------
getHeight() const437     uint32 Image::getHeight() const
438     {
439         return mHeight;
440     }
441     //-----------------------------------------------------------------------------
getNumFaces(void) const442     size_t Image::getNumFaces(void) const
443     {
444         if(hasFlag(IF_CUBEMAP))
445             return 6;
446         return 1;
447     }
448     //-----------------------------------------------------------------------------
getRowSpan() const449     size_t Image::getRowSpan() const
450     {
451         return mWidth * mPixelSize;
452     }
453 
454     //-----------------------------------------------------------------------------
getFormat() const455     PixelFormat Image::getFormat() const
456     {
457         return mFormat;
458     }
459 
460     //-----------------------------------------------------------------------------
getBPP() const461     uchar Image::getBPP() const
462     {
463         return mPixelSize * 8;
464     }
465 
466     //-----------------------------------------------------------------------------
getHasAlpha(void) const467     bool Image::getHasAlpha(void) const
468     {
469         return PixelUtil::getFlags(mFormat) & PFF_HASALPHA;
470     }
471     //-----------------------------------------------------------------------------
applyGamma(uchar * buffer,Real gamma,size_t size,uchar bpp)472     void Image::applyGamma( uchar *buffer, Real gamma, size_t size, uchar bpp )
473     {
474         if( gamma == 1.0f )
475             return;
476 
477         OgreAssert( bpp == 24 || bpp == 32, "only 24/32-bit supported");
478 
479         uint stride = bpp >> 3;
480 
481         uchar gammaramp[256];
482         const Real exponent = 1.0f / gamma;
483         for(int i = 0; i < 256; i++) {
484             gammaramp[i] = static_cast<uchar>(Math::Pow(i/255.0f, exponent)*255+0.5f);
485         }
486 
487         for( size_t i = 0, j = size / stride; i < j; i++, buffer += stride )
488         {
489             buffer[0] = gammaramp[buffer[0]];
490             buffer[1] = gammaramp[buffer[1]];
491             buffer[2] = gammaramp[buffer[2]];
492         }
493     }
494     //-----------------------------------------------------------------------------
resize(ushort width,ushort height,Filter filter)495     void Image::resize(ushort width, ushort height, Filter filter)
496     {
497         OgreAssert(mAutoDelete, "resizing dynamic images is not supported");
498         assert(mDepth == 1);
499 
500         // reassign buffer to temp image, make sure auto-delete is true
501         Image temp;
502         temp.loadDynamicImage(mBuffer, mWidth, mHeight, 1, mFormat, true);
503         // do not delete[] mBuffer!  temp will destroy it
504 
505         // set new dimensions, allocate new buffer
506         mWidth = width;
507         mHeight = height;
508         mBufSize = PixelUtil::getMemorySize(mWidth, mHeight, 1, mFormat);
509         mBuffer = OGRE_ALLOC_T(uchar, mBufSize, MEMCATEGORY_GENERAL);
510         mNumMipmaps = 0; // Loses precomputed mipmaps
511 
512         // scale the image from temp into our resized buffer
513         Image::scale(temp.getPixelBox(), getPixelBox(), filter);
514     }
515     //-----------------------------------------------------------------------
scale(const PixelBox & src,const PixelBox & scaled,Filter filter)516     void Image::scale(const PixelBox &src, const PixelBox &scaled, Filter filter)
517     {
518         assert(PixelUtil::isAccessible(src.format));
519         assert(PixelUtil::isAccessible(scaled.format));
520         MemoryDataStreamPtr buf; // For auto-delete
521         PixelBox temp;
522         switch (filter)
523         {
524         default:
525         case FILTER_NEAREST:
526             if(src.format == scaled.format)
527             {
528                 // No intermediate buffer needed
529                 temp = scaled;
530             }
531             else
532             {
533                 // Allocate temporary buffer of destination size in source format
534                 temp = PixelBox(scaled.getWidth(), scaled.getHeight(), scaled.getDepth(), src.format);
535                 buf.reset(OGRE_NEW MemoryDataStream(temp.getConsecutiveSize()));
536                 temp.data = buf->getPtr();
537             }
538             // super-optimized: no conversion
539             switch (PixelUtil::getNumElemBytes(src.format))
540             {
541             case 1: NearestResampler<1>::scale(src, temp); break;
542             case 2: NearestResampler<2>::scale(src, temp); break;
543             case 3: NearestResampler<3>::scale(src, temp); break;
544             case 4: NearestResampler<4>::scale(src, temp); break;
545             case 6: NearestResampler<6>::scale(src, temp); break;
546             case 8: NearestResampler<8>::scale(src, temp); break;
547             case 12: NearestResampler<12>::scale(src, temp); break;
548             case 16: NearestResampler<16>::scale(src, temp); break;
549             default:
550                 // never reached
551                 assert(false);
552             }
553             if(temp.data != scaled.data)
554             {
555                 // Blit temp buffer
556                 PixelUtil::bulkPixelConversion(temp, scaled);
557             }
558             break;
559 
560         case FILTER_LINEAR:
561         case FILTER_BILINEAR:
562             switch (src.format)
563             {
564             case PF_L8: case PF_A8: case PF_BYTE_LA:
565             case PF_R8G8B8: case PF_B8G8R8:
566             case PF_R8G8B8A8: case PF_B8G8R8A8:
567             case PF_A8B8G8R8: case PF_A8R8G8B8:
568             case PF_X8B8G8R8: case PF_X8R8G8B8:
569                 if(src.format == scaled.format)
570                 {
571                     // No intermediate buffer needed
572                     temp = scaled;
573                 }
574                 else
575                 {
576                     // Allocate temp buffer of destination size in source format
577                     temp = PixelBox(scaled.getWidth(), scaled.getHeight(), scaled.getDepth(), src.format);
578                     buf.reset(OGRE_NEW MemoryDataStream(temp.getConsecutiveSize()));
579                     temp.data = buf->getPtr();
580                 }
581                 // super-optimized: byte-oriented math, no conversion
582                 switch (PixelUtil::getNumElemBytes(src.format))
583                 {
584                 case 1: LinearResampler_Byte<1>::scale(src, temp); break;
585                 case 2: LinearResampler_Byte<2>::scale(src, temp); break;
586                 case 3: LinearResampler_Byte<3>::scale(src, temp); break;
587                 case 4: LinearResampler_Byte<4>::scale(src, temp); break;
588                 default:
589                     // never reached
590                     assert(false);
591                 }
592                 if(temp.data != scaled.data)
593                 {
594                     // Blit temp buffer
595                     PixelUtil::bulkPixelConversion(temp, scaled);
596                 }
597                 break;
598             case PF_FLOAT32_RGB:
599             case PF_FLOAT32_RGBA:
600                 if (scaled.format == PF_FLOAT32_RGB || scaled.format == PF_FLOAT32_RGBA)
601                 {
602                     // float32 to float32, avoid unpack/repack overhead
603                     LinearResampler_Float32::scale(src, scaled);
604                     break;
605                 }
606                 // else, fall through
607             default:
608                 // non-optimized: floating-point math, performs conversion but always works
609                 LinearResampler::scale(src, scaled);
610             }
611             break;
612         }
613     }
614 
615     //-----------------------------------------------------------------------------
616 
getColourAt(size_t x,size_t y,size_t z) const617     ColourValue Image::getColourAt(size_t x, size_t y, size_t z) const
618     {
619         ColourValue rval;
620         PixelUtil::unpackColour(&rval, mFormat, &mBuffer[mPixelSize * (z * mWidth * mHeight + mWidth * y + x)]);
621         return rval;
622     }
623 
624     //-----------------------------------------------------------------------------
625 
setColourAt(ColourValue const & cv,size_t x,size_t y,size_t z)626     void Image::setColourAt(ColourValue const &cv, size_t x, size_t y, size_t z)
627     {
628         size_t pixelSize = PixelUtil::getNumElemBytes(getFormat());
629         PixelUtil::packColour(cv, getFormat(), &(getData())[pixelSize * (z * getWidth() * getHeight() + y * getWidth() + x)]);
630     }
631 
632     //-----------------------------------------------------------------------------
633 
getPixelBox(size_t face,size_t mipmap) const634     PixelBox Image::getPixelBox(size_t face, size_t mipmap) const
635     {
636         // Image data is arranged as:
637         // face 0, top level (mip 0)
638         // face 0, mip 1
639         // face 0, mip 2
640         // face 1, top level (mip 0)
641         // face 1, mip 1
642         // face 1, mip 2
643         // etc
644         if(mipmap > getNumMipmaps())
645             OGRE_EXCEPT( Exception::ERR_NOT_IMPLEMENTED,
646             "Mipmap index out of range",
647             "Image::getPixelBox" ) ;
648         if(face >= getNumFaces())
649             OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Face index out of range",
650             "Image::getPixelBox");
651         // Calculate mipmap offset and size
652         uint8 *offset = mBuffer;
653         // Base offset is number of full faces
654         uint32 width = getWidth(), height=getHeight(), depth=getDepth();
655         size_t numMips = getNumMipmaps();
656 
657         // Figure out the offsets
658         size_t fullFaceSize = 0;
659         size_t finalFaceSize = 0;
660         uint32 finalWidth = 0, finalHeight = 0, finalDepth = 0;
661         for(size_t mip=0; mip <= numMips; ++mip)
662         {
663             if (mip == mipmap)
664             {
665                 finalFaceSize = fullFaceSize;
666                 finalWidth = width;
667                 finalHeight = height;
668                 finalDepth = depth;
669             }
670             fullFaceSize += PixelUtil::getMemorySize(width, height, depth, getFormat());
671 
672             /// Half size in each dimension
673             if(width!=1) width /= 2;
674             if(height!=1) height /= 2;
675             if(depth!=1) depth /= 2;
676         }
677         // Advance pointer by number of full faces, plus mip offset into
678         offset += face * fullFaceSize;
679         offset += finalFaceSize;
680         // Return subface as pixelbox
681         PixelBox src(finalWidth, finalHeight, finalDepth, getFormat(), offset);
682         return src;
683     }
684     //-----------------------------------------------------------------------------
calculateSize(size_t mipmaps,size_t faces,uint32 width,uint32 height,uint32 depth,PixelFormat format)685     size_t Image::calculateSize(size_t mipmaps, size_t faces, uint32 width, uint32 height, uint32 depth,
686         PixelFormat format)
687     {
688         size_t size = 0;
689         for(size_t mip=0; mip<=mipmaps; ++mip)
690         {
691             size += PixelUtil::getMemorySize(width, height, depth, format)*faces;
692             if(width!=1) width /= 2;
693             if(height!=1) height /= 2;
694             if(depth!=1) depth /= 2;
695         }
696         return size;
697     }
698     //---------------------------------------------------------------------
loadTwoImagesAsRGBA(const String & rgbFilename,const String & alphaFilename,const String & groupName,PixelFormat fmt)699     Image & Image::loadTwoImagesAsRGBA(const String& rgbFilename, const String& alphaFilename,
700         const String& groupName, PixelFormat fmt)
701     {
702         Image rgb, alpha;
703 
704         rgb.load(rgbFilename, groupName);
705         alpha.load(alphaFilename, groupName);
706 
707         return combineTwoImagesAsRGBA(rgb, alpha, fmt);
708 
709     }
710     //---------------------------------------------------------------------
loadTwoImagesAsRGBA(const DataStreamPtr & rgbStream,const DataStreamPtr & alphaStream,PixelFormat fmt,const String & rgbType,const String & alphaType)711     Image& Image::loadTwoImagesAsRGBA(const DataStreamPtr& rgbStream,
712                                       const DataStreamPtr& alphaStream, PixelFormat fmt,
713                                       const String& rgbType, const String& alphaType)
714     {
715         Image rgb, alpha;
716 
717         rgb.load(rgbStream, rgbType);
718         alpha.load(alphaStream, alphaType);
719 
720         return combineTwoImagesAsRGBA(rgb, alpha, fmt);
721 
722     }
723     //---------------------------------------------------------------------
combineTwoImagesAsRGBA(const Image & rgb,const Image & alpha,PixelFormat fmt)724     Image & Image::combineTwoImagesAsRGBA(const Image& rgb, const Image& alpha, PixelFormat fmt)
725     {
726         // the images should be the same size, have the same number of mipmaps
727         if (rgb.getWidth() != alpha.getWidth() ||
728             rgb.getHeight() != alpha.getHeight() ||
729             rgb.getDepth() != alpha.getDepth())
730         {
731             OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
732                 "Images must be the same dimensions", "Image::combineTwoImagesAsRGBA");
733         }
734         if (rgb.getNumMipmaps() != alpha.getNumMipmaps() ||
735             rgb.getNumFaces() != alpha.getNumFaces())
736         {
737             OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
738                 "Images must have the same number of surfaces (faces & mipmaps)",
739                 "Image::combineTwoImagesAsRGBA");
740         }
741         // Format check
742         if (PixelUtil::getComponentCount(fmt) != 4)
743         {
744             OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
745                 "Target format must have 4 components",
746                 "Image::combineTwoImagesAsRGBA");
747         }
748         if (PixelUtil::isCompressed(fmt) || PixelUtil::isCompressed(rgb.getFormat())
749             || PixelUtil::isCompressed(alpha.getFormat()))
750         {
751             OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
752                 "Compressed formats are not supported in this method",
753                 "Image::combineTwoImagesAsRGBA");
754         }
755 
756         freeMemory();
757 
758         mWidth = rgb.getWidth();
759         mHeight = rgb.getHeight();
760         mDepth = rgb.getDepth();
761         mFormat = fmt;
762         mNumMipmaps = rgb.getNumMipmaps();
763         size_t numFaces = rgb.getNumFaces();
764 
765         // Set flags
766         mFlags = 0;
767         if (mDepth != 1)
768             mFlags |= IF_3D_TEXTURE;
769         if(numFaces == 6)
770             mFlags |= IF_CUBEMAP;
771 
772         mBufSize = calculateSize(mNumMipmaps, numFaces, mWidth, mHeight, mDepth, mFormat);
773 
774         mPixelSize = static_cast<uchar>(PixelUtil::getNumElemBytes( mFormat ));
775 
776         mBuffer = static_cast<uchar*>(OGRE_MALLOC(mBufSize, MEMCATEGORY_GENERAL));
777 
778         // make sure we delete
779         mAutoDelete = true;
780 
781 
782         for (size_t face = 0; face < numFaces; ++face)
783         {
784             for (uint8 mip = 0; mip <= mNumMipmaps; ++mip)
785             {
786                 // convert the RGB first
787                 PixelBox srcRGB = rgb.getPixelBox(face, mip);
788                 PixelBox dst = getPixelBox(face, mip);
789                 PixelUtil::bulkPixelConversion(srcRGB, dst);
790 
791                 // now selectively add the alpha
792                 PixelBox srcAlpha = alpha.getPixelBox(face, mip);
793                 uchar* psrcAlpha = srcAlpha.data;
794                 uchar* pdst = dst.data;
795                 for (size_t d = 0; d < mDepth; ++d)
796                 {
797                     for (size_t y = 0; y < mHeight; ++y)
798                     {
799                         for (size_t x = 0; x < mWidth; ++x)
800                         {
801                             ColourValue colRGBA, colA;
802                             // read RGB back from dest to save having another pointer
803                             PixelUtil::unpackColour(&colRGBA, mFormat, pdst);
804                             PixelUtil::unpackColour(&colA, alpha.getFormat(), psrcAlpha);
805 
806                             // combine RGB from alpha source texture
807                             colRGBA.a = (colA.r + colA.g + colA.b) / 3.0f;
808 
809                             PixelUtil::packColour(colRGBA, mFormat, pdst);
810 
811                             psrcAlpha += PixelUtil::getNumElemBytes(alpha.getFormat());
812                             pdst += PixelUtil::getNumElemBytes(mFormat);
813 
814                         }
815                     }
816                 }
817 
818 
819             }
820         }
821 
822         return *this;
823 
824     }
825     //---------------------------------------------------------------------
826 
827 
828 }
829