@@ -59,15 +59,41 @@ struct ExrChannelLayout {
5959 int idxG = -1 ;
6060 int idxB = -1 ;
6161 int idxA = -1 ;
62- int idxY = -1 ;
62+
63+ std::map<std::string, int > customChannels = {};
6364
6465 int CountChannels () const {
6566 return
6667 (idxR >= 0 ? 1 : 0 ) +
6768 (idxG >= 0 ? 1 : 0 ) +
6869 (idxB >= 0 ? 1 : 0 ) +
6970 (idxA >= 0 ? 1 : 0 ) +
70- (idxY >= 0 ? 1 : 0 );
71+ customChannels.size ();
72+ }
73+
74+ std::string NameOf (int idx) const {
75+ int rgbaOffset =
76+ (idxR >= 0 ? 1 : 0 ) +
77+ (idxG >= 0 ? 1 : 0 ) +
78+ (idxB >= 0 ? 1 : 0 ) +
79+ (idxA >= 0 ? 1 : 0 );
80+
81+ if (idx < rgbaOffset) {
82+ char * fixed = (char *)alloca (4 );
83+ int offset = 0 ;
84+ if (idxR >= 0 ) fixed[offset++] = ' R' ;
85+ if (idxG >= 0 ) fixed[offset++] = ' G' ;
86+ if (idxB >= 0 ) fixed[offset++] = ' B' ;
87+ if (idxA >= 0 ) fixed[offset++] = ' A' ;
88+
89+ return std::string { fixed[idx] };
90+ }
91+
92+ int i = idx - rgbaOffset;
93+ auto it = customChannels.cbegin ();
94+ for (; it != customChannels.cend (); ++it)
95+ if (i-- == 0 ) break ;
96+ return it->first ;
7197 }
7298};
7399
@@ -156,48 +182,43 @@ int CacheExrImage(const char* filename) {
156182 }
157183
158184 // Read the number of channels in each layer
159- int freeChannels = 0 ;
160185 for (int chan = 0 ; chan < result.header .num_channels ; ++chan) {
161- // Extract the layer name by assuming a channel name of the form "layername.R", "layername.B" and so on
162- size_t len = strlen (result.header .channels [chan].name );
163-
164- std::string layerName;
165- if (len <= 2 ) // Plain channels without a layer will be merged into an unnamed default layer (e.g., "R", "G", and "B")
186+ // Extract the layer name by assuming a channel name of the form "layername.channelname"
187+ std::string name = result.header .channels [chan].name ;
188+ size_t len = name.size ();
189+
190+ size_t idxSep = name.rfind (' .' );
191+ std::string layerName, chanName;
192+ if (idxSep == name.npos ) {
193+ // Plain channels without a layer will be merged into an unnamed default layer (e.g., "R", "G", and "B")
166194 layerName = " " ;
167- else if (result. header . channels [chan]. name [len - 2 ] != ' . ' ) // Keep the full name as the layer name
168- layerName = std::string (result. header . channels [chan]. name , len);
169- else // Remove channel name from the layer name (e.g., .R / .G / .B)
170- layerName = std::string (result. header . channels [chan]. name , len - 2 );
171-
172- char chanName = result. header . channels [chan]. name [len - 1 ];
195+ chanName = name;
196+ } else {
197+ // Remove channel name from the layer name
198+ layerName = name. substr ( 0 , idxSep );
199+ chanName = name. substr (idxSep + 1 );
200+ }
173201
174202 // Update the channel layout info
175203 auto iter = result.channelsPerLayer .find (layerName);
176204 if (iter == result.channelsPerLayer .end ()) {
177205 result.channelsPerLayer [layerName] = ExrChannelLayout ();
178206 result.layerNames .emplace_back (layerName);
179207 }
180-
181208 auto & layout = result.channelsPerLayer [layerName];
182209
183- switch (chanName) {
184- case ' R' :
210+ // Handle known channel names R G B and A so we can reorder them
211+ // All other channel names will be alphabethically sorted
212+ if (chanName == " R" )
185213 layout.idxR = chan;
186- break ;
187- case ' G' :
214+ else if (chanName == " G" )
188215 layout.idxG = chan;
189- break ;
190- case ' B' :
216+ else if (chanName == " B" )
191217 layout.idxB = chan;
192- break ;
193- case ' A' :
218+ else if (chanName == " A" )
194219 layout.idxA = chan;
195- break ;
196- case ' Y' :
197- default :
198- layout.idxY = chan;
199- break ;
200- }
220+ else if (!layout.customChannels .emplace (chanName, chan).second )
221+ std::cerr << " Duplicate channel '" << chanName << " ' in layer '" << layerName << " ' ignored." << std::endl;
201222 }
202223
203224 cacheMutex.lock ();
@@ -234,44 +255,24 @@ bool CopyCachedExrLayer(int id, std::string layerName, float* out) {
234255 int numChannels = layerInfo.CountChannels ();
235256
236257 auto swizzle = [numChannels, &layerInfo, &layerName](unsigned char ** images, int srcIdx, int dstIdx, float * out) {
237- if (numChannels == 1 ) { // Y
238- assert (layerInfo.idxY >= 0 );
239- auto chanImg = (float *)images[layerInfo.idxY ];
240- out[dstIdx + 0 ] = chanImg[srcIdx];
241- } else if (numChannels == 3 ) { // RGB
242- assert (layerInfo.idxR >= 0 );
243- auto chanImg = (float *)images[layerInfo.idxR ];
244- out[dstIdx + 0 ] = chanImg[srcIdx];
245-
246- assert (layerInfo.idxG >= 0 );
247- chanImg = (float *)images[layerInfo.idxG ];
248- out[dstIdx + 1 ] = chanImg[srcIdx];
249-
250- assert (layerInfo.idxB >= 0 );
251- chanImg = (float *)images[layerInfo.idxB ];
252- out[dstIdx + 2 ] = chanImg[srcIdx];
253- } else if (numChannels == 4 ) { // RGBA
254- assert (layerInfo.idxR >= 0 );
255- auto chanImg = (float *)images[layerInfo.idxR ];
256- out[dstIdx + 0 ] = chanImg[srcIdx];
257-
258- assert (layerInfo.idxG >= 0 );
259- chanImg = (float *)images[layerInfo.idxG ];
260- out[dstIdx + 1 ] = chanImg[srcIdx];
261-
262- assert (layerInfo.idxB >= 0 );
263- chanImg = (float *)images[layerInfo.idxB ];
264- out[dstIdx + 2 ] = chanImg[srcIdx];
265-
266- assert (layerInfo.idxA >= 0 );
267- chanImg = (float *)images[layerInfo.idxA ];
268- out[dstIdx + 3 ] = chanImg[srcIdx];
269- } else {
270- std::cerr << " ERROR while reading .exr layer " << layerName << " : Images with "
271- << numChannels << " channels are currently not supported. " << std::endl;
272- cacheMutex.unlock ();
273- return false ;
274- }
258+ int offset = 0 ;
259+ auto add = [&](int idx) {
260+ auto chanImg = (float *)images[idx];
261+ out[dstIdx + offset] = chanImg[srcIdx];
262+ offset++;
263+ };
264+
265+ if (layerInfo.idxR >= 0 )
266+ add (layerInfo.idxR );
267+ if (layerInfo.idxG >= 0 )
268+ add (layerInfo.idxG );
269+ if (layerInfo.idxB >= 0 )
270+ add (layerInfo.idxB );
271+ if (layerInfo.idxA >= 0 )
272+ add (layerInfo.idxA );
273+ for (auto it = layerInfo.customChannels .begin (); it != layerInfo.customChannels .end (); ++it)
274+ add (it->second );
275+
275276 return true ;
276277 };
277278
@@ -896,6 +897,20 @@ SIIO_API void GetExrLayerName(int id, int layerIdx, char* out) {
896897 cacheMutex.unlock ();
897898}
898899
900+ SIIO_API int GetExrChannelNameLen (int id, const char * layerName, int channelIdx) {
901+ cacheMutex.lock ();
902+ const auto & chanName = exrImages[id].channelsPerLayer [layerName].NameOf (channelIdx);
903+ cacheMutex.unlock ();
904+ return chanName.size ();
905+ }
906+
907+ SIIO_API void GetExrChannelName (int id, const char * layerName, int channelIdx, char * out) {
908+ cacheMutex.lock ();
909+ const auto & chanName = exrImages[id].channelsPerLayer [layerName].NameOf (channelIdx);
910+ strcpy (out, chanName.c_str ());
911+ cacheMutex.unlock ();
912+ }
913+
899914SIIO_API bool CopyCachedLayer (int id, const char * name, float * out) {
900915 return CopyCachedExrLayer (id, name, out);
901916}
@@ -960,7 +975,6 @@ SIIO_API int GetExrLayerNames(const char* filename, char*** names) {
960975 }
961976
962977 // Read the number of channels in each layer
963- int freeChannels = 0 ;
964978 std::unordered_set<std::string> layerNames;
965979 for (int chan = 0 ; chan < header.num_channels ; ++chan) {
966980 // Extract the layer name by assuming a channel name of the form "layername.R", "layername.B" and so on
0 commit comments