Skip to content

Commit df5b557

Browse files
committed
Add UseAlpha option to settings and tests
Introduce a UseAlpha setting to choose between using embedded image alpha or external mask files. Updates include: - settings.h/cpp: add useAlpha field, default true, load from config and include in printed settings. - sldf_config.toml: add UseAlpha entry and explanatory comment. - UI.CPP: add "Use Alpha" menu toggle and status messages. - processing.cpp / solidify.cpp: respect settings.useAlpha when discovering/using external mask files and log when external discovery is skipped. - tests: add solidify_processing_tests to validate behavior when UseAlpha is true/false, and update settings tests to check the new flag. - CMakeLists.txt: register the new processing test target and link sources. This change allows users to explicitly opt into using per-image alpha channels instead of matching external mask filenames.
1 parent 52225e1 commit df5b557

9 files changed

Lines changed: 170 additions & 2 deletions

File tree

CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,18 @@ if(BUILD_TESTING)
517517
solidify_configure_image_tool(solidify_settings_tests)
518518
add_test(NAME solidify_settings_tests COMMAND solidify_settings_tests)
519519

520+
add_executable(solidify_processing_tests
521+
tests/solidify_processing_tests.cpp
522+
Solidify/src/processing.cpp
523+
Solidify/src/settings.cpp
524+
Solidify/src/imageio.cpp
525+
Solidify/src/imageops.cpp
526+
Solidify/src/pushpull.cpp
527+
Solidify/src/solidify.cpp
528+
)
529+
solidify_configure_image_tool(solidify_processing_tests)
530+
add_test(NAME solidify_processing_tests COMMAND solidify_processing_tests)
531+
520532
add_executable(solidify_pushpull_bench
521533
tests/solidify_pushpull_bench.cpp
522534
Solidify/src/pushpull.cpp

Solidify/src/UI.CPP

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,10 @@ AppMenuBar()
410410
settings.isSolidify = !settings.isSolidify;
411411
SetStatus(settings.isSolidify ? "Solidify Enabled" : "Solidify Disabled");
412412
}
413+
if (ImGui::MenuItem("Use Alpha", nullptr, settings.useAlpha)) {
414+
settings.useAlpha = !settings.useAlpha;
415+
SetStatus(settings.useAlpha ? "Use Alpha Enabled" : "Use Alpha Disabled");
416+
}
413417

