1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "nel/misc/file.h"
21 #include "nel/misc/bitmap.h"
22 #include "nel/misc/path.h"
23 #include "nel/misc/debug.h"
27 void writeInstructions()
29 std::cout
<< "Syntax: textures_optimizer [-a] [-g] <input>" << std::endl
;
30 std::cout
<< std::endl
;
31 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
;
32 std::cout
<< " By default, it only make checks and display if texture can optimized or not" << std::endl
;
33 std::cout
<< std::endl
;
34 std::cout
<< "with" << std::endl
;
35 std::cout
<< "-a : Remove alpha channel if useless (255)" << std::endl
;
36 std::cout
<< "-g : Convert to grayscale if all pixels are gray" << std::endl
;
37 std::cout
<< "-t : Apply texture optimizations (same as -a -g)" << std::endl
;
38 std::cout
<< "-m : Apply mask optimizations (convert to grayscale using red value and remove alpha)" << std::endl
;
39 std::cout
<< std::endl
;
40 std::cout
<< "-h or -? for this help" << std::endl
;
41 std::cout
<< std::endl
;
44 bool FixAlpha
= false;
45 bool FixGrayscale
= false;
46 bool TextureOptimizations
= false;
47 bool MaskOptimizations
= false;
49 std::vector
<std::string
> InputFilenames
;
51 bool parseOptions(int argc
, char **argv
)
53 // process each argument
54 for(sint i
= 1; i
< argc
; ++i
)
56 std::string option
= argv
[i
];
58 if (option
.length() > 0)
60 bool isOption
= option
[0] == '-';
63 // authorize / for options only under Windows,
64 // because under Linux it could be a full path
65 if (!isOption
) isOption
= (option
[0] == '/');
71 // remove option prefix
72 option
= option
.substr(1);
80 else if (option
== "g")
84 // Texture optimizations
85 else if (option
== "t")
87 TextureOptimizations
= true;
92 else if (option
== "m")
94 MaskOptimizations
= true;
96 else if (option
== "h" || option
== "?")
102 nlwarning("Unknown option -%s", option
.c_str());
110 std::string ext
= NLMISC::toLowerAscii(NLMISC::CFile::getExtension(option
));
112 if (ext
== "png" || ext
== "tga")
114 InputFilenames
.push_back(option
);
118 nlwarning("Only PNG and TGA files supported, %s won't be processed", option
.c_str());
124 return !InputFilenames
.empty();
127 #include "nel/misc/system_utils.h"
129 int main(int argc
, char **argv
)
131 NLMISC::CApplicationContext applicationContext
;
133 if (!parseOptions(argc
, argv
))
139 for(uint i
= 0; i
< InputFilenames
.size(); ++i
)
141 std::string ext
= NLMISC::toLowerAscii(NLMISC::CFile::getExtension(InputFilenames
[i
]));
143 NLMISC::CIFile input
;
145 if (!input
.open(InputFilenames
[i
]))
147 std::cerr
<< "Unable to open " << InputFilenames
[i
] << std::endl
;
151 NLMISC::CBitmap bitmap
;
153 // all 8 bits textures are grayscale and not alpha
154 bitmap
.loadGrayscaleAsAlpha(false);
156 uint8 depth
= bitmap
.load(input
);
158 // don't need file so close it
163 std::cerr
<< "Unable to decode " << InputFilenames
[i
] << std::endl
;
167 bool modified
= false;
168 bool hasAlpha
= false;
169 bool isGrayscale
= false;
171 if (bitmap
.getPixelFormat() == NLMISC::CBitmap::RGBA
&& depth
== 32)
175 else if (bitmap
.getPixelFormat() == NLMISC::CBitmap::AlphaLuminance
)
180 else if (bitmap
.getPixelFormat() == NLMISC::CBitmap::Luminance
)
184 else if (bitmap
.getPixelFormat() == NLMISC::CBitmap::Alpha
)
190 if (MaskOptimizations
&& (!isGrayscale
|| hasAlpha
))
192 std::cout
<< InputFilenames
[i
] << " (mask with wrong format)" << std::endl
;
196 // get a pointer on original RGBA data
197 uint32 size
= bitmap
.getPixels().size();
198 uint32
*data
= (uint32
*)bitmap
.getPixels().getPtr();
199 uint32
*endData
= (uint32
*)((uint8
*)data
+ size
);
201 NLMISC::CRGBA
*color
= NULL
;
203 // process all pixels
204 while(data
< endData
)
206 color
= (NLMISC::CRGBA
*)data
;
208 // copy red value to green and blue,
209 // because only red is used for mask
210 color
->B
= color
->G
= color
->R
;
219 // already in grayscale, just remove alpha
220 bitmap
.convertToType(NLMISC::CBitmap::Luminance
);
228 if (!isGrayscale
&& bitmap
.isGrayscale())
230 std::cout
<< InputFilenames
[i
] << " (grayscale image with RGB colors)" << std::endl
;
234 if (!bitmap
.convertToType(hasAlpha
? NLMISC::CBitmap::AlphaLuminance
:NLMISC::CBitmap::Luminance
))
236 std::cerr
<< "Unable to convert to Luminance" << std::endl
;
247 if (hasAlpha
&& bitmap
.isAlphaUniform(&alpha
))
249 std::cout
<< InputFilenames
[i
] << " (image with uniform alpha channel " << (sint
)alpha
<< ")" << std::endl
;
251 if (FixAlpha
&& alpha
== 255)
261 if (!modified
) continue;
263 NLMISC::COFile output
;
265 if (!output
.open(InputFilenames
[i
]))
267 std::cerr
<< "Unable to open" << std::endl
;
271 uint32 newDepth
= isGrayscale
? 8:24;
273 if (hasAlpha
) newDepth
+= 8;
279 res
= bitmap
.writePNG(output
, newDepth
);
281 else if (ext
== "tga")
283 res
= bitmap
.writePNG(output
, newDepth
);
288 std::cerr
<< "Unable to encode" << std::endl
;