-
-
Notifications
You must be signed in to change notification settings - Fork 443
Expand file tree
/
Copy pathTestSDKImage.cxx
More file actions
265 lines (210 loc) · 10.7 KB
/
Copy pathTestSDKImage.cxx
File metadata and controls
265 lines (210 loc) · 10.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
#include "PseudoUnitTest.h"
#include <image.h>
#include <algorithm>
#include <fstream>
#include <functional>
#include <iostream>
#include <limits>
#include <random>
#include <set>
#include <sstream>
int TestSDKImage([[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
{
PseudoUnitTest test;
const std::string testingDir(argv[1]);
const std::string tmpDir(argv[2]);
// check supported formats
std::vector<std::string> formats = f3d::image::getSupportedFormats();
test("supported formats PNG", std::ranges::find(formats, ".png") != formats.end());
#if F3D_MODULE_EXR
test("supported formats EXR", std::ranges::find(formats, ".exr") != formats.end());
#endif
#if F3D_MODULE_WEBP
test("supported formats WebP", std::ranges::find(formats, ".webp") != formats.end());
#endif
constexpr unsigned int width = 64;
constexpr unsigned int height = 64;
constexpr unsigned int channels = 3;
// fill with deterministic random values
// do not use std::uniform_int_distribution, it's not giving the same result on different
// platforms
std::mt19937 randGenerator;
f3d::image generated(width, height, channels);
std::vector<uint8_t> pixels(width * height * channels);
std::ranges::generate(pixels, [&]() { return static_cast<uint8_t>(randGenerator() % 256); });
generated.setContent(pixels.data());
f3d::image generated16(width, height, channels, f3d::image::ChannelType::SHORT);
std::vector<uint16_t> pixels16(width * height * channels);
std::ranges::generate(pixels16, [&]() { return static_cast<uint16_t>(randGenerator() % 65536); });
generated16.setContent(pixels16.data());
std::uniform_real_distribution<float> dist(
std::numeric_limits<float>::min(), std::numeric_limits<float>::max());
f3d::image generated32(width, height, channels, f3d::image::ChannelType::FLOAT);
std::vector<float> pixels32(width * height * channels);
std::ranges::generate(pixels32, [&]() { return dist(randGenerator); });
generated32.setContent(pixels32.data());
// test save in different formats and different types
generated.save(tmpDir + "/TestSDKImage.png");
generated.save(tmpDir + "/TestSDKImage.jpg", f3d::image::SaveFormat::JPG);
generated.save(tmpDir + "/TestSDKImage.bmp", f3d::image::SaveFormat::BMP);
generated.save(tmpDir + "/TestSDKImage.tif", f3d::image::SaveFormat::TIF);
generated16.save(tmpDir + "/TestSDKImage16.png");
generated16.save(tmpDir + "/TestSDKImage16.tif", f3d::image::SaveFormat::TIF);
generated32.save(tmpDir + "/TestSDKImage32.tif", f3d::image::SaveFormat::TIF);
// test saveBuffer in different formats
std::vector<unsigned char> bufferPNG = generated.saveBuffer();
test("generated buffer not empty", bufferPNG.size() > 0);
std::vector<unsigned char> bufferJPG = generated.saveBuffer(f3d::image::SaveFormat::JPG);
test("generated JPG buffer not empty", bufferJPG.size() > 0);
test.expect<f3d::image::write_exception>("save incompatible buffer to TIF format",
[&]() { std::ignore = generated.saveBuffer(f3d::image::SaveFormat::TIF); });
std::vector<unsigned char> bufferBMP = generated.saveBuffer(f3d::image::SaveFormat::BMP);
test("generated BMP buffer not empty", bufferBMP.size() > 0);
// test constructor with different channel sizes
f3d::image img16(width, height, channels, f3d::image::ChannelType::SHORT);
f3d::image img32(width, height, channels, f3d::image::ChannelType::FLOAT);
// test exceptions
test.expect<f3d::image::write_exception>("save incompatible buffer to BMP format",
[&]() { std::ignore = img16.saveBuffer(f3d::image::SaveFormat::BMP); });
test.expect<f3d::image::write_exception>("save incompatible buffer to PNG format",
[&]() { std::ignore = img32.saveBuffer(f3d::image::SaveFormat::PNG); });
f3d::image img2Ch(4, 4, 2);
f3d::image img5Ch(4, 4, 5);
test.expect<f3d::image::write_exception>("save incompatible channel count to BMP format",
[&]() { std::ignore = img5Ch.saveBuffer(f3d::image::SaveFormat::BMP); });
test.expect<f3d::image::write_exception>("save incompatible channel count to JPG format",
[&]() { std::ignore = img2Ch.saveBuffer(f3d::image::SaveFormat::JPG); });
test.expect<f3d::image::write_exception>("save image to invalid path",
[&]() { img2Ch.save("/" + std::string(257, 'x') + "/file.ext"); });
test.expect<f3d::image::write_exception>("save image to invalid filename",
[&]() { img2Ch.save(testingDir + std::string(257, 'x') + ".ext"); });
// check 16-bits image code paths
f3d::image shortImg(testingDir + "/data/16bit.png");
test("check 16-bits image channel type",
shortImg.getChannelType() == f3d::image::ChannelType::SHORT);
test("check 16-bits image channel type size", shortImg.getChannelTypeSize(), 2u);
// check reading a 32-bits image
f3d::image hdrImg(testingDir + "/data/palermo_park_1k.hdr");
test("check 32-bits HDR image channel type",
hdrImg.getChannelType() == f3d::image::ChannelType::FLOAT);
test("check 32-bits HDR image channel type size", hdrImg.getChannelTypeSize(), 4u);
hdrImg.save(tmpDir + "/TestSDKImage32hdr.tif", f3d::image::SaveFormat::TIF);
#if F3D_MODULE_EXR
// check reading EXR
f3d::image exrImg(testingDir + "/data/kloofendal_43d_clear_1k.exr");
test("check 32-bits EXR image channel type",
exrImg.getChannelType() == f3d::image::ChannelType::FLOAT);
#endif
#if F3D_MODULE_WEBP
// check reading WebP
f3d::image webpImg(testingDir + "/data/image.webp");
test("check width WebP image channel type", webpImg.getWidth(), 1024u);
#endif
// check reading invalid image
test.expect<f3d::image::read_exception>(
"read invalid image", [&]() { f3d::image invalidImg(testingDir + "/data/invalid.png"); });
// check reading inexistent image, do not create a "/dummy/folder/img.png"
test.expect<f3d::image::read_exception>(
"read image from incorrect path", [&]() { f3d::image img("/dummy/folder/img.png"); });
// check reading image with invalid path
test.expect<f3d::image::read_exception>("read image from invalid path",
[&]() { f3d::image img("/" + std::string(257, 'x') + "/file.ext"); });
// check generated image with baseline
test("check generated image width", generated.getWidth(), width);
test("check generated image height", generated.getHeight(), height);
test("check generated image channel count", generated.getChannelCount(), channels);
test("check generated image channel type",
generated.getChannelType() == f3d::image::ChannelType::BYTE);
test("check generated image not empty", generated.getContent() != nullptr);
// XXX: PseudoUnitTest could be improved for native image testing
f3d::image baseline(testingDir + "/baselines/TestSDKImage.png");
test("check generated image is the same as png baseline", generated == baseline);
// XXX: enable following code once https://github.com/f3d-app/f3d/issues/1558 is fixed
/*
f3d::image baselineTIF(testingDir + "/baselines/TestSDKImage.tif");
if (generated != baselineTIF)
{
std::cerr << "Generated image is different from the tif baseline: "
<< generated.compare(baselineTIF) << "\n";
return EXIT_FAILURE;
}*/
// test operators
f3d::image imgCopy = generated; // copy constructor
test("check copy constructor", imgCopy == generated);
imgCopy = baseline; // copy assignment
test("check copy assignment", imgCopy == baseline);
f3d::image imgMove = std::move(imgCopy); // move constructor
test("check move constructor", imgMove == baseline);
imgCopy = std::move(imgMove); // move assignment
test("check move assignment", imgCopy == baseline);
// test toTerminalText
{
test.expect<f3d::image::write_exception>("invalid toTerminalText with BYTE",
[&]() { std::ignore = f3d::image(3, 3, 1, f3d::image::ChannelType::BYTE).toTerminalText(); });
test.expect<f3d::image::write_exception>("invalid toTerminalText with SHORT", [&]() {
std::ignore = f3d::image(3, 3, 4, f3d::image::ChannelType::SHORT).toTerminalText();
});
const auto fileToString = [](const std::string& path) {
std::ifstream file(path);
std::stringstream ss;
ss << file.rdbuf();
return ss.str();
};
test("toTerminalText with RGB image",
f3d::image(testingDir + "/data/toTerminalText-rgb.png").toTerminalText(),
fileToString(testingDir + "/data/toTerminalText-rgb.txt"));
test("toTerminalText with RGBA image",
f3d::image(testingDir + "/data/toTerminalText-rgba.png").toTerminalText(),
fileToString(testingDir + "/data/toTerminalText-rgba.txt"));
}
// test metadata
{
f3d::image img(4, 2, 3);
img.setMetadata("foo", "bar");
img.setMetadata("hello", "world");
test("check metadata", img.getMetadata("foo") == "bar" && img.getMetadata("hello") == "world");
const std::vector<std::string> keys = img.allMetadata();
test("check all metadata",
std::set<std::string>(keys.begin(), keys.end()) == std::set<std::string>({ "foo", "hello" }));
test.expect<f3d::image::metadata_exception>(
"invalid get metadata", [&]() { std::ignore = img.getMetadata("baz"); });
test.expect<f3d::image::metadata_exception>("remove and get metadata", [&]() {
img.setMetadata("foo", ""); // empty value, should remove key
std::ignore = img.getMetadata("foo"); // expected to throw
});
test(
"check all metata after removal", img.allMetadata(), std::vector<std::string>({ "hello" }));
img.setMetadata("foo", ""); // make sure removing twice is ok
}
{
f3d::image img1(4, 2, 3);
img1.setMetadata("foo", "bar");
img1.setMetadata("hello", "world");
img1.save(tmpDir + "/metadata.png");
f3d::image img2(tmpDir + "/metadata.png");
test("saving/loading file metadata",
img2.getMetadata("foo") == "bar" && img2.getMetadata("hello") == "world");
}
{
f3d::image img1(4, 2, 3);
img1.setMetadata("foo", "bar");
img1.setMetadata("hello", "world");
{
std::vector<unsigned char> buffer = img1.saveBuffer();
std::ofstream outfile(tmpDir + "/metadata-buffer.png", std::ios::out | std::ios::binary);
outfile.write(reinterpret_cast<const char*>(buffer.data()), buffer.size());
}
f3d::image img2(tmpDir + "/metadata-buffer.png");
test("saving/loading buffer metadata",
img2.getMetadata("foo") == "bar" && img2.getMetadata("hello") == "world");
}
// Test image::compare dedicated code paths
test("compare images with different channel types", generated.compare(generated16), 1.);
f3d::image generatedCount(width, height, channels + 1);
test("compare images with different channel count", generated.compare(generatedCount), 1.);
f3d::image generatedSize(width + 1, height, channels);
test("compare images with different size", generated.compare(generatedSize), 1.);
f3d::image empty(0, 0, 0);
test("compare empty images", empty.compare(empty), 0.);
return test.result();
}