// Position.C // Copyright (c) 2009 The Foundry Visionmongers Ltd. All Rights Reserved. // Moves the input by an integer number of pixels. // Notice that the controls are an X/Y position controlled by the user // and a 0,0 position that the user cannot move. This is so the plugin // can figure out the translation between 0,0 and the output // position. Without this proxy scaling produces X,Y positions that are // not useful for moving the corner of the image to, because they // produce positions that point at a certain pixel. Moving the // lower-left corner here is not always correct as the lower-left // corner is not always the same point relative to the image when // you compare the proxy and full-size images. This can be seen if // you imagine them having different aspect ratios. #include "DDImage/Iop.h" #include "DDImage/Row.h" #include "DDImage/Knobs.h" #include "DDImage/Format.h" #include "DDImage/Matrix4.h" using namespace DD::Image; static const char* const CLASS = "Position"; static const char* const HELP = "Moves the input by an integer number of pixels."; class Position : public Iop { void _validate(bool) override; void _request(int, int, int, int, ChannelMask, int) override; void engine(int y, int x, int r, ChannelMask, Row & t) override; double x, y; double x0, y0; int dx, dy; Matrix4 matrix_; public: Position(Node* node) : Iop(node) { x = y = x0 = y0 = 0; } void knobs(Knob_Callback) override; const char* Class() const override { return CLASS; } const char* node_help() const override { return HELP; } static const Iop::Description d; Matrix4* matrix() { return &matrix_; } int slowness() const { return 1; } // this is a really fast operator... }; void Position::_validate(bool) { // Figure out the integer translations. Floor(x+.5) is used so that // values always round the same way even if negative and even if .5 dx = int(floor(this->x - x0 + .5)); dy = int(floor(this->y - y0 + .5)); // move the bounding box: copy_info(); info_.move(dx, dy); // create the transformation matrix for the GUI: matrix_.translation(x, y); } void Position::_request(int x, int y, int r, int t, ChannelMask channels, int count) { // adjust the input viewport: x -= dx; r -= dx; y -= dy; t -= dy; // get that rectangle: input0().request(x, y, r, t, channels, count); } void Position::engine(int Y, int X, int R, ChannelMask channels, Row& row) { // move the row over so it reads the input area: row.offset(-dx); // read the data: row.get(input0(), Y - dy, X - dx, R - dx, channels); // move the data back row.offset(dx); } void Position::knobs(Knob_Callback f) { XY_knob(f, &x, "translate"); XY_knob(f, &x0, nullptr, INVISIBLE); Tooltip(f, "translate\n" "This is rounded to the nearest number of pixels so no filtering is done."); } static Iop* build(Node* node) { return new Position(node); } const Iop::Description Position::d(CLASS, "Transform/Position", build);