Write image data to disk.
161{
163
164 if (!data.is_consistent()) {
165 m_last_error =
"ImageData variant does not match declared ImageFormat";
167 return false;
168 }
169
170 if (data.width == 0 || data.height == 0 || data.channels == 0) {
173 return false;
174 }
175
176 const auto* src = data.as_float();
177 if (!src) {
178 m_last_error =
"EXR requires float ImageData (uint8 and uint16 not supported)";
180 return false;
181 }
182
183 const size_t expected_elements = static_cast<size_t>(data.width) * data.height * data.channels;
184 if (src->size() < expected_elements) {
186 + std::to_string(expected_elements)
187 + " got " + std::to_string(src->size());
189 return false;
190 }
191
193 auto planes =
deinterleave(*src, data.width, data.height, data.channels,
194 options.flip_vertically);
195
196
197
198
200 InitEXRImage(&
image);
201
202 image.num_channels =
static_cast<int>(data.channels);
203 image.width =
static_cast<int>(data.width);
204 image.height =
static_cast<int>(data.height);
205
206 std::vector<float*> plane_ptrs(data.channels);
207 for (uint32_t c = 0; c < data.channels; ++c) {
208 plane_ptrs[c] = planes[c].data();
209 }
210 image.images =
reinterpret_cast<unsigned char**
>(plane_ptrs.data());
211
212
213
214
215 EXRHeader header;
216 InitEXRHeader(&header);
217
218 header.num_channels = static_cast<int>(data.channels);
219 header.compression_type = resolve_compression(options.compression);
220
221 std::vector<EXRChannelInfo> channel_infos(data.channels);
222 std::vector<int> pixel_types(data.channels, TINYEXR_PIXELTYPE_FLOAT);
223 std::vector<int> requested_pixel_types(data.channels, TINYEXR_PIXELTYPE_FLOAT);
224
225 for (uint32_t c = 0; c < data.channels; ++c) {
226 std::memset(&channel_infos[c], 0, sizeof(EXRChannelInfo));
227 const std::string& n = names[c];
228 const size_t copy_len = std::min<size_t>(n.size(), 255);
229 std::memcpy(channel_infos[c].name, n.data(), copy_len);
230 channel_infos[c].name[copy_len] = '\0';
231 }
232
233 header.channels = channel_infos.data();
234 header.pixel_types = pixel_types.data();
235 header.requested_pixel_types = requested_pixel_types.data();
236
237
238
239
240 unsigned char* encoded = nullptr;
241 const char* err = nullptr;
242
243 const size_t encoded_size = SaveEXRImageToMemory(
244 &
image, &header, &encoded, &err);
245
246 if (encoded_size == 0 || encoded == nullptr) {
248 : "SaveEXRImageToMemory returned 0 bytes";
249 if (err) {
250 FreeEXRErrorMessage(err);
251 }
253 return false;
254 }
255
256 const bool ok = flush_to_disk(filepath, encoded, encoded_size);
257
258 std::free(encoded);
259
260 if (!ok) {
263 return false;
264 }
265
267 "Wrote EXR: {} ({}x{}, {} channels, compression {}, {} bytes)",
268 filepath, data.width, data.height, data.channels,
269 header.compression_type, encoded_size);
270
271 return true;
272}
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
std::vector< std::string > resolve_channel_names(const ImageData &data, const ImageWriteOptions &options) const
Resolve channel name list based on options and channel count.
static std::vector< std::vector< float > > deinterleave(const std::vector< float > &src, uint32_t width, uint32_t height, uint32_t channels, bool flip_vertically)
Deinterleave source float buffer into N planar channel buffers.
@ FileIO
Filesystem I/O operations.
@ IO
Networking, file handling, streaming.