Skip to content

Commit 8b8f7a1

Browse files
committed
Enhance PSD metadata extraction and UI table styling
Refactored PSD metadata extraction into a PSDMetadata struct with support for additional fields (ICC, IPTC, copyright, URL, etc.) and improved logging. Updated image loading to use the new metadata structure and ICC profile handling. Improved ImGui table and text rendering by tightening cell and frame padding. Minor fixes in EXIF error handling and PNG color profile assignment.
1 parent 261b1d3 commit 8b8f7a1

File tree

7 files changed

+224
-49
lines changed

7 files changed

+224
-49
lines changed

src/image_gui.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -399,8 +399,7 @@ void Image::draw_info()
399399
ImGui::PE::WrappedText(property_name, value, tooltip, bold_font);
400400
};
401401

402-
static const ImGuiTableFlags table_flags =
403-
ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_NoBordersInBodyUntilResize;
402+
static const ImGuiTableFlags table_flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_NoBordersInBodyUntilResize;
404403

405404
auto get_tooltip = [](const json &field_obj)
406405
{
@@ -624,13 +623,15 @@ void Image::draw_info()
624623
{
625624
draw_filter_input();
626625
ImGui::BeginChild("Header info child", ImVec2(0, 0), ImGuiChildFlags_None, ImGuiWindowFlags_NoBackground);
626+
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0, 0));
627627
if (ImGui::PE::Begin("Image info", table_flags))
628628
{
629629
ImGui::Indent(HelloImGui::EmSize(0.5f));
630630
add_fields(metadata["header"]);
631631
ImGui::Unindent(HelloImGui::EmSize(0.5f));
632632
}
633633
ImGui::PE::End();
634+
ImGui::PopStyleVar();
634635
ImGui::EndChild();
635636
ImGui::EndTabItem();
636637
}
@@ -657,13 +658,15 @@ void Image::draw_info()
657658
ImGui::PopStyleColor(1);
658659
if (open)
659660
{
661+
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0, 0));
660662
if (ImGui::PE::Begin("Image info", table_flags))
661663
{
662664
ImGui::Indent(HelloImGui::EmSize(0.5f));
663665
add_fields(table_obj);
664666
ImGui::Unindent(HelloImGui::EmSize(0.5f));
665667
}
666668
ImGui::PE::End();
669+
ImGui::PopStyleVar();
667670
}
668671
}
669672
ImGui::EndChild();
@@ -732,13 +735,15 @@ void Image::draw_info()
732735

733736
if (open)
734737
{
738+
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0, 0));
735739
if (ImGui::PE::Begin("Image info", table_flags))
736740
{
737741
// ImGui::Indent(HelloImGui::EmSize(0.5f));
738742
add_xmp_fields(table_obj, 0, name + " " + ns);
739743
// ImGui::Unindent(HelloImGui::EmSize(0.5f));
740744
}
741745
ImGui::PE::End();
746+
ImGui::PopStyleVar();
742747
}
743748

744749
// if (open)
@@ -1186,8 +1191,7 @@ void Image::draw_colorspace()
11861191

