// // SPDX-License-Identifier: BSD-3-Clause // Copyright (c) Weta Digital, Ltd and Contributors to the OpenEXR Project. // #ifndef INCLUDED_IMF_PARTHELPER_H #define INCLUDED_IMF_PARTHELPER_H //----------------------------------------------------------------------------- // // Functions to help split channels into separate parts: provide a list of // channels, with desired views. call SplitChannels to assign a part to each // layer, or correct the name of the channel. // Also can enumerate the parts in a file and list which parts channels are in // // This is a good way to offer a 'create Multipart file' checkbox to the user in a // write dialog box: Populate a list of MultiViewChannelName objects, // call SplitChannels with whether single or multipart files are required. // Then write the number of parts it specifies, using internal_name for the channel // names in the ChannelList and FrameBuffer objects. There should be no need // for different codepaths for single part and multipart files // // Similarly, on reading a file as a MultiPartInputFile, use GetChannelsInMultiPartFile to // enumerate all channels in the file, using internal_name in FrameBuffer objects // to read the channel // // //----------------------------------------------------------------------------- #include "ImfChannelList.h" #include "ImfExport.h" #include "ImfForward.h" #include "ImfMultiPartInputFile.h" #include "ImfMultiView.h" #include "ImfNamespace.h" #include "ImfStandardAttributes.h" #include "ImfStringVectorAttribute.h" #include #include #include OPENEXR_IMF_INTERNAL_NAMESPACE_HEADER_ENTER struct MultiViewChannelName { public: std::string name; ///< name of channel std::string view; ///< view for channel int part_number; ///< part number: updated by SplitChannels std::string internal_name; ///< name used in headers: in singlepart mode, may contain viewname //return layer for this channel, or "" if no layer std::string getLayer () const { std::size_t q = name.rfind ('.'); if (q == name.npos) { return ""; } return name.substr (0, q); } std::string getSuffix () const { std::size_t q = name.rfind ('.'); if (q == name.npos) { return name; } return name.substr (q + 1); } }; // ///\brief assigns individual channels to different parts based on their layer and view name /// input is an array, list, vector etc of MultiViewChannelName objects /// on entry, each MultiViewChannelName name/view must be set (view can be empty if not multiview) /// /// if singlepart set, then on exit part_number will be zero, and internal_name will have view name inserted /// otherwise, each channel will be assigned to a different part based on its layer name and view name /// /// @param begin pointer to first MultiViewChannelName item /// @param end pointer to end of MultiViewChannelName item array /// @return total number of parts required // template inline int SplitChannels ( const T& begin, const T& end, bool multipart = true, const std::string& heroView = std::string ()) { if (!multipart) { for (T i = begin; i != end; i++) { i->part_number = 0; //does this have a view name set? if (i->view == "") { i->internal_name = i->name; } else { std::string lname = i->getLayer (); // no layer, only non-hero views get view name in layer name if (lname == "") { if (i->view == heroView) { i->internal_name = i->name; } else { i->internal_name = i->view + "." + i->name; } } else { i->internal_name = lname + "." + i->view + "." + i->getSuffix (); } } } // single part created return 1; } else { // step 1: extract individual layers and parts // for each layer, enumerate which views are active std::map> viewsInLayers; for (T i = begin; i != end; i++) { viewsInLayers[i->getLayer ()].insert (i->view); } // step 2: assign a part number to each layer/view std::map, int> layerToPart; int partCount = 0; for (std::map>::const_iterator layer = viewsInLayers.begin (); layer != viewsInLayers.end (); layer++) { // if this layer has a heroView, insert that first bool layer_has_hero = layer->second.find (heroView) != layer->second.end (); if (layer_has_hero) { layerToPart[std::make_pair (layer->first, heroView)] = partCount++; } // insert other layers which aren't the hero view for (std::set::const_iterator view = layer->second.begin (); view != layer->second.end (); view++) { if (*view != heroView) { layerToPart[std::make_pair (layer->first, *view)] = partCount++; } } } // step 3: update part number of each provided channel for (T i = begin; i != end; i++) { i->internal_name = i->name; i->part_number = layerToPart[std::make_pair (i->getLayer (), i->view)]; } // return number of parts created return partCount; } } // // populate the chans vector with a list of channels in the file // and their corresponding part number // template inline void GetChannelsInMultiPartFile (const MultiPartInputFile& file, T& chans) { bool has_multiview = false; StringVector mview; if (file.parts () == 1) { if (hasMultiView (file.header (0))) { mview = multiView (file.header (0)); has_multiview = true; } } for (int p = 0; p < file.parts (); p++) { const ChannelList& c = file.header (p).channels (); std::string view = ""; if (file.header (p).hasView ()) { view = file.header (p).view (); } for (ChannelList::ConstIterator i = c.begin (); i != c.end (); i++) { MultiViewChannelName m; m.name = std::string (i.name ()); m.internal_name = m.name; if (has_multiview) { m.view = viewFromChannelName (m.name, mview); m.name = removeViewName (m.internal_name, m.view); } else { m.view = view; } m.part_number = p; chans.push_back (m); } } } OPENEXR_IMF_INTERNAL_NAMESPACE_HEADER_EXIT #endif