Mon, 20 Jun 2022 17:27:30 +0300
Add a basic message log
#include "colors.h" const ColorDefinition unknownColor{{}, {}, "Unknown", "???"}; template<typename... Args> static QString replaced(QString text, Args&&... args) { text.replace(args...); return text; } /** * @brief Parses an LDConfig.ldr line from a string * @param string LDConfig.ldr line to parse */ static auto loadColorFromString(const QString& string) { std::optional<std::pair<ColorIndex, ColorDefinition>> result; static const QRegExp pattern{QStringLiteral( R"(^\s*0 \!COLOUR\s+([^\s]+)\s+)" R"(CODE\s+(\d+)\s+)" R"(VALUE\s+(\#[0-9a-fA-F]{3,6})\s+)" R"(EDGE\s+(\#[0-9a-fA-F]{3,6}))" R"((?:\s+ALPHA\s+(\d+))?)" )}; if (pattern.indexIn(string) != -1) { const int code = pattern.cap(2).toInt(); const QString name = pattern.cap(1); ColorDefinition definition = { .faceColor = pattern.cap(3), .edgeColor = pattern.cap(4), .name = name, .displayName = replaced(name, "_", " "), }; if (not pattern.cap(5).isEmpty()) { const int alpha = pattern.cap(5).toInt(); definition.faceColor.setAlpha(alpha); } result = std::make_pair(ColorIndex{code}, definition); } return result; } /** * @brief Loads colors from LDConfig.ldr */ std::optional<ColorTable> loadColorTable(QIODevice &device, QTextStream &errors) { std::optional<ColorTable> result; if (device.isReadable()) { result.emplace(); QTextStream stream{&device}; QString line; while (stream.readLineInto(&line)) { const auto pair = loadColorFromString(line); if (pair.has_value()) { (*result)[pair->first] = pair->second; } } } else { errors << "could not read colors"; } return result; } /** * @brief Calculates the luma-value for the given color. * @details c.f. https://en.wikipedia.org/wiki/Luma_(video) * @param color * @returns luma value [0, 1] */ qreal luma(const QColor& color) { return luma(color.redF(), color.greenF(), color.blueF()); } //! @brief Returns a direct color index that codes the specified color value ColorIndex directColor(const QColor& color) { return directColor(color.red(), color.green(), color.blue()); } //! @brief Returns a face color for @param color, taking direct colors into account std::optional<QColor> colorFace(ColorIndex color, const ColorTable& colorTable) { std::optional<QColor> result; if (isDirectColor(color)) { const std::array<int, 3> rgb = directColorRgb(color); result = QColor{rgb[0], rgb[1], rgb[2]}; } else { const ColorDefinition* def = findInMap(colorTable, color); if (def != nullptr) { result = def->faceColor; } } return result; } //! @brief Returns an edge color for @param color, taking direct colors into account std::optional<QColor> colorEdge(ColorIndex color, const ColorTable& colorTable) { if (isDirectColor(color)) { const std::array<int, 3> rgb = directColorRgb(color); return (luma(rgb[0], rgb[1], rgb[2]) < 0.4) ? Qt::white : Qt::black; } else { return colorFace(color, colorTable); } } QDataStream& operator<<(QDataStream& stream, ColorIndex color) { return stream << color.index; } QDataStream& operator>>(QDataStream& stream, ColorIndex& color) { return stream >> color.index; } std::optional<QString> colorDisplayName(ColorIndex color, const ColorTable &colorTable) { std::optional<QString> result; if (isDirectColor(color)) { result = colorFace(color, colorTable).value_or(QColor{}).name(); } else { const ColorDefinition* def = findInMap(colorTable, color); if (def != nullptr) { result = def->displayName; } } return result; }