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