#pragma once #include "Palette.h" /// /// PaletteList class. /// namespace EmberNs { /// /// Holds a list of palettes read from an Xml file. Since the default list from flam3-palettes.xml is fairly large at 700 palettes, /// the list member is kept as a static. This class derives from EmberReport in order to report any errors that occurred while reading the Xml. /// 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. /// Template argument expected to be float or double. /// template class EMBER_API PaletteList : public EmberReport { public: static const char* m_DefaultFilename; /// /// Empty constructor which initializes the palette map with the default palette file. /// PaletteList() { Add(string(m_DefaultFilename)); } /// /// Read an Xml palette file into memory. /// This must be called before any palette file usage. /// /// The full path to the file to read /// If true, override the initialization state and force a read, else observe the initialization state. /// Whether anything was read bool Add(const string& filename, bool force = false) { bool added = false; auto& palettes = m_Palettes[filename]; if (palettes.empty() || force) { string buf; const char* loc = __FUNCTION__; if (ReadFile(filename.c_str(), buf)) { xmlDocPtr doc = xmlReadMemory(static_cast(buf.data()), int(buf.size()), filename.c_str(), nullptr, XML_PARSE_NONET); if (doc != nullptr) { auto rootNode = xmlDocGetRootElement(doc); auto pfilename = shared_ptr(new string(filename)); palettes.clear(); palettes.reserve(buf.size() / 2048);//Roughly what it takes per palette. ParsePalettes(rootNode, pfilename, palettes); xmlFreeDoc(doc); added = true; } else { m_ErrorReport.push_back(string(loc) + " : Couldn't load xml doc"); } } else { m_ErrorReport.push_back(string(loc) + " : Couldn't read palette file " + filename); } } return added; } /// /// Get the palette at a random index in a random file in the map. /// Palette* GetRandomPalette() { auto p = m_Palettes.begin(); size_t i = 0, paletteFileIndex = QTIsaac::GlobalRand->Rand() % Size(); //Move p forward i elements. while (i < paletteFileIndex && p != m_Palettes.end()) { ++i; ++p; } if (i < Size()) { size_t paletteIndex = QTIsaac::GlobalRand->Rand() % p->second.size(); if (paletteIndex < p->second.size()) return &p->second[paletteIndex]; } return nullptr; } /// /// Get the palette at a specified index in the specified file in the map. /// /// The filename of the palette to retrieve /// The index of the palette to read. A value of -1 indicates a random palette. /// A pointer to the requested palette if the index was in range, else nullptr. Palette* GetPalette(const string& filename, size_t i) { auto& palettes = m_Palettes[filename]; if (!palettes.empty() && i < palettes.size()) return &palettes[i]; return nullptr; } /// /// Get a pointer to a palette with a specified name in the specified file in the map. /// /// The filename of the palette to retrieve /// The name of the palette to retrieve /// A pointer to the palette if found, else nullptr Palette* GetPaletteByName(const string& filename, const string& name) { for (auto& palettes : m_Palettes) if (palettes.first == filename) for (auto& palette : palettes.second) if (palette.m_Name == name) return &palette; return nullptr; } /// /// 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. /// /// The filename of the palette to retrieve /// The index of the palette to read. /// The hue adjustment to apply /// The palette to store the output /// True if successful, else false. bool GetHueAdjustedPalette(const string& filename, size_t i, T hue, Palette& palette) { bool b = false; if (Palette* unadjustedPal = GetPalette(filename, i)) { unadjustedPal->MakeHueAdjustedPalette(palette, hue); b = true; } return b; } /// /// Clear the palette list and reset the initialization state. /// void Clear() { m_Palettes.clear(); } /// /// Get the size of the palettes map. /// This will be the number of files read. /// /// The size of the palettes map size_t Size() { return m_Palettes.size(); } /// /// Get the size of specified palette vector in the palettes map. /// /// The index of the palette in the map to retrieve /// The size of the palette vector at the specified index in the palettes map size_t Size(size_t index) { size_t i = 0; auto p = m_Palettes.begin(); while (i < index && p != m_Palettes.end()) { ++i; ++p; } return p->second.size(); } /// /// Get the size of specified palette vector in the palettes map. /// /// The filename of the palette in the map to retrieve /// The size of the palette vector at the specified index in the palettes map size_t Size(const string& s) { return m_Palettes[s].size(); } /// /// Get the name of specified palette in the palettes map. /// /// The index of the palette in the map to retrieve /// The name of the palette vector at the specified index in the palettes map const string& Name(size_t index) { size_t i = 0; auto p = m_Palettes.begin(); while (i < index && p != m_Palettes.end()) { ++i; ++p; } return p->first; } private: /// /// 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. /// /// The parent note of all palettes in the Xml file. /// The name of the Xml file. /// The vector to store the paresed palettes associated with this file in. void ParsePalettes(xmlNode* node, const shared_ptr& filename, vector>& palettes) { bool hexError = false; char* val; const char* loc = __FUNCTION__; xmlAttrPtr attr; while (node) { if (node->type == XML_ELEMENT_NODE && !Compare(node->name, "palette")) { attr = node->properties; Palette palette; while (attr) { val = reinterpret_cast(xmlGetProp(node, attr->name)); if (!Compare(attr->name, "data")) { int colorIndex = 0; uint r, g, b; int colorCount = 0; hexError = false; do { int ret = sscanf_s(static_cast(&(val[colorIndex])),"00%2x%2x%2x", &r, &g, &b); if (ret != 3) { m_ErrorReport.push_back(string(loc) + " : Problem reading hexadecimal color data " + string(&val[colorIndex])); hexError = true; break; } colorIndex += 8; while (isspace(int(val[colorIndex]))) colorIndex++; palette[colorCount].r = T(r) / T(255);//Store as normalized colors in the range of 0-1. palette[colorCount].g = T(g) / T(255); palette[colorCount].b = T(b) / T(255); colorCount++; } while (colorCount < COLORMAP_LENGTH); } else if (!Compare(attr->name, "number")) { palette.m_Index = atoi(val); } else if (!Compare(attr->name, "name")) { palette.m_Name = string(val); } xmlFree(val); attr = attr->next; } if (!hexError) { palette.m_Filename = filename; palettes.push_back(palette); } } else { ParsePalettes(node->children, filename, palettes); } node = node->next; } } static map>> m_Palettes;//The map of filenames to vectors that store the palettes. }; }