Skip to content

Commit 4c8205b

Browse files
committed
optimisation
1 parent d1057f1 commit 4c8205b

File tree

2 files changed

+189
-189
lines changed

2 files changed

+189
-189
lines changed

ExifBulider/ExifBuilder.h

Lines changed: 186 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,194 @@
44
#include <string>
55
#include <variant>
66

7+
////////////////////////////////////////////////////////////////////////////////////
8+
// ExifTag structure:
9+
//
10+
// - tag: The tag ID (e.g., 0x010F for Manufacturer)
11+
//
12+
// - type:
13+
// 0x0001 - BYTE (8-bit unsigned integer)
14+
// 0x0002 - ASCII (8-bit byte containing one 7-bit ASCII code)
15+
// 0x0003 - SHORT (16-bit unsigned integer)
16+
// 0x0004 - LONG (32-bit unsigned integer)
17+
// 0x0005 - RATIONAL (Two LONGs: numerator and denominator)
18+
// 0x0007 - UNDEFINED (8-bit byte that can take any value depending on the field definition)
19+
// 0x0009 - SLONG (32-bit signed integer)
20+
// 0x000A - SRATIONAL (Two SLONGs: numerator and denominator)
21+
//
722
struct ExifTag {
8-
uint16_t tag; // Tag ID
9-
uint16_t type; // Data type (e.g., ASCII, BYTE, SHORT, LONG, RATIONAL)
10-
uint32_t count; // Number of data items
11-
std::variant<uint32_t, std::string, std::vector<uint8_t>> value; // Either a direct value, a string, or raw data
23+
uint16_t tag;
24+
uint16_t type;
25+
uint32_t count;
26+
std::vector<uint8_t> value;
1227

13-
ExifTag(uint16_t tag, uint16_t type, uint32_t count, uint32_t value)
14-
: tag(tag), type(type), count(count), value(value) {}
28+
// Constructor for 8-bit integer values (BYTE)
29+
ExifTag(uint16_t t, uint16_t tp, uint32_t cnt, uint8_t val)
30+
: tag(t), type(tp), count(cnt), value({ val }) {}
1531

16-
ExifTag(uint16_t tag, uint16_t type, const std::string& value)
17-
: tag(tag), type(type), count(value.size() + 1), value(value) {} // +1 for null terminator
32+
// Constructor for 16-bit integer values (SHORT)
33+
ExifTag(uint16_t t, uint16_t tp, uint32_t cnt, uint16_t val)
34+
: tag(t), type(tp), count(cnt), value(2) {
35+
std::memcpy(value.data(), &val, 2);
36+
}
1837

19-
ExifTag(uint16_t tag, uint16_t type, const std::vector<uint8_t>& value)
20-
: tag(tag), type(type), count(value.size()), value(value) {}
38+
// Constructor for 32-bit integer values (LONG)
39+
ExifTag(uint16_t t, uint16_t tp, uint32_t cnt, uint32_t val)
40+
: tag(t), type(tp), count(cnt), value(4) {
41+
std::memcpy(value.data(), &val, 4);
42+
}
43+
44+
// Constructor for RATIONAL (Two 32-bit integers: numerator and denominator)
45+
ExifTag(uint16_t t, uint16_t tp, uint32_t cnt, uint32_t num, uint32_t denom)
46+
: tag(t), type(tp), count(cnt), value(8) {
47+
std::memcpy(value.data(), &num, 4);
48+
std::memcpy(value.data() + 4, &denom, 4);
49+
}
50+
51+
// Constructor for string values, copying the string into the vector
52+
ExifTag(uint16_t t, uint16_t tp, const std::string& val)
53+
: tag(t), type(tp), count(static_cast<uint32_t>(val.size() + 1)), value(val.begin(), val.end()) {
54+
value.push_back('\0'); // Null-terminate the string
55+
}
56+
};
57+
58+
// ExifBuilder class
59+
class ExifBuilder {
60+
private:
61+
std::vector<ExifTag> tags; // List of EXIF tags
62+
std::vector<uint8_t> extraData; // Buffer for extra data (strings, RATIONALs, etc.)
63+
64+
public:
65+
void addTag(ExifTag&& tag) {
66+
tags.push_back(std::move(tag));
67+
}
68+
69+
std::vector<uint8_t> buildExifBlob() {
70+
std::vector<uint8_t> exifBlob;
71+
72+
// Placeholder for APP1 header (to be filled in later)
73+
exifBlob.insert(exifBlob.end(), { 0xFF, 0xE1, 0x00, 0x00 }); // APP1 marker, length placeholder
74+
exifBlob.insert(exifBlob.end(), { 'E', 'x', 'i', 'f', 0x00, 0x00 }); // "Exif" identifier and padding
75+
76+
// Write TIFF Header
77+
bool bigendian = true;
78+
appendUInt16(exifBlob, bigendian ? 0x4D4D : 0x4949); // Big-endian indicator
79+
appendUInt16(exifBlob, bigendian ? 0x002A : 0x2000); // TIFF version
80+
appendUInt32(exifBlob, bigendian ? 0x00000008 : 0x08000000); // Offset to the first IFD
81+
82+
// Number of directory entries
83+
appendUInt16(exifBlob, static_cast<uint16_t>(tags.size()));
84+
85+
// Calculate data offset (just after IFD entries and next IFD offset)
86+
size_t dataOffset = 8 + 2 + (tags.size() * 12) + 4;
87+
88+
// Process each tag
89+
for (auto& tag : tags) {
90+
appendUInt16(exifBlob, tag.tag);
91+
appendUInt16(exifBlob, tag.type);
92+
appendUInt32(exifBlob, tag.count);
93+
94+
if (tagFitsInField(tag)) {
95+
writeTagValue(exifBlob, tag); // Write values directly as is
96+
}
97+
else {
98+
appendUInt32(exifBlob, static_cast<uint32_t>(dataOffset));
99+
appendExtraData(tag, dataOffset);
100+
}
101+
}
102+
103+
// Write the next IFD offset (0 indicates no more IFDs)
104+
appendUInt32(exifBlob, 0);
105+
106+
// Append the extra data (strings, RATIONALs, etc.)
107+
exifBlob.insert(exifBlob.end(), extraData.begin(), extraData.end());
108+
109+
// Update the APP1 segment length
110+
uint16_t exifLength = static_cast<uint16_t>(exifBlob.size() - 2); // Length excluding the APP1 marker (FF E1)
111+
exifBlob[2] = (exifLength >> 8) & 0xFF;
112+
exifBlob[3] = exifLength & 0xFF;
113+
114+
return exifBlob;
115+
}
116+
117+
private:
118+
// Corrected function to append a 16-bit integer in big-endian format to a vector
119+
static void appendUInt16(std::vector<uint8_t>& vec, uint16_t value, bool bigendian = true) {
120+
if (bigendian) {
121+
vec.push_back((value >> 8) & 0xFF);
122+
vec.push_back(value & 0xFF);
123+
}
124+
else {
125+
vec.push_back(value & 0xFF);
126+
vec.push_back((value >> 8) & 0xFF);
127+
}
128+
}
129+
130+
// Corrected function to append a 32-bit integer in big-endian format to a vector
131+
static void appendUInt32(std::vector<uint8_t>& vec, uint32_t value, bool bigendian = true) {
132+
if (bigendian) {
133+
vec.push_back((value >> 24) & 0xFF);
134+
vec.push_back((value >> 16) & 0xFF);
135+
vec.push_back((value >> 8) & 0xFF);
136+
vec.push_back(value & 0xFF);
137+
} else {
138+
vec.push_back(value & 0xFF);
139+
vec.push_back((value >> 8) & 0xFF);
140+
vec.push_back((value >> 16) & 0xFF);
141+
vec.push_back((value >> 24) & 0xFF);
142+
}
143+
}
144+
145+
void writeTagValue(std::vector<uint8_t>& buffer, const ExifTag& tag, bool bigendian = true) {
146+
// byte order alwas from left to the right.
147+
// in case of SHORT, added a padding 0 byte to the right.
148+
// in case of less 4-bytes STRING, added a padding 0 byte to the right,
149+
// otherwise use an offset to the extra data.
150+
// big endian similar to the standard writing, little endian inverted (intel/x86/x64).
151+
size_t bufSize = buffer.size();
152+
switch (tag.type) {
153+
case 0x0001: // BYTE
154+
buffer.resize(bufSize + 4, 0);
155+
buffer[bufSize] = tag.value[0];
156+
break;
157+
case 0x0003: // SHORT
158+
buffer.resize(bufSize + 4, 0);
159+
buffer[bufSize + (bigendian ? 1 : 0)] = tag.value[0];
160+
buffer[bufSize + (bigendian ? 0 : 1)] = tag.value[1];
161+
break;
162+
case 0x0004: // LONG
163+
buffer.resize(bufSize + 4, 0);
164+
buffer[bufSize + (bigendian ? 3 : 0)] = tag.value[0];
165+
buffer[bufSize + (bigendian ? 2 : 1)] = tag.value[1];
166+
buffer[bufSize + (bigendian ? 1 : 2)] = tag.value[2];
167+
buffer[bufSize + (bigendian ? 0 : 3)] = tag.value[3];
168+
break;
169+
case 0x0002: // ASCII
170+
buffer.resize(bufSize + tag.value.size(), 0);
171+
std::copy(tag.value.begin(), tag.value.end(), buffer.begin() + bufSize);
172+
break;
173+
}
174+
}
175+
176+
bool tagFitsInField(const ExifTag& tag) const {
177+
if (tag.type == 0x0001 || tag.type == 0x0003 || tag.type == 0x0004) {
178+
return true;
179+
}
180+
else if (tag.type == 0x0002) {
181+
return tag.value.size() <= 4;
182+
}
183+
// tag.type == 0x0005 (RATIONAL) is always stored in extra data
184+
return false;
185+
}
186+
187+
void appendExtraData(const ExifTag& tag, size_t& dataOffset) {
188+
const auto& data = tag.value;
189+
extraData.insert(extraData.end(), data.begin(), data.end());
190+
dataOffset += data.size();
191+
// add a padding 0 byte.
192+
if (data.size() % 2 != 0) {
193+
extraData.push_back(0);
194+
++dataOffset;
195+
}
196+
}
21197
};

0 commit comments

Comments
 (0)