Fix game:addSpawnShapesByZone
[ryzomcore.git] / nel / tools / 3d / panoply_maker / panoply_maker.cpp
blob46081a69d5c318c3e99cdaa5dad8a54768f14462
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2010-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
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/>.
21 #include "color_modifier.h"
22 #include "color_mask.h"
23 #include "hls_bank_texture_info.h"
24 #define HAS_INFO_GENERATION 0
25 #if HAS_INFO_GENERATION
26 #include "info_color_generation.h"
27 #include "info_mask_generation.h"
28 #endif
30 #include <nel/misc/types_nl.h>
31 #include <nel/misc/config_file.h>
32 #include <nel/misc/path.h>
33 #include <nel/misc/file.h>
34 #include <nel/misc/bitmap.h>
35 #include <nel/misc/debug.h>
37 #include <time.h>
39 using namespace NLMISC;
40 using namespace std;
43 string DivideBy2Dir= "/d4/";
44 //string HlsInfoDir= "hlsInfo/";
47 // ========================================================================================================
48 // This tool is for creating various colored texture from a base texture.
49 // Parts of a base texture can have hue, contrast, luminosity shifting etc.
50 // Each part is defined by a mask. The red component of it is considered as an alpha value (not the alpha, because it is faster to create a grey texture with photoshop..)
51 // The result is serialized in png or tga files.
52 // ========================================================================================================
53 // why this tool ? : it is useful to create various colored cloth and skin textures
54 // Not all hardware allow it to manage that at runtime (lack for palettized textures or pixel shaders...)
55 //=========================================================================================================
58 /// describes the building infos
59 struct CBuildInfo
61 std::string InputPath;
62 std::string OutputPath;
63 std::string HlsInfoPath;
64 std::string CachePath;
65 std::vector<std::string> BitmapExtensions; // the supported extension for bitmaps
66 std::string OutputFormat; // png or tga
67 std::string DefaultSeparator;
68 TColorMaskVect ColorMasks;
69 // how to shift right the size of the src Bitmap for the .hlsinfo
70 uint LowDefShift;
71 uint OptimizeTextures; // 0 = don't check, 1 = check
75 /// Temporary
76 static void validateCgiInfo();
77 static void validateGtmInfo();
78 /// Temporary
81 /** Build the infos we need from a config file
82 * It build a list of masks infos
84 static void BuildMasksFromConfigFile(NLMISC::CConfigFile &cf,
85 TColorMaskVect &colorMasks);
87 /// Build the colored versions
88 static void BuildColoredVersions(const CBuildInfo &bi);
90 ///
91 static void BuildColoredVersionForOneBitmap(const CBuildInfo &bi, const std::string &fileNameWithExtension, bool mustDivideBy2);
92 /** Check if building if reneeded by looking in the cache directory.
93 * If the texture is found in the cache it is just copied
95 static bool CheckIfNeedRebuildColoredVersionForOneBitmap(const CBuildInfo &bi, const std::string &fileNameWithExtension, bool mustDivideBy2);
99 /// replace slashes by the matching os value in a file name
100 static std::string replaceSlashes(const std::string &src)
102 std::string result = src;
103 for(uint k = 0; k < result.size(); ++k)
104 #ifdef NL_OS_WINDOWS
105 if (result[k] == '/') result[k] = '\\';
106 #else
107 if (result[k] == '\\') result[k] = '/';
108 #endif
109 return result;
114 ///=====================================================
115 int main(int argc, char* argv[])
117 // Filter addSearchPath
118 NLMISC::createDebug();
120 //"panoply.cfg" "gtm" "fyros"
122 #if HAS_INFO_GENERATION
123 if(!strcmp(argv[2], "gtm") || !strcmp(argv[2], "cgi"))
125 NLMISC::CConfigFile cf;
127 std::string _Path_Input_TexBases;
128 std::string _Path_Input_Masks;
129 std::string _Path_Output_MasksOptimized;
130 std::string _Path_Output_Gtm;
131 std::string _Path_Output_Cgi;
135 /// load the config file
136 cf.load(argv[1]);
138 /// look paths
141 NLMISC::CConfigFile::CVar &additionnal_paths = cf.getVar ("additionnal_paths");
142 for (uint k = 0; k < (uint) additionnal_paths.size(); ++k)
144 NLMISC::CPath::addSearchPath(NLMISC::CPath::standardizePath(additionnal_paths.asString(k)),true, false);
147 catch (const NLMISC::EUnknownVar &)
151 /// repertory of textures bases (no-colorized)
154 _Path_Input_TexBases = NLMISC::CPath::standardizePath(cf.getVar ("input_path_texbase").asString());
156 catch (const NLMISC::EUnknownVar &)
160 /// repertory of masks (original)
163 _Path_Input_Masks = NLMISC::CPath::standardizePath(cf.getVar ("input_path_mask").asString());
165 catch (const NLMISC::EUnknownVar &)
169 /// optimized masks output directory created
172 _Path_Output_MasksOptimized = NLMISC::CPath::standardizePath(cf.getVar ("output_path_mask_optimized").asString());
174 catch (const NLMISC::EUnknownVar &)
178 /// file of infos about colorization average for the client
181 _Path_Output_Cgi = NLMISC::CPath::standardizePath(cf.getVar ("output_path_cgi").asString());
183 catch (const NLMISC::EUnknownVar &)
187 /// file of infos about multiplexing texture for the client
190 _Path_Output_Gtm = NLMISC::CPath::standardizePath(cf.getVar ("output_path_gtm").asString());
192 catch (const NLMISC::EUnknownVar &)
197 catch (const std::exception &e)
199 nlerror("Panoply building failed: %s", e.what());
200 return -1;
203 /// oriented program
205 if( !strcmp(argv[2], "gtm")) /// masks optimized
207 CInfoMaskGeneration infoMaskGen(_Path_Input_TexBases,
208 _Path_Input_Masks,
209 _Path_Output_MasksOptimized,
210 _Path_Output_Gtm,
211 argv[3],
213 infoMaskGen.init();
214 infoMaskGen.process();
216 else if( !strcmp(argv[2], "cgi")) /// colorized information
218 CInfoColorGeneration infoColor(_Path_Input_TexBases,
219 _Path_Input_Masks,
220 _Path_Output_Cgi,
221 argv[3],
223 infoColor.init();
224 infoColor.process();
227 else
229 #endif
230 NLMISC::InfoLog->addNegativeFilter ("adding the path");
232 if (argc != 2)
234 nlinfo("Usage : %s [config_file name]", argv[0]);
235 return -1;
238 CBuildInfo bi;
240 /////////////////////////////////////////
241 // reads infos from the config files //
242 /////////////////////////////////////////
244 NLMISC::CConfigFile cf;
247 /// load the config file
248 cf.load(argv[1]);
250 /// colors masks
251 BuildMasksFromConfigFile(cf, bi.ColorMasks);
253 /// look paths
256 NLMISC::CConfigFile::CVar &additionnal_paths = cf.getVar ("additionnal_paths");
257 for (uint k = 0; k < (uint) additionnal_paths.size(); ++k)
259 NLMISC::CPath::addSearchPath(NLMISC::CPath::standardizePath(additionnal_paths.asString(k)));
262 catch (const NLMISC::EUnknownVar &)
266 /// input
269 bi.InputPath = NLMISC::CPath::standardizePath(cf.getVar ("input_path").asString());
271 catch (const NLMISC::EUnknownVar &)
275 /// output
278 bi.OutputPath = NLMISC::CPath::standardizePath(cf.getVar ("output_path").asString());
280 catch (const NLMISC::EUnknownVar &)
284 /// hls info path
287 bi.HlsInfoPath = NLMISC::CPath::standardizePath(cf.getVar("hls_info_path").asString());
289 catch (const NLMISC::EUnknownVar &)
291 bi.HlsInfoPath = "hlsInfo/";
294 /// output
297 bi.CachePath = NLMISC::CPath::standardizePath(cf.getVar ("cache_path").asString());
299 catch (const NLMISC::EUnknownVar &)
303 /// output format
306 bi.OutputFormat = "." + cf.getVar ("output_format").asString();
308 catch (const NLMISC::EUnknownVar &)
310 bi.OutputFormat = ".tga";
313 /// default ascii character for unused masks
316 bi.DefaultSeparator = cf.getVar ("default_separator").asString();
318 catch (const NLMISC::EUnknownVar &)
320 bi.DefaultSeparator = '_';
322 /// extension for bitmaps
325 NLMISC::CConfigFile::CVar &bitmap_extensions = cf.getVar ("bitmap_extensions");
326 for (uint k = 0; k < (uint) bitmap_extensions.size(); ++k)
328 std::string ext = "." + NLMISC::toLowerAscii(bitmap_extensions.asString(k));
329 if (std::find(bi.BitmapExtensions.begin(), bi.BitmapExtensions.end(), ext) == bi.BitmapExtensions.end())
331 bi.BitmapExtensions.push_back(ext);
335 catch (const NLMISC::EUnknownVar &)
337 bi.BitmapExtensions[0].resize(1);
338 bi.BitmapExtensions[0] = bi.OutputFormat;
343 bi.LowDefShift = cf.getVar ("low_def_shift").asInt();
345 catch (const NLMISC::EUnknownVar &)
347 // tranform 512*512 to 64*64 by default
348 bi.LowDefShift= 3;
353 bi.OptimizeTextures = cf.getVar ("optimize_textures").asInt();
355 catch (const NLMISC::EUnknownVar &)
357 // don't check files by default
358 bi.OptimizeTextures = 0;
361 catch (const std::exception &e)
363 nlerror("Panoply building failed: %s", e.what());
364 return -1;
367 ////////////////////////////////
368 // Build the colored versions //
369 ////////////////////////////////
372 BuildColoredVersions(bi);
374 catch (const std::exception &e)
376 nlerror("Something went wrong while building bitmap: %s", e.what());
377 return -1;
379 return 0;
380 #if HAS_INFO_GENERATION
382 #endif
385 ///======================================================
386 #if HAS_INFO_GENERATION
387 static void validateCgiInfo()
389 NLMISC::CIFile f;
392 vector<StrInfoTexColor> temp;
393 uint version;
397 f.open(CPath::lookup("info_color_texbase_fyros.cgi"));
399 f.serialCont(temp);
402 catch(const std::exception &e)
404 nlerror("Panoply building failed: %s", e.what());
407 uint16 a = temp.size();
409 f.close();
412 ///======================================================
414 static void validateGtmInfo()
418 #endif
419 ///======================================================
420 static void BuildMasksFromConfigFile(NLMISC::CConfigFile &cf,
421 TColorMaskVect &colorMasks)
424 /// get a list of the alpha mask extensions
425 NLMISC::CConfigFile::CVar &mask_extensions = cf.getVar ("mask_extensions");
426 colorMasks.resize(mask_extensions.size());
428 /// For each kind of mask, build a list of the color modifiers
429 for (uint k = 0; k < (uint) mask_extensions.size(); ++k)
431 colorMasks[k].MaskExt = mask_extensions.asString(k);
432 NLMISC::CConfigFile::CVar &luminosities = cf.getVar (colorMasks[k].MaskExt + "_luminosities");
433 NLMISC::CConfigFile::CVar &contrasts = cf.getVar (colorMasks[k].MaskExt + "_constrasts");
434 NLMISC::CConfigFile::CVar &hues = cf.getVar (colorMasks[k].MaskExt + "_hues");
435 NLMISC::CConfigFile::CVar &lightness = cf.getVar (colorMasks[k].MaskExt + "_lightness");
436 NLMISC::CConfigFile::CVar &saturation = cf.getVar (colorMasks[k].MaskExt + "_saturations");
437 NLMISC::CConfigFile::CVar &colorIDs = cf.getVar (colorMasks[k].MaskExt + "_color_id");
439 if (luminosities.size() != contrasts.size()
440 || luminosities.size() != hues.size()
441 || luminosities.size() != lightness.size()
442 || luminosities.size() != saturation.size()
443 || luminosities.size() != colorIDs.size()
446 throw NLMISC::Exception("All color descriptors must have the same number of arguments");
448 colorMasks[k].CMs.resize(luminosities.size());
449 for (uint l = 0; l < (uint) luminosities.size(); ++l)
451 CColorModifier &cm = colorMasks[k].CMs[l];
452 cm.Contrast = contrasts.asFloat(l);
453 cm.Luminosity = luminosities.asFloat(l);
454 cm.Hue = hues.asFloat(l);
455 cm.Lightness = lightness.asFloat(l);
456 cm.Saturation = saturation.asFloat(l);
458 cm.ColID = colorIDs.asString(l);
463 ///======================================================
464 static void BuildColoredVersions(const CBuildInfo &bi)
466 if (!NLMISC::CFile::isExists(bi.InputPath))
468 nlerror("Path not found: %s", bi.InputPath.c_str());
469 return;
471 for(uint sizeVersion= 0; sizeVersion<2; sizeVersion++)
473 std::vector<std::string> files;
474 if(sizeVersion==0)
475 // get the original (not to dvide) dir
476 NLMISC::CPath::getPathContent (bi.InputPath, false, false, true, files);
477 else
478 // get the dir content with texture that must be divided by 2.
479 NLMISC::CPath::getPathContent (bi.InputPath+DivideBy2Dir, false, false, true, files);
481 // For all files found
482 for (uint k = 0; k < files.size(); ++k)
484 for (uint l = 0; l < bi.BitmapExtensions.size(); ++l)
486 std::string fileExt = "." + NLMISC::toLowerAscii(NLMISC::CFile::getExtension(files[k]));
487 if (fileExt == bi.BitmapExtensions[l])
489 //nlwarning("Processing : %s ", files[k].c_str());
492 if (CheckIfNeedRebuildColoredVersionForOneBitmap(bi, NLMISC::CFile::getFilename(files[k]),
493 sizeVersion==1) )
495 BuildColoredVersionForOneBitmap(bi,
496 NLMISC::CFile::getFilename(files[k]),
497 sizeVersion==1);
499 else
501 //nlwarning(("No need to rebuild " + NLMISC::CFile::getFilename(files[k])).c_str());
504 catch (const std::exception &e)
506 nlerror("Processing of %s failed: %s", files[k].c_str(), e.what());
515 /// used to loop throiugh the process, avoiding unused masks
516 struct CLoopInfo
518 NLMISC::CBitmap Mask;
519 uint Counter;
520 uint MaskID;
524 ///======================================================
525 static bool CheckIfNeedRebuildColoredVersionForOneBitmap(const CBuildInfo &bi, const std::string &fileNameWithExtension,
526 bool mustDivideBy2)
528 if (bi.CachePath.empty()) return true;
529 uint32 srcDate = (uint32) NLMISC::CFile::getFileModificationDate(replaceSlashes(bi.InputPath + fileNameWithExtension));
530 static std::vector<CLoopInfo> masks;
531 /// check the needed masks
532 masks.clear();
534 std::string fileName = NLMISC::CFile::getFilenameWithoutExtension(fileNameWithExtension);
535 std::string fileExt = NLMISC::toLowerAscii(NLMISC::CFile::getExtension(fileNameWithExtension));
537 for (uint k = 0; k < bi.ColorMasks.size(); ++k)
539 std::string maskName = fileName + "_" + bi.ColorMasks[k].MaskExt + "." + fileExt;
540 std::string maskFileName = NLMISC::CPath::lookup(maskName,
541 false, false);
542 if (!maskFileName.empty()) // found the mask ?
544 CLoopInfo li;
545 li.Counter = 0;
546 li.MaskID = k;
548 if (NLMISC::CFile::fileExists(maskFileName))
550 srcDate = std::max(srcDate, (uint32) NLMISC::CFile::getFileModificationDate(replaceSlashes(maskFileName)));
551 masks.push_back(li);
556 // get hls info version that is in the cache. if not possible, must rebuild
557 std::string outputHLSInfo = bi.HlsInfoPath + fileName + ".hlsinfo";
558 std::string cacheHLSInfo = bi.CachePath + fileName + ".hlsinfo";
559 if (!NLMISC::CFile::fileExists(cacheHLSInfo.c_str()) )
560 return true;
561 else
563 // Must now if was moved beetween normal dir and d4/ dir.
564 CHLSBankTextureInfo hlsInfo;
565 // read .hlsInfo cache
566 CIFile f;
567 if(!f.open(cacheHLSInfo))
568 return true;
569 f.serial(hlsInfo);
570 f.close();
571 // check if same DividedBy2 Flag.
572 if(hlsInfo.DividedBy2!=mustDivideBy2)
573 return true;
575 // ok, can move the cache
576 if (!NLMISC::CFile::moveFile(outputHLSInfo, cacheHLSInfo))
578 nlerror("Couldn't move %s to %s", cacheHLSInfo.c_str(), outputHLSInfo.c_str());
579 return true;
584 /// check is each generated texture has the same date or is more recent
585 for(;;)
587 uint l;
588 std::string outputFileName = fileName;
590 /// build current tex name
591 for (l = 0; l < masks.size(); ++l)
593 uint maskID = masks[l].MaskID;
594 uint colorID = masks[l].Counter;
595 /// complete the file name
596 outputFileName += bi.DefaultSeparator + bi.ColorMasks[maskID].CMs[colorID].ColID;
599 // compare date
600 std::string searchName = replaceSlashes(bi.CachePath + outputFileName + bi.OutputFormat);
601 if ((uint32) NLMISC::CFile::getFileModificationDate(searchName) < srcDate)
603 return true; // not found or more old => need rebuild
606 // get version that is in the cache
607 std::string cacheDest = bi.OutputPath + outputFileName + bi.OutputFormat;
609 if (!NLMISC::CFile::moveFile(cacheDest, searchName))
611 nlerror("Couldn't move %s to %s", searchName.c_str(), cacheDest.c_str());
612 return true;
615 /// increment counters
616 for (l = 0; l < (uint) masks.size(); ++l)
618 ++ (masks[l].Counter);
620 /// check if we have done all colors for this mask
621 if (masks[l].Counter == bi.ColorMasks[masks[l].MaskID].CMs.size())
623 masks[l].Counter = 0;
625 else
627 break;
630 if (l == masks.size()) break; // all cases dones
632 return false; // nothing to rebuild
637 ///======================================================
638 static void BuildColoredVersionForOneBitmap(const CBuildInfo &bi, const std::string &fileNameWithExtension,
639 bool mustDivideBy2)
641 uint32 depth;
642 NLMISC::CBitmap srcBitmap;
643 NLMISC::CBitmap resultBitmap;
645 /// **** load the src bitmap
647 // where to load it.
648 string actualInputPath;
649 if(mustDivideBy2)
650 actualInputPath= bi.InputPath + DivideBy2Dir;
651 else
652 actualInputPath= bi.InputPath;
654 // load
655 std::string fullInputBitmapPath = actualInputPath + fileNameWithExtension;
657 NLMISC::CIFile is;
660 if (is.open(fullInputBitmapPath))
662 // 8 bits textures are grayscale
663 srcBitmap.loadGrayscaleAsAlpha(false);
665 depth = srcBitmap.load(is);
666 is.close();
668 if (depth == 0 || srcBitmap.getPixels().empty())
670 throw NLMISC::Exception("Failed to load bitmap");
673 // if bitmap is RGBA but has an alpha channel fully opaque (255),
674 // we can save it as RGB to optimize it
675 uint8 value = 0;
676 if (bi.OptimizeTextures > 0 && depth == 32 && srcBitmap.isAlphaUniform(&value) && value == 255)
678 nlwarning("Texture %s can be optimized, run textures_optimizer", fullInputBitmapPath.c_str());
681 if (srcBitmap.PixelFormat != NLMISC::CBitmap::RGBA)
683 srcBitmap.convertToType(NLMISC::CBitmap::RGBA);
686 else
688 nlerror("Unable to open %s. Processing next", fullInputBitmapPath.c_str());
689 return;
692 catch (const NLMISC::Exception &e)
694 nlerror("File or format error with %s (%s). Processing next...", fullInputBitmapPath.c_str(), e.what());
695 return;
699 /// **** Build and prepare build of the .hlsinfo to write.
700 CHLSBankTextureInfo hlsInfo;
701 CBitmap hlsInfoSrcBitmap;
702 hlsInfoSrcBitmap= srcBitmap;
703 // reduce size of the bitmap of LowDef shift
704 uint reduceShift= bi.LowDefShift;
705 if(reduceShift>0)
707 uint w= hlsInfoSrcBitmap.getWidth()>>reduceShift;
708 uint h= hlsInfoSrcBitmap.getHeight()>>reduceShift;
709 w= max(w, 1U);
710 h= max(h, 1U);
711 hlsInfoSrcBitmap.resample(w, h);
713 // Compress DXTC5 src bitmap
714 hlsInfo.SrcBitmap.build(hlsInfoSrcBitmap);
715 // Store info about if where in d4/ dir or not
716 hlsInfo.DividedBy2= mustDivideBy2;
719 /// **** check the needed masks
720 static std::vector<CLoopInfo> masks;
721 masks.clear();
723 std::string fileName = NLMISC::CFile::getFilenameWithoutExtension(fileNameWithExtension);
724 std::string fileExt = NLMISC::toLowerAscii(NLMISC::CFile::getExtension(fileNameWithExtension));
726 uint k;
727 for (k = 0; k < bi.ColorMasks.size(); ++k)
729 std::string maskName = fileName + "_" + bi.ColorMasks[k].MaskExt + "." + fileExt;
730 std::string maskFileName = NLMISC::CPath::lookup(maskName, false, false);
732 if (!maskFileName.empty()) // found the mask ?
734 CLoopInfo li;
735 li.Counter = 0;
736 li.MaskID = k;
738 /// try to load the bitmap
739 NLMISC::CIFile is;
743 if (is.open(maskFileName))
745 // masks are always opaque, if the mask is 8bits, it's in grayscale
746 li.Mask.loadGrayscaleAsAlpha(false);
748 uint8 maskDepth = li.Mask.load(is);
750 is.close();
752 if (maskDepth == 0 || li.Mask.getPixels().empty())
754 throw NLMISC::Exception("Failed to load mask");
757 // display a warning if checks enabled
758 if (li.Mask.getPixelFormat() == CBitmap::RGBA && bi.OptimizeTextures > 0 && !li.Mask.isGrayscale())
760 nlwarning("Mask %s is using colors, results may by incorrect! Run textures_optimizer to fix it.", maskFileName.c_str());
763 // convert image to real grayscale
764 if (li.Mask.PixelFormat != NLMISC::CBitmap::Luminance)
766 li.Mask.convertToType(NLMISC::CBitmap::Luminance);
769 /// make sure the mask has the same size
770 if (li.Mask.getWidth() != srcBitmap.getWidth()
771 || li.Mask.getHeight() != srcBitmap.getHeight())
773 throw NLMISC::Exception("Bitmap and mask do not have the same size");
776 masks.push_back(li);
778 else
780 nlerror("Unable to open %s. Processing next", maskFileName.c_str());
781 return;
784 catch (const std::exception &e)
786 nlerror("Error with %s: %s. Aborting this bitmap processing", maskFileName.c_str(), e.what());
787 return;
792 // **** Add the masks to the .hlsInfo
793 hlsInfo.Masks.resize(masks.size());
794 for (k = 0; k < masks.size(); ++k)
796 CLoopInfo &li= masks[k];
797 CBitmap tmp= li.Mask;
798 tmp.resample(hlsInfoSrcBitmap.getWidth(), hlsInfoSrcBitmap.getHeight());
799 hlsInfo.Masks[k].build(tmp);
803 // **** generate each texture
804 // NB : if there are no masks the texture just will be copied
805 for(;;)
807 resultBitmap = srcBitmap;
808 uint l;
809 std::string outputFileName = fileName;
811 // Add an instance entry to the hlsInfo
812 uint instId= (uint)hlsInfo.Instances.size();
813 hlsInfo.Instances.resize(instId+1);
814 CHLSBankTextureInfo::CTextureInstance &hlsTextInstance= hlsInfo.Instances[instId];
815 hlsTextInstance.Mods.resize(masks.size());
817 /// build current tex
818 for (l = 0; l < masks.size(); ++l)
820 uint maskID = masks[l].MaskID;
821 uint colorID = masks[l].Counter;
823 /// get the color modifier
824 const CColorModifier &cm = bi.ColorMasks[maskID].CMs[colorID];
826 /// apply the mask
827 float deltaHueApplied;
828 cm.convertBitmap(resultBitmap, resultBitmap, masks[l].Mask, deltaHueApplied);
830 /// save the setup in hlsInfo
831 hlsTextInstance.Mods[l].DHue= deltaHueApplied;
832 hlsTextInstance.Mods[l].DLum= cm.Lightness;
833 hlsTextInstance.Mods[l].DSat= cm.Saturation;
835 /// complete the file name
836 outputFileName += bi.DefaultSeparator + bi.ColorMasks[maskID].CMs[colorID].ColID;
839 // save good hlsInfo instance name
840 hlsTextInstance.Name = outputFileName + bi.OutputFormat;
842 nlinfo("Writing %s", outputFileName.c_str());
843 /// Save the result. We let propagate exceptions (if there's no more space disk it useless to continue...)
845 std::string fullOutputPath = bi.OutputPath + outputFileName + bi.OutputFormat;
849 NLMISC::COFile os;
850 if (os.open(fullOutputPath))
852 // divide by 2 when needed.
853 if(mustDivideBy2)
854 resultBitmap.resample( (resultBitmap.getWidth()+1)/2, (resultBitmap.getHeight()+1)/2 );
855 // write the file
856 if (bi.OutputFormat == ".png")
858 resultBitmap.writePNG(os, depth);
860 else
862 resultBitmap.writeTGA(os, depth);
865 else
867 nlerror("Couldn't open %s for writing", fullOutputPath.c_str());
870 catch(const NLMISC::EStream &e)
872 nlerror("Couldn't write %s: %s", fullOutputPath.c_str(), e.what());
877 /// increment counters
878 for (l = 0; l < (uint) masks.size(); ++l)
880 ++ (masks[l].Counter);
882 /// check if we have done all colors for this mask
883 if (masks[l].Counter == bi.ColorMasks[masks[l].MaskID].CMs.size())
885 masks[l].Counter = 0;
887 else
889 break;
892 if (l == masks.size()) break; // all cases dones
895 // **** save the TMP hlsInfo
896 std::string fullHlsInfoPath = bi.HlsInfoPath + fileName + ".hlsinfo";
898 NLMISC::COFile os;
899 if (os.open(fullHlsInfoPath))
901 os.serial(hlsInfo);
903 else
905 nlerror("Couldn't write %s", fullHlsInfoPath.c_str());