@@ -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+
28832956static void
28842957action_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