1 //
2 // SPDX-License-Identifier: BSD-3-Clause
3 // Copyright (c) Weta Digital, Ltd and Contributors to the OpenEXR Project.
4 //
5
6 //-----------------------------------------------------------------------------
7 //
8 // Functions related to accessing channels
9 // and views in multi-view OpenEXR files.
10 //
11 //-----------------------------------------------------------------------------
12
13 #include <ImfMultiView.h>
14
15 using namespace std;
16 #include "ImfNamespace.h"
17
18 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
19
20 namespace {
21
22 StringVector
parseString(string name,char c='.')23 parseString (string name, char c = '.')
24 {
25 //
26 // Turn name into a list of strings, separating
27 // on char 'c' with whitespace stripped.
28 //
29
30 StringVector r;
31
32 while (name.size() > 0)
33 {
34 size_t s = name.find (c);
35 string sec = name.substr (0, s);
36
37 //
38 // Strip spaces from beginning
39 //
40
41 while (sec.size() > 0 && sec[0] == ' ')
42 sec.erase (0, 1);
43
44 //
45 // Strip spaces from end
46 //
47
48 while (sec.size() > 0 && sec[sec.size() - 1] == ' ')
49 sec.erase (sec.size() - 1);
50
51 r.push_back (sec);
52
53 //
54 // Strip off name including ending 'c'
55 //
56
57 if (s == name.npos)
58 name = "";
59 else
60 name = name.substr (s + 1);
61 }
62
63 return r;
64 }
65
66
67 int
viewNum(const string & view,const StringVector & multiView)68 viewNum (const string &view, const StringVector &multiView)
69 {
70 //
71 // returns which view number is called 'view'
72 // returns -1 if no member of multiView is 'view'
73 // (i.e. if viewNum() returns -1, 'view' isn't a view name
74 // if viewNum() returns 0, 'view' is the default view
75 // otherwise, it's some other (valid) view
76 //
77
78 for (size_t i = 0; i < multiView.size(); ++i)
79 {
80 if (multiView[i] == view)
81 return i;
82 }
83
84 return -1;
85 }
86
87 } // namespace
88
89
90 string
defaultViewName(const StringVector & multiView)91 defaultViewName (const StringVector &multiView)
92 {
93 if (multiView.size() > 0)
94 return multiView[0];
95 else
96 return "";
97 }
98
99
100 string
viewFromChannelName(const string & channel,const StringVector & multiView)101 viewFromChannelName (const string &channel,
102 const StringVector &multiView)
103 {
104 //
105 // Given the name of a channel, return the name of the view to
106 // which it belongs.
107 //
108
109 //
110 // View name is penultimate section of name separated by periods ('.'s)
111 //
112
113 StringVector s = parseString (channel, '.');
114
115 if (s.size() == 0)
116 return ""; // nothing in, nothing out
117
118 if (s.size() == 1)
119 {
120 //
121 // Return default view name.
122 // The rules say ALL channels with no periods
123 // in the name belong to the default view.
124 //
125
126 return multiView[0];
127 }
128 else
129 {
130 //
131 // size >= 2 - the last part is the channel name,
132 // the next-to-last part is the view name.
133 // Check if that part of the name really is
134 // a valid view and, if it is, return it.
135 //
136
137 const string &viewName = s[s.size() - 2];
138
139 if (viewNum (viewName, multiView) >= 0)
140 return viewName;
141 else
142 return ""; // not associated with any particular view
143 }
144 }
145
146
147 ChannelList
channelsInView(const string & viewName,const ChannelList & channelList,const StringVector & multiView)148 channelsInView (const string & viewName,
149 const ChannelList & channelList,
150 const StringVector & multiView)
151 {
152 //
153 // Return a list of all channels belonging to view viewName.
154 //
155
156 ChannelList q;
157
158 for (ChannelList::ConstIterator i = channelList.begin();
159 i != channelList.end();
160 ++i)
161 {
162 //
163 // Get view name for this channel
164 //
165
166 string view = viewFromChannelName (i.name(), multiView);
167
168
169 //
170 // Insert channel into q if it's a member of view viewName
171 //
172
173 if (view == viewName)
174 q.insert (i.name(), i.channel());
175 }
176
177 return q;
178 }
179
180
181 ChannelList
channelsInNoView(const ChannelList & channelList,const StringVector & multiView)182 channelsInNoView (const ChannelList &channelList,
183 const StringVector &multiView)
184 {
185 //
186 // Return a list of channels not associated with any named view.
187 //
188
189 return channelsInView ("", channelList, multiView);
190 }
191
192
193
194 bool
areCounterparts(const string & channel1,const string & channel2,const StringVector & multiView)195 areCounterparts (const string &channel1,
196 const string &channel2,
197 const StringVector &multiView)
198 {
199 //
200 // Given two channels, return true if they are the same
201 // channel in two different views.
202 //
203
204 StringVector chan1 = parseString (channel1);
205 size_t size1 = chan1.size(); // number of SECTIONS in string
206 // name (not string length)
207
208 StringVector chan2 = parseString (channel2);
209 size_t size2 = chan2.size();
210
211 if (size1 == 0 || size2 == 0)
212 return false;
213
214 //
215 // channel1 and channel2 can't be counterparts
216 // if either channel is in no view.
217 //
218
219 if (size1 > 1 && viewNum (chan1[size1 - 2], multiView) == -1)
220 return false;
221
222 if (size2 > 1 && viewNum (chan2[size2 - 2], multiView) == -1)
223 return false;
224
225 if (viewFromChannelName (channel1, multiView) ==
226 viewFromChannelName (channel2, multiView))
227 {
228 //
229 // channel1 and channel2 are not counterparts
230 // if they are in the same view.
231 //
232
233 return false;
234 }
235
236 if (size1 == 1)
237 {
238 //
239 // channel1 is a default channel - the channels will only be
240 // counterparts if channel2 is of the form <view>.<channel1>
241 //
242
243 return size2 == 2 && chan1[0] == chan2[1];
244 }
245
246 if (size2 == 1)
247 {
248 //
249 // channel2 is a default channel - the channels will only be
250 // counterparts if channel1 is of the form <view>.<channel2>
251 //
252
253 return size1 == 2 && chan2[0] == chan1[1];
254 }
255
256 //
257 // Neither channel is a default channel. To be counterparts both
258 // channel names must have the same number of components, and
259 // all components except the penultimate one must be the same.
260 //
261
262 if (size1 != size2)
263 return false;
264
265 for(size_t i = 0; i < size1; ++i)
266 {
267 if (i != size1 - 2 && chan1[i] != chan2[i])
268 return false;
269 }
270
271 return true;
272 }
273
274
275 ChannelList
channelInAllViews(const string & channelName,const ChannelList & channelList,const StringVector & multiView)276 channelInAllViews (const string &channelName,
277 const ChannelList &channelList,
278 const StringVector &multiView)
279 {
280 //
281 // Given the name of a channel, return a
282 // list of the same channel in all views.
283 //
284
285 ChannelList q;
286
287 for (ChannelList::ConstIterator i=channelList.begin();
288 i != channelList.end();
289 ++i)
290 {
291 if (i.name() == channelName ||
292 areCounterparts (i.name(), channelName, multiView))
293 {
294 q.insert (i.name(), i.channel());
295 }
296 }
297
298 return q;
299 }
300
301
302 string
channelInOtherView(const string & channelName,const ChannelList & channelList,const StringVector & multiView,const string & otherViewName)303 channelInOtherView (const string &channelName,
304 const ChannelList &channelList,
305 const StringVector &multiView,
306 const string &otherViewName)
307 {
308 //
309 // Given the name of a channel in one view, return the
310 // corresponding channel name for view otherViewName.
311 //
312
313 for (ChannelList::ConstIterator i=channelList.begin();
314 i != channelList.end();
315 ++i)
316 {
317 if (viewFromChannelName (i.name(), multiView) == otherViewName &&
318 areCounterparts (i.name(), channelName, multiView))
319 {
320 return i.name();
321 }
322 }
323
324 return "";
325 }
326
327
328 string
insertViewName(const string & channel,const StringVector & multiView,int i)329 insertViewName (const string &channel,
330 const StringVector &multiView,
331 int i)
332 {
333 //
334 // Insert multiView[i] into the channel name if appropriate.
335 //
336
337 StringVector s = parseString (channel, '.');
338
339 if (s.size() == 0)
340 return ""; // nothing in, nothing out
341
342 if (s.size() == 1 && i == 0)
343 {
344 //
345 // Channel in the default view, with no periods in its name.
346 // Do not insert view name.
347 //
348
349 return channel;
350 }
351
352 //
353 // View name becomes penultimate section of new channel name.
354 //
355
356 string newName;
357
358 for (size_t j = 0; j < s.size(); ++j)
359 {
360 if (j < s.size() - 1)
361 newName += s[j] + ".";
362 else
363 newName += multiView[i] + "." + s[j];
364 }
365
366 return newName;
367 }
368
369
370 string
removeViewName(const string & channel,const string & view)371 removeViewName(const string & channel,const string & view)
372 {
373 StringVector s = parseString (channel, '.');
374
375 if (s.size() == 0)
376 return ""; // nothing in, nothing out
377
378 if (s.size() == 1)
379 {
380 //
381 // Channel in the default view, since no periods in its name.
382 // No viewname to remove
383 //
384
385 return channel;
386 }
387
388 string newName;
389 for( size_t j = 0 ; j < s.size() ; ++j)
390 {
391 // only add the penultimate string part
392 // if it doesn't match the view name
393 if(j+2!=s.size() || s[j]!=view)
394 {
395 newName += s[j];
396 if(j+1!=s.size())
397 {
398 newName += ".";
399 }
400 }
401 }
402
403 return newName;
404
405 }
406
407 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
408