11871192
float col2_w = 0.f;
11881193
float col2_big_enough = HelloImGui::EmSize(12.f);
1189-
static const ImGuiTableFlags table_flags =
1190-
ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_NoBordersInBodyUntilResize;
1194+
static const ImGuiTableFlags table_flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_NoBordersInBodyUntilResize;
11911195
if (ImGui::PE::Begin("Colorspace", table_flags))
11921196
{
11931197
ImGui::Indent(HelloImGui::EmSize(0.5f));

src/imageio/exif.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -174,17 +174,17 @@ Exif::Exif(const uint8_t *data_ptr, size_t data_size) : m_impl(std::make_unique<
174174
[](ExifLog *log, ExifLogCode kind, const char *domain, const char *format, va_list args, void *user_data)
175175
{
176176
bool *error = static_cast<bool *>(user_data);
177-
char buf[1024];
178-
vsnprintf(buf, sizeof(buf), format, args);
177+
char msg[1024];
178+
vsnprintf(msg, sizeof(msg), format, args);
179179

180180
switch (kind)
181181
{
182-
case EXIF_LOG_CODE_NONE: spdlog::info("{}: {}", domain, buf); break;
183-
case EXIF_LOG_CODE_DEBUG: spdlog::debug("{}: {}", domain, buf); break;
182+
case EXIF_LOG_CODE_NONE: spdlog::info("{}: {}", domain, msg); break;
183+
case EXIF_LOG_CODE_DEBUG: spdlog::debug("{}: {}", domain, msg); break;
184184
case EXIF_LOG_CODE_NO_MEMORY:
185185
case EXIF_LOG_CODE_CORRUPT_DATA:
186186
*error = true;
187-
spdlog::error("log: {}: {}", domain, buf);
187+
spdlog::error("log: {}: {}", domain, msg);
188188
break;
189189
}
190190
},
@@ -195,7 +195,10 @@ Exif::Exif(const uint8_t *data_ptr, size_t data_size) : m_impl(std::make_unique<
195195
// 3) Load the EXIF data from memory buffer
196196
exif_data_load_data(m_impl->exif_data.get(), m_impl->data.data(), m_impl->data.size());
197197

198-
if (!m_impl->exif_data || error)
198+
if (error)
199+
spdlog::warn("There were errors while loading EXIF data, but trying to continue.");
200+
201+
if (!m_impl->exif_data)
199202
throw std::invalid_argument{"Failed to decode EXIF data."};
200203
}
201204
catch (const std::exception &e)

src/imageio/png.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -779,10 +779,10 @@ vector<ImagePtr> load_png_image(istream &is, string_view filename, const ImageLo
779779
}
780780
else
781781
spdlog::info("Image is already in linear color space.");
782-
783-
image->metadata["color profile"] = profile_desc;
784782
}
785783

784+
image->metadata["color profile"] = profile_desc;
785+
786786
for (int c = 0; c < size.z; ++c)
787787
image->channels[c].copy_from_interleaved(float_pixels.data(), size.x, size.y, size.z, c,
788788
[](float v) { return v; });

src/imageio/psd.cpp

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ static std::vector<uint8_t> read_bytes(std::istream &stream, size_t count)
3636
return data;
3737
}
3838

