Skip to content

Commit 1c65854

Browse files
committed
oiiotool: new --layersplit action for splitting channel-name-based layers on the stack
Signed-off-by: Loïc Vital <[email protected]>
1 parent 85ece69 commit 1c65854

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed

src/oiiotool/oiiotool.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2880,6 +2880,79 @@ action_subimage_split(Oiiotool& ot, cspan<const char*> argv)
28802880

28812881

28822882

2883+
// --layersplit
2884+
static void
2885+
action_layer_split(Oiiotool& ot, cspan<const char*> argv)
2886+
{
2887+
if (ot.postpone_callback(1, action_layer_split, argv))
2888+
return;
2889+
string_view command = ot.express(argv[0]);
2890+
OTScopedTimer timer(ot, command);
2891+
2892+
ImageRecRef A = ot.pop();
2893+
ot.read(A);
2894+
2895+
// Split and push the individual channel-name-based layers onto the stack
2896+
ImageSpec* spec = A->spec();
2897+
int chbegin = 0;
2898+
std::vector<std::string> newchannelnames;
2899+
for (size_t i = 0; i < spec->channelnames.size(); ++i) {
2900+
// Parse full channel name to extract the layer name
2901+
// and the actual channel name, which will be used for
2902+
// renaming channels during the split
2903+
// Examples:
2904+
// chname = "R" -> layername = "", newchname = "R"
2905+
// chname = "diffuse.G" -> layername = "diffuse", newchname = "G"
2906+
const std::string& chname = spec->channelnames[i];
2907+
const auto parts = Strutil::splits(chname, ".", 2);
2908+
const std::string layername = (parts.size() < 2) ? "" : parts[0];
2909+
const std::string newchname = (parts.size() < 2) ? parts[0] : parts[1];
2910+
newchannelnames.push_back(newchname);
2911+
2912+
bool pushlayer = false;
2913+
if (i < spec->channelnames.size() - 1) {
2914+
// Parse the layer name of the next channel,
2915+
// a different value means that we will be processing
2916+
// a new layer at the next iteration, therefore
2917+
// we should push the current one on the stack
2918+
const std::string& nextchname = spec->channelnames[i + 1];
2919+
const auto nextparts = Strutil::splits(nextchname, ".", 2);
2920+
const std::string nextlayername = (nextparts.size() < 2)
2921+
? ""
2922+
: nextparts[0];
2923+
pushlayer = (nextlayername != layername);
2924+
} else {
2925+
// Force flag to true at last iteration so that
2926+
// last layer is also pushed on the stack
2927+
pushlayer = true;
2928+
}
2929+
2930+
if (pushlayer) {
2931+
// Split the current layer by isolating its channels
2932+
// in a new ImageBuf and renaming them, and store the
2933+
// layer name in the oiio:subimagename metadata so we
2934+
// can reuse it later (e.g for creating a multi-part image)
2935+
const int chend = i + 1;
2936+
ImageBufRef img(new ImageBuf());
2937+
std::vector<int> channelorder(chend - chbegin);
2938+
std::iota(channelorder.begin(), channelorder.end(), chbegin);
2939+
ImageBufAlgo::channels(*img, (*A)(), chend - chbegin, channelorder,
2940+
{}, newchannelnames);
2941+
img->specmod().attribute("oiio:subimagename", layername);
2942+
2943+
// Create corresponding ImageRec and push it on the stack
2944+
ImageRecRef R(new ImageRec(img, true));
2945+
ot.push(R);
2946+
2947+
// Prepare processing of next layer
2948+
chbegin = chend;
2949+
newchannelnames.clear();
2950+
}
2951+
}
2952+
}
2953+
2954+
2955+
28832956
static void
28842957
action_subimage_append_n(Oiiotool& ot, int n, string_view command)
28852958
{
@@ -6898,6 +6971,9 @@ Oiiotool::getargs(int argc, char* argv[])
68986971
ap.arg("--siappendall")
68996972
.help("Append all images on the stack into a single multi-subimage image")
69006973
.OTACTION(action_subimage_append_all);
6974+
ap.arg("--layersplit")
6975+
.help("Split the top image's channel-name-based layers into separate images on the stack")
6976+
.OTACTION(action_layer_split);
69016977
ap.arg("--deepen")
69026978
.help("Deepen normal 2D image to deep")
69036979
.OTACTION(action_deepen);

0 commit comments

Comments
 (0)