src/colors.cpp

changeset 205
1a4342d80de7
parent 178
a23024fc98e0
child 250
2837b549e616
--- a/src/colors.cpp	Wed Jun 08 19:33:00 2022 +0300
+++ b/src/colors.cpp	Wed Jun 08 20:41:21 2022 +0300
@@ -1,115 +1,72 @@
-/*
- *  LDForge: LDraw parts authoring CAD
- *  Copyright (C) 2013 - 2020 Teemu Piippo
- *
- *  This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation, either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
 #include "colors.h"
 
-const ldraw::ColorDefinition ldraw::ColorTable::unknownColor{{}, {}, "Unknown", "???"};
+const ColorDefinition unknownColor{{}, {}, "Unknown", "???"};
 
-/**
- * @brief Clears the color table
- */
-void ldraw::ColorTable::clear()
-{
-	this->definitions = {};
-}
-
-/**
- * @brief Loads colors from LDConfig.ldr
- * @param device Opened LDConfig.ldr I/O device
- * @param errors Where to write any errors into
- * @returns whether or not it succeeded.
- */
-Result ldraw::ColorTable::load(QIODevice& device, QTextStream& errors)
+template<typename... Args>
+static QString replaced(QString text, Args&&... args)
 {
-	this->clear();
-	if (device.isReadable())
-	{
-		QTextStream stream{&device};
-		QString line;
-		while (stream.readLineInto(&line))
-		{
-			this->loadColorFromString(line);
-		}
-		return Success;
-	}
-	else
-	{
-		errors << "could not read colors";
-		return Failure;
-	}
-}
-
-/**
- * @brief Gets color information by color index.
- * @param color
- * @returns color table information
- */
-const ldraw::ColorDefinition& ldraw::ColorTable::operator[](Color color) const
-{
-	auto it = this->definitions.find(color.index);
-	if (it != this->definitions.end())
-	{
-		return it->second;
-	}
-	else
-	{
-		return unknownColor;
-	}
-}
-
-/**
- * @brief Gets the amount of elements in the color table
- * @returns int
- */
-int ldraw::ColorTable::size() const
-{
-	return this->definitions.size();
+	text.replace(args...);
+	return text;
 }
 
 /**
  * @brief Parses an LDConfig.ldr line from a string
  * @param string LDConfig.ldr line to parse
  */
-void ldraw::ColorTable::loadColorFromString(const QString& string)
+static auto loadColorFromString(const QString& string)
 {
-	const QRegExp pattern{
-		R"(^\s*0 \!COLOUR\s+([^\s]+)\s+)"_q +
-		R"(CODE\s+(\d+)\s+)"_q +
-		R"(VALUE\s+(\#[0-9a-fA-F]{3,6})\s+)"_q +
-		R"(EDGE\s+(\#[0-9a-fA-F]{3,6}))"_q +
-		R"((?:\s+ALPHA\s+(\d+))?)"_q
-	};
+	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();
-		ColorDefinition& definition = this->definitions[code];
-		definition = {}; // in case there's an existing definition
-		definition.name = pattern.cap(1);
-		definition.displayName = definition.name;
-		definition.displayName.replace("_", " ");
-		definition.faceColor = pattern.cap(3);
-		definition.edgeColor = pattern.cap(4);
+		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;
 }
 
 /**
@@ -118,109 +75,67 @@
  * @param color
  * @returns luma value [0, 1]
  */
-double luma(const QColor& color)
+qreal luma(const QColor& color)
 {
-	return 0.2126 * color.redF() + 0.7152 * color.greenF() + 0.0722 * color.blueF();
+	return luma(color.redF(), color.greenF(), color.blueF());
 }
 
-/**
- * @brief Returns a direct color index that codes the specified color value
- * @param color
- * @returns direct color index
- */
-ldraw::Color ldraw::directColor(const QColor& color)
+//! @brief Returns a direct color index that codes the specified color value
+ColorIndex directColor(const QColor& color)
 {
-	return ldraw::Color{0x2000000 | (color.red() << 16) | (color.green() << 8) | color.blue()};
+	return directColor(color.red(), color.green(), color.blue());
 }
 
-/**
- * @brief Checks whether or not the specified color index is a direct color
- * @param color Color to check
- * @returns bool
- */
-bool ldraw::isDirectColor(ldraw::Color color)
+//! @brief Returns a face color for @param color, taking direct colors into account
+std::optional<QColor> colorFace(ColorIndex color, const ColorTable& colorTable)
 {
-	return color.index >= 0x2000000;
+	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 Extracts the color value from a direct color index
- * @param color Direct color index
- * @returns color value. Returns a default-constructed QColor in case a non-direct color is given.
- */
-QColor ldraw::directColorFace(ldraw::Color color)
+//! @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))
-	{
-		return {(color.index >> 16) & 0xff, (color.index >> 8) & 0xff, color.index & 0xff};
+	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 {};
+	else {
+		return colorFace(color, colorTable);
 	}
 }
 
-/**
- * @brief Gets the face color for the specified color index
- * @param color Color index to get face color for
- * @param colorTable Color table to use for lookup
- * @returns QColor
- */
-QColor ldraw::colorFace(ldraw::Color color, const ldraw::ColorTable& colorTable)
-{
-	if (isDirectColor(color))
-	{
-		return directColorFace(color);
-	}
-	else
-	{
-		return colorTable[color].faceColor;
-	}
-}
-
-QColor ldraw::colorEdge(ldraw::Color color, const ldraw::ColorTable& colorTable)
-{
-	if (isDirectColor(color))
-	{
-		QColor const faceColor = directColorFace(color);
-		return (luma(faceColor) < 0.4) ? Qt::white : Qt::black;
-	}
-	else
-	{
-		return colorTable[color].faceColor;
-	}
-}
-
-/**
- * @brief Writes a color index into a @c QDataStream
- * @param stream
- * @param color
- * @returns stream
- */
-QDataStream& operator<<(QDataStream& stream, ldraw::Color color)
+QDataStream& operator<<(QDataStream& stream, ColorIndex color)
 {
 	return stream << color.index;
 }
 
-/**
- * @brief Reads a color index from a @c QDataStream
- * @param stream
- * @param color
- * @returns stream
- */
-QDataStream& operator>>(QDataStream& stream, ldraw::Color& color)
+QDataStream& operator>>(QDataStream& stream, ColorIndex& color)
 {
 	return stream >> color.index;
 }
 
-QString ldraw::colorDisplayName(ldraw::Color color, const ldraw::ColorTable &colorTable)
+std::optional<QString> colorDisplayName(ColorIndex color, const ColorTable &colorTable)
 {
-	if (isDirectColor(color))
-	{
-		return directColorFace(color).name();
+	std::optional<QString> result;
+	if (isDirectColor(color)) {
+		result = colorFace(color, colorTable).value_or(QColor{}).name();
 	}
-	else
-	{
-		return colorTable[color].displayName;
+	else {
+		const ColorDefinition* def = findInMap(colorTable, color);
+		if (def != nullptr) {
+			result = def->displayName;
+		}
 	}
+	return result;
 }

mercurial