From 3c1bf8e86447f8b975de112e66db11bcecef76ed Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 21 Aug 2024 22:26:46 +0200 Subject: [PATCH] Core: Extend Color API * Fix issues with alpha value <> transparency * Use type traits to convert between different color classes (fixes #14515) --- src/App/Color.cpp | 10 + src/App/Color.h | 113 +++++-- src/App/ColorModel.h | 5 +- src/App/Part.cpp | 2 +- src/App/PropertyStandard.h | 26 +- src/Gui/TaskElementColors.cpp | 2 +- src/Gui/Tools/color_traits.h | 373 +++++++++++++++++++++ src/Mod/Mesh/App/Core/MeshIO.cpp | 4 +- src/Mod/Mesh/Gui/ViewProviderCurvature.cpp | 2 +- src/Mod/Part/Gui/ViewProvider.cpp | 2 +- src/Mod/Test/Document.py | 4 +- tests/src/App/Color.cpp | 1 + 12 files changed, 494 insertions(+), 50 deletions(-) create mode 100644 src/Gui/Tools/color_traits.h diff --git a/src/App/Color.cpp b/src/App/Color.cpp index 1b634ee4e8..cfc5c59bfd 100644 --- a/src/App/Color.cpp +++ b/src/App/Color.cpp @@ -67,6 +67,16 @@ void Color::set(float red, float green, float blue, float alpha) a = alpha; } +float Color::transparency() const +{ + return 1.0F - a; +} + +void Color::setTransparency(float value) +{ + a = 1.0F - value; +} + // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers) Color& Color::setPackedValue(uint32_t rgba) { diff --git a/src/App/Color.h b/src/App/Color.h index bb374ae70d..3697c437d7 100644 --- a/src/App/Color.h +++ b/src/App/Color.h @@ -25,7 +25,7 @@ #define APP_COLOR_H #ifdef __GNUC__ -# include +#include #endif #include #include @@ -35,6 +35,50 @@ namespace App { +template +struct color_traits +{ + color_traits() = default; + explicit color_traits(const color_type& ct) + : ct(ct) + {} + float redF() const + { + return static_cast(ct.redF()); + } + float greenF() const + { + return static_cast(ct.greenF()); + } + float blueF() const + { + return static_cast(ct.blueF()); + } + int red() const + { + return ct.red(); + } + int green() const + { + return ct.green(); + } + int blue() const + { + return ct.blue(); + } + int alpha() const + { + return ct.alpha(); + } + static color_type makeColor(int red, int green, int blue, int alpha = 255) + { + return color_type{red, green, blue, alpha}; + } + +private: + color_type ct; +}; + /** Color class */ class AppExport Color @@ -42,9 +86,9 @@ class AppExport Color public: /** * Defines the color as (R,G,B,A) whereas all values are in the range [0,1]. - * \a A defines the transparency. + * \a A defines the alpha value. */ - explicit Color(float R=0.0,float G=0.0, float B=0.0, float A=0.0); + explicit Color(float R = 0.0, float G = 0.0, float B = 0.0, float A = 1.0); /** * Does basically the same as the constructor above unless that (R,G,B,A) is @@ -61,9 +105,11 @@ public: bool operator!=(const Color& c) const; /** * Defines the color as (R,G,B,A) whereas all values are in the range [0,1]. - * \a A defines the transparency, 0 means complete opaque and 1 invisible. + * \a A defines the alpha value, 1 means fully opaque and 0 transparent. */ - void set(float R,float G, float B, float A=0.0); + void set(float R, float G, float B, float A = 1.0); + float transparency() const; + void setTransparency(float value); Color& operator=(const Color& c) = default; Color& operator=(Color&& c) = default; /** @@ -97,43 +143,56 @@ public: */ void setPackedARGB(uint32_t); - template - static uint32_t asPackedRGBA(const T& color) { - return (color.red() << 24) | (color.green() << 16) | (color.blue() << 8) | color.alpha(); + template + static uint32_t asPackedRGBA(const T& color) + { + color_traits ct{color}; + return (ct.red() << 24) | (ct.green() << 16) | (ct.blue() << 8) | ct.alpha(); } - template - static T fromPackedRGBA(uint32_t color) { - return T((color >> 24) & 0xff, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff); + template + static T fromPackedRGBA(uint32_t color) + { + return color_traits::makeColor((color >> 24) & 0xff, + (color >> 16) & 0xff, + (color >> 8) & 0xff, + (color & 0xff)); } - template - static uint32_t asPackedRGB(const T& color) { - return (color.red() << 24) | (color.green() << 16) | (color.blue() << 8); + template + static uint32_t asPackedRGB(const T& color) + { + color_traits ct{color}; + return (ct.red() << 24) | (ct.green() << 16) | (ct.blue() << 8); } - template - static T fromPackedRGB(uint32_t color) { - return T((color >> 24) & 0xff, (color >> 16) & 0xff, (color >> 8) & 0xff); + template + static T fromPackedRGB(uint32_t color) + { + return color_traits::makeColor((color >> 24) & 0xff, + (color >> 16) & 0xff, + (color >> 8) & 0xff); } /** * creates FC Color from template type, e.g. Qt QColor */ - template - void setValue(const T& q) { - set(q.redF(),q.greenF(),q.blueF()); + template + void setValue(const T& q) + { + color_traits ct{q}; + set(ct.redF(), ct.greenF(), ct.blueF()); } /** * returns a template type e.g. Qt color equivalent to FC color * */ - template + template inline T asValue() const { // clang-format off - return(T(int(std::lround(r * 255.0F)), - int(std::lround(g * 255.0F)), - int(std::lround(b * 255.0F)))); + return color_traits::makeColor(int(std::lround(r * 255.0F)), + int(std::lround(g * 255.0F)), + int(std::lround(b * 255.0F))); // clang-format on } /** @@ -149,9 +208,9 @@ public: bool fromHexString(const std::string& hex); /// color values, public accessible - float r,g,b,a; + float r {}, g {}, b {}, a {}; }; -} //namespace App +} // namespace App -#endif // APP_COLOR_H +#endif // APP_COLOR_H diff --git a/src/App/ColorModel.h b/src/App/ColorModel.h index 690701fc62..5ca292642a 100644 --- a/src/App/ColorModel.h +++ b/src/App/ColorModel.h @@ -540,8 +540,9 @@ inline Color ColorGradient::getColor (float fVal) const { Color color = _getColor(fVal); if (isOutsideInvisible()) { - if (isOutOfRange(fVal)) - color.a = 0.8f; + if (isOutOfRange(fVal)) { + color.a = 0.2F; + } } return color; diff --git a/src/App/Part.cpp b/src/App/Part.cpp index 3ce9e5d207..07e80a78ee 100644 --- a/src/App/Part.cpp +++ b/src/App/Part.cpp @@ -55,7 +55,7 @@ Part::Part() ADD_PROPERTY_TYPE(License, (""), 0, App::Prop_None, "License string of the Item"); ADD_PROPERTY_TYPE(LicenseURL, (""), 0, App::Prop_None, "URL to the license text/contract"); // color and appearance - ADD_PROPERTY(Color, (1.0, 1.0, 1.0, 1.0)); // set transparent -> not used + ADD_PROPERTY(Color, (1.0, 1.0, 1.0, 0.0)); // set transparent -> not used GroupExtension::initExtension(this); } diff --git a/src/App/PropertyStandard.h b/src/App/PropertyStandard.h index 3a8068f486..4bdf33b300 100644 --- a/src/App/PropertyStandard.h +++ b/src/App/PropertyStandard.h @@ -1038,19 +1038,19 @@ public: */ void setValue(const Material& mat); void setValue(const Color& col); - void setValue(float r, float g, float b, float a = 0.0F); + void setValue(float r, float g, float b, float a = 1.0F); void setValue(uint32_t rgba); void setAmbientColor(const Color& col); - void setAmbientColor(float r, float g, float b, float a = 0.0F); + void setAmbientColor(float r, float g, float b, float a = 1.0F); void setAmbientColor(uint32_t rgba); void setDiffuseColor(const Color& col); - void setDiffuseColor(float r, float g, float b, float a = 0.0F); + void setDiffuseColor(float r, float g, float b, float a = 1.0F); void setDiffuseColor(uint32_t rgba); void setSpecularColor(const Color& col); - void setSpecularColor(float r, float g, float b, float a = 0.0F); + void setSpecularColor(float r, float g, float b, float a = 1.0F); void setSpecularColor(uint32_t rgba); void setEmissiveColor(const Color& col); - void setEmissiveColor(float r, float g, float b, float a = 0.0F); + void setEmissiveColor(float r, float g, float b, float a = 1.0F); void setEmissiveColor(uint32_t rgba); void setShininess(float); void setTransparency(float); @@ -1123,32 +1123,32 @@ public: void setValue(int index, const Material& mat); void setAmbientColor(const Color& col); - void setAmbientColor(float r, float g, float b, float a = 0.0F); + void setAmbientColor(float r, float g, float b, float a = 1.0F); void setAmbientColor(uint32_t rgba); void setAmbientColor(int index, const Color& col); - void setAmbientColor(int index, float r, float g, float b, float a = 0.0F); + void setAmbientColor(int index, float r, float g, float b, float a = 1.0F); void setAmbientColor(int index, uint32_t rgba); void setDiffuseColor(const Color& col); - void setDiffuseColor(float r, float g, float b, float a = 0.0F); + void setDiffuseColor(float r, float g, float b, float a = 1.0F); void setDiffuseColor(uint32_t rgba); void setDiffuseColor(int index, const Color& col); - void setDiffuseColor(int index, float r, float g, float b, float a = 0.0F); + void setDiffuseColor(int index, float r, float g, float b, float a = 1.0F); void setDiffuseColor(int index, uint32_t rgba); void setDiffuseColors(const std::vector& colors); void setSpecularColor(const Color& col); - void setSpecularColor(float r, float g, float b, float a = 0.0F); + void setSpecularColor(float r, float g, float b, float a = 1.0F); void setSpecularColor(uint32_t rgba); void setSpecularColor(int index, const Color& col); - void setSpecularColor(int index, float r, float g, float b, float a = 0.0F); + void setSpecularColor(int index, float r, float g, float b, float a = 1.0F); void setSpecularColor(int index, uint32_t rgba); void setEmissiveColor(const Color& col); - void setEmissiveColor(float r, float g, float b, float a = 0.0F); + void setEmissiveColor(float r, float g, float b, float a = 1.0F); void setEmissiveColor(uint32_t rgba); void setEmissiveColor(int index, const Color& col); - void setEmissiveColor(int index, float r, float g, float b, float a = 0.0F); + void setEmissiveColor(int index, float r, float g, float b, float a = 1.0F); void setEmissiveColor(int index, uint32_t rgba); void setShininess(float); diff --git a/src/Gui/TaskElementColors.cpp b/src/Gui/TaskElementColors.cpp index 4e8dc92e14..f9f43d092b 100644 --- a/src/Gui/TaskElementColors.cpp +++ b/src/Gui/TaskElementColors.cpp @@ -180,7 +180,7 @@ public: auto color = item->data(Qt::UserRole).value(); std::string sub = qPrintable(item->data(Qt::UserRole+1).value()); info.emplace(qPrintable(item->data(Qt::UserRole+1).value()), - App::Color(color.redF(),color.greenF(),color.blueF(),1.0-color.alphaF())); + App::Color(color.redF(), color.greenF(), color.blueF(), color.alphaF())); } if(!App::GetApplication().getActiveTransaction()) App::GetApplication().setActiveTransaction("Set colors"); diff --git a/src/Gui/Tools/color_traits.h b/src/Gui/Tools/color_traits.h new file mode 100644 index 0000000000..6c46801165 --- /dev/null +++ b/src/Gui/Tools/color_traits.h @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later + +/*************************************************************************** + * Copyright (c) 2024 Werner Mayer * + * * + * This file is part of FreeCAD. * + * * + * FreeCAD is free software: you can redistribute it and/or modify it * + * under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 2.1 of the * + * License, or (at your option) any later version. * + * * + * FreeCAD 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 * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with FreeCAD. If not, see * + * . * + * * + **************************************************************************/ + +#ifndef GUI_COLORTRAITS_H +#define GUI_COLORTRAITS_H + +#include +#include +#include +#include + +namespace App +{ +// Specialization for SbColor +template<> +struct color_traits +{ + using color_type = SbColor; + color_traits() = default; + explicit color_traits(const color_type& ct) + : ct(ct) + {} + float redF() const + { + return ct[0]; + } + float greenF() const + { + return ct[1]; + } + float blueF() const + { + return ct[2]; + } + float alphaF() const + { + return 1.0F; + } + void setRedF(float red) + { + ct[0] = red; + } + void setGreenF(float green) + { + ct[1] = green; + } + void setBlueF(float blue) + { + ct[2] = blue; + } + void setAlphaF(float alpha) + { + (void)alpha; + } + int red() const + { + return int(std::lround(ct[0] * 255.0F)); + } + int green() const + { + return int(std::lround(ct[1] * 255.0F)); + } + int blue() const + { + return int(std::lround(ct[2] * 255.0F)); + } + int alpha() const + { + return 255; + } + void setRed(int red) + { + ct[0] = static_cast(red) / 255.0F; + } + void setGreen(int green) + { + ct[1] = static_cast(green) / 255.0F; + } + void setBlue(int blue) + { + ct[2] = static_cast(blue) / 255.0F; + } + void setAlpha(int alpha) + { + (void)alpha; + } + static color_type makeColor(int red, int green, int blue, int alpha = 255) + { + (void)alpha; + return color_type{static_cast(red) / 255.0F, + static_cast(green) / 255.0F, + static_cast(blue) / 255.0F}; + } + +private: + color_type ct; +}; + +// Specialization for SbColor4f +template<> +struct color_traits +{ + using color_type = SbColor4f; + color_traits() = default; + explicit color_traits(const color_type& ct) + : ct(ct) + {} + float redF() const + { + return ct[0]; + } + float greenF() const + { + return ct[1]; + } + float blueF() const + { + return ct[2]; + } + float alphaF() const + { + return ct[3]; + } + void setRedF(float red) + { + ct[0] = red; + } + void setGreenF(float green) + { + ct[1] = green; + } + void setBlueF(float blue) + { + ct[2] = blue; + } + void setAlphaF(float alpha) + { + ct[3] = alpha; + } + int red() const + { + return int(std::lround(ct[0] * 255.0F)); + } + int green() const + { + return int(std::lround(ct[1] * 255.0F)); + } + int blue() const + { + return int(std::lround(ct[2] * 255.0F)); + } + int alpha() const + { + return int(std::lround(ct[3] * 255.0F)); + } + void setRed(int red) + { + ct[0] = static_cast(red) / 255.0F; + } + void setGreen(int green) + { + ct[1] = static_cast(green) / 255.0F; + } + void setBlue(int blue) + { + ct[2] = static_cast(blue) / 255.0F; + } + void setAlpha(int alpha) + { + ct[3] = static_cast(alpha) / 255.0F; + } + static color_type makeColor(int red, int green, int blue, int alpha = 255) + { + return color_type{static_cast(red) / 255.0F, + static_cast(green) / 255.0F, + static_cast(blue) / 255.0F, + static_cast(alpha) / 255.0F}; + } + +private: + color_type ct; +}; + +// Specialization for Color +template<> +struct color_traits +{ + using color_type = App::Color; + color_traits() = default; + explicit color_traits(const color_type& ct) + : ct(ct) + {} + float redF() const + { + return ct.r; + } + float greenF() const + { + return ct.g; + } + float blueF() const + { + return ct.b; + } + float alphaF() const + { + return ct.a; + } + void setRedF(float red) + { + ct.r = red; + } + void setGreenF(float green) + { + ct.g = green; + } + void setBlueF(float blue) + { + ct.b = blue; + } + void setAlphaF(float alpha) + { + ct.a = alpha; + } + int red() const + { + return int(std::lround(ct.r * 255.0F)); + } + int green() const + { + return int(std::lround(ct.g * 255.0F)); + } + int blue() const + { + return int(std::lround(ct.b * 255.0F)); + } + int alpha() const + { + return int(std::lround(ct.a * 255.0F)); + } + void setRed(int red) + { + ct.r = static_cast(red) / 255.0F; + } + void setGreen(int green) + { + ct.g = static_cast(green) / 255.0F; + } + void setBlue(int blue) + { + ct.b = static_cast(blue) / 255.0F; + } + void setAlpha(int alpha) + { + ct.a = static_cast(alpha) / 255.0F; + } + static color_type makeColor(int red, int green, int blue, int alpha = 255) + { + return color_type{static_cast(red) / 255.0F, + static_cast(green) / 255.0F, + static_cast(blue) / 255.0F, + static_cast(alpha) / 255.0F}; + } + +private: + color_type ct; +}; + +// Specialization for QColor +template<> +struct color_traits +{ + using color_type = QColor; + color_traits() = default; + explicit color_traits(const color_type& ct) + : ct(ct) + {} + float redF() const + { + return static_cast(ct.redF()); + } + float greenF() const + { + return static_cast(ct.greenF()); + } + float blueF() const + { + return static_cast(ct.blueF()); + } + float alphaF() const + { + return static_cast(ct.alphaF()); + } + void setRedF(float red) + { + ct.setRedF(red); + } + void setGreenF(float green) + { + ct.setGreenF(green); + } + void setBlueF(float blue) + { + ct.setBlueF(blue); + } + void setAlphaF(float alpha) + { + ct.setAlphaF(alpha); + } + int red() const + { + return ct.red(); + } + int green() const + { + return ct.green(); + } + int blue() const + { + return ct.blue(); + } + int alpha() const + { + return ct.alpha(); + } + void setRed(int red) + { + ct.setRed(red); + } + void setGreen(int green) + { + ct.setGreen(green); + } + void setBlue(int blue) + { + ct.setBlue(blue); + } + void setAlpha(int alpha) + { + ct.setAlpha(alpha); + } + static color_type makeColor(int red, int green, int blue, int alpha = 255) + { + return color_type{red, green, blue, alpha}; + } + +private: + color_type ct; +}; + +} // namespace App + +#endif // GUI_COLORTRAITS_H diff --git a/src/Mod/Mesh/App/Core/MeshIO.cpp b/src/Mod/Mesh/App/Core/MeshIO.cpp index 4630cff29e..56cb3c10e8 100644 --- a/src/Mod/Mesh/App/Core/MeshIO.cpp +++ b/src/Mod/Mesh/App/Core/MeshIO.cpp @@ -525,7 +525,7 @@ bool MeshInput::LoadOFF(std::istream& rstrIn) str >> std::ws >> a; // no transparency if (!str) { - a = 0.0f; + a = 1.0f; } if (r > 1.0f || g > 1.0f || b > 1.0f || a > 1.0f) { @@ -578,7 +578,7 @@ bool MeshInput::LoadOFF(std::istream& rstrIn) str >> std::ws >> a; // no transparency if (!str) { - a = 0.0f; + a = 1.0f; } if (r > 1.0f || g > 1.0f || b > 1.0f || a > 1.0f) { diff --git a/src/Mod/Mesh/Gui/ViewProviderCurvature.cpp b/src/Mod/Mesh/Gui/ViewProviderCurvature.cpp index d01d1b1068..c3bfd13e02 100644 --- a/src/Mod/Mesh/Gui/ViewProviderCurvature.cpp +++ b/src/Mod/Mesh/Gui/ViewProviderCurvature.cpp @@ -363,7 +363,7 @@ void ViewProviderMeshCurvature::setVertexCurvatureMode(int mode) for (auto const& value : fValues | boost::adaptors::indexed(0)) { App::Color c = pcColorBar->getColor(value.value()); diffcol[value.index()].setValue(c.r, c.g, c.b); - transp[value.index()] = c.a; + transp[value.index()] = c.transparency(); } pcColorMat->diffuseColor.finishEditing(); diff --git a/src/Mod/Part/Gui/ViewProvider.cpp b/src/Mod/Part/Gui/ViewProvider.cpp index df263526ef..ce8f98bff0 100644 --- a/src/Mod/Part/Gui/ViewProvider.cpp +++ b/src/Mod/Part/Gui/ViewProvider.cpp @@ -88,7 +88,7 @@ void ViewProviderPart::applyTransparency(float transparency, std::vector