2016-01-01 15:04:45 +00:00
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
# include "nel/misc/file.h"
# include "nel/misc/bitmap.h"
# include "nel/misc/path.h"
# include "nel/misc/debug.h"
# include <iostream>
void writeInstructions ( )
{
std : : cout < < " Syntax: textures_optimizer [-a] [-g] <input> " < < std : : endl ;
std : : cout < < std : : endl ;
std : : cout < < " Try to optimize TGA or PNG textures by removing useless alpha channel or converting a RGB with black and white values to grayscale " < < std : : endl ;
std : : cout < < " By default, it only make checks and display if texture can optimized or not " < < std : : endl ;
std : : cout < < std : : endl ;
std : : cout < < " with " < < std : : endl ;
2016-01-05 11:57:26 +00:00
std : : cout < < " -a : Remove alpha channel if useless (255) " < < std : : endl ;
2016-01-01 15:04:45 +00:00
std : : cout < < " -g : Convert to grayscale if all pixels are gray " < < std : : endl ;
2016-01-05 11:57:26 +00:00
std : : cout < < " -t : Apply texture optimizations (same as -a -g) " < < std : : endl ;
std : : cout < < " -m : Apply mask optimizations (convert to grayscale using red value and remove alpha) " < < std : : endl ;
2016-01-01 15:04:45 +00:00
std : : cout < < std : : endl ;
std : : cout < < " -h or -? for this help " < < std : : endl ;
std : : cout < < std : : endl ;
}
bool FixAlpha = false ;
bool FixGrayscale = false ;
2016-01-05 11:57:26 +00:00
bool TextureOptimizations = false ;
bool MaskOptimizations = false ;
2016-01-01 15:04:45 +00:00
std : : vector < std : : string > InputFilenames ;
bool parseOptions ( int argc , char * * argv )
{
// process each argument
for ( sint i = 1 ; i < argc ; + + i )
{
std : : string option = argv [ i ] ;
if ( option . length ( ) > 0 )
{
bool isOption = option [ 0 ] = = ' - ' ;
# ifdef NL_OS_WINDOWS
// authorize / for options only under Windows,
// because under Linux it could be a full path
if ( ! isOption ) isOption = ( option [ 0 ] = = ' / ' ) ;
# endif
// Option
if ( isOption )
{
// remove option prefix
option = option . substr ( 1 ) ;
// Fix alpha
if ( option = = " a " )
{
FixAlpha = true ;
}
// Fix grayscale
else if ( option = = " g " )
{
FixGrayscale = true ;
}
2016-01-05 11:57:26 +00:00
// Texture optimizations
else if ( option = = " t " )
{
TextureOptimizations = true ;
FixAlpha = true ;
FixGrayscale = true ;
}
// Mask optimizations
else if ( option = = " m " )
{
MaskOptimizations = true ;
}
2016-01-01 15:04:45 +00:00
else if ( option = = " h " | | option = = " ? " )
{
return false ;
}
else
{
nlwarning ( " Unknown option -%s " , option . c_str ( ) ) ;
return false ;
}
}
// Filename
else
{
std : : string ext = NLMISC : : toLower ( NLMISC : : CFile : : getExtension ( option ) ) ;
if ( ext = = " png " | | ext = = " tga " )
{
InputFilenames . push_back ( option ) ;
}
else
{
nlwarning ( " Only PNG and TGA files supported, %s won't be processed " , option . c_str ( ) ) ;
}
}
}
}
return ! InputFilenames . empty ( ) ;
}
# include "nel/misc/system_utils.h"
int main ( int argc , char * * argv )
{
NLMISC : : CApplicationContext applicationContext ;
if ( ! parseOptions ( argc , argv ) )
{
writeInstructions ( ) ;
return 0 ;
}
for ( uint i = 0 ; i < InputFilenames . size ( ) ; + + i )
{
std : : string ext = NLMISC : : toLower ( NLMISC : : CFile : : getExtension ( InputFilenames [ i ] ) ) ;
NLMISC : : CIFile input ;
if ( ! input . open ( InputFilenames [ i ] ) )
{
std : : cerr < < " Unable to open " < < InputFilenames [ i ] < < std : : endl ;
return 1 ;
}
NLMISC : : CBitmap bitmap ;
2016-01-02 19:29:24 +00:00
// all 8 bits textures are grayscale and not alpha
bitmap . loadGrayscaleAsAlpha ( false ) ;
2016-01-01 15:04:45 +00:00
uint8 depth = bitmap . load ( input ) ;
// don't need file so close it
input . close ( ) ;
if ( depth = = 0 )
{
std : : cerr < < " Unable to decode " < < InputFilenames [ i ] < < std : : endl ;
return 1 ;
}
bool modified = false ;
bool hasAlpha = false ;
bool isGrayscale = false ;
if ( bitmap . getPixelFormat ( ) = = NLMISC : : CBitmap : : RGBA & & depth = = 32 )
{
hasAlpha = true ;
}
else if ( bitmap . getPixelFormat ( ) = = NLMISC : : CBitmap : : AlphaLuminance )
{
hasAlpha = true ;
isGrayscale = true ;
}
else if ( bitmap . getPixelFormat ( ) = = NLMISC : : CBitmap : : Luminance )
{
isGrayscale = true ;
}
else if ( bitmap . getPixelFormat ( ) = = NLMISC : : CBitmap : : Alpha )
{
hasAlpha = true ;
isGrayscale = true ;
}
2016-01-05 11:57:26 +00:00
if ( MaskOptimizations & & ( ! isGrayscale | | hasAlpha ) )
2016-01-01 15:04:45 +00:00
{
2016-01-05 11:57:26 +00:00
std : : cout < < InputFilenames [ i ] < < " (mask with wrong format) " < < std : : endl ;
2016-01-01 15:04:45 +00:00
2016-01-05 11:57:26 +00:00
if ( ! isGrayscale )
2016-01-01 15:04:45 +00:00
{
2016-01-05 11:57:26 +00:00
// get a pointer on original RGBA data
uint32 size = bitmap . getPixels ( ) . size ( ) ;
uint32 * data = ( uint32 * ) bitmap . getPixels ( ) . getPtr ( ) ;
uint32 * endData = ( uint32 * ) ( ( uint8 * ) data + size ) ;
NLMISC : : CRGBA * color = NULL ;
// process all pixels
while ( data < endData )
2016-01-01 15:04:45 +00:00
{
2016-01-05 11:57:26 +00:00
color = ( NLMISC : : CRGBA * ) data ;
2016-01-01 15:04:45 +00:00
2016-01-05 11:57:26 +00:00
// copy red value to green and blue,
// because only red is used for mask
color - > B = color - > G = color - > R ;
// make opaque
color - > A = 255 ;
+ + data ;
}
2016-01-01 15:04:45 +00:00
}
2016-01-05 11:57:26 +00:00
// already in grayscale, just remove alpha
bitmap . convertToType ( NLMISC : : CBitmap : : Luminance ) ;
2016-01-01 15:04:45 +00:00
2016-01-05 11:57:26 +00:00
isGrayscale = true ;
hasAlpha = false ;
modified = true ;
}
else
2016-01-01 15:04:45 +00:00
{
2016-01-05 11:57:26 +00:00
if ( ! isGrayscale & & bitmap . isGrayscale ( ) )
{
std : : cout < < InputFilenames [ i ] < < " (grayscale image with RGB colors) " < < std : : endl ;
if ( FixGrayscale )
{
if ( ! bitmap . convertToType ( hasAlpha ? NLMISC : : CBitmap : : AlphaLuminance : NLMISC : : CBitmap : : Luminance ) )
{
std : : cerr < < " Unable to convert to Luminance " < < std : : endl ;
return 1 ;
}
isGrayscale = true ;
modified = true ;
}
}
uint8 alpha = 0 ;
if ( hasAlpha & & bitmap . isAlphaUniform ( & alpha ) )
{
std : : cout < < InputFilenames [ i ] < < " (image with uniform alpha channel " < < ( sint ) alpha < < " ) " < < std : : endl ;
2016-01-01 15:04:45 +00:00
2016-01-05 11:54:47 +00:00
if ( FixAlpha & & alpha = = 255 )
{
bitmap . makeOpaque ( ) ;
2016-01-01 15:04:45 +00:00
2016-01-05 11:57:26 +00:00
hasAlpha = false ;
modified = true ;
}
2016-01-01 15:04:45 +00:00
}
}
if ( ! modified ) continue ;
NLMISC : : COFile output ;
if ( ! output . open ( InputFilenames [ i ] ) )
{
std : : cerr < < " Unable to open " < < std : : endl ;
return 1 ;
}
uint32 newDepth = isGrayscale ? 8 : 24 ;
if ( hasAlpha ) newDepth + = 8 ;
bool res = false ;
if ( ext = = " png " )
{
res = bitmap . writePNG ( output , newDepth ) ;
}
else if ( ext = = " tga " )
{
res = bitmap . writePNG ( output , newDepth ) ;
}
if ( ! res )
{
std : : cerr < < " Unable to encode " < < std : : endl ;
return 1 ;
}
}
return 0 ;
}