39-
void extract_psd_exif_xmp(std::istream &stream, std::vector<uint8_t> &exif, std::vector<uint8_t> &xmp)
39+
const char *PSDMetadata::color_mode_names[10] = {
40+
"Bitmap", "Grayscale", "Indexed", "RGB", "CMYK", "Invalid5", "Invalid6", "Multichannel", "Duotone", "Lab",
41+
};
42+
43+
// See https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/
44+
void PSDMetadata::read(std::istream &stream)
4045
{
4146
// Read and verify PSD signature
4247
char signature[4];
@@ -52,8 +57,12 @@ void extract_psd_exif_xmp(std::istream &stream, std::vector<uint8_t> &exif, std:
5257
// Skip reserved bytes (6 bytes) - must be zero
5358
skip_bytes(stream, 6);
5459

55-
// Skip channels (2), height (4), width (4), depth (2), color mode (2) = 14 bytes
56-
skip_bytes(stream, 14);
60+
// Channels (2), height (4), width (4), depth (2), color mode (2) = 14 bytes
61+
num_channels = read_uint16_be(stream);
62+
height = read_uint32_be(stream);
63+
width = read_uint32_be(stream);
64+
depth = read_uint16_be(stream);
65+
color_mode = static_cast<ColorMode>(read_uint16_be(stream));
5766

5867
// Read and skip Color Mode Data section
5968
uint32_t color_mode_data_length = read_uint32_be(stream);
@@ -107,20 +116,52 @@ void extract_psd_exif_xmp(std::istream &stream, std::vector<uint8_t> &exif, std:
107116
if (data_size > 100 * 1024 * 1024) // 100MB limit
108117
throw std::runtime_error("Resource data size too large");
109118

110-
// Check if this is EXIF or XMP
111-
if (resource_id == 1058 && exif.empty()) // EXIF (0x0422 in hex)
119+
// Extract metadata based on resource ID
120+
if (resource_id == 1028 && iptc.empty()) // IPTC-NAA
121+
iptc = read_bytes(stream, data_size);
122+
else if (resource_id == 1034) // Copyright flag
123+
{
124+
if (data_size > 0)
125+
{
126+
uint8_t flag;
127+
stream.read(reinterpret_cast<char *>(&flag), 1);
128+
is_copyright = (flag != 0);
129+
skip_bytes(stream, data_size - 1);
130+
}
131+
}
132+
else if (resource_id == 1035) // URL
133+
{
134+
if (data_size > 0)
135+
{
136+
auto url_data = read_bytes(stream, data_size);
137+
url = std::string(url_data.begin(), url_data.end());
138+
}
139+
}
140+
else if (resource_id == 1039 && icc_profile.empty()) // ICC Profile
141+
icc_profile = read_bytes(stream, data_size);
142+
else if (resource_id == 1041) // ICC Untagged
143+
{
144+
if (data_size >= 1)
145+
{
146+
uint8_t flag;
147+
stream.read(reinterpret_cast<char *>(&flag), 1);
148+
is_icc_untagged = (flag != 0);
149+
skip_bytes(stream, data_size - 1);
150+
}
151+
}
152+
else if (resource_id == 1036 && thumbnail.empty()) // Thumbnail
153+
thumbnail = read_bytes(stream, data_size);
154+
else if (resource_id == 1058 && exif.empty()) // EXIF data 1
112155
exif = read_bytes(stream, data_size);
156+
else if (resource_id == 1059 && exif3.empty()) // EXIF data 3
157+
exif3 = read_bytes(stream, data_size);
113158
else if (resource_id == 1060 && xmp.empty()) // XMP (0x0424 in hex)
114159
xmp = read_bytes(stream, data_size);
115-
else // Skip this resource data
160+
else
116161
skip_bytes(stream, data_size);
117162

118163
// Resource data is padded to even length
119164
if (data_size % 2 == 1)
120165
skip_bytes(stream, 1);
121-
122-
// Early exit if we found both
123-
if (!exif.empty() && !xmp.empty())
124-
break;
125166
}
126167
}

src/imageio/psd.h

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,37 @@
22
#include <iostream>
33
#include <vector>
44

5-
void extract_psd_exif_xmp(std::istream &stream, std::vector<uint8_t> &exif, std::vector<uint8_t> &xmp);
5+
struct PSDMetadata
6+
{
7+
uint16_t num_channels = uint16_t(-1); // Supported range is 1 to 56.
8+
uint32_t width = uint32_t(-1); // Supported range is 1 to 30,000. (PSB max of 300,000.)
9+
uint32_t height = uint32_t(-1); // Supported range is 1 to 30,000. (PSB max of 300,000.)
10+
uint16_t depth = uint16_t(-1); // Supported values are 1, 8, 16 and 32.
11+
enum ColorMode : uint16_t
12+
{
13+
Bitmap = 0,
14+
Grayscale = 1,
15+
Indexed = 2,
16+
RGB = 3,
17+
CMYK = 4,
18+
Multichannel = 7,
19+
Duotone = 8,
20+
Lab = 9,
21+
NotSet = uint16_t(-1),
22+
};
23+
// Image properties
24+
ColorMode color_mode = NotSet;
25+
26+
static const char *color_mode_names[10];
27+
28+
// Metadata blocks
29+
std::vector<uint8_t> exif, exif3, xmp, iptc, icc_profile, thumbnail;
30+
31+
// Flags
32+
uint8_t is_copyright = uint8_t(-1); // arbitrary value meaning "not set"
33+
uint8_t is_icc_untagged = uint8_t(-1); // arbitrary value meaning "not set"
34+
35+
std::string url;
36+
37+
void read(std::istream &stream);
38+
};

0 commit comments

Comments
 (0)