From 4d0c35dd2dd791a8d38bcf9bd93388ab0db9f50a Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Mon, 17 Nov 2025 10:15:19 -0600 Subject: [PATCH] Core: Convert transparency to alpha (#24891) * Core: Convert transparency to alpha Create new `Base::getVersion()` function for extracting a program version enumeration given a version string. Convert transparency to alpha value for old project files. * Base/App: Address review comments --------- Co-authored-by: wmayer --- src/App/PropertyStandard.cpp | 47 ++++++++++++++++++++ src/App/PropertyStandard.h | 5 +++ src/Base/ProgramVersion.h | 84 ++++++++++++++++++++++++++++++++++++ src/Gui/Document.cpp | 1 + 4 files changed, 137 insertions(+) create mode 100644 src/Base/ProgramVersion.h diff --git a/src/App/PropertyStandard.cpp b/src/App/PropertyStandard.cpp index 6ebe158bdb..63e4fa57dc 100644 --- a/src/App/PropertyStandard.cpp +++ b/src/App/PropertyStandard.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -2226,6 +2227,25 @@ unsigned int PropertyBoolList::getMemSize() const // PropertyColor //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +namespace +{ +/// The definition of "alpha" was corrected in FreeCAD 1.1 -- returns true if the reader is working +/// on a file that pre-dates that correction. +bool readerRequiresAlphaConversion(const Base::XMLReader &reader) +{ + return Base::getVersion(reader.ProgramVersion) < Base::Version::v1_1; +} + +/// Given a material, invert the alpha channel of all of its colors. +void convertAlphaInMaterial(App::Material& material) +{ + material.ambientColor.a = 1.0F - material.ambientColor.a; + material.diffuseColor.a = 1.0F - material.diffuseColor.a; + material.specularColor.a = 1.0F - material.specularColor.a; + material.emissiveColor.a = 1.0F - material.emissiveColor.a; +} +} + TYPESYSTEM_SOURCE(App::PropertyColor, App::Property) //************************************************************************** @@ -2367,6 +2387,12 @@ void PropertyColor::Restore(Base::XMLReader& reader) reader.readElement("PropertyColor"); // get the value of my Attribute unsigned long rgba = reader.getAttribute("value"); + if (readerRequiresAlphaConversion(reader)) { + // Convert transparency / alpha value + constexpr unsigned long alphaMax = 0xff; + unsigned long alpha = alphaMax - (rgba & alphaMax); + rgba = rgba - (rgba & alphaMax) + alpha; + } setValue(rgba); } @@ -2448,6 +2474,8 @@ void PropertyColorList::Restore(Base::XMLReader& reader) // initiate a file read reader.addFile(file.c_str(), this); } + + requiresAlphaConversion = readerRequiresAlphaConversion(reader); } } @@ -2472,6 +2500,11 @@ void PropertyColorList::RestoreDocFile(Base::Reader& reader) str >> value; it.setPackedValue(value); } + if (requiresAlphaConversion) { + for (auto& it : values) { + it.a = 1.0F - it.a; + } + } setValues(values); } @@ -2702,6 +2735,9 @@ void PropertyMaterial::Restore(Base::XMLReader& reader) _cMat.emissiveColor.setPackedValue(reader.getAttribute("emissiveColor")); _cMat.shininess = (float)reader.getAttribute("shininess"); _cMat.transparency = (float)reader.getAttribute("transparency"); + if (readerRequiresAlphaConversion(reader)) { + convertAlphaInMaterial(_cMat); + } if (reader.hasAttribute("image")) { _cMat.image = reader.getAttribute("image"); } @@ -3247,6 +3283,8 @@ void PropertyMaterialList::Restore(Base::XMLReader& reader) // initiate a file read reader.addFile(file.c_str(), this); } + + requiresAlphaConversion = readerRequiresAlphaConversion(reader); } } @@ -3330,6 +3368,7 @@ void PropertyMaterialList::RestoreDocFileV0(uint32_t count, Base::Reader& reader str >> valueF; it.transparency = valueF; } + convertAlpha(values); setValues(values); } @@ -3360,9 +3399,17 @@ void PropertyMaterialList::RestoreDocFileV3(Base::Reader& reader) readString(str, it.imagePath); readString(str, it.uuid); } + convertAlpha(values); setValues(values); } +void PropertyMaterialList::convertAlpha(std::vector& materials) const +{ + if (requiresAlphaConversion) { + std::ranges::for_each(materials, convertAlphaInMaterial); + } +} + void PropertyMaterialList::readString(Base::InputStream& str, std::string& value) { uint32_t uCt {}; diff --git a/src/App/PropertyStandard.h b/src/App/PropertyStandard.h index f864a554a5..9294662440 100644 --- a/src/App/PropertyStandard.h +++ b/src/App/PropertyStandard.h @@ -1091,6 +1091,9 @@ public: protected: Base::Color getPyValue(PyObject* py) const override; + +private: + bool requiresAlphaConversion {false}; // In 1.1 the handling of alpha was inverted }; @@ -1294,8 +1297,10 @@ private: void verifyIndex(int index) const; void setMinimumSizeOne(); int resizeByOneIfNeeded(int index); + void convertAlpha(std::vector& materials) const; Format formatVersion {Version_0}; + bool requiresAlphaConversion {false}; // In 1.1 the handling of alpha was inverted }; diff --git a/src/Base/ProgramVersion.h b/src/Base/ProgramVersion.h new file mode 100644 index 0000000000..4f23f52588 --- /dev/null +++ b/src/Base/ProgramVersion.h @@ -0,0 +1,84 @@ +// 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 BASE_PROGRAM_VERSION_H +#define BASE_PROGRAM_VERSION_H + +#include +#include +#include +#include + +namespace Base +{ + +enum class Version : std::uint8_t +{ + v0_1x, + v0_16, + v0_17, + v0_18, + v0_19, + v0_20, + v0_21, + v0_22, + v1_0, + v1_1, + v1_x, +}; + +inline Version getVersion(std::string_view str) +{ + // clang-format off + struct VersionItem + { + std::string_view name; + Version version; + }; + static const std::initializer_list items = { + {.name="0.16", .version=Version::v0_16}, + {.name="0.17", .version=Version::v0_17}, + {.name="0.18", .version=Version::v0_18}, + {.name="0.19", .version=Version::v0_19}, + {.name="0.20", .version=Version::v0_20}, + {.name="0.21", .version=Version::v0_21}, + {.name="0.22", .version=Version::v0_22}, + {.name="1.0" , .version=Version::v1_0 }, + {.name="1.1" , .version=Version::v1_1 }, + }; + // clang-format on + auto it = std::ranges::find_if(items, [str](const auto& item) { + return str.compare(0, item.name.size(), item.name) == 0; + }); + if (it != items.end()) { + return it->version; + } + if (!str.empty() && str[0] == '0') { + return Version::v0_1x; + } + return Version::v1_x; +} + +} // namespace Base + +#endif // BASE_PROGRAM_VERSION_H diff --git a/src/Gui/Document.cpp b/src/Gui/Document.cpp index 42855bdf8f..d8692b824b 100644 --- a/src/Gui/Document.cpp +++ b/src/Gui/Document.cpp @@ -1823,6 +1823,7 @@ void Document::RestoreDocFile(Base::Reader& reader) localreader->readElement("Document"); long scheme = localreader->getAttribute("SchemaVersion"); localreader->DocumentSchema = scheme; + localreader->ProgramVersion = d->_pcDocument->getProgramVersion(); bool hasExpansion = localreader->hasAttribute("HasExpansion"); if (hasExpansion) {