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; +}