mirror of
https://bitbucket.org/mfeemster/fractorium.git
synced 2025-01-21 05:00:06 -05:00
4c0f03a52a
-Add Ctrl+g shortcut for generating a sequence. --Bug fixes -Fix indendation for variations because of type icon. -Fix bug when duplicating flame where the new flame wasn't being properly selected. -Fix bug where clearing a flame was changing size and quality when it shouldn't have. -Fix bug where reading an Xml palette was failing on linux. --Code changes -No longer pad string with null terminator in ReadFile() because std::string already does it.
794 lines
24 KiB
C++
794 lines
24 KiB
C++
#include "EmberPch.h"
|
|
#include "PaletteList.h"
|
|
#include "XmlToEmber.h"
|
|
|
|
namespace EmberNs
|
|
{
|
|
/// <summary>
|
|
/// Empty constructor which initializes the palette map with the default palette file.
|
|
/// </summary>
|
|
template <typename T>
|
|
PaletteList<T>::PaletteList()
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Destructor which saves any modifiable palettes to file, just in case they were modified.
|
|
/// </summary>
|
|
template <typename T>
|
|
PaletteList<T>::~PaletteList()
|
|
{
|
|
for (auto& palFile : s_Palettes)
|
|
{
|
|
if (IsModifiable(palFile.first))
|
|
Save(palFile.first);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a new palette file with the given name and vector of palettes, and save it.
|
|
/// </summary>
|
|
/// <param name="filename">The full path to the file to add</param>
|
|
/// <param name="palettes">The list of palettes which comprise the file</param>
|
|
/// <returns>True if the file did not exist, was successfully added and saved, else false.</returns>
|
|
template <typename T>
|
|
bool PaletteList<T>::AddPaletteFile(const string& filename, const vector<Palette<T>>& palettes)
|
|
{
|
|
if (!GetPaletteListByFullPath(filename))
|
|
{
|
|
s_Palettes.insert(make_pair(filename, palettes));
|
|
Save(filename);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create an new empty palette file with the given name with a single modifiable palette in it.
|
|
/// </summary>
|
|
/// <param name="filename">The full path to the file to add</param>
|
|
/// <param name="palettes">The list of palettes which comprise the file</param>
|
|
/// <returns>True if the file did not exist, was successfully added and saved, else false.</returns>
|
|
template <typename T>
|
|
bool PaletteList<T>::AddEmptyPaletteFile(const string& filename)
|
|
{
|
|
if (!GetPaletteListByFullPath(filename))
|
|
{
|
|
const auto item = s_Palettes.insert(make_pair(filename, vector<Palette<T>>()));
|
|
Palette<T> p;
|
|
p.m_Index = 0;
|
|
p.m_Name = "empty-default";
|
|
p.m_Filename = make_shared<string>(filename);
|
|
p.m_SourceColors = map<T, v4T>
|
|
{
|
|
{ T(0), v4T(T(0), T(0), T(0), T(1)) },
|
|
{ T(1), v4T(T(0), T(0), T(0), T(1)) }
|
|
};
|
|
item.first->second.push_back(p);
|
|
Save(filename);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a new palette to an existing palette file and save the file.
|
|
/// </summary>
|
|
/// <param name="filename">The full path to the existing palette file to add</param>
|
|
/// <param name="palette">The new palette to add to the file</param>
|
|
/// <returns>True if the palette file existed, the palette was added, and the file was successfully saved, else false.</returns>
|
|
template <typename T>
|
|
bool PaletteList<T>::AddPaletteToFile(const string& filename, const Palette<T>& palette)
|
|
{
|
|
if (const auto p = GetPaletteListByFullPathOrFilename(filename))
|
|
{
|
|
p->push_back(palette);
|
|
p->back().m_Filename = make_shared<string>(filename);//Ensure the filename matches because this could have been duplicated from another palette file.
|
|
p->back().m_Index = static_cast<int>(p->size()) - 1;
|
|
Save(filename);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Replace an existing palette in a palette file with a new one and save the file.
|
|
/// The match is done based on palette name, so if there are duplicate names in
|
|
/// the file, only the first one will be replaced.
|
|
/// </summary>
|
|
/// <param name="filename">The full path to the existing palette file to replace a palette in</param>
|
|
/// <param name="palette">The new palette to use to replace an existing one in the file</param>
|
|
/// <returns>True if the palette file existed, the palette was replaced, and the file was successfully saved, else false.</returns>
|
|
template <typename T>
|
|
bool PaletteList<T>::Replace(const string& filename, const Palette<T>& palette)
|
|
{
|
|
if (const auto p = GetPaletteListByFullPathOrFilename(filename))
|
|
{
|
|
for (auto& pal : *p)
|
|
{
|
|
if (pal.m_Name == palette.m_Name)
|
|
{
|
|
const auto index = pal.m_Index;
|
|
pal = palette;
|
|
pal.m_Index = index;
|
|
Save(filename);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Replace an existing palette in a palette file with a new one and save the file.
|
|
/// The match is done based on the passed in index.
|
|
/// </summary>
|
|
/// <param name="filename">The full path to the existing palette file to replace a palette in</param>
|
|
/// <param name="palette">The new palette to use to replace an existing one in the file</param>
|
|
/// <param name="index">The 0-based index of the palette to replace</param>
|
|
/// <returns>True if the palette file existed, the palette was replaced, and the file was successfully saved, else false.</returns>
|
|
template <typename T>
|
|
bool PaletteList<T>::Replace(const string& filename, const Palette<T>& palette, int index)
|
|
{
|
|
if (const auto p = GetPaletteListByFullPathOrFilename(filename))
|
|
{
|
|
if (index < p->size())
|
|
{
|
|
(*p)[index] = palette;
|
|
(*p)[index].m_Index = index;
|
|
Save(filename);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Delete an existing palette from a palette file.
|
|
/// The match is done based on the passed in index.
|
|
/// </summary>
|
|
/// <param name="filename">The full path to the existing palette file to delete a palette from</param>
|
|
/// <param name="index">The 0-based index of the palette to delete</param>
|
|
/// <returns>True if the palette file existed, the palette was deleted, and the file was successfully saved, else false.</returns>
|
|
template <typename T>
|
|
bool PaletteList<T>::Delete(const string& filename, int index)
|
|
{
|
|
int i = 0;
|
|
|
|
if (const auto p = GetPaletteListByFullPathOrFilename(filename))
|
|
{
|
|
if (index < p->size())
|
|
{
|
|
p->erase(p->begin() + index);
|
|
|
|
for (auto& pal : *p)
|
|
pal.m_Index = i++;
|
|
|
|
Save(filename);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Read an Xml palette file into memory.
|
|
/// This must be called before any palette file usage.
|
|
/// </summary>
|
|
/// <param name="filename">The full path to the file to read</param>
|
|
/// <param name="force">If true, override the initialization state and force a read, else observe the initialization state.</param>
|
|
/// <returns>Whether anything was read</returns>
|
|
template <typename T>
|
|
bool PaletteList<T>::Add(const string& filename, bool force)
|
|
{
|
|
bool added = true;
|
|
const auto contains = GetPaletteListByFullPathOrFilename(filename) != nullptr;
|
|
const auto filenameonly = GetFilename(filename);
|
|
|
|
if (contains && !force)//Don't allow any palettes with the same name, even if they reside in different paths.
|
|
return false;
|
|
|
|
const auto palettes = s_Palettes.insert(make_pair(filename, vector<Palette<T>>()));
|
|
|
|
if (force || palettes.second)
|
|
{
|
|
string buf;
|
|
const char* loc = __FUNCTION__;
|
|
|
|
if (ReadFile(filename.c_str(), buf))
|
|
{
|
|
const auto lower = ToLower(filename);
|
|
const auto pfilename = shared_ptr<string>(new string(filename));
|
|
|
|
if (EndsWith(lower, ".xml"))
|
|
{
|
|
//Subtract 1 to make reading with nullterminate set to true work on linux.
|
|
const auto doc = xmlReadMemory(static_cast<const char*>(buf.data()), static_cast<int>(buf.size()), filename.c_str(), nullptr, XML_PARSE_NONET);
|
|
|
|
if (doc)
|
|
{
|
|
auto rootNode = xmlDocGetRootElement(doc);
|
|
|
|
if (!Compare(rootNode->name, "palettes"))
|
|
{
|
|
palettes.first->second.clear();
|
|
palettes.first->second.reserve(buf.size() / 2048);//Roughly the size in bytes it takes to store the xml text of palette.
|
|
ParsePalettes(rootNode, pfilename, palettes.first->second);
|
|
xmlFreeDoc(doc);
|
|
}
|
|
|
|
if (palettes.first->second.empty())
|
|
{
|
|
added = false;//Reading failed, likely not a valid palette file.
|
|
s_Palettes.erase(filename);
|
|
AddToReport(string(loc) + " : Couldn't parse xml doc");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
added = false;
|
|
s_Palettes.erase(filename);
|
|
AddToReport(string(loc) + " : Couldn't load xml doc");
|
|
}
|
|
}
|
|
else if (EndsWith(lower, ".ugr") || EndsWith(lower, ".gradient") || EndsWith(lower, ".gradients"))
|
|
{
|
|
if (!ParsePalettes(buf, pfilename, palettes.first->second))
|
|
{
|
|
added = false;
|
|
s_Palettes.erase(filename);
|
|
AddToReport(string(loc) + " : Couldn't read palette file " + filename);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
added = false;
|
|
s_Palettes.erase(filename);
|
|
AddToReport(string(loc) + " : Couldn't read palette file " + filename);
|
|
}
|
|
}
|
|
|
|
return added;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the palette at a random index in a random file in the map.
|
|
/// Attempt to avoid selecting a palette which is all black.
|
|
/// </summary>
|
|
/// <returns>A pointer to a random palette in a random file if successful, else nullptr.</returns>
|
|
template <typename T>
|
|
Palette<T>* PaletteList<T>::GetRandomPalette()
|
|
{
|
|
size_t attempts = 0;
|
|
|
|
while (attempts < Size() * 10)
|
|
{
|
|
auto p = s_Palettes.begin();
|
|
const auto paletteFileIndex = QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRand(Size());
|
|
size_t i = 0;
|
|
|
|
//Move p forward i elements.
|
|
while (i < paletteFileIndex && p != s_Palettes.end())
|
|
{
|
|
++i;
|
|
++p;
|
|
}
|
|
|
|
if (i < Size())
|
|
{
|
|
const size_t paletteIndex = QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRand(p->second.size());
|
|
|
|
if (paletteIndex < p->second.size() && !p->second[paletteIndex].IsEmpty())
|
|
return &p->second[paletteIndex];
|
|
}
|
|
|
|
attempts++;
|
|
}
|
|
|
|
return Size() ? (&s_Palettes.begin()->second[0]) : nullptr;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the palette at a specified index in the specified file in the map.
|
|
/// </summary>
|
|
/// <param name="filename">The filename of the palette to retrieve</param>
|
|
/// <param name="i">The index of the palette to read. A value of -1 indicates a random palette.</param>
|
|
/// <returns>A pointer to the requested palette if the index was in range, else nullptr.</returns>
|
|
template <typename T>
|
|
Palette<T>* PaletteList<T>::GetPaletteByFilename(const string& filename, size_t i)
|
|
{
|
|
if (const auto palettes = GetPaletteListByFilename(filename))
|
|
if (i < palettes->size())
|
|
return &(*palettes)[i];
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the palette at a specified index in the specified file in the map.
|
|
/// </summary>
|
|
/// <param name="filename">The full path and filename of the palette to retrieve</param>
|
|
/// <param name="i">The index of the palette to read. A value of -1 indicates a random palette.</param>
|
|
/// <returns>A pointer to the requested palette if the index was in range, else nullptr.</returns>
|
|
template <typename T>
|
|
Palette<T>* PaletteList<T>::GetPaletteByFullPath(const string& filename, size_t i)
|
|
{
|
|
if (const auto palettes = GetPaletteListByFullPath(filename))
|
|
if (i < palettes->size())
|
|
return &(*palettes)[i];
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a pointer to a palette with a specified name in the specified full path and filename in the map.
|
|
/// </summary>
|
|
/// <param name="filename">The filename of the palette to retrieve</param>
|
|
/// <param name="name">The name of the palette to retrieve</param>
|
|
/// <returns>A pointer to the requested palette if found, else nullptr.</returns>
|
|
template <typename T>
|
|
Palette<T>* PaletteList<T>::GetPaletteByName(const string& filename, const string& name)
|
|
{
|
|
if (const auto palettes = GetPaletteListByFullPathOrFilename(filename))
|
|
for (auto& palette : *palettes)
|
|
if (palette.m_Name == name)
|
|
return &palette;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the palette file with the specified filename in the map.
|
|
/// </summary>
|
|
/// <param name="filename">The filename of the palette to retrieve</param>
|
|
/// <returns>A pointer to the requested palette if found, else nullptr.</returns>
|
|
template <typename T>
|
|
vector<Palette<T>>* PaletteList<T>::GetPaletteListByFilename(const string& filename)
|
|
{
|
|
const auto filenameonly = GetFilename(filename);
|
|
|
|
for (auto& palettes : s_Palettes)
|
|
if (GetFilename(palettes.first) == filenameonly)
|
|
return &palettes.second;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the palette file with the specified full path and filename in the map.
|
|
/// </summary>
|
|
/// <param name="filename">The full path and filename of the palette to retrieve</param>
|
|
/// <returns>A pointer to the requested palette if found, else nullptr.</returns>
|
|
template <typename T>
|
|
vector<Palette<T>>* PaletteList<T>::GetPaletteListByFullPath(const string& filename)
|
|
{
|
|
const auto palettes = s_Palettes.find(filename);
|
|
|
|
if (palettes != s_Palettes.end() && !palettes->second.empty())
|
|
return &palettes->second;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the palette file with the specified full path and filename in the map.
|
|
/// If that does not work, try getting it with the filename alone.
|
|
/// </summary>
|
|
/// <param name="filename">The full path and filename or just the filename of the palette to retrieve</param>
|
|
/// <returns>A pointer to the requested palette if found, else nullptr.</returns>
|
|
template <typename T>
|
|
vector<Palette<T>>* PaletteList<T>::GetPaletteListByFullPathOrFilename(const string& filename)
|
|
{
|
|
auto p = GetPaletteListByFullPath(filename);
|
|
|
|
if (!p)
|
|
p = GetPaletteListByFilename(filename);
|
|
|
|
return p;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get full path and filename of the pallete with the specified filename
|
|
/// </summary>
|
|
/// <param name="filename">The filename only of the palette to retrieve</param>
|
|
/// <returns>A pointer to the requested palette if found, else nullptr.</returns>
|
|
template <typename T>
|
|
string PaletteList<T>::GetFullPathFromFilename(const string& filename)
|
|
{
|
|
const auto filenameonly = GetFilename(filename);
|
|
|
|
for (auto& palettes : s_Palettes)
|
|
if (GetFilename(palettes.first) == filenameonly)
|
|
return palettes.first;
|
|
|
|
return "";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a copy of the palette at a specified index in the specified file in the map
|
|
/// with its hue adjusted by the specified amount.
|
|
/// </summary>
|
|
/// <param name="filename">The filename of the palette to retrieve</param>
|
|
/// <param name="i">The index of the palette to read.</param>
|
|
/// <param name="hue">The hue adjustment to apply</param>
|
|
/// <param name="palette">The palette to store the output</param>
|
|
/// <returns>True if successful, else false.</returns>
|
|
template <typename T>
|
|
bool PaletteList<T>::GetHueAdjustedPalette(const string& filename, size_t i, T hue, Palette<T>& palette)
|
|
{
|
|
if (const auto unadjustedPal = GetPaletteByFullPath(filename, i))
|
|
{
|
|
unadjustedPal->MakeHueAdjustedPalette(palette, hue);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clear the palette list and reset the initialization state.
|
|
/// </summary>
|
|
template <typename T>
|
|
void PaletteList<T>::Clear()
|
|
{
|
|
s_Palettes.clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the size of the palettes map.
|
|
/// This will be the number of files read.
|
|
/// </summary>
|
|
/// <returns>The size of the palettes map</returns>
|
|
template <typename T>
|
|
size_t PaletteList<T>::Size() { return s_Palettes.size(); }
|
|
|
|
/// <summary>
|
|
/// Get the size of specified palette vector in the palettes map.
|
|
/// </summary>
|
|
/// <param name="index">The index of the palette in the map to retrieve</param>
|
|
/// <returns>The size of the palette vector at the specified index in the palettes map</returns>
|
|
template <typename T>
|
|
size_t PaletteList<T>::Size(size_t index)
|
|
{
|
|
size_t i = 0;
|
|
auto p = s_Palettes.begin();
|
|
|
|
while (i < index && p != s_Palettes.end())
|
|
{
|
|
++i;
|
|
++p;
|
|
}
|
|
|
|
return p->second.size();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the size of specified palette vector in the palettes map.
|
|
/// </summary>
|
|
/// <param name="s">The filename of the palette in the map to retrieve</param>
|
|
/// <returns>The size of the palette vector at the specified index in the palettes map</returns>
|
|
template <typename T>
|
|
size_t PaletteList<T>::Size(const string& s)
|
|
{
|
|
return s_Palettes[s].size();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the name of specified palette in the palettes map.
|
|
/// </summary>
|
|
/// <param name="index">The index of the palette in the map to retrieve</param>
|
|
/// <returns>The name of the palette vector at the specified index in the palettes map</returns>
|
|
template <typename T>
|
|
const string& PaletteList<T>::Name(size_t index)
|
|
{
|
|
size_t i = 0;
|
|
auto p = s_Palettes.begin();
|
|
|
|
while (i < index && p != s_Palettes.end())
|
|
{
|
|
++i;
|
|
++p;
|
|
}
|
|
|
|
return p->first;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether all palettes in the passed in palette file are modifiable,
|
|
/// meaning whether the source colors have at least one element in them.
|
|
/// </summary>
|
|
/// <param name="filename">The full path to the existing palette file to search for a modifiable palette in</param>
|
|
/// <returns>True if at all palettes in the file were modifiable, else false.</returns>
|
|
template <typename T>
|
|
bool PaletteList<T>::IsModifiable(const string& filename)
|
|
{
|
|
if (const auto palFile = GetPaletteListByFullPathOrFilename(filename))
|
|
for (auto& pal : *palFile)
|
|
if (pal.m_SourceColors.empty())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a const ref to the underlying static palette structure.
|
|
/// </summary>
|
|
/// <returns>s_Palettes</returns>
|
|
template <typename T>
|
|
const map<string, vector<Palette<T>>>& PaletteList<T>::Palettes() const
|
|
{
|
|
return s_Palettes;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Saves an existing file to disk.
|
|
/// </summary>
|
|
/// <param name="filename">The full path to the existing palette file to save</param>
|
|
/// <returns>True if successful, else false.</returns>
|
|
template <typename T>
|
|
bool PaletteList<T>::Save(const string& filename)
|
|
{
|
|
const auto fullpath = GetFullPathFromFilename(filename);
|
|
|
|
try
|
|
{
|
|
size_t index = 0;
|
|
ostringstream os;
|
|
|
|
if (const auto palFile = GetPaletteListByFullPathOrFilename(filename))
|
|
{
|
|
ofstream f(fullpath);
|
|
os << "<palettes>\n";
|
|
|
|
if (f.is_open())
|
|
{
|
|
for (auto& pal : *palFile)
|
|
{
|
|
os << "<palette number=\"" << index++ << "\" name=\"" << pal.m_Name << "\"";
|
|
|
|
if (!pal.m_SourceColors.empty())
|
|
{
|
|
os << " source_colors=\"";
|
|
|
|
for (auto& sc : pal.m_SourceColors)//Need to clamp these each from 0 to 1. Use our custom clamp funcs.//TODO
|
|
os << Clamp<T>(sc.first, 0, 1) << "," << Clamp<T>(sc.second.r, 0, 1) << "," << Clamp<T>(sc.second.g, 0, 1) << "," << Clamp<T>(sc.second.b, 0, 1) << " ";
|
|
|
|
os << "\"";
|
|
}
|
|
|
|
os << " data=\"";
|
|
|
|
for (int i = 0; i < 32; i++)
|
|
{
|
|
for (int j = 0; j < 8; j++)
|
|
{
|
|
const size_t idx = 8 * i + j;
|
|
os << "00";
|
|
os << hex << setw(2) << setfill('0') << static_cast<int>(std::rint(pal[idx][0] * 255));
|
|
os << hex << setw(2) << setfill('0') << static_cast<int>(std::rint(pal[idx][1] * 255));
|
|
os << hex << setw(2) << setfill('0') << static_cast<int>(std::rint(pal[idx][2] * 255));
|
|
}
|
|
|
|
os << "\n";
|
|
}
|
|
|
|
os << "\"/>\n";
|
|
}
|
|
}
|
|
|
|
os << "</palettes>";
|
|
string s = os.str();
|
|
f.write(s.c_str(), s.size());
|
|
return true;
|
|
}
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
cout << "Error: Writing palette file " << fullpath << " failed: " << e.what() << "\n";
|
|
return false;
|
|
}
|
|
catch (...)
|
|
{
|
|
cout << "Error: Writing palette file " << fullpath << " failed.\n";
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parses an Xml node for all palettes present and stores them in the passed in palette vector.
|
|
/// Note that although the Xml color values are expected to be 0-255, they are converted and
|
|
/// stored as normalized colors, with values from 0-1.
|
|
/// </summary>
|
|
/// <param name="node">The parent note of all palettes in the Xml file.</param>
|
|
/// <param name="filename">The name of the Xml file.</param>
|
|
/// <param name="palettes">The vector to store the parsed palettes associated with this file in.</param>
|
|
template <typename T>
|
|
void PaletteList<T>::ParsePalettes(xmlNode* node, const shared_ptr<string>& filename, vector<Palette<T>>& palettes)
|
|
{
|
|
char* val;
|
|
xmlAttrPtr attr;
|
|
int index = 0;
|
|
Locale lcl;//This is required to properly read commas in the custom palette file. Because foreign locales treat a comma as the decimal point, which causes errors.
|
|
|
|
while (node)
|
|
{
|
|
if (node->type == XML_ELEMENT_NODE && !Compare(node->name, "palette"))
|
|
{
|
|
attr = node->properties;
|
|
Palette<T> palette;
|
|
|
|
while (attr)
|
|
{
|
|
val = reinterpret_cast<char*>(xmlGetProp(node, attr->name));
|
|
|
|
if (!Compare(attr->name, "data"))
|
|
{
|
|
string s1, s;
|
|
size_t tmp, colorCount = 0;
|
|
stringstream ss, temp(val); ss >> std::hex;
|
|
s.reserve(2048);
|
|
|
|
while (temp >> s1)
|
|
s += s1;
|
|
|
|
auto length = s.size();
|
|
|
|
for (size_t strIndex = 0; strIndex < length;)
|
|
{
|
|
strIndex += 2;//Skip past the 00 at the beginning of each RGB.
|
|
|
|
for (glm::length_t i = 0; i < 3 && colorCount < palette.Size(); i++)
|
|
{
|
|
const char tmpStr[3] = { s[strIndex++], s[strIndex++], 0 };//Read out and convert the string two characters at a time.
|
|
ss.clear();//Reset and fill the string stream.
|
|
ss.str(tmpStr);
|
|
ss >> tmp;//Do the conversion.
|
|
palette.m_Entries[colorCount][i] = static_cast<T>(tmp) / static_cast<T>(255);//Hex palette is [0..255], convert to [0..1].
|
|
}
|
|
|
|
colorCount++;
|
|
}
|
|
}
|
|
else if (!Compare(attr->name, "source_colors"))
|
|
{
|
|
const string s(val);
|
|
const auto vec1 = Split(s, ' ');
|
|
|
|
for (auto& v : vec1)
|
|
{
|
|
const auto vec2 = Split(v, ',');
|
|
|
|
if (vec2.size() == 4)
|
|
{
|
|
float d1 = Clamp(std::stof(vec2[0]), 0.0f, 1.0f);
|
|
palette.m_SourceColors[d1] = v4F(Clamp(std::stof(vec2[1]), 0.0f, 1.0f),
|
|
Clamp(std::stof(vec2[2]), 0.0f, 1.0f),
|
|
Clamp(std::stof(vec2[3]), 0.0f, 1.0f), 1.0f);
|
|
}
|
|
}
|
|
}
|
|
else if (!Compare(attr->name, "name"))
|
|
{
|
|
palette.m_Name = string(val);
|
|
}
|
|
|
|
xmlFree(val);
|
|
attr = attr->next;
|
|
}
|
|
|
|
palette.m_Index = index++;
|
|
palette.m_Filename = filename;
|
|
palettes.push_back(palette);
|
|
}
|
|
else
|
|
{
|
|
ParsePalettes(node->children, filename, palettes);
|
|
}
|
|
|
|
node = node->next;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parses a gradient file for all palettes present and stores them in the passed in palette vector.
|
|
/// Note that although the Xml color values are expected to be 0-255, they are converted and
|
|
/// stored as normalized colors, with values from 0-1.
|
|
/// This format is from Ultra Fractal and Apophysis.
|
|
/// </summary>
|
|
/// <param name="buf">The data to parse.</param>
|
|
/// <param name="filename">The name of the gradient file.</param>
|
|
/// <param name="palettes">The vector to store the parsed palettes associated with this file in.</param>
|
|
/// <returns>True if at least one palette is read, else false.</returns>
|
|
template <typename T>
|
|
bool PaletteList<T>::ParsePalettes(const string& buf, const shared_ptr<string>& filename, vector<Palette<T>>& palettes)
|
|
{
|
|
int paletteIndex = 0;
|
|
size_t index = 0;
|
|
string line;
|
|
string name;
|
|
bool reading = false;
|
|
bool found = false;
|
|
istringstream iss(buf);
|
|
vector<string> splitVec;
|
|
const char leftBrace = '{';
|
|
const char rightBrace = '}';
|
|
const string titleStr = "title=";
|
|
const string indexStr = "index=";
|
|
const string colorStr = "color=";
|
|
const string titleDelStr = " =\"";
|
|
const string colorDelStr = " =";
|
|
Palette<T> palette;
|
|
Color<T> col;
|
|
palettes.clear();
|
|
|
|
while (std::getline(iss, line))
|
|
{
|
|
if (!reading && Contains(line, leftBrace))
|
|
{
|
|
reading = true;
|
|
}
|
|
else if (Contains(line, rightBrace))
|
|
{
|
|
if (found)
|
|
palettes.push_back(palette);
|
|
|
|
reading = false;
|
|
found = false;
|
|
}
|
|
|
|
if (reading)
|
|
{
|
|
if (Find(line, titleStr))
|
|
{
|
|
splitVec = Split(line, titleDelStr, true);
|
|
|
|
if (splitVec.size() > 2)
|
|
name = splitVec[1];
|
|
}
|
|
else if (Find(line, indexStr) && Find(line, colorStr))
|
|
{
|
|
if (!found)
|
|
{
|
|
index = 0;
|
|
found = true;
|
|
palette.Clear();
|
|
palette.m_Index = paletteIndex++;
|
|
palette.m_Name = name;
|
|
palette.m_Filename = filename;
|
|
}
|
|
|
|
splitVec = Split(line, colorDelStr, true);
|
|
|
|
if (splitVec.size() > 3 && index < 256)
|
|
{
|
|
int val = std::stoi(splitVec[3]);
|
|
col.r = (val & 0xFF) / T(255);//Hex palette is [0..255], convert to [0..1].
|
|
col.g = ((val >> 8) & 0xFF) / T(255);
|
|
col.b = ((val >> 16) & 0xFF) / T(255);
|
|
palette[index] = col;
|
|
}
|
|
|
|
index++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return !palettes.empty();
|
|
}
|
|
|
|
template EMBER_API class PaletteList<float>;
|
|
|
|
#ifdef DO_DOUBLE
|
|
template EMBER_API class PaletteList<double>;
|
|
#endif
|
|
}
|