414418
if (ImGui::BeginMenu("Alpha/Mask")) {
415419
if (ImGui::MenuItem("Disable", nullptr, settings.alphaMode == 0)) {

Solidify/src/processing.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ toLower(const std::string& str)
3838
std::string
3939
checkAlpha(const std::vector<std::string>& fileNames)
4040
{
41+
if (settings.useAlpha) {
42+
return "";
43+
}
44+
4145
for (const std::string& ss : settings.mask_substr) {
4246
const std::string lowMask = toLower(ss);
4347
for (const std::string& fileName : fileNames) {
@@ -195,6 +199,8 @@ doProcessing(const std::vector<std::string>& filePaths, SolidifyProgressCallback
195199
spdlog::error("Mask load failed: {}", mask_file);
196200
return false;
197201
}
202+
} else if (settings.useAlpha) {
203+
spdlog::info("Use Alpha enabled. External mask file discovery skipped.");
198204
}
199205

200206
std::vector<std::string> processFiles;

Solidify/src/settings.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ loadSettings(Settings& outSettings, const std::string& filename)
173173
Settings loaded;
174174

175175
get_value(data, "Global", "Solidify", loaded.isSolidify);
176+
get_value(data, "Global", "UseAlpha", loaded.useAlpha);
176177
get_value(data, "Global", "ExportAlpha", loaded.alphaMode);
177178
get_value(data, "Global", "Console", loaded.conEnable);
178179
get_value(data, "Global", "Threads", loaded.numThreads);
@@ -403,6 +404,7 @@ printSettings(Settings& settings)
403404
{
404405
spdlog::info("--- Current Settings ---");
405406
spdlog::info("Solidify: {}", settings.isSolidify ? "Enabled" : "Disabled");
407+
spdlog::info("Use Alpha: {}", settings.useAlpha ? "Embedded image alpha" : "External mask file");
406408
spdlog::info("Alpha/Mask: {}", settings.alphaMode == 0
407409
? "Remove Alpha"
408410
: (settings.alphaMode == 1 ? "Preserve Alpha" : "Export Alpha only"));

Solidify/src/settings.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ enum JpegSubsamplingMode : int {
6969
};
7070

7171
struct Settings {
72-
bool isSolidify, conEnable;
72+
bool isSolidify, useAlpha, conEnable;
7373
uint normMode, rangeMode, repairMode, alphaMode;
7474
uint swapBasis, swapInvertMask, grayscaleMode;
7575
int fileFormat, defFormat;
@@ -98,6 +98,7 @@ struct Settings {
9898
{
9999
conEnable = true;
100100
isSolidify = true;
101+
useAlpha = true;
101102

102103
alphaMode = 0;
103104
numThreads = 3;

Solidify/src/sldf_config.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
[Global]
22
# Global app settings
33
Solidify = true
4+
# true uses each image's embedded alpha and ignores external mask filename matching.
5+
# false uses files matching MaskNames as an external mask.
6+
UseAlpha = true
47
ExportAlpha = 0
58
MaskNames = ["_mask.", "_mask_", "_alpha.", "_alpha_"]
69
Console = true

Solidify/src/solidify.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ solidify_main(const std::string& inputFileName, const std::string& outputFileNam
221221
bool external_alpha = false;
222222
bool grayscale = false;
223223

224-
if (mask_pair.first.initialized() && mask_pair.second.initialized()) {
224+
if (!settings.useAlpha && mask_pair.first.initialized() && mask_pair.second.initialized()) {
225225
external_alpha = true;
226226
}
227227

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* Solidify - texture push-pull processing utility
3+
* Copyright (c) 2023-2026 Erium Vladlen.
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Lesser General Public License as published
7+
* by the Free Software Foundation, either version 2.1 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*/
18+
19+
#include "processing.h"
20+
#include "settings.h"
21+
22+
#include <OpenImageIO/imagebuf.h>
23+
#include <OpenImageIO/imageio.h>
24+
25+
#include <cstdint>
26+
#include <filesystem>
27+
#include <iostream>
28+
29+
namespace {
30+
31+
namespace fs = std::filesystem;
32+
33+
static int g_failures = 0;
34+
35+
static void expectTrue(bool value, const char* expr, const char* file, int line)
36+
{
37+
if (!value) {
38+
std::cerr << file << ':' << line << ": expected " << expr << '\n';
39+
++g_failures;
40+
}
41+
}
42+
43+
#define EXPECT_TRUE(expr) expectTrue((expr), #expr, __FILE__, __LINE__)
44+
45+
static bool writeRgbaPng(const fs::path& path)
46+
{
47+
constexpr int width = 8;
48+
constexpr int height = 8;
49+
OIIO::ImageSpec spec(width, height, 4, OIIO::TypeDesc::UINT8);
50+
spec.channelnames[3] = "A";
51+
spec.alpha_channel = 3;
52+
53+
OIIO::ImageBuf image(spec);
54+
uint8_t* pixels = static_cast<uint8_t*>(image.localpixels());
55+
for (int y = 0; y < height; ++y) {
56+
for (int x = 0; x < width; ++x) {
57+
const bool hole = x >= 3 && x <= 4 && y >= 3 && y <= 4;
58+
const size_t base = (static_cast<size_t>(y) * width + static_cast<size_t>(x)) * 4u;
59+
const uint8_t alpha = hole ? 0u : 255u;
60+
pixels[base + 0] = hole ? 0u : 90u;
61+
pixels[base + 1] = hole ? 0u : 120u;
62+
pixels[base + 2] = hole ? 0u : 160u;
63+
pixels[base + 3] = alpha;
64+
}
65+
}
66+
67+
return image.write(path.string());
68+
}
69+
70+
static void configureProcessing(bool useAlpha)
71+
{
72+
settings.reSettings();
73+
settingsDefaults = settings;
74+
settings.isSolidify = true;
75+
settings.useAlpha = useAlpha;
76+
settings.alphaMode = 0;
77+
settings.normMode = 0;
78+
settings.repairMode = 0;
79+
settings.rangeMode = 0;
80+
settings.swapBasis = 0;
81+
settings.swapInvertMask = 0;
82+
settings.grayscaleMode = 0;
83+
settings.fileFormat = 2;
84+
settings.bitDepth = -1;
85+
settings.numThreads = 1;
86+
settings.queueLimit = 1;
87+
settings.pngStrategy = PngCompression_Default;
88+
settings.pngCompressionLevel = 4;
89+
}
90+
91+
static void testUseAlphaProcessesTextureNamedMask()
92+
{
93+
const fs::path testDir = fs::temp_directory_path() / "solidify_processing_tests";
94+
std::error_code ec;
95+
fs::remove_all(testDir, ec);
96+
ec.clear();
97+
fs::create_directories(testDir, ec);
98+
EXPECT_TRUE(!ec);
99+
100+
const fs::path albedoPath = testDir / "cloth_albedo.png";
101+
const fs::path maskPath = testDir / "cloth_mask.png";
102+
EXPECT_TRUE(writeRgbaPng(albedoPath));
103+
EXPECT_TRUE(writeRgbaPng(maskPath));
104+
105+
configureProcessing(true);
106+
EXPECT_TRUE(doProcessing({ testDir.string() }, nullptr));
107+
EXPECT_TRUE(fs::exists(testDir / "cloth_albedo_fill.png"));
108+
EXPECT_TRUE(fs::exists(testDir / "cloth_mask_fill.png"));
109+
110+
fs::remove(testDir / "cloth_albedo_fill.png", ec);
111+
ec.clear();
112+
fs::remove(testDir / "cloth_mask_fill.png", ec);
113+
ec.clear();
114+
115+
configureProcessing(false);
116+
EXPECT_TRUE(doProcessing({ testDir.string() }, nullptr));
117+
EXPECT_TRUE(fs::exists(testDir / "cloth_albedo_fill.png"));
118+
EXPECT_TRUE(!fs::exists(testDir / "cloth_mask_fill.png"));
119+
120+
fs::remove_all(testDir, ec);
121+
}
122+
123+
} // namespace
124+
125+
int main()
126+
{
127+
testUseAlphaProcessesTextureNamedMask();
128+
129+
if (g_failures != 0) {
130+
std::cerr << g_failures << " processing test expectation(s) failed.\n";
131+
return 1;
132+
}
133+
134+
std::cout << "Solidify processing tests passed.\n";
135+
return 0;
136+
}

tests/solidify_settings_tests.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ static void writeTextFile(const fs::path& path, const char* text)
5252
static void expectConfigA(const Settings& value)
5353
{
5454
EXPECT_TRUE(value.isSolidify == false);
55+
EXPECT_TRUE(value.useAlpha == false);
5556
EXPECT_TRUE(value.alphaMode == 2);
5657
EXPECT_TRUE(value.conEnable == false);
5758
EXPECT_TRUE(value.numThreads == 8);
@@ -91,6 +92,7 @@ static void expectConfigA(const Settings& value)
9192
static void expectConfigB(const Settings& value)
9293
{
9394
EXPECT_TRUE(value.isSolidify == true);
95+
EXPECT_TRUE(value.useAlpha == true);
9496
EXPECT_TRUE(value.alphaMode == 1);
9597
EXPECT_TRUE(value.conEnable == true);
9698
EXPECT_TRUE(value.numThreads == 2);
@@ -132,6 +134,7 @@ static void testReloadUpdatesSettingsAndDefaults()
132134
static constexpr const char* kConfigA = R"toml(
133135
[Global]
134136
Solidify = false
137+
UseAlpha = false
135138
ExportAlpha = 2
136139
MaskNames = ["_maskA"]
137140
Console = false
@@ -181,6 +184,7 @@ RawRotation = 6
181184
static constexpr const char* kConfigB = R"toml(
182185
[Global]
183186
Solidify = true
187+
UseAlpha = true
184188
ExportAlpha = 1
185189
MaskNames = ["_maskB", "_alphaB"]
186190
Console = true

0 commit comments

Comments
 (0)