228 lines
7.1 KiB
C++
228 lines
7.1 KiB
C++
//
|
|
// 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 <map>
|
|
#include <set>
|
|
#include <string>
|
|
|
|
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 <typename T>
|
|
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<std::string, std::set<std::string>> 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<std::pair<std::string, std::string>, int> layerToPart;
|
|
|
|
int partCount = 0;
|
|
|
|
for (std::map<std::string, std::set<std::string>>::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<std::string>::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<MultiViewChannelName> with a list of channels in the file
|
|
// and their corresponding part number
|
|
//
|
|
template <class T>
|
|
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
|