diff --git a/rtengine/iccstore.cc b/rtengine/iccstore.cc index bad4e8dfa..24f2bb936 100644 --- a/rtengine/iccstore.cc +++ b/rtengine/iccstore.cc @@ -29,9 +29,90 @@ #include -namespace rtengine +namespace { +void loadProfiles (const Glib::ustring& dirName, + std::map* profiles, + std::map* profileContents, + std::map* profileNames, + bool nameUpper, bool onlyRgb) +{ + if (dirName.empty ()) + return; + + try { + + Glib::Dir dir (dirName); + + for (Glib::DirIterator entry = dir.begin (); entry != dir.end (); ++entry) { + + const Glib::ustring fileName = *entry; + + if (fileName.size () < 4) + continue; + + const Glib::ustring extension = fileName.substr (fileName.size () - 4).casefold (); + + if (extension.compare(".icc") == 0 && extension.compare(".icm") == 0) + continue; + + const Glib::ustring filePath = Glib::build_filename (dirName, fileName); + + if (!safe_file_test (filePath, Glib::FILE_TEST_IS_REGULAR)) + continue; + + Glib::ustring name = fileName.substr (0, fileName.size() - 4); + + if (nameUpper) + name = name.uppercase (); + + if (profiles) { + const rtengine::ProfileContent content (filePath); + const cmsHPROFILE profile = content.toProfile (); + + if (profile && (!onlyRgb || cmsGetColorSpace (profile) == cmsSigRgbData)) { + profiles->insert (std::make_pair (name, profile)); + + if (profileContents) + profileContents->insert (std::make_pair (name, content)); + } + } + + if (profileNames) + profileNames->insert (std::make_pair (name, filePath)); + } + } + catch (Glib::Exception&) {} +} + +inline void getSupportedIntent (cmsHPROFILE profile, cmsUInt32Number intent, cmsUInt32Number direction, std::uint8_t& result) +{ + if (cmsIsIntentSupported (profile, intent, direction)) + result |= 1 << intent; +} + +inline std::uint8_t getSupportedIntents (cmsHPROFILE profile, cmsUInt32Number direction) +{ + if (!profile) + return 0; + + std::uint8_t result = 0; + + getSupportedIntent (profile, INTENT_PERCEPTUAL, direction, result); + getSupportedIntent (profile, INTENT_RELATIVE_COLORIMETRIC, direction, result); + getSupportedIntent (profile, INTENT_SATURATION, direction, result); + getSupportedIntent (profile, INTENT_ABSOLUTE_COLORIMETRIC, direction, result); + + return result; +} + +inline cmsHPROFILE createXYZProfile () +{ + double mat[3][3] = { {1.0, 0, 0}, {0, 1.0, 0}, {0, 0, 1.0} }; + return rtengine::ICCStore::createFromMatrix (mat, false, "XYZ"); +} + const double (*wprofiles[])[3] = {xyz_sRGB, xyz_adobe, xyz_prophoto, xyz_widegamut, xyz_bruce, xyz_beta, xyz_best}; const double (*iwprofiles[])[3] = {sRGB_xyz, adobe_xyz, prophoto_xyz, widegamut_xyz, bruce_xyz, beta_xyz, best_xyz}; const char* wpnames[] = {"sRGB", "Adobe RGB", "ProPhoto", "WideGamut", "BruceRGB", "Beta RGB", "BestRGB"}; @@ -43,8 +124,12 @@ const char* wpgamma[] = {"default", "BT709_g2.2_s4.5", "sRGB_g2.4_s12.92", "line // high g=1.3 s=3.35 for high dynamic images //low g=2.6 s=6.9 for low contrast images +} -std::vector getGamma () //return gamma +namespace rtengine +{ + +std::vector getGamma () { std::vector res; @@ -56,7 +141,6 @@ std::vector getGamma () //return gamma return res; } - std::vector getWorkingProfiles () { @@ -69,32 +153,38 @@ std::vector getWorkingProfiles () return res; } -std::vector ICCStore::getOutputProfiles () +std::vector ICCStore::getProfiles () const { MyMutex::MyLock lock(mutex_); std::vector res; - for (std::map::iterator i = fileProfiles.begin(); i != fileProfiles.end(); i++) { - Glib::ustring name(i->first); - std::string::size_type i2 = name.find_last_of('/'); - - if( i2 == std::string::npos ) { - i2 = name.find_last_of('\\'); - } - - if( i2 == std::string::npos ) { - res.push_back ( name ); // list only profiles inside selected profiles directory - } - } + for (ProfileMap::const_iterator profile = fileProfiles.begin (); profile != fileProfiles.end (); ++profile) + res.push_back (profile->first); return res; } +std::vector ICCStore::getProfilesFromDir (const Glib::ustring& dirName) const +{ -cmsHPROFILE -ICCStore::makeStdGammaProfile(cmsHPROFILE iprof) + MyMutex::MyLock lock(mutex_); + + std::vector res; + + ProfileMap profiles; + + loadProfiles (profilesDir, &profiles, NULL, NULL, false, true); + loadProfiles (dirName, &profiles, NULL, NULL, false, true); + + for (ProfileMap::const_iterator profile = profiles.begin (); profile != profiles.end (); ++profile) + res.push_back (profile->first); + + return res; +} + +cmsHPROFILE ICCStore::makeStdGammaProfile (cmsHPROFILE iprof) { // forgive me for the messy code, quick hack to change gamma of an ICC profile to the RT standard gamma if (!iprof) { @@ -189,14 +279,15 @@ ICCStore::makeStdGammaProfile(cmsHPROFILE iprof) return oprof; } -ICCStore* -ICCStore::getInstance(void) +ICCStore* ICCStore::getInstance () { static ICCStore instance_; return &instance_; } -ICCStore::ICCStore () +ICCStore::ICCStore () : + xyz (createXYZProfile ()), + srgb (cmsCreate_sRGBProfile ()) { //cmsErrorAction (LCMS_ERROR_SHOW); @@ -208,234 +299,172 @@ ICCStore::ICCStore () wMatrices[wpnames[i]] = wprofiles[i]; iwMatrices[wpnames[i]] = iwprofiles[i]; } - - double mat[3][3] = { {1.0, 0, 0}, {0, 1.0, 0}, {0, 0, 1.0}}; - xyz = createFromMatrix (mat, false, "XYZ"); - srgb = cmsCreate_sRGBProfile (); } -int ICCStore::numOfWProfiles () +TMatrix ICCStore::workingSpaceMatrix (const Glib::ustring& name) const { - return sizeof(wpnames) / sizeof(wpnames[0]); -} - -TMatrix ICCStore::workingSpaceMatrix (Glib::ustring name) -{ - - std::map::iterator r = wMatrices.find (name); + const MatrixMap::const_iterator r = wMatrices.find (name); if (r != wMatrices.end()) { return r->second; } else { - return wMatrices["sRGB"]; + return wMatrices.find ("sRGB")->second; } } -TMatrix ICCStore::workingSpaceInverseMatrix (Glib::ustring name) +TMatrix ICCStore::workingSpaceInverseMatrix (const Glib::ustring& name) const { - std::map::iterator r = iwMatrices.find (name); + const MatrixMap::const_iterator r = iwMatrices.find (name); if (r != iwMatrices.end()) { return r->second; } else { - return iwMatrices["sRGB"]; + return iwMatrices.find ("sRGB")->second; } } -cmsHPROFILE ICCStore::workingSpace (Glib::ustring name) +cmsHPROFILE ICCStore::workingSpace (const Glib::ustring& name) const { - std::map::iterator r = wProfiles.find (name); + const ProfileMap::const_iterator r = wProfiles.find (name); if (r != wProfiles.end()) { return r->second; } else { - return wProfiles["sRGB"]; + return wProfiles.find ("sRGB")->second; } } -cmsHPROFILE ICCStore::workingSpaceGamma (Glib::ustring name) +cmsHPROFILE ICCStore::workingSpaceGamma (const Glib::ustring& name) const { - std::map::iterator r = wProfilesGamma.find (name); + const ProfileMap::const_iterator r = wProfilesGamma.find (name); if (r != wProfilesGamma.end()) { return r->second; } else { - return wProfilesGamma["sRGB"]; + return wProfilesGamma.find ("sRGB")->second; } } -cmsHPROFILE ICCStore::getProfile (Glib::ustring name) +cmsHPROFILE ICCStore::getProfile (const Glib::ustring& name) const { MyMutex::MyLock lock(mutex_); - std::map::iterator r = fileProfiles.find (name); + const ProfileMap::const_iterator r = fileProfiles.find (name); - if (r != fileProfiles.end()) { + if (r != fileProfiles.end ()) return r->second; - } else { - if (!name.compare (0, 5, "file:") && safe_file_test (name.substr(5), Glib::FILE_TEST_EXISTS) && !safe_file_test (name.substr(5), Glib::FILE_TEST_IS_DIR)) { - ProfileContent pc (name.substr(5)); - if (pc.data) { - cmsHPROFILE profile = pc.toProfile (); + if (name.compare (0, 5, "file:") == 0) { + const ProfileContent content (name.substr (5)); + const cmsHPROFILE profile = content.toProfile (); - if (profile) { - fileProfiles[name] = profile; - fileProfileContents[name] = pc; - return profile; - } - } + if (profile) { + const_cast(fileProfiles).insert(std::make_pair(name, profile)); + const_cast(fileProfileContents).insert(std::make_pair(name, content)); + + return profile; } } return NULL; } -cmsHPROFILE ICCStore::getStdProfile (Glib::ustring name) +cmsHPROFILE ICCStore::getStdProfile (const Glib::ustring& name) const { + const Glib::ustring nameUpper = name.uppercase (); + MyMutex::MyLock lock(mutex_); - std::map::iterator r = fileStdProfiles.find (name.uppercase()); + const ProfileMap::const_iterator r = fileStdProfiles.find (nameUpper); - if (r == fileStdProfiles.end()) { - // profile is not yet in store - std::map::iterator f = fileStdProfilesFileNames.find (name.uppercase()); - - if(f != fileStdProfilesFileNames.end()) { - // but there exists one => load it - ProfileContent pc (f->second); - - if (pc.data) { - cmsHPROFILE profile = pc.toProfile (); - - if (profile) { - fileStdProfiles[name.uppercase()] = profile; - } - - // profile is not valid or it is now stored => remove entry from fileStdProfilesFileNames - fileStdProfilesFileNames.erase(f); - return profile; - } else { - // profile not valid => remove entry from fileStdProfilesFileNames - fileStdProfilesFileNames.erase(f); - return NULL; - } - } else { - // profile does not exist - return NULL; - } - } else { - // return profile from store + // return profile from store + if (r != fileStdProfiles.end ()) return r->second; - } + + // profile is not yet in store + const NameMap::const_iterator f = fileStdProfilesFileNames.find (nameUpper); + + // profile does not exist + if (f == fileStdProfilesFileNames.end ()) + return NULL; + + // but there exists one => load it + const ProfileContent content (f->second); + const cmsHPROFILE profile = content.toProfile (); + + if (profile) + const_cast(fileStdProfiles).insert (std::make_pair (f->first, profile)); + + // profile is not valid or it is now stored => remove entry from fileStdProfilesFileNames + const_cast(fileStdProfilesFileNames).erase (f); + return profile; } -ProfileContent ICCStore::getContent (Glib::ustring name) +ProfileContent ICCStore::getContent (const Glib::ustring& name) const { MyMutex::MyLock lock(mutex_); - return fileProfileContents[name]; + const ContentMap::const_iterator r = fileProfileContents.find (name); + + return r != fileProfileContents.end () ? r->second : ProfileContent(); +} + +std::uint8_t ICCStore::getInputIntents (cmsHPROFILE profile) const +{ + + MyMutex::MyLock lock (mutex_); + + return getSupportedIntents (profile, LCMS_USED_AS_INPUT); +} + +std::uint8_t ICCStore::getOutputIntents (cmsHPROFILE profile) const +{ + + MyMutex::MyLock lock (mutex_); + + return getSupportedIntents (profile, LCMS_USED_AS_OUTPUT); +} + +std::uint8_t ICCStore::getProofIntents (cmsHPROFILE profile) const +{ + + MyMutex::MyLock lock (mutex_); + + return getSupportedIntents (profile, LCMS_USED_AS_PROOF); } // Reads all profiles from the given profiles dir -void ICCStore::init (Glib::ustring usrICCDir, Glib::ustring rtICCDir) +void ICCStore::init (const Glib::ustring& usrICCDir, const Glib::ustring& rtICCDir) { MyMutex::MyLock lock(mutex_); - // + // RawTherapee's profiles take precedence if a user's profile of the same name exists + profilesDir = Glib::build_filename (rtICCDir, "output"); fileProfiles.clear(); fileProfileContents.clear(); - // RawTherapee's profiles take precedence if a user's profile of the same name exists - loadICCs(Glib::build_filename(rtICCDir, "output"), false, fileProfiles, &fileProfileContents, true, true); - loadICCs(usrICCDir, false, fileProfiles, &fileProfileContents, true, true); + loadProfiles (profilesDir, &fileProfiles, &fileProfileContents, NULL, false, true); + loadProfiles (usrICCDir, &fileProfiles, &fileProfileContents, NULL, false, true); // Input profiles // Load these to different areas, since the short name (e.g. "NIKON D700" may overlap between system/user and RT dir) + stdProfilesDir = Glib::build_filename (rtICCDir, "input"); fileStdProfiles.clear(); fileStdProfilesFileNames.clear(); - loadICCs(Glib::build_filename(rtICCDir, "input"), true, fileStdProfiles, NULL); -} - -void ICCStore::loadICCs(Glib::ustring rootDirName, bool nameUpper, std::map& resultProfiles, std::map *resultProfileContents, bool prefetch, bool onlyRgb) -{ - if (rootDirName != "") { - std::deque qDirs; - - qDirs.push_front(rootDirName); - - while (!qDirs.empty()) { - // process directory - Glib::ustring dirname = qDirs.back(); - qDirs.pop_back(); - - Glib::Dir* dir = NULL; - - try { - if (!safe_file_test (dirname, Glib::FILE_TEST_IS_DIR)) { - return; - } - - dir = new Glib::Dir (dirname); - } catch (Glib::Exception& fe) { - return; - } - - dirname = dirname + "/"; - - for (Glib::DirIterator i = dir->begin(); i != dir->end(); ++i) { - Glib::ustring fname = dirname + *i; - Glib::ustring sname = *i; - - // ignore directories - if (!safe_file_test (fname, Glib::FILE_TEST_IS_DIR)) { - size_t lastdot = sname.find_last_of ('.'); - - if (lastdot != Glib::ustring::npos && lastdot <= sname.size() - 4 && (!sname.casefold().compare (lastdot, 4, ".icm") || !sname.casefold().compare (lastdot, 4, ".icc"))) { - Glib::ustring name = nameUpper ? sname.substr(0, lastdot).uppercase() : sname.substr(0, lastdot); - - if(!prefetch) { - fileStdProfilesFileNames[name] = fname; - } else { - ProfileContent pc (fname); - - if (pc.data) { - cmsHPROFILE profile = pc.toProfile (); - - if (profile && (!onlyRgb || cmsGetColorSpace(profile) == cmsSigRgbData)) { - resultProfiles[name] = profile; - - if(resultProfileContents) { - (*resultProfileContents)[name] = pc; - } - } - } - } - } - } - - // Removed recursive scanning, see issue #1730. - // To revert to the recursive method, just uncomment the next line. - - //else qDirs.push_front(fname); // for later scanning - } - - delete dir; - } - } + loadProfiles (stdProfilesDir, NULL, NULL, &fileStdProfilesFileNames, true, false); } // Determine the first monitor default profile of operating system, if selected -void ICCStore::findDefaultMonitorProfile() +void ICCStore::findDefaultMonitorProfile () { - defaultMonitorProfile = ""; + defaultMonitorProfile.clear (); #ifdef WIN32 // Get current main monitor. Could be fine tuned to get the current windows monitor (multi monitor setup), @@ -466,7 +495,7 @@ void ICCStore::findDefaultMonitorProfile() } } -ProfileContent::ProfileContent (Glib::ustring fileName) : data(NULL), length(0) +ProfileContent::ProfileContent (const Glib::ustring& fileName) : data(NULL), length(0) { FILE* f = safe_g_fopen (fileName, "rb"); @@ -518,9 +547,7 @@ ProfileContent& ProfileContent::operator= (const ProfileContent& other) length = other.length; - if (data) { - delete [] data; - } + delete [] data; if (other.data) { data = new char[length + 1]; @@ -532,15 +559,7 @@ ProfileContent& ProfileContent::operator= (const ProfileContent& other) return *this; } -ProfileContent::~ProfileContent () -{ - - if (data) { - delete [] data; - } -} - -cmsHPROFILE ProfileContent::toProfile () +cmsHPROFILE ProfileContent::toProfile () const { if (data) { @@ -550,7 +569,7 @@ cmsHPROFILE ProfileContent::toProfile () } } -cmsHPROFILE ICCStore::createFromMatrix (const double matrix[3][3], bool gamma, Glib::ustring name) +cmsHPROFILE ICCStore::createFromMatrix (const double matrix[3][3], bool gamma, const Glib::ustring& name) { static const unsigned phead[] = { @@ -644,4 +663,5 @@ cmsHPROFILE ICCStore::createFromMatrix (const double matrix[3][3], bool gamma, G delete [] oprof; return p; } + } diff --git a/rtengine/iccstore.h b/rtengine/iccstore.h index acb31e4cf..524db9cd2 100644 --- a/rtengine/iccstore.h +++ b/rtengine/iccstore.h @@ -37,74 +37,129 @@ public: char* data; int length; - ProfileContent (): data(NULL), length(0) {} - ProfileContent (Glib::ustring fileName); - ProfileContent (const ProfileContent& other); - ProfileContent (cmsHPROFILE hProfile); + ProfileContent (); ~ProfileContent (); + + ProfileContent (const ProfileContent& other); ProfileContent& operator= (const rtengine::ProfileContent& other); - cmsHPROFILE toProfile (); + + ProfileContent (const Glib::ustring& fileName); + ProfileContent (cmsHPROFILE hProfile); + cmsHPROFILE toProfile () const; }; class ICCStore { + typedef std::map ProfileMap; + typedef std::map MatrixMap; + typedef std::map ContentMap; + typedef std::map NameMap; - std::map wProfiles; - std::map wProfilesGamma; - std::map wMatrices; - std::map iwMatrices; + ProfileMap wProfiles; + ProfileMap wProfilesGamma; + MatrixMap wMatrices; + MatrixMap iwMatrices; // these contain profiles from user/system directory (supplied on init) - std::map fileProfiles; - std::map fileProfileContents; + Glib::ustring profilesDir; + ProfileMap fileProfiles; + ContentMap fileProfileContents; // these contain standard profiles from RT. keys are all in uppercase - std::map fileStdProfilesFileNames; - std::map fileStdProfiles; + Glib::ustring stdProfilesDir; + NameMap fileStdProfilesFileNames; + ProfileMap fileStdProfiles; - cmsHPROFILE xyz; - cmsHPROFILE srgb; + Glib::ustring defaultMonitorProfile; - MyMutex mutex_; + const cmsHPROFILE xyz; + const cmsHPROFILE srgb; + + mutable MyMutex mutex_; ICCStore (); - void loadICCs(Glib::ustring rootDirName, bool nameUpper, std::map& resultProfiles, std::map *resultProfileContents, bool prefetch = false, bool onlyRgb = false); public: - static ICCStore* getInstance(void); - static cmsHPROFILE makeStdGammaProfile(cmsHPROFILE iprof); + static ICCStore* getInstance (); - Glib::ustring defaultMonitorProfile; // Main monitors standard profile name, from OS - void findDefaultMonitorProfile(); + void init (const Glib::ustring& usrICCDir, const Glib::ustring& stdICCDir); - int numOfWProfiles (); - cmsHPROFILE createFromMatrix (const double matrix[3][3], bool gamma = false, Glib::ustring name = ""); - cmsHPROFILE workingSpace (Glib::ustring name); - cmsHPROFILE workingSpaceGamma (Glib::ustring name); - TMatrix workingSpaceMatrix (Glib::ustring name); - TMatrix workingSpaceInverseMatrix (Glib::ustring name); + static cmsHPROFILE makeStdGammaProfile (cmsHPROFILE iprof); + static cmsHPROFILE createFromMatrix (const double matrix[3][3], bool gamma = false, const Glib::ustring& name = Glib::ustring()); - cmsHPROFILE getProfile (Glib::ustring name); - cmsHPROFILE getStdProfile(Glib::ustring name); + // Main monitors standard profile name, from OS + const Glib::ustring& getDefaultMonitorProfile () const; + void findDefaultMonitorProfile (); - void init (Glib::ustring usrICCDir, Glib::ustring stdICCDir); - ProfileContent getContent (Glib::ustring name); + cmsHPROFILE workingSpace (const Glib::ustring& name) const; + cmsHPROFILE workingSpaceGamma (const Glib::ustring& name) const; + TMatrix workingSpaceMatrix (const Glib::ustring& name) const; + TMatrix workingSpaceInverseMatrix (const Glib::ustring& name) const; - cmsHPROFILE getXYZProfile () - { - return xyz; - } - cmsHPROFILE getsRGBProfile () - { - return srgb; - } - std::vector getOutputProfiles (); + cmsHPROFILE getProfile (const Glib::ustring& name) const; + cmsHPROFILE getStdProfile (const Glib::ustring& name) const; + ProfileContent getContent (const Glib::ustring& name) const; + + cmsHPROFILE getXYZProfile () const; + cmsHPROFILE getsRGBProfile () const; + + std::vector getProfiles () const; + std::vector getProfilesFromDir (const Glib::ustring& dirName) const; + + std::uint8_t getInputIntents (cmsHPROFILE profile) const; + std::uint8_t getOutputIntents (cmsHPROFILE profile) const; + std::uint8_t getProofIntents (cmsHPROFILE profile) const; + + std::uint8_t getInputIntents (const Glib::ustring& name) const; + std::uint8_t getOutputIntents (const Glib::ustring& name) const; + std::uint8_t getProofIntents (const Glib::ustring& name) const; }; #define iccStore ICCStore::getInstance() -//extern const char* wpnames[]; +inline ProfileContent::ProfileContent () : + data(NULL), + length(0) +{ } + +inline ProfileContent::~ProfileContent () +{ + delete [] data; +} + +inline const Glib::ustring& ICCStore::getDefaultMonitorProfile () const +{ + return defaultMonitorProfile; +} + +inline std::uint8_t ICCStore::getInputIntents (const Glib::ustring &name) const +{ + return getInputIntents (getProfile (name)); +} + +inline std::uint8_t ICCStore::getOutputIntents (const Glib::ustring &name) const +{ + return getOutputIntents (getProfile (name)); +} + +inline std::uint8_t ICCStore::getProofIntents (const Glib::ustring &name) const +{ + return getProofIntents (getProfile (name)); +} + +inline cmsHPROFILE ICCStore::getXYZProfile () const +{ + return xyz; +} + +inline cmsHPROFILE ICCStore::getsRGBProfile () const +{ + return srgb; +} + +} + #endif diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 337b5935f..62255cd39 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -213,7 +213,7 @@ void ImProcFunctions::firstAnalysis (Imagefloat* original, const ProcParams* par #if defined(WIN32) if (settings->autoMonitorProfile) { - monitorProfile = iccStore->defaultMonitorProfile; + monitorProfile = iccStore->getDefaultMonitorProfile (); } #endif diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index 91b7df178..5ea834b2d 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -1159,7 +1159,7 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p bool pro = false; Glib::ustring chpro, outProfile; bool present_space[9] = {false, false, false, false, false, false, false, false, false}; - std::vector opnames = iccStore->getOutputProfiles (); + std::vector opnames = iccStore->getProfiles (); //test if files are in system for (int j = 0; j < 9; j++) { diff --git a/rtgui/icmpanel.cc b/rtgui/icmpanel.cc index e9e4e05ff..51ee408a2 100644 --- a/rtgui/icmpanel.cc +++ b/rtgui/icmpanel.cc @@ -185,7 +185,7 @@ ICMPanel::ICMPanel () : FoldableToolPanel(this, "icm", M("TP_ICM_LABEL")), iunch onames->append_text (M("TP_ICM_NOICM")); onames->set_active (0); - std::vector opnames = iccStore->getOutputProfiles (); + std::vector opnames = iccStore->getProfiles (); for (size_t i = 0; i < opnames.size(); i++) { onames->append_text (opnames[i]);