diff --git a/code/nel/tools/3d/CMakeLists.txt b/code/nel/tools/3d/CMakeLists.txt
index 55481097b..e446162e7 100644
--- a/code/nel/tools/3d/CMakeLists.txt
+++ b/code/nel/tools/3d/CMakeLists.txt
@@ -35,6 +35,7 @@ IF(WITH_NEL_TOOLS)
SUBDIRS(
build_interface
get_neighbors
+ textures_optimizer
tga_cut
tga_resize)
ENDIF()
diff --git a/code/nel/tools/3d/textures_optimizer/CMakeLists.txt b/code/nel/tools/3d/textures_optimizer/CMakeLists.txt
new file mode 100644
index 000000000..469592ec4
--- /dev/null
+++ b/code/nel/tools/3d/textures_optimizer/CMakeLists.txt
@@ -0,0 +1,9 @@
+FILE(GLOB SRC *.cpp *.h)
+
+ADD_EXECUTABLE(textures_optimizer ${SRC})
+
+TARGET_LINK_LIBRARIES(textures_optimizer nelmisc)
+NL_DEFAULT_PROPS(textures_optimizer "NeL, Tools, 3D: Textures optimizer")
+NL_ADD_RUNTIME_FLAGS(textures_optimizer)
+
+INSTALL(TARGETS textures_optimizer RUNTIME DESTINATION ${NL_BIN_PREFIX} COMPONENT tools3d)
diff --git a/code/nel/tools/3d/textures_optimizer/main.cpp b/code/nel/tools/3d/textures_optimizer/main.cpp
new file mode 100644
index 000000000..df0039b0b
--- /dev/null
+++ b/code/nel/tools/3d/textures_optimizer/main.cpp
@@ -0,0 +1,233 @@
+// NeL - MMORPG Framework
+// 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 .
+
+#include "nel/misc/file.h"
+#include "nel/misc/bitmap.h"
+#include "nel/misc/path.h"
+#include "nel/misc/debug.h"
+
+#include
+
+void writeInstructions()
+{
+ std::cout << "Syntax: textures_optimizer [-a] [-g] " << 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;
+ std::cout << "-a : Remove alpha channel if useless" << std::endl;
+ std::cout << "-g : Convert to grayscale if all pixels are gray" << std::endl;
+ std::cout << std::endl;
+ std::cout << "-h or -? for this help" << std::endl;
+ std::cout << std::endl;
+}
+
+bool FixAlpha = false;
+bool FixGrayscale = false;
+
+std::vector 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;
+ }
+ 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;
+
+ 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;
+ }
+
+ 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 " << alpha << ")" << std::endl;
+
+ if (FixAlpha && (alpha == 0 || alpha == 255))
+ {
+ bitmap.makeOpaque();
+
+ hasAlpha = false;
+ modified = true;
+ }
+ }
+
+ 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;
+}