mirror of
https://bitbucket.org/mfeemster/fractorium.git
synced 2025-07-18 14:14:53 -04:00
--User changes
-Add a palette editor. -Add support for reading .ugr/.gradient/.gradients palette files. -Allow toggling on spinners whose minimum value is not zero. -Allow toggling display of image, affines and grid. -Add new variations: cylinder2, circlesplit, tile_log, truchet_fill, waves2_radial. --Bug fixes -cpow2 was wrong. -Palettes with rapid changes in color would produce slightly different outputs from Apo/Chaotica. This was due to a long standing bug from flam3. -Use exec() on Apple and show() on all other OSes for dialog boxes. -Trying to render a sequence with no frames would crash. -Selecting multiple xforms and rotating them would produce the wrong rotation. -Better handling when parsing flames using different encoding, such as unicode and UTF-8. -Switching between SP/DP didn't reselect the selected flame in the Library tab. --Code changes -Make all types concerning palettes be floats, including PaletteTableWidgetItem. -PaletteTableWidgetItem is no longer templated because all palettes are float. -Include the source colors for user created gradients. -Change parallel_for() calls to work with very old versions of TBB which are lingering on some systems. -Split conditional out of accumulation loop on the CPU for better performance. -Vectorize summing when doing density filter for better performance. -Make all usage of palettes be of type float, double is pointless. -Allow palettes to reside in multiple folders, while ensuring only one of each name is added. -Refactor some palette path searching code. -Make ReadFile() throw and catch an exception if the file operation fails. -A little extra safety in foci and foci3D with a call to Zeps(). -Cast to (real_t) in the OpenCL string for the w variation, which was having trouble compiling on Mac. -Fixing missing comma between paths in InitPaletteList(). -Move Xml and PaletteList classes into cpp to shorten build times when working on them. -Remove default param values for IterOpenCLKernelCreator<T>::SharedDataIndexDefines in cpp file. -Change more NULL to nullptr.
This commit is contained in:
779
Source/Ember/PaletteList.cpp
Normal file
779
Source/Ember/PaletteList.cpp
Normal file
@ -0,0 +1,779 @@
|
||||
#include "EmberPch.h"
|
||||
#include "PaletteList.h"
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
/// <summary>
|
||||
/// Empty constructor which initializes the palette map with the default palette file.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
PaletteList<T>::PaletteList()
|
||||
{
|
||||
//Add(string(m_DefaultFilename));
|
||||
}
|
||||
|
||||
/// <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))
|
||||
{
|
||||
auto item = 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))
|
||||
{
|
||||
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 (auto p = GetPaletteListByFullPathOrFilename(filename))
|
||||
{
|
||||
p->push_back(palette);
|
||||
p->back().m_Index = 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 (auto p = GetPaletteListByFullPathOrFilename(filename))
|
||||
{
|
||||
for (auto& pal : *p)
|
||||
{
|
||||
if (pal.m_Name == palette.m_Name)
|
||||
{
|
||||
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 (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 (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;
|
||||
bool contains = GetPaletteListByFullPathOrFilename(filename) != nullptr;
|
||||
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;
|
||||
|
||||
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))
|
||||
{
|
||||
auto lower = ToLower(filename);
|
||||
auto pfilename = shared_ptr<string>(new string(filename));
|
||||
|
||||
if (EndsWith(lower, ".xml"))
|
||||
{
|
||||
xmlDocPtr doc = xmlReadMemory(static_cast<const char*>(buf.data()), int(buf.size()), filename.c_str(), nullptr, XML_PARSE_NONET);
|
||||
|
||||
if (doc)
|
||||
{
|
||||
auto rootNode = xmlDocGetRootElement(doc);
|
||||
palettes.first->second.clear();
|
||||
palettes.first->second.reserve(buf.size() / 2048);//Roughly what it takes per 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.
|
||||
/// </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()
|
||||
{
|
||||
auto p = s_Palettes.begin();
|
||||
size_t i = 0, paletteFileIndex = QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRand() % Size();
|
||||
|
||||
//Move p forward i elements.
|
||||
while (i < paletteFileIndex && p != s_Palettes.end())
|
||||
{
|
||||
++i;
|
||||
++p;
|
||||
}
|
||||
|
||||
if (i < Size())
|
||||
{
|
||||
size_t paletteIndex = QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedRand() % p->second.size();
|
||||
|
||||
if (paletteIndex < p->second.size())
|
||||
return &p->second[paletteIndex];
|
||||
}
|
||||
|
||||
return 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 (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 (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 (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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 (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 at least one palette in the passed in palette file is 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 least one palette in the file was modifiable, else false.</returns>
|
||||
template <typename T>
|
||||
bool PaletteList<T>::IsModifiable(const string& filename)
|
||||
{
|
||||
if (auto palFile = GetPaletteListByFullPathOrFilename(filename))
|
||||
{
|
||||
for (auto& pal : *palFile)
|
||||
if (!pal.m_SourceColors.empty())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
auto fullpath = GetFullPathFromFilename(filename);
|
||||
|
||||
try
|
||||
{
|
||||
size_t index = 0;
|
||||
ostringstream os;
|
||||
|
||||
if (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 << sc.first << "," << sc.second.r << "," << sc.second.g << "," << sc.second.b << " ";
|
||||
|
||||
os << "\"";
|
||||
}
|
||||
|
||||
os << " data=\"";
|
||||
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
size_t idx = 8 * i + j;
|
||||
os << "00";
|
||||
os << hex << setw(2) << setfill('0') << int(std::rint(pal[idx][0] * 255));
|
||||
os << hex << setw(2) << setfill('0') << int(std::rint(pal[idx][1] * 255));
|
||||
os << hex << setw(2) << setfill('0') << 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;
|
||||
|
||||
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] = T(tmp) / T(255);//Hex palette is [0..255], convert to [0..1].
|
||||
}
|
||||
|
||||
colorCount++;
|
||||
}
|
||||
}
|
||||
else if (!Compare(attr->name, "source_colors"))
|
||||
{
|
||||
string s(val);
|
||||
auto vec1 = Split(s, ' ');
|
||||
|
||||
for (auto& v : vec1)
|
||||
{
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user