void DisplayIop::engine( int y, int x, int r, const DD::Image::ChannelSet &channels, DD::Image::Row &row ) { Channel outputChannels[4] = { Chan_Red, Chan_Green, Chan_Blue, Chan_Alpha }; const char *inputChannels[] = { "R", "G", "B", "A", nullptr }; const ImagePrimitive *image = nullptr; Box2i inputDataWindow; Box2i inputDisplayWindow; if( firstDisplayIop()->m_driver ) { image = firstDisplayIop()->m_driver->image().get(); inputDataWindow = image->getDataWindow(); inputDisplayWindow = image->getDisplayWindow(); } int i = 0; while( inputChannels[i] ) { const FloatVectorData *inputData = image ? image->getChannel<float>( inputChannels[i] ) : nullptr; if( inputData ) { float *output = row.writable( outputChannels[i] ) + x; // remap coordinate relative to our data window. nuke images have pixel origin at bottom, // cortex images have pixel origin at top. V2i pDataWindow = V2i( x, inputDisplayWindow.max.y - y ) - inputDataWindow.min; const float *input = &((inputData->readable())[pDataWindow.y*(inputDataWindow.size().x+1) + pDataWindow.x]); memcpy( output, input, (r-x) * sizeof( float ) ); } else { row.erase( outputChannels[i] ); } i++; } }
// See Saturation::pixel_engine for a well-commented example. // Note that this is copied by others (OCIODisplay) void OCIOFileTransform::pixel_engine( const DD::Image::Row& in, int /* rowY unused */, int rowX, int rowXBound, DD::Image::ChannelMask outputChannels, DD::Image::Row& out) { int rowWidth = rowXBound - rowX; DD::Image::ChannelSet done; foreach (requestedChannel, outputChannels) { // Skip channels which had their trios processed already, if (done & requestedChannel) { continue; } // Pass through channels which are not selected for processing // and non-rgb channels. if (colourIndex(requestedChannel) >= 3) { out.copy(in, requestedChannel, rowX, rowXBound); continue; } DD::Image::Channel rChannel = DD::Image::brother(requestedChannel, 0); DD::Image::Channel gChannel = DD::Image::brother(requestedChannel, 1); DD::Image::Channel bChannel = DD::Image::brother(requestedChannel, 2); done += rChannel; done += gChannel; done += bChannel; const float *rIn = in[rChannel] + rowX; const float *gIn = in[gChannel] + rowX; const float *bIn = in[bChannel] + rowX; float *rOut = out.writable(rChannel) + rowX; float *gOut = out.writable(gChannel) + rowX; float *bOut = out.writable(bChannel) + rowX; // OCIO modifies in-place // Note: xOut can equal xIn in some circumstances, such as when the // 'Black' (throwaway) scanline is uses. We thus must guard memcpy, // which does not allow for overlapping regions. if (rOut != rIn) memcpy(rOut, rIn, sizeof(float)*rowWidth); if (gOut != gIn) memcpy(gOut, gIn, sizeof(float)*rowWidth); if (bOut != bIn) memcpy(bOut, bIn, sizeof(float)*rowWidth); try { OCIO::PlanarImageDesc img(rOut, gOut, bOut, NULL, rowWidth, /*height*/ 1); m_processor->apply(img); } catch(OCIO::Exception &e) { error(e.what()); } } }
// See Saturation::pixel_engine for a well-commented example. // Note that this is copied by others (OCIODisplay) void OCIOFileTransform::pixel_engine( const DD::Image::Row& in, int /* rowY unused */, int rowX, int rowXBound, DD::Image::ChannelMask outputChannels, DD::Image::Row& out) { int rowWidth = rowXBound - rowX; DD::Image::ChannelSet done; foreach (requestedChannel, outputChannels) { // Skip channels which had their trios processed already, if (done & requestedChannel) { continue; } // Pass through channels which are not selected for processing // and non-rgb channels. if (!(layersToProcess & requestedChannel) || colourIndex(requestedChannel) >= 3) { out.copy(in, requestedChannel, rowX, rowXBound); continue; } DD::Image::Channel rChannel = DD::Image::brother(requestedChannel, 0); DD::Image::Channel gChannel = DD::Image::brother(requestedChannel, 1); DD::Image::Channel bChannel = DD::Image::brother(requestedChannel, 2); done += rChannel; done += gChannel; done += bChannel; const float *rIn = in[rChannel] + rowX; const float *gIn = in[gChannel] + rowX; const float *bIn = in[bChannel] + rowX; float *rOut = out.writable(rChannel) + rowX; float *gOut = out.writable(gChannel) + rowX; float *bOut = out.writable(bChannel) + rowX; // OCIO modifies in-place memcpy(rOut, rIn, sizeof(float)*rowWidth); memcpy(gOut, gIn, sizeof(float)*rowWidth); memcpy(bOut, bIn, sizeof(float)*rowWidth); try { OCIO::PlanarImageDesc img(rOut, gOut, bOut, rowWidth, /*height*/ 1); processor->apply(img); } catch(OCIO::Exception &e) { error(e.what()); } } }
void nuke_ld_3de4_base::engine(int y,int x,int r,DD::Image::ChannelMask channels,DD::Image::Row& outrow) { if(!input(0)) { return; } int w = format().w(); int h = format().h(); if((w <= 0) | (h <= 0)) { return; } double inv_w = 1.0 / w; double inv_h = 1.0 / h; // We construct (x_s,y_s) in a way, so that the image area is mapped to the unit interval [0,1]^2, // which is required by our 3DE4 lens distortion plugin class. Nuke's coordinates are pixel based, // (0,0) is the left lower corner of the left lower pixel, while (1,1) is the right upper corner // of that pixel. The center of any pixel (ix,iy) is (ix+0.5,iy+0.5), so we add 0.5 here. double y_s = (0.5 + y) * inv_h; // Determine bounding box and write down results. vector<ldpk::vec2d> pos; // Reserve, so that push_back is performant. pos.reserve(r - x); // Box for "lock the tile". DD::Image::Box box; for(int i = x;i < r;++i) { // We don't forget shifting by (half,half)! double x_s = (0.5 + i) * inv_w; double x_d,y_d; // Image processing, reversed mapping. Weave in 3DE4's field of view. if(_knob_direction == undistort) { _ldm->distort(map_in_fov_x(x_s),map_in_fov_y(y_s),x_d,y_d); } else { _ldm->undistort(map_in_fov_x(x_s),map_in_fov_y(y_s),x_d,y_d); } // The result already contains the (half,half) shift. Reformulate in Nuke's coordinates. Weave "out" 3DE4's field of view. double px = map_out_fov_x(x_d) * w; double py = map_out_fov_y(y_d) * h; // Build the box for "lock the tile..." if(i == x) { box = DD::Image::Box(int(floor(px)),int(floor(py)),int(ceil(px)),int(ceil(py))); } else { box.merge(int(floor(px)),int(floor(py)),int(ceil(px)),int(ceil(py))); } // We store the results. pos.push_back(ldpk::vec2d(px,py)); } // Add margin for reconstruction filter. Cubic will need two pixels more, others maybe three. box.pad(3); // Transfer the stored result. We set pixels x to r in outrow. // Begin extension by David Cattermole and Ben Dickson, RSP. Thank you! // Minor changes added by SDV for version 1.9.1. if(_knob_output_mode == OUTPUT_STMAP) { // Output source pixel coordinates in format compatible with STMap double inv_w0 = 1.0 / input0().format().width(); double inv_h0 = 1.0 / input0().format().height(); for(int i = 0;i < pos.size();++i) { foreach(z,channels) { if(z == DD::Image::Chan_Red) { outrow.writable(z)[i + x] = float(pos[i][0] * inv_w0); } else if(z == DD::Image::Chan_Green) { outrow.writable(z)[i + x] = float(pos[i][1] * inv_h0); } else { outrow.writable(z)[i + x] = 0.0; } } } }