bool ChannelCombinationInstance::ExecuteOn( View& view )
{
   ImageWindow sourceWindow[ 3 ];
   ImageVariant sourceImage[ 3 ];

   AutoViewLock lock( view );

   ImageVariant image = view.Image();

   if ( image.IsComplexSample() )
      throw Error( "ChannelCombination cannot be executed on complex images." );

   if ( image->ColorSpace() != ColorSpace::RGB )
      throw Error( "ChannelCombination requires a RGB color image." );

   Console().EnableAbort();

   StandardStatus status;
   image->SetStatusCallback( &status );

   String baseId;
   Rect r;
   int w0, h0;

   if ( view.IsPreview() )
   {
      ImageWindow w = view.Window();
      View mainView = w.MainView();

      baseId = mainView.Id();
      r = w.PreviewRect( view.Id() );
      mainView.GetSize( w0, h0 );
   }
   else
   {
      baseId = view.Id();
      r = image->Bounds();
      w0 = r.Width();
      h0 = r.Height();
   }

   int numberOfSources = 0;

   for ( int i = 0; i < 3; ++i )
      if ( channelEnabled[i] )
      {
         String id = channelId[i];
         if ( id.IsEmpty() )
            id = baseId + '_' + ColorSpaceId::ChannelId( colorSpace, i );

         sourceWindow[i] = ImageWindow::WindowById( id );

         if ( sourceWindow[i].IsNull() )
            throw Error( "ChannelCombination: Source image not found: " + id );

         sourceImage[i] = sourceWindow[i].MainView().Image();

         if ( !sourceImage[i] )
            throw Error( "ChannelCombination: Invalid source image: " + id );

         if ( sourceImage[i]->IsColor() )
            throw Error( "ChannelCombination: Invalid source color space: " + id );

         if ( sourceImage[i]->Width() != w0 || sourceImage[i]->Height() != h0 )
            throw Error( "ChannelCombination: Incompatible source image dimensions: " + id );

         ++numberOfSources;
      }

   if ( numberOfSources == 0 )
      return false;

   const char* what = "";
   switch ( colorSpace )
   {
   case ColorSpaceId::RGB:
      what = "RGB channels";
      break;
   case ColorSpaceId::CIEXYZ:
      what = "normalized CIE XYZ components";
      break;
   case ColorSpaceId::CIELab:
      what = "normalized CIE L*a*b* components";
      break;
   case ColorSpaceId::CIELch:
      what = "normalized CIE L*c*h* components";
      break;
   case ColorSpaceId::HSV:
      what = "normalized HSV components";
      break;
   case ColorSpaceId::HSI:
      what = "normalized HSI components";
      break;
   }
   image->Status().Initialize( String( "Combining " ) + what, image->NumberOfPixels() );

   if ( image.IsFloatSample() )
      switch ( image.BitsPerSample() )
      {
      case 32:
         CombineChannels( static_cast<Image&>( *image ), colorSpace, baseId, r,
                          sourceImage[0], sourceImage[1], sourceImage[2] );
         break;
      case 64:
         CombineChannels( static_cast<DImage&>( *image ), colorSpace, baseId, r,
                          sourceImage[0], sourceImage[1], sourceImage[2] );
         break;
      }
   else
      switch ( image.BitsPerSample() )
      {
      case  8:
         CombineChannels( static_cast<UInt8Image&>( *image ), colorSpace, baseId, r,
                          sourceImage[0], sourceImage[1], sourceImage[2] );
         break;
      case 16:
         CombineChannels( static_cast<UInt16Image&>( *image ), colorSpace, baseId, r,
                          sourceImage[0], sourceImage[1], sourceImage[2] );
         break;
      case 32:
         CombineChannels( static_cast<UInt32Image&>( *image ), colorSpace, baseId, r,
                          sourceImage[0], sourceImage[1], sourceImage[2] );
         break;
      }

   return true;
}