Core: Extend Color API

* Fix issues with alpha value <> transparency
* Use type traits to convert between different color classes (fixes #14515)
This commit is contained in:
wmayer
2024-08-21 22:26:46 +02:00
committed by Chris Hennes
parent e5c3d25316
commit 3c1bf8e864
12 changed files with 494 additions and 50 deletions

View File

@@ -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)
{

View File

@@ -25,7 +25,7 @@
#define APP_COLOR_H
#ifdef __GNUC__
# include <cstdint>
#include <cstdint>
#endif
#include <cmath>
#include <string>
@@ -35,6 +35,50 @@
namespace App
{
template<class color_type>
struct color_traits
{
color_traits() = default;
explicit color_traits(const color_type& ct)
: ct(ct)
{}
float redF() const
{
return static_cast<float>(ct.redF());
}
float greenF() const
{
return static_cast<float>(ct.greenF());
}
float blueF() const
{
return static_cast<float>(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 <typename T>
static uint32_t asPackedRGBA(const T& color) {
return (color.red() << 24) | (color.green() << 16) | (color.blue() << 8) | color.alpha();
template<typename T>
static uint32_t asPackedRGBA(const T& color)
{
color_traits<T> ct{color};
return (ct.red() << 24) | (ct.green() << 16) | (ct.blue() << 8) | ct.alpha();
}
template <typename T>
static T fromPackedRGBA(uint32_t color) {
return T((color >> 24) & 0xff, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
template<typename T>
static T fromPackedRGBA(uint32_t color)
{
return color_traits<T>::makeColor((color >> 24) & 0xff,
(color >> 16) & 0xff,
(color >> 8) & 0xff,
(color & 0xff));
}
template <typename T>
static uint32_t asPackedRGB(const T& color) {
return (color.red() << 24) | (color.green() << 16) | (color.blue() << 8);
template<typename T>
static uint32_t asPackedRGB(const T& color)
{
color_traits<T> ct{color};
return (ct.red() << 24) | (ct.green() << 16) | (ct.blue() << 8);
}
template <typename T>
static T fromPackedRGB(uint32_t color) {
return T((color >> 24) & 0xff, (color >> 16) & 0xff, (color >> 8) & 0xff);
template<typename T>
static T fromPackedRGB(uint32_t color)
{
return color_traits<T>::makeColor((color >> 24) & 0xff,
(color >> 16) & 0xff,
(color >> 8) & 0xff);
}
/**
* creates FC Color from template type, e.g. Qt QColor
*/
template <typename T>
void setValue(const T& q) {
set(q.redF(),q.greenF(),q.blueF());
template<typename T>
void setValue(const T& q)
{
color_traits<T> ct{q};
set(ct.redF(), ct.greenF(), ct.blueF());
}
/**
* returns a template type e.g. Qt color equivalent to FC color
*
*/
template <typename T>
template<typename T>
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<T>::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

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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<App::Color>& 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);

View File

@@ -180,7 +180,7 @@ public:
auto color = item->data(Qt::UserRole).value<QColor>();
std::string sub = qPrintable(item->data(Qt::UserRole+1).value<QString>());
info.emplace(qPrintable(item->data(Qt::UserRole+1).value<QString>()),
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");

View File

@@ -0,0 +1,373 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2024 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* 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 *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef GUI_COLORTRAITS_H
#define GUI_COLORTRAITS_H
#include <App/Color.h>
#include <Inventor/SbColor.h>
#include <Inventor/SbColor4f.h>
#include <QColor>
namespace App
{
// Specialization for SbColor
template<>
struct color_traits<SbColor>
{
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<float>(red) / 255.0F;
}
void setGreen(int green)
{
ct[1] = static_cast<float>(green) / 255.0F;
}
void setBlue(int blue)
{
ct[2] = static_cast<float>(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<float>(red) / 255.0F,
static_cast<float>(green) / 255.0F,
static_cast<float>(blue) / 255.0F};
}
private:
color_type ct;
};
// Specialization for SbColor4f
template<>
struct color_traits<SbColor4f>
{
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<float>(red) / 255.0F;
}
void setGreen(int green)
{
ct[1] = static_cast<float>(green) / 255.0F;
}
void setBlue(int blue)
{
ct[2] = static_cast<float>(blue) / 255.0F;
}
void setAlpha(int alpha)
{
ct[3] = static_cast<float>(alpha) / 255.0F;
}
static color_type makeColor(int red, int green, int blue, int alpha = 255)
{
return color_type{static_cast<float>(red) / 255.0F,
static_cast<float>(green) / 255.0F,
static_cast<float>(blue) / 255.0F,
static_cast<float>(alpha) / 255.0F};
}
private:
color_type ct;
};
// Specialization for Color
template<>
struct color_traits<App::Color>
{
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<float>(red) / 255.0F;
}
void setGreen(int green)
{
ct.g = static_cast<float>(green) / 255.0F;
}
void setBlue(int blue)
{
ct.b = static_cast<float>(blue) / 255.0F;
}
void setAlpha(int alpha)
{
ct.a = static_cast<float>(alpha) / 255.0F;
}
static color_type makeColor(int red, int green, int blue, int alpha = 255)
{
return color_type{static_cast<float>(red) / 255.0F,
static_cast<float>(green) / 255.0F,
static_cast<float>(blue) / 255.0F,
static_cast<float>(alpha) / 255.0F};
}
private:
color_type ct;
};
// Specialization for QColor
template<>
struct color_traits<QColor>
{
using color_type = QColor;
color_traits() = default;
explicit color_traits(const color_type& ct)
: ct(ct)
{}
float redF() const
{
return static_cast<float>(ct.redF());
}
float greenF() const
{
return static_cast<float>(ct.greenF());
}
float blueF() const
{
return static_cast<float>(ct.blueF());
}
float alphaF() const
{
return static_cast<float>(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

View File

@@ -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) {

View File

@@ -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();

View File

@@ -88,7 +88,7 @@ void ViewProviderPart::applyTransparency(float transparency, std::vector<App::Co
for (auto& j : colors) {
// transparency hasn't been set for this face
if (j.a == 0.0) {
j.a = transparency/100.0F; // transparency comes in percent
j.setTransparency(transparency/100.0F); // transparency comes in percent
}
}
}

View File

@@ -1451,11 +1451,11 @@ class DocumentPlatformCases(unittest.TestCase):
self.assertTrue(abs(self.Doc.Test.ColourList[0][0] - 1.0) < 0.01)
self.assertTrue(abs(self.Doc.Test.ColourList[0][1] - 0.5) < 0.01)
self.assertTrue(abs(self.Doc.Test.ColourList[0][2] - 0.0) < 0.01)
self.assertTrue(abs(self.Doc.Test.ColourList[0][3] - 0.0) < 0.01)
self.assertTrue(abs(self.Doc.Test.ColourList[0][3] - 1.0) < 0.01)
self.assertTrue(abs(self.Doc.Test.ColourList[1][0] - 0.0) < 0.01)
self.assertTrue(abs(self.Doc.Test.ColourList[1][1] - 0.5) < 0.01)
self.assertTrue(abs(self.Doc.Test.ColourList[1][2] - 1.0) < 0.01)
self.assertTrue(abs(self.Doc.Test.ColourList[1][3] - 0.0) < 0.01)
self.assertTrue(abs(self.Doc.Test.ColourList[1][3] - 1.0) < 0.01)
def testVectorList(self):
self.Doc.Test.VectorList = [(-0.05, 2.5, 5.2), (-0.05, 2.5, 5.2)]

View File

@@ -243,5 +243,6 @@ TEST(TestColor, fromHexString)
EXPECT_FLOAT_EQ(color.r, 85.0F / 255.0F);
EXPECT_FLOAT_EQ(color.g, 170.0F / 255.0F);
EXPECT_FLOAT_EQ(color.b, 255.0F / 255.0F);
EXPECT_FLOAT_EQ(color.a, 255.0F / 255.0F);
}
// NOLINTEND