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