1 /* ***** BEGIN LICENSE BLOCK *****
2  * This file is part of openfx-misc <https://github.com/devernay/openfx-misc>,
3  * Copyright (C) 2013-2018 INRIA
4  *
5  * openfx-misc is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * openfx-misc is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with openfx-misc.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
17  * ***** END LICENSE BLOCK ***** */
18 
19 /*
20  * OFX Distortion plugins.
21  */
22 
23 /* TODO:
24  - optionally expand/contract RoD in LensDistortion,
25     see PFBarrelCommon::calculateOutputRect (sample 10 points on each side of image rect)
26  */
27 /*
28    Although the indications from nuke/fnOfxExtensions.h were followed, and the
29    kFnOfxImageEffectActionGetTransform action was implemented in the Support
30    library, that action is never called by the Nuke host, so it cannot be tested.
31    The code is left here for reference or for further extension.
32 
33    There is also an open question about how the last plugin in a transform chain
34    may get the concatenated transform from upstream, the untransformed source image,
35    concatenate its own transform and apply the resulting transform in its render
36    action. Should the host be doing this instead?
37  */
38 // This node concatenates transforms upstream.
39 
40 #include <cmath>
41 #include <cfloat> // DBL_MAX
42 #include <cstdlib> // atoi
43 #include <cstdio> // fopen
44 #include <iostream>
45 #include <sstream>
46 #include <set>
47 #include <map>
48 #include <vector>
49 #include <algorithm>
50 #include <limits>
51 
52 #ifdef __APPLE__
53 #include <OpenGL/gl.h>
54 #else
55 #ifdef _WIN32
56 #define WIN32_LEAN_AND_MEAN
57 #ifndef NOMINMAX
58 #define NOMINMAX
59 #endif
60 #include <windows.h>
61 #endif
62 
63 #include <GL/gl.h>
64 #endif
65 
66 #include "ofxsImageEffect.h"
67 #include "ofxsProcessing.H"
68 #include "ofxsCoords.h"
69 #include "ofxsMaskMix.h"
70 #include "ofxsFilter.h"
71 #include "ofxsMatrix2D.h"
72 #include "ofxsCopier.h"
73 #include "ofxsMacros.h"
74 #include "ofxsMultiPlane.h"
75 #include "ofxsGenerator.h"
76 #include "ofxsFormatResolution.h"
77 
78 #include "DistortionModel.h"
79 
80 using namespace OFX;
81 
82 OFXS_NAMESPACE_ANONYMOUS_ENTER
83 
84 #define kPluginIDistortName "IDistortOFX"
85 #define kPluginIDistortGrouping "Transform"
86 #define kPluginIDistortDescription \
87     "Distort an image, based on a displacement map.\n" \
88     "The U and V channels give the offset in pixels in the destination image to the pixel where the color is taken. " \
89     "For example, if at pixel (45,12) the UV value is (-1.5,3.2), then the color at this pixel is taken from (43.5,15.2) in the source image. " \
90     "This plugin concatenates transforms upstream, so that if the nodes upstream output a 3x3 transform " \
91     "(e.g. Transform, CornerPin, Dot, NoOp, Switch), the original image is sampled only once.\n" \
92     "This plugin concatenates transforms upstream." \
93 
94 #define kPluginIDistortIdentifier "net.sf.openfx.IDistort"
95 
96 #define kPluginSTMapName "STMapOFX"
97 #define kPluginSTMapGrouping "Transform"
98 #define kPluginSTMapDescription \
99     "Move pixels around an image, based on a UVmap.\n" \
100     "The U and V channels give, for each pixel in the destination image, the normalized position of the pixel where the color is taken. " \
101     "(0,0) is the bottom left corner of the input image, while (1,1) is the top right corner. " \
102     "This plugin concatenates transforms upstream, so that if the nodes upstream output a 3x3 transform " \
103     "(e.g. Transform, CornerPin, Dot, NoOp, Switch), the original image is sampled only once.\n" \
104     "This plugin concatenates transforms upstream." \
105 
106 #define kPluginSTMapIdentifier "net.sf.openfx.STMap"
107 
108 #define kPluginLensDistortionName "LensDistortionOFX"
109 #define kPluginLensDistortionGrouping "Transform"
110 #define kPluginLensDistortionDescription \
111     "Add or remove lens distortion, or produce an STMap that can be used to apply that transform.\n" \
112     "The region of definition of the transformed image is computed from the region of definition of the Source input. If the input is defined outside of the project format, this may result in a very large region. A Crop effect may be inserted before LensDistortion to avoid this. If the input region of definition is inside the format, the Crop To Format parameter may be used to avoid expanding it.\n" \
113     "LensDistortion can directly apply distortion/undistortion, but if the distortion parameters are not animated, the most efficient way to use LensDistortion and avoid repeated distortion function calculations is the following:\n" \
114     "- If the footage size is not the same as the project size, insert a FrameHold plugin between the footage to distort or undistort and the Source input of LensDistortion. This connection is only used to get the size of the input footage.\n" \
115     "- Set Output Mode to \"STMap\" in LensDistortion.\n" \
116     "- feed the LensDistortion output into the UV input of STMap, and feed the footage into the Source input of STMap.\n" \
117     "This plugin concatenates transforms upstream." \
118 
119 #define kPluginLensDistortionIdentifier "net.sf.openfx.LensDistortion"
120 
121 // Some hosts (e.g. Resolve) may not support normalized defaults (setDefaultCoordinateSystem(eCoordinatesNormalised))
122 #define kParamDefaultsNormalised "defaultsNormalised"
123 
124 /* LensDistortion TODO:
125    - cache the STmap for a set of input parameter and input image size
126    - compute the inverse map and undistort
127    - implement other distortion models (PFBarrel, OpenCV)
128  */
129 
130 // History:
131 // version 1.0: initial version
132 // version 2.0: use kNatronOfxParamProcess* parameters
133 #define kPluginVersionMajor 2 // Incrementing this number means that you have broken backwards compatibility of the plug-in.
134 #define kPluginVersionMinor 0 // Increment this when you have fixed a bug or made it faster.
135 
136 // version 1.0: initial version
137 // version 2.0: use kNatronOfxParamProcess* parameters
138 // version 3.0: use format instead of rod as the default for distortion domain
139 // version 4.0: add the CropToFormat parameter
140 #define kPluginVersionLensDistortionMajor 4 // Incrementing this number means that you have broken backwards compatibility of the plug-in.
141 #define kPluginVersionLensDistortionMinor 0 // Increment this when you have fixed a bug or made it faster.
142 
143 #define kSupportsTiles 1
144 #define kSupportsMultiResolution 1
145 #define kSupportsRenderScale 1
146 #define kSupportsMultipleClipPARs false
147 #define kSupportsMultipleClipDepths false
148 #define kRenderThreadSafety eRenderFullySafe
149 
150 static bool gHostSupportsDefaultCoordinateSystem = true; // for kParamDefaultsNormalised
151 
152 enum DistortionPluginEnum
153 {
154     eDistortionPluginSTMap,
155     eDistortionPluginIDistort,
156     eDistortionPluginLensDistortion,
157 };
158 
159 #ifdef OFX_EXTENSIONS_NATRON
160 #define kParamProcessR kNatronOfxParamProcessR
161 #define kParamProcessRLabel kNatronOfxParamProcessRLabel, kNatronOfxParamProcessRHint
162 #define kParamProcessG kNatronOfxParamProcessG
163 #define kParamProcessGLabel kNatronOfxParamProcessGLabel, kNatronOfxParamProcessGHint
164 #define kParamProcessB kNatronOfxParamProcessB
165 #define kParamProcessBLabel kNatronOfxParamProcessBLabel, kNatronOfxParamProcessBHint
166 #define kParamProcessA kNatronOfxParamProcessA
167 #define kParamProcessALabel kNatronOfxParamProcessALabel, kNatronOfxParamProcessAHint
168 #else
169 #define kParamProcessR      "processR"
170 #define kParamProcessRLabel "R", "Process red component."
171 #define kParamProcessG      "processG"
172 #define kParamProcessGLabel "G", "Process green component."
173 #define kParamProcessB      "processB"
174 #define kParamProcessBLabel "B", "Process blue component."
175 #define kParamProcessA      "processA"
176 #define kParamProcessALabel "A", "Process alpha component."
177 #endif
178 
179 #define kParamChannelU "channelU"
180 #define kParamChannelULabel "U Channel", "Input U channel from UV."
181 
182 #define kParamChannelUChoice kParamChannelU "Choice"
183 
184 #define kParamChannelV "channelV"
185 #define kParamChannelVLabel "V Channel", "Input V channel from UV."
186 
187 #define kParamChannelVChoice kParamChannelV "Choice"
188 
189 #define kParamChannelA "channelA"
190 #define kParamChannelALabel "Alpha Channel", "Input Alpha channel from UV. The Output alpha is set to this value. If \"Unpremult UV\" is checked, the UV values are divided by alpha."
191 
192 #define kParamChannelAChoice kParamChannelA "Choice"
193 
194 #define kParamChannelUnpremultUV "unpremultUV"
195 #define kParamChannelUnpremultUVLabel "Unpremult UV", "Unpremult UV by Alpha from UV. Check if UV values look small for small values of Alpha (3D software sometimes write premultiplied UV values)."
196 
197 #define kParamPremultChanged "premultChanged"
198 
199 #define kClipUV "UV"
200 
201 #ifdef OFX_EXTENSIONS_NATRON
202 #define OFX_COMPONENTS_OK(c) ((c)== ePixelComponentAlpha || (c) == ePixelComponentXY || (c) == ePixelComponentRGB || (c) == ePixelComponentRGBA)
203 #else
204 #define OFX_COMPONENTS_OK(c) ((c)== ePixelComponentAlpha || (c) == ePixelComponentRGB || (c) == ePixelComponentRGBA)
205 #endif
206 
207 
208 enum InputChannelEnum
209 {
210     eInputChannelR = 0,
211     eInputChannelG,
212     eInputChannelB,
213     eInputChannelA,
214     eInputChannel0,
215     eInputChannel1,
216 };
217 
218 #define kParamWrapU "wrapU"
219 #define kParamWrapULabel "U Wrap Mode", "Wrap mode for U coordinate."
220 
221 #define kParamWrapV "wrapV"
222 #define kParamWrapVLabel "V Wrap Mode", "Wrap mode for V coordinate."
223 
224 #define kParamWrapOptionClamp "Clamp", "Texture edges are black (if blackOutside is checked) or stretched indefinitely.", "clamp"
225 #define kParamWrapOptionRepeat "Repeat", "Texture is repeated.", "repeat"
226 #define kParamWrapOptionMirror "Mirror", "Texture is mirrored alternatively.", "mirror"
227 
228 enum WrapEnum
229 {
230     eWrapClamp = 0,
231     eWrapRepeat,
232     eWrapMirror,
233 };
234 
235 
236 #define kParamUVOffset "uvOffset"
237 #define kParamUVOffsetLabel "UV Offset", "Offset to apply to the U and V channel (useful if these were stored in a file that cannot handle negative numbers)"
238 
239 #define kParamUVScale "uvScale"
240 #define kParamUVScaleLabel "UV Scale", "Scale factor to apply to the U and V channel (useful if these were stored in a file that can only store integer values)"
241 
242 #define kParamFormat kParamGeneratorExtent
243 #define kParamFormatLabel "Format", "Reference format for lens distortion."
244 
245 #define kParamDistortionModel "model"
246 #define kParamDistortionModelLabel "Model", "Choice of the distortion model, i.e. the function that goes from distorted to undistorted image coordinates."
247 #define kParamDistortionModelOptionNuke "Nuke", "The model used in Nuke's LensDistortion plugin.", "nuke"
248 #define kParamDistortionModelOptionPFBarrel "PFBarrel", "The PFBarrel model used in PFTrack by PixelFarm.", "pfbarrel"
249 #define kParamDistortionModelOption3DEClassic "3DE Classic", "Degree-2 anamorphic and degree-4 radial mixed model, used in 3DEqualizer by Science-D-Visions. Works, but it is recommended to use 3DE4 Radial Standard Degree 4 or 3DE4 Anamorphic Standard Degree 4 instead.", "3declassic"
250 #define kParamDistortionModelOption3DEAnamorphic6 "3DE4 Anamorphic Degree 6", "Degree-6 anamorphic model, used in 3DEqualizer by Science-D-Visions.", "3deanamorphic6"
251 #define kParamDistortionModelOption3DEFishEye8 "3DE4 Radial Fisheye Degree 8", "Radial lens distortion model with equisolid-angle fisheye projection, used in 3DEqualizer by Science-D-Visions.", "3defisheye8"
252 #define kParamDistortionModelOption3DEStandard "3DE4 Radial Standard Degree 4", "Radial lens distortion model, a.k.a. radial decentered cylindric degree 4, which compensates for decentered lenses (and beam splitter artefacts in stereo rigs), used in 3DEqualizer by Science-D-Visions.", "3deradial4"
253 #define kParamDistortionModelOption3DEAnamorphic4 "3DE4 Anamorphic Standard Degree 4", "Degree-4 anamorphic model with anamorphic lens rotation, which handles 'human-touched' mounted anamorphic lenses, used in 3DEqualizer by Science-D-Visions.", "3deanamorphic4"
254 #define kParamDistortionModelOptionPanoTools "PanoTools", "The model used in PanoTools, PTGui, PTAssembler, Hugin. See http://wiki.panotools.org/Lens_correction_model", "panotools"
255 
256 /*
257    Possible distortion models:
258    (see also <http://michaelkarp.net/distortion.htm>)
259 
260    From Oblique <https://github.com/madesjardins/Obq_Shaders/wiki/Obq_LensDistortion>
261    PFBarrel: PFTrack's distortion model.
262    Nuke: Nuke's distortion model.
263    3DE Classic LD Model: Degree-2 anamorphic and degree-4 radial mixed model
264  Science-D-Visions LDPK (3DEqualizer). see <http://www.3dequalizer.com/user_daten/tech_docs/pdf/ldpk.pdf>
265    3DE4 Anamorphic, Degree 6:
266    3DE4 Radial - Fisheye, Degree 8:
267    3DE4 Radial - Standard, Degree 4: A deprecated model.
268    3DE4 Radial - Decentered Cylindric, Degree 4:
269    3DE4 Anamorphic Rotate Squeeze, Degree 4:
270 
271    From RV4 <http://www.tweaksoftware.com/static/documentation/rv/rv-4.0.17/html/rv_reference.html#RVLensWarp>
272    “brown”, “opencv”, “pfbarrel”, “adobe”, “3de4_anamorphic_degree_6”
273 
274    Panorama Tools/PtGUI/Hugin/Card3D:
275    http://wiki.panotools.org/Lens_correction_model
276    http://www.ptgui.com/ptguihelp/main_lens.htm
277    http://www.nukepedia.com/written-tutorials/the-lens-distortion-model-in-the-card-node-explained/
278    https://web.archive.org/web/20010409044720/http://www.fh-furtwangen.de/~dersch/barrel/barrel.html
279 
280    OpenCV / Matlab Camera Calibration Toolbox:
281    - https://docs.opencv.org/trunk/d9/d0c/group__calib3d.html
282      intrinsic parameters are (fx, fy, cx, cy). fx,fy are the focal lengths expressed in pixel units. (cx,cy) is a principal point that is usually at the image center. These depend on the image resolution.
283      distortion coefficients are a vector (k1,k2,p1,p2[,k3[,k4,k5,k6[,s1,s2,s3,s4[,τx,τy]]]]) of 4, 5, 8, 12 or 14 elements. These are independent of image resolution.
284 
285    OpenCV 3 Fisheye / Matlab Camera Calibration Toolbox Fisheye / Kannala-Brandt:
286    - A Generic Camera Model and Calibration Method for Conventional, Wide-Angle, and Fish-Eye Lenses <http://www.ee.oulu.fi/~jkannala/publications/tpami2006.pdf>
287      The "undocumented" fisheye model contained in the calibration toolbox follows the equidistance projection model described by equation (3) in this very nice paper. The distortion model follows equation (6), to the exception that k1=1 (otherwise indistinguishable from f).
288    - https://docs.opencv.org/trunk/db/d58/group__calib3d__fisheye.html
289      intrinsic parameters are (fx, fy, cx, cy). fx,fy are the focal lengths expressed in pixel units. (cx,cy) is a principal point that is usually at the image center. These depend on the image resolution.
290      distortion coefficients are (k1,k2,k3,k4): θd=θ(1+k1θ^2+k2θ^4+k3θ^6+k4θ^8) (θ is the angle of the incoming ray)
291     The distorted point coordinates are [x'; y'] where
292       x′=(θd/r)a, y′=(θd/r)b (r = sqrt((x/z)^2+(y/z)^2)
293     Finally, conversion into pixel coordinates: The final pixel coordinates vector [u; v] where:
294       u=fx(x′+αy′)+cx, v=fyy′+cy
295    - https://stackoverflow.com/questions/31089265/what-are-the-main-references-to-the-fish-eye-camera-model-in-opencv3-0-0dev
296  */
297 
298 enum DistortionModelEnum
299 {
300     eDistortionModelNuke,
301     eDistortionModelPFBarrel,
302     eDistortionModel3DEClassic,
303     eDistortionModel3DEAnamorphic6,
304     eDistortionModel3DEFishEye8,
305     eDistortionModel3DEStandard,
306     eDistortionModel3DEAnamorphic4,
307     eDistortionModelPanoTools,
308 };
309 
310 
311 #define kParamDistortionDirection "direction"
312 #define kParamDistortionDirectionLabel "Direction", "Should the output corrspond to applying or to removing distortion."
313 #define kParamDistortionDirectionOptionDistort "Distort", "The output corresponds to applying distortion."
314 #define kParamDistortionDirectionOptionUndistort "Undistort", "The output corresponds to removing distortion."
315 
316 enum DirectionEnum {
317     eDirectionDistort,
318     eDirectionUndistort,
319 };
320 
321 #define kParamDistortionOutputMode "outputMode"
322 #define kParamDistortionOutputModeLabel "Output Mode", "Choice of the output, which may be either a distorted/undistorted image, or a distortion/undistortion STMap."
323 #define kParamDistortionOutputModeOptionImage "Image", "The output is the distorted/undistorted Source."
324 #define kParamDistortionOutputModeOptionSTMap "STMap", "The output is a distortion/undistortion STMap. It is recommended to insert a FrameHold node at the Source input so that the STMap is computed only once if the parameters are not animated."
325 
326 enum OutputModeEnum {
327     eOutputModeImage,
328     eOutputModeSTMap,
329 };
330 
331 #define kParamK1 "k1"
332 #define kParamK1Label "K1", "Nuke: First radial distortion coefficient (coefficient for r^2)."
333 
334 #define kParamK2 "k2"
335 #define kParamK2Label "K2", "Nuke: Second radial distortion coefficient (coefficient for r^4)."
336 
337 #if 0
338 #define kParamK3 "k3"
339 #define kParamK3Label "K3", "Nuke: Third radial distortion coefficient (coefficient for r^6)."
340 
341 #define kParamP1 "p1"
342 #define kParamP1Label "P1", "Nuke: First tangential distortion coefficient."
343 
344 #define kParamP2 "p2"
345 #define kParamP2Label "P2", "Nuke: Second tangential distortion coefficient."
346 #endif
347 
348 #define kParamCenter "center"
349 #define kParamCenterLabel "Center", "Nuke: Offset of the distortion center from the image center."
350 
351 #define kParamSqueeze "anamorphicSqueeze"
352 #define kParamSqueezeLabel "Squeeze", "Nuke: Anamorphic squeeze (only for anamorphic lens)."
353 
354 #define kParamAsymmetric "asymmetricDistortion"
355 #define kParamAsymmetricLabel "Asymmetric", "Nuke: Asymmetric distortion (only for anamorphic lens)."
356 
357 #define kParamPFFile "pfFile"
358 #define kParamPFFileLabel "File", "The location of the PFBarrel .pfb file to use. Keyframes are set if present in the file."
359 
360 #define kParamPFFileReload "pfReload"
361 #define kParamPFFileReloadLabel "Reload", "Click to reload the PFBarrel file"
362 
363 #define kParamPFC3 "pfC3"
364 #define kParamPFC3Label "C3", "PFBarrel: Low order radial distortion coefficient."
365 
366 #define kParamPFC5 "pfC5"
367 #define kParamPFC5Label "C5", "PFBarrel: Low order radial distortion coefficient."
368 
369 #define kParamPFSqueeze "pfSqueeze"
370 #define kParamPFSqueezeLabel "Squeeze", "PFBarrel: Anamorphic squeeze (only for anamorphic lens)."
371 
372 #define kParamPFP "pfP"
373 #define kParamPFPLabel "Center", "PFBarrel: The distortion center of the lens (specified as a factor rather than a pixel value)"
374 
375 // 3D Equalizer 4
376 
377 //Double_knob(callback,&_xa_fov_unit,DD::Image::IRange(0.0,1.0),"field_of_view_xa_unit","fov left [unit coord]");
378 #define kParam3DE4_xa_fov_unit "tde4_field_of_view_xa_unit"
379 #define kParam3DE4_xa_fov_unitLabel "fov left [unit coord]", "3DE4: Field of view."
380 
381 //Double_knob(callback,&_ya_fov_unit,DD::Image::IRange(0.0,1.0),"field_of_view_ya_unit","fov bottom [unit coord]");
382 #define kParam3DE4_ya_fov_unit "tde4_field_of_view_ya_unit"
383 #define kParam3DE4_ya_fov_unitLabel "fov bottom [unit coord]", "3DE4: Field of view."
384 
385 //Double_knob(callback,&_xb_fov_unit,DD::Image::IRange(0.0,1.0),"field_of_view_xb_unit","fov right [unit coord]");
386 #define kParam3DE4_xb_fov_unit "tde4_field_of_view_xb_unit"
387 #define kParam3DE4_xb_fov_unitLabel "fov right [unit coord]", "3DE4: Field of view."
388 
389 //Double_knob(callback,&_yb_fov_unit,DD::Image::IRange(0.0,1.0),"field_of_view_yb_unit","fov top [unit coord]");
390 #define kParam3DE4_yb_fov_unit "tde4_field_of_view_yb_unit"
391 #define kParam3DE4_yb_fov_unitLabel "fov top [unit coord]", "3DE4: Field of view."
392 
393 // First the seven built-in parameters, in this order.
394 
395 //fl_cm: Double_knob(callback,&_values_double[i_par_double++],DD::Image::IRange(0.5,50.0),"tde4_focal_length_cm","tde4 focal length [cm]");
396 #define kParam3DE4_focal_length_cm "tde4_focal_length_cm"
397 #define kParam3DE4_focal_length_cmLabel "tde4 focal length [cm]", "3DE4: Focal length."
398 
399 //fd_cm: Double_knob(callback,&_values_double[i_par_double++],DD::Image::IRange(10.0,1000.0),"tde4_custom_focus_distance_cm","tde4 focus distance [cm]");
400 #define kParam3DE4_custom_focus_distance_cm "tde4_custom_focus_distance_cm"
401 #define kParam3DE4_custom_focus_distance_cmLabel "tde4 focus distance [cm]", "3DE4: Focus distance."
402 
403 //w_fb_cm: Double_knob(callback,&_values_double[i_par_double++],DD::Image::IRange(.1,10.0),"tde4_filmback_width_cm","tde4 filmback width [cm]");
404 #define kParam3DE4_filmback_width_cm "tde4_filmback_width_cm"
405 #define kParam3DE4_filmback_width_cmLabel "tde4 filmback width [cm]", "3DE4: Filmback width."
406 
407 //h_fb_cm: Double_knob(callback,&_values_double[i_par_double++],DD::Image::IRange(.1,10.0),"tde4_filmback_height_cm","tde4 filmback height [cm]");
408 #define kParam3DE4_filmback_height_cm "tde4_filmback_height_cm"
409 #define kParam3DE4_filmback_height_cmLabel "tde4 filmback height [cm]", "3DE4: Filmback height."
410 
411 //x_lco_cm: Double_knob(callback,&_values_double[i_par_double++],DD::Image::IRange(-5.0,5.0),"tde4_lens_center_offset_x_cm","tde4 lens center offset x [cm]");
412 #define kParam3DE4_lens_center_offset_x_cm "tde4_lens_center_offset_x_cm"
413 #define kParam3DE4_lens_center_offset_x_cmLabel "tde4 lens center offset x [cm]", "3DE4: Lens center horizontal offset."
414 
415 //y_lco_cm: Double_knob(callback,&_values_double[i_par_double++],DD::Image::IRange(-5.0,5.0),"tde4_lens_center_offset_y_cm","tde4 lens center offset y [cm]");
416 #define kParam3DE4_lens_center_offset_y_cm "tde4_lens_center_offset_y_cm"
417 #define kParam3DE4_lens_center_offset_y_cmLabel "tde4 lens center offset y [cm]", "3DE4: Lens center vertical offset."
418 
419 //pa: Double_knob(callback,&_values_double[i_par_double++],DD::Image::IRange(0.25,4.0),"tde4_pixel_aspect","tde4 pixel aspect");
420 #define kParam3DE4_pixel_aspect "tde4_pixel_aspect"
421 #define kParam3DE4_pixel_aspectLabel "tde4 pixel aspect", "3DE4: Pixel aspect ratio."
422 
423 
424 // 3DE_Classic_LD_Model
425 //"Distortion", -0.5 .. 0.5
426 #define kParam3DEDistortion "tde4_Distortion"
427 #define kParam3DEDistortionLabel "Distortion", "3DE Classic: Distortion."
428 //"Anamorphic Squeeze", 0.25 ... 4.
429 #define kParam3DEAnamorphicSqueeze "tde4_Anamorphic_Squeeze"
430 #define kParam3DEAnamorphicSqueezeLabel "Anamorphic Squeeze", "3DE Classic: Anamorphic Squeeze."
431 
432 //"Curvature X", -0.5 .. 0.5
433 #define kParam3DECurvatureX "tde4_Curvature_X"
434 #define kParam3DECurvatureXLabel "Curvature X", "3DE Classic: Curvature X."
435 
436 //"Curvature Y", -0.5 .. 0.5
437 #define kParam3DECurvatureY "tde4_Curvature_Y"
438 #define kParam3DECurvatureYLabel "Curvature Y", "3DE Classic: Curvature Y."
439 
440 //"Quartic Distortion" -0.5 .. 0.5
441 #define kParam3DEQuarticDistortion "tde4_Quartic_Distortion"
442 #define kParam3DEQuarticDistortionLabel "Quartic Distortion", "3DE Classic: Quartic Distortion."
443 
444 // 3DE4_Radial_Standard_Degree_4
445 #define kParam3DEDistortionDegree2 "tde4_Distortion_Degree_2"
446 #define kParam3DEDistortionDegree2Label "Distortion - Degree 2", "3DE Standard and Fisheye: Distortion."
447 #define kParam3DEUDegree2 "tde4_U_Degree_2"
448 #define kParam3DEUDegree2Label "U - Degree 2", "3DE Standard: U - Degree 2."
449 #define kParam3DEVDegree2 "tde4_V_Degree_2"
450 #define kParam3DEVDegree2Label "V - Degree 2", "3DE Standard: V - Degree 2."
451 #define kParam3DEQuarticDistortionDegree4 "tde4_Quartic_Distortion_Degree_4"
452 #define kParam3DEQuarticDistortionDegree4Label "Quartic Distortion - Degree 4", "3DE Standard and Fisheye: Quartic Distortion - Degree 4."
453 #define kParam3DEUDegree4 "tde4_U_Degree_4"
454 #define kParam3DEUDegree4Label "U - Degree 4", "3DE Standard: U - Degree 4."
455 #define kParam3DEVDegree4 "tde4_V_Degree_4"
456 #define kParam3DEVDegree4Label "V - Degree 4", "3DE Standard: V - Degree 4."
457 #define kParam3DEPhiCylindricDirection "tde4_Phi_Cylindric_Direction"
458 #define kParam3DEPhiCylindricDirectionLabel "Phi - Cylindric Direction", "3DE Standard: Phi - Cylindric Direction."
459 #define kParam3DEBCylindricBending "tde4_B_Cylindric_Bending"
460 #define kParam3DEBCylindricBendingLabel "B - Cylindric Bending", "3DE Standard: B - Cylindric Bending."
461 
462 // 3DE4_Anamorphic_Standard_Degree_4
463 #define kParam3DECx02Degree2 "tde4_Cx02_Degree_2"
464 #define kParam3DECx02Degree2Label "Cx02 - Degree 2", "3DE Anamorphic 4 and 6: Cx02 - Degree 2."
465 #define kParam3DECy02Degree2 "tde4_Cy02_Degree_2"
466 #define kParam3DECy02Degree2Label "Cy02 - Degree 2", "3DE Anamorphic 4 and 6: Cy02 - Degree 2."
467 #define kParam3DECx22Degree2 "tde4_Cx22_Degree_2"
468 #define kParam3DECx22Degree2Label "Cx22 - Degree 2", "3DE Anamorphic 4 and 6: Cx22 - Degree 2."
469 #define kParam3DECy22Degree2 "tde4_Cy22_Degree_2"
470 #define kParam3DECy22Degree2Label "Cy22 - Degree 2", "3DE Anamorphic 4 and 6: Cy22 - Degree 2."
471 #define kParam3DECx04Degree4 "tde4_Cx04_Degree_4"
472 #define kParam3DECx04Degree4Label "Cx04 - Degree 4", "3DE Anamorphic 4 and 6: Cx04 - Degree 4."
473 #define kParam3DECy04Degree4 "tde4_Cy04_Degree_4"
474 #define kParam3DECy04Degree4Label "Cy04 - Degree 4", "3DE Anamorphic 4 and 6: Cy04 - Degree 4."
475 #define kParam3DECx24Degree4 "tde4_Cx24_Degree_4"
476 #define kParam3DECx24Degree4Label "Cx24 - Degree 4", "3DE Anamorphic 4 and 6: Cx24 - Degree 4."
477 #define kParam3DECy24Degree4 "tde4_Cy24_Degree_4"
478 #define kParam3DECy24Degree4Label "Cy24 - Degree 4", "3DE Anamorphic 4 and 6: Cy24 - Degree 4."
479 #define kParam3DECx44Degree4 "tde4_Cx44_Degree_4"
480 #define kParam3DECx44Degree4Label "Cx44 - Degree 4", "3DE Anamorphic 4 and 6: Cx44 - Degree 4."
481 #define kParam3DECy44Degree4 "tde4_Cy44_Degree_4"
482 #define kParam3DECy44Degree4Label "Cy44 - Degree 4", "3DE Anamorphic 4 and 6: Cy44 - Degree 4."
483 #define kParam3DELensRotation "tde4_Lens_Rotation"
484 #define kParam3DELensRotationLabel "Lens Rotation 4", "3DE Anamorphic 4: Lens Rotation 4."
485 #define kParam3DESqueezeX "tde4_Squeeze_X"
486 #define kParam3DESqueezeXLabel "Squeeze-X", "3DE Anamorphic 4: Squeeze-X."
487 #define kParam3DESqueezeY "tde4_Squeeze_Y"
488 #define kParam3DESqueezeYLabel "Squeeze-Y", "3DE Anamorphic 4: Squeeze-Y."
489 
490 // 3DE4_Anamorphic_Degree_6
491 #define kParam3DECx06Degree6 "tde4_Cx06_Degree_6"
492 #define kParam3DECx06Degree6Label "Cx06 - Degree 6", "3DE Anamorphic 6: Cx06 - Degree 6."
493 #define kParam3DECy06Degree6 "tde4_Cy06_Degree_6"
494 #define kParam3DECy06Degree6Label "Cy06 - Degree 6", "3DE Anamorphic 6: Cy06 - Degree 6."
495 #define kParam3DECx26Degree6 "tde4_Cx26_Degree_6"
496 #define kParam3DECx26Degree6Label "Cx26 - Degree 6", "3DE Anamorphic 6: Cx26 - Degree 6."
497 #define kParam3DECy26Degree6 "tde4_Cy26_Degree_6"
498 #define kParam3DECy26Degree6Label "Cy26 - Degree 6", "3DE Anamorphic 6: Cy26 - Degree 6."
499 #define kParam3DECx46Degree6 "tde4_Cx46_Degree_6"
500 #define kParam3DECx46Degree6Label "Cx46 - Degree 6", "3DE Anamorphic 6: Cx46 - Degree 6."
501 #define kParam3DECy46Degree6 "tde4_Cy46_Degree_6"
502 #define kParam3DECy46Degree6Label "Cy46 - Degree 6", "3DE Anamorphic 6: Cy46 - Degree 6."
503 #define kParam3DECx66Degree6 "tde4_Cx66_Degree_6"
504 #define kParam3DECx66Degree6Label "Cx66 - Degree 6", "3DE Anamorphic 6: Cx66 - Degree 6."
505 #define kParam3DECy66Degree6 "tde4_Cy66_Degree_6"
506 #define kParam3DECy66Degree6Label "Cy66 - Degree 6", "3DE Anamorphic 6: Cy66 - Degree 6."
507 
508 // 3DE4_Radial_Fisheye_Degree_8
509 #define kParam3DEDegree6 "tde4_Degree_6"
510 #define kParam3DEDegree6Label "Degree 6", "3DE Fisheye: Degree 6."
511 #define kParam3DEDegree8 "tde4_Degree_8"
512 #define kParam3DEDegree8Label "Degree 8", "3DE Fisheye: Degree 8."
513 
514 // PanoTools
515 #define kParamPanoToolsA "pt_a"
516 #define kParamPanoToolsALabel "a", "PanoTools: Radial lens distortion 3rd degree coefficient a."
517 #define kParamPanoToolsB "pt_b"
518 #define kParamPanoToolsBLabel "b", "PanoTools: Radial lens distortion 2nd degree coefficient b."
519 #define kParamPanoToolsC "pt_c"
520 #define kParamPanoToolsCLabel "c", "PanoTools: Radial lens distortion 1st degree coefficient c."
521 #define kParamPanoToolsD "pt_d"
522 #define kParamPanoToolsDLabel "d", "PanoTools: Horizontal lens shift (in pixels)."
523 #define kParamPanoToolsE "pt_e"
524 #define kParamPanoToolsELabel "e", "PanoTools: Vertical lens shift (in pixels)."
525 #define kParamPanoToolsG "pt_g"
526 #define kParamPanoToolsGLabel "g", "PanoTools: Vertical lens shear (in pixels). Use to remove slight misalignment of the line scanner relative to the film transport."
527 #define kParamPanoToolsT "pt_t"
528 #define kParamPanoToolsTLabel "t", "PanoTools: Horizontal lens shear (in pixels)."
529 
530 #define kParamCropToFormat "cropToFormat"
531 #define kParamCropToFormatLabel "Crop To Format"
532 #define kParamCropToFormatHint "If the source is inside the format and the effect extends it outside of the format, crop it to avoid unnecessary calculations. To avoid unwanted crops, only the borders that were inside of the format in the source clip will be cropped."
533 
534 
535 
536 // PFBarrel file reader
537 
538 // Copyright (C) 2011 The Pixel Farm Ltd
539 // The class that implements compositor-neutral functionality
540 
541 class PFBarrelCommon
542 {
543 
544 public:
545 
546     class FileReader
547     {
548 
549     public:
550 
551         FileReader(const std::string &filename);
552         ~FileReader();
553 
554         std::string readRawLine(void);
555         std::string readLine(void);
556         double readDouble(void);
557         int readInt(void);
558 
559         void dump(void);
560 
561         FILE *f_;
562         std::string error_;
563 
564         int version_;
565         int orig_w_, orig_h_;
566         double orig_pa_;
567         int undist_w_, undist_h_;
568         int model_;
569         double squeeze_;
570         int nkeys_;
571         std::vector<int> frame_;
572         std::vector<double> c3_;
573         std::vector<double> c5_;
574         std::vector<double> xp_;
575         std::vector<double> yp_;
576     };
577 };
578 
FileReader(const std::string & filename)579 PFBarrelCommon::FileReader::FileReader(const std::string &filename)
580 {
581     std::string ln;
582 
583     version_= -1;
584     error_= "";
585     orig_w_= -1;
586     orig_h_= -1;
587     orig_pa_ = 1.;
588     undist_w_= -1;
589     undist_h_= -1;
590     model_= -1;
591     squeeze_= -1;
592     nkeys_= 0;
593 
594     f_= std::fopen(filename.c_str(), "r");
595 
596     if (!f_) {
597         error_= "Failed to open file";
598         return;
599     }
600 
601     ln= readRawLine();
602     if (ln=="#PFBarrel 2011 v1") {
603         version_= 1;
604     } else if (ln=="#PFBarrel 2011 v2") {
605         version_= 2;
606     } else {
607         error_= "Bad header";
608         return;
609     }
610 
611     orig_w_= readInt(); if (error_!="") return;
612     orig_h_= readInt(); if (error_!="") return;
613 
614     if (version_==2) {
615         orig_pa_= readDouble(); if (error_!="") return;
616     } else {
617         orig_pa_= 1.0;
618     }
619 
620     undist_w_= readInt(); if (error_!="") return;
621     undist_h_= readInt(); if (error_!="") return;
622 
623     ln= readLine(); if (error_!="") return;
624 
625     if (ln=="Low Order") {
626         model_= 0;
627     } else if (ln=="High Order") {
628         model_= 1;
629     } else {
630         error_= "Bad model";
631         return;
632     }
633 
634     squeeze_= readDouble(); if (error_!="") return;
635     nkeys_= readInt(); if (error_!="") return;
636 
637     for (int i=0; i<nkeys_; i++) {
638         frame_.push_back(readInt()); if (error_!="") return;
639         c3_.push_back(readDouble()); if (error_!="") return;
640 
641         double c5= readDouble(); if (error_!="") return;
642         if (model_==0)
643             c5_.push_back(0.0);
644         else
645             c5_.push_back(c5);
646 
647         xp_.push_back(readDouble()); if (error_!="") return;
648         yp_.push_back(readDouble()); if (error_!="") return;
649     }
650 }
651 
652 
653 
~FileReader()654 PFBarrelCommon::FileReader::~FileReader()
655 {
656     if (f_) {
657         std::fclose(f_);
658         f_ = NULL;
659     }
660 }
661 
662 
663 
readLine(void)664 std::string PFBarrelCommon::FileReader::readLine(void)
665 {
666     std::string rv;
667     while (error_=="" && (rv=="" || rv[0]=='#')) {
668         rv= readRawLine();
669     }
670 
671     return rv;
672 }
673 
674 
675 
readDouble(void)676 double PFBarrelCommon::FileReader::readDouble(void)
677 {
678     return std::atof(readLine().c_str());
679 }
680 
681 
682 
readInt(void)683 int PFBarrelCommon::FileReader::readInt(void)
684 {
685     return std::atoi(readLine().c_str());
686 }
687 
688 
689 
readRawLine(void)690 std::string PFBarrelCommon::FileReader::readRawLine(void)
691 {
692     std::string rv;
693 
694     char buf[512];
695 
696     char* s = std::fgets(buf, sizeof(buf), f_);
697     if (s != NULL) {
698         rv= s;
699         rv= rv.erase(rv.length()-1);
700     } else {
701         error_= "Parse error";
702     }
703 
704     return rv;
705 }
706 
707 
708 #ifdef DEBUG
dump(void)709 void PFBarrelCommon::FileReader::dump(void)
710 {
711     std::printf("VERSION [%i]\n", version_);
712     std::printf("ERROR [%s]\n", error_.c_str());
713     std::printf("ORIG WH %i %i PA %f\n", orig_w_, orig_h_, orig_pa_);
714     std::printf("UNDIST WH %i %i\n", undist_w_, undist_h_);
715     std::printf("MODEL %i\n", model_);
716     std::printf("SQUEEZE %f\n", squeeze_);
717     std::printf("NKEYS %i\n", nkeys_);
718 
719     for (int i = 0; i < nkeys_; i++) {
720         std::printf("KEY %i FRAME %i\n", i, frame_[i]);
721         std::printf("KEY %i C3 %f\n", i, c3_[i]);
722         std::printf("KEY %i C5 %f\n", i, c5_[i]);
723         std::printf("KEY %i XP %f\n", i, xp_[i]);
724         std::printf("KEY %i YP %f\n", i, yp_[i]);
725     }
726 }
727 #endif
728 
729 
730 
731 static bool gIsMultiPlaneV1;
732 static bool gIsMultiPlaneV2;
733 
734 struct InputPlaneChannel
735 {
736     Image* img;
737     int channelIndex;
738     bool fillZero;
739 
InputPlaneChannelInputPlaneChannel740     InputPlaneChannel() : img(NULL), channelIndex(-1), fillZero(true) {}
741 };
742 
743 class DistortionProcessorBase
744     : public ImageProcessor
745 {
746 protected:
747     const Image *_srcImg;
748     const Image *_maskImg;
749     bool _processR;
750     bool _processG;
751     bool _processB;
752     bool _processA;
753     bool _transformIsIdentity;
754     Matrix3x3 _srcTransformInverse;
755     OfxRectD _format;
756     std::vector<InputPlaneChannel> _planeChannels;
757     bool _unpremultUV;
758     double _uOffset;
759     double _vOffset;
760     double _uScale;
761     double _vScale;
762     WrapEnum _uWrap;
763     WrapEnum _vWrap;
764     OfxPointD _renderScale;
765     const DistortionModel* _distortionModel;
766     DirectionEnum _direction;
767     OutputModeEnum _outputMode;
768     bool _blackOutside;
769     bool _doMasking;
770     double _mix;
771     bool _maskInvert;
772 
773 public:
774 
DistortionProcessorBase(ImageEffect & instance)775     DistortionProcessorBase(ImageEffect &instance)
776         : ImageProcessor(instance)
777         , _srcImg(NULL)
778         , _maskImg(NULL)
779         , _processR(true)
780         , _processG(true)
781         , _processB(true)
782         , _processA(false)
783         , _transformIsIdentity(true)
784         , _srcTransformInverse()
785         , _planeChannels()
786         , _unpremultUV(true)
787         , _uOffset(0.)
788         , _vOffset(0.)
789         , _uScale(1.)
790         , _vScale(1.)
791         , _uWrap(eWrapClamp)
792         , _vWrap(eWrapClamp)
793         , _distortionModel(NULL)
794         , _direction(eDirectionDistort)
795         , _outputMode(eOutputModeImage)
796         , _blackOutside(false)
797         , _doMasking(false)
798         , _mix(1.)
799         , _maskInvert(false)
800     {
801         _renderScale.x = _renderScale.y = 1.;
802         _format.x1 = _format.y1 = 0.;
803         _format.x2 = _format.y2 = 1.;
804     }
805 
setSrcImgs(const Image * src)806     void setSrcImgs(const Image *src) {_srcImg = src; }
807 
setMaskImg(const Image * v,bool maskInvert)808     void setMaskImg(const Image *v,
809                     bool maskInvert) { _maskImg = v; _maskInvert = maskInvert; }
810 
doMasking(bool v)811     void doMasking(bool v) {_doMasking = v; }
812 
setValues(bool processR,bool processG,bool processB,bool processA,bool transformIsIdentity,const Matrix3x3 & srcTransformInverse,const OfxRectD & format,const std::vector<InputPlaneChannel> & planeChannels,bool unpremultUV,double uOffset,double vOffset,double uScale,double vScale,WrapEnum uWrap,WrapEnum vWrap,const OfxPointD & renderScale,const DistortionModel * distortionModel,DirectionEnum direction,OutputModeEnum outputMode,bool blackOutside,double mix)813     void setValues(bool processR,
814                    bool processG,
815                    bool processB,
816                    bool processA,
817                    bool transformIsIdentity,
818                    const Matrix3x3 &srcTransformInverse,
819                    const OfxRectD& format,
820                    const std::vector<InputPlaneChannel>& planeChannels,
821                    bool unpremultUV,
822                    double uOffset,
823                    double vOffset,
824                    double uScale,
825                    double vScale,
826                    WrapEnum uWrap,
827                    WrapEnum vWrap,
828                    const OfxPointD& renderScale,
829                    const DistortionModel* distortionModel,
830                    DirectionEnum direction,
831                    OutputModeEnum outputMode,
832                    bool blackOutside,
833                    double mix)
834     {
835         _processR = processR;
836         _processG = processG;
837         _processB = processB;
838         _processA = processA;
839         _transformIsIdentity = transformIsIdentity;
840         _srcTransformInverse = srcTransformInverse;
841         _format = format;
842         _planeChannels = planeChannels;
843         _unpremultUV = unpremultUV;
844         _uOffset = uOffset;
845         _vOffset = vOffset;
846         _uScale = uScale;
847         _vScale = vScale;
848         _uWrap = uWrap;
849         _vWrap = vWrap;
850         _renderScale = renderScale;
851         _distortionModel = distortionModel;
852         _direction = direction;
853         _outputMode = outputMode;
854         _blackOutside = blackOutside;
855         _mix = mix;
856     }
857 
858 private:
859 };
860 
861 
862 
863 
864 // The "filter" and "clamp" template parameters allow filter-specific optimization
865 // by the compiler, using the same generic code for all filters.
866 template <class PIX, int nComponents, int maxValue, DistortionPluginEnum plugin, FilterEnum filter, bool clamp>
867 class DistortionProcessor
868     : public DistortionProcessorBase
869 {
870 public:
DistortionProcessor(ImageEffect & instance)871     DistortionProcessor(ImageEffect &instance)
872         : DistortionProcessorBase(instance)
873     {
874     }
875 
876 private:
877 
878 
wrap(double x,WrapEnum wrap)879     static inline double wrap(double x,
880                               WrapEnum wrap)
881     {
882         switch (wrap) {
883         default:
884         case eWrapClamp:
885 
886             return x;
887         case eWrapRepeat:
888 
889             return x - std::floor(x);
890         case eWrapMirror: {
891             double x2 = x / 2 - std::floor(x / 2);
892 
893             return (x2 <= 0.5) ? (2 * x2) : (2 - 2 * x2);
894         }
895         }
896     }
897 
getPix(unsigned channel,int x,int y)898     const PIX * getPix(unsigned channel,
899                        int x,
900                        int y)
901     {
902         return (const PIX *)  (_planeChannels[channel].img ? _planeChannels[channel].img->getPixelAddress(x, y) : 0);
903     }
904 
getVal(unsigned channel,const PIX * p,const PIX * pp)905     double getVal(unsigned channel,
906                   const PIX* p,
907                   const PIX* pp)
908     {
909         if (!_planeChannels[channel].img) {
910             return _planeChannels[channel].fillZero ? 0. : 1.;
911         }
912         if (!p) {
913             return pp ? pp[_planeChannels[channel].channelIndex] : 0.;
914         }
915 
916         return p[_planeChannels[channel].channelIndex];
917     }
918 
unpremult(double a,double * u,double * v)919     void unpremult(double a,
920                    double *u,
921                    double *v)
922     {
923         if ( _unpremultUV && (a != 0.) ) {
924             *u /= a;
925             *v /= a;
926         }
927     }
928 
929     void multiThreadProcessImages(OfxRectI procWindow);
930 };
931 
932 template <class PIX, int nComponents, int maxValue, DistortionPluginEnum plugin, FilterEnum filter, bool clamp>
933 void
multiThreadProcessImages(OfxRectI procWindow)934 DistortionProcessor<PIX, nComponents, maxValue, plugin, filter, clamp>::multiThreadProcessImages(OfxRectI procWindow)
935 {
936     assert(nComponents == 1 || nComponents == 3 || nComponents == 4);
937     assert(_dstImg);
938     assert(!(plugin == eDistortionPluginSTMap || plugin == eDistortionPluginIDistort) || _planeChannels.size() == 3);
939 
940     // required for STMap and LensDistortion
941     //if (plugin == eDistortionPluginSTMap || _outputMode == eOutputModeSTMap) {
942     int srcx1 = _format.x1;
943     int srcx2 = _format.x2;
944     int srcy1 = _format.y1;
945     int srcy2 = _format.y2;
946     //}
947     float tmpPix[4];
948     for (int y = procWindow.y1; y < procWindow.y2; y++) {
949         if ( _effect.abort() ) {
950             break;
951         }
952 
953         PIX *dstPix = (PIX *) _dstImg->getPixelAddress(procWindow.x1, y);
954 
955         for (int x = procWindow.x1; x < procWindow.x2; x++) {
956             double sx, sy, sxx, sxy, syx, syy; // the source pixel coordinates and their derivatives
957             double a = 1.;
958 
959             switch (plugin) {
960             case eDistortionPluginSTMap:
961             case eDistortionPluginIDistort: {
962                 const PIX *uPix    = getPix(0, x, y  );
963                 const PIX *uPix_xn = getPix(0, x + 1, y  );
964                 const PIX *uPix_xp = getPix(0, x - 1, y  );
965                 const PIX *uPix_yn = getPix(0, x, y + 1);
966                 const PIX *uPix_yp = getPix(0, x, y - 1);
967                 const PIX *vPix, *vPix_xn, *vPix_xp, *vPix_yn, *vPix_yp;
968                 if (_planeChannels[1].img == _planeChannels[0].img) {
969                     vPix    = uPix;
970                     vPix_xn = uPix_xn;
971                     vPix_xp = uPix_xp;
972                     vPix_yn = uPix_yn;
973                     vPix_yp = uPix_yp;
974                 } else {
975                     vPix =    getPix(1, x, y  );
976                     vPix_xn = getPix(1, x + 1, y  );
977                     vPix_xp = getPix(1, x - 1, y  );
978                     vPix_yn = getPix(1, x, y + 1);
979                     vPix_yp = getPix(1, x, y - 1);
980                 }
981                 const PIX *aPix, *aPix_xn, *aPix_xp, *aPix_yn, *aPix_yp;
982                 if (_planeChannels[2].img == _planeChannels[0].img) {
983                     aPix = uPix;
984                     aPix_xn = uPix_xn;
985                     aPix_xp = uPix_xp;
986                     aPix_yn = uPix_yn;
987                     aPix_yp = uPix_yp;
988                 } else if (_planeChannels[2].img == _planeChannels[1].img) {
989                     aPix = vPix;
990                     aPix_xn = vPix_xn;
991                     aPix_xp = vPix_xp;
992                     aPix_yn = vPix_yn;
993                     aPix_yp = vPix_yp;
994                 } else {
995                     aPix =    getPix(2, x, y  );
996                     aPix_xn = getPix(2, x + 1, y  );
997                     aPix_xp = getPix(2, x - 1, y  );
998                     aPix_yn = getPix(2, x, y + 1);
999                     aPix_yp = getPix(2, x, y - 1);
1000                 }
1001                 // compute gradients before wrapping
1002                 double u = getVal(0, uPix, NULL);
1003                 double v = getVal(1, vPix, NULL);
1004                 a = getVal(2, aPix, NULL);
1005                 unpremult(a, &u, &v);
1006 
1007                 double ux, uy, vx, vy;
1008                 {
1009                     double u_xn = getVal(0, uPix_xn, uPix);
1010                     double u_xp = getVal(0, uPix_xp, uPix);
1011                     double u_yn = getVal(0, uPix_yn, uPix);
1012                     double u_yp = getVal(0, uPix_yp, uPix);
1013                     double v_xn = getVal(1, vPix_xn, vPix);
1014                     double v_xp = getVal(1, vPix_xp, vPix);
1015                     double v_yn = getVal(1, vPix_yn, vPix);
1016                     double v_yp = getVal(1, vPix_yp, vPix);
1017                     if (_unpremultUV) {
1018                         unpremult(getVal(2, aPix_xn, aPix), &u_xn, &v_xn);
1019                         unpremult(getVal(2, aPix_xp, aPix), &u_xp, &v_xp);
1020                         unpremult(getVal(2, aPix_yn, aPix), &u_yn, &v_yn);
1021                         unpremult(getVal(2, aPix_yp, aPix), &u_yp, &v_yp);
1022                     }
1023                     ux = (u_xn - u_xp) / 2.;
1024                     vx = (v_xn - v_xp) / 2.;
1025                     uy = (u_yn - u_yp) / 2.;
1026                     vy = (v_yn - v_yp) / 2.;
1027                 }
1028                 u = (u - _uOffset) * _uScale;
1029                 ux *= _uScale;
1030                 uy *= _uScale;
1031                 v = (v - _vOffset) * _vScale;
1032                 vx *= _vScale;
1033                 vy *= _vScale;
1034                 switch (plugin) {
1035                     case eDistortionPluginSTMap:
1036                         // wrap u and v
1037                         u = wrap(u, _uWrap);
1038                         v = wrap(v, _vWrap);
1039                         sx = srcx1 + u * (srcx2 - srcx1);
1040                         sy = srcy1 + v * (srcy2 - srcy1);         // 0,0 corresponds to the lower left corner of the first pixel
1041                         // scale gradients by (srcx2 - srcx1)
1042                         if (filter != eFilterImpulse) {
1043                             sxx = ux * (srcx2 - srcx1);
1044                             sxy = uy * (srcx2 - srcx1);
1045                             syx = vx * (srcy2 - srcy1);
1046                             syy = vy * (srcy2 - srcy1);
1047                         }
1048                         break;
1049                     case eDistortionPluginIDistort:
1050                         // 0,0 corresponds to the lower left corner of the first pixel, so we have to add 0.5
1051                         // (x,y) = (0,0) and (u,v) = (0,0) means to pick color at (0.5,0.5)
1052                         sx = x + u + 0.5;
1053                         sy = y + v + 0.5;
1054                         if (filter != eFilterImpulse) {
1055                             sxx = 1 + ux;
1056                             sxy = uy;
1057                             syx = vx;
1058                             syy = 1 + vy;
1059                         }
1060                         break;
1061                     default:
1062                         assert(false);
1063                         break;
1064                 }
1065                 break;
1066             }
1067             case eDistortionPluginLensDistortion: {
1068                 assert(_distortionModel);
1069                 // undistort/distort take pixel coordinates, do not divide by renderScale
1070                 if (_direction == eDirectionDistort) {
1071                     _distortionModel->undistort( x + 0.5,
1072                                                  y + 0.5,
1073                                                  &sx, &sy);
1074                 } else {
1075                     _distortionModel->distort( x + 0.5,
1076                                                y + 0.5,
1077                                                &sx, &sy);
1078                 }
1079                 sxx = 1;     // TODO: Jacobian
1080                 sxy = 0;
1081                 syx = 0;
1082                 syy = 1;
1083                 break;
1084             }
1085             } // switch
1086             double Jxx = 1., Jxy = 0., Jyx = 0., Jyy = 1.;
1087             if (_transformIsIdentity) {
1088                 if (filter != eFilterImpulse) {
1089                     Jxx = sxx;
1090                     Jxy = sxy;
1091                     Jyx = syx;
1092                     Jyy = syy;
1093                 }
1094             } else {
1095                 const Matrix3x3 & H = _srcTransformInverse;
1096                 double transformedx = H(0,0) * sx + H(0,1) * sy + H(0,2);
1097                 double transformedy = H(1,0) * sx + H(1,1) * sy + H(1,2);
1098                 double transformedz = H(2,0) * sx + H(2,1) * sy + H(2,2);
1099                 if (transformedz == 0) {
1100                     sx = sy = std::numeric_limits<double>::infinity();
1101                 } else {
1102                     sx = transformedx / transformedz;
1103                     sy = transformedy / transformedz;
1104                     if (filter != eFilterImpulse) {
1105                         Jxx = (H(0,0) * transformedz - transformedx * H(2,0)) / (transformedz * transformedz);
1106                         Jxy = (H(0,1) * transformedz - transformedx * H(2,1)) / (transformedz * transformedz);
1107                         Jyx = (H(1,0) * transformedz - transformedy * H(2,0)) / (transformedz * transformedz);
1108                         Jyy = (H(1,1) * transformedz - transformedy * H(2,1)) / (transformedz * transformedz);
1109                     }
1110                 }
1111             }
1112 
1113             if (_outputMode == eOutputModeSTMap) {
1114                 // 0,0 corresponds to the lower left corner of the first pixel
1115                 tmpPix[0] = (sx - srcx1) / (srcx2 - srcx1); // u
1116                 tmpPix[1] = (sy - srcy1) / (srcy2 - srcy1); // v
1117                 tmpPix[2] = 1.; // a
1118                 tmpPix[3] = 1.; // be opaque
1119             } else {
1120                 if (filter == eFilterImpulse) {
1121                     ofxsFilterInterpolate2D<PIX, nComponents, filter, clamp>(sx, sy, _srcImg, _blackOutside, tmpPix);
1122                 } else {
1123                     ofxsFilterInterpolate2DSuper<PIX, nComponents, filter, clamp>(sx, sy, Jxx, Jxy, Jyx, Jyy, _srcImg, _blackOutside, tmpPix);
1124                 }
1125                 for (unsigned c = 0; c < nComponents; ++c) {
1126                     tmpPix[c] *= a;
1127                 }
1128             }
1129             ofxsMaskMix<PIX, nComponents, maxValue, true>(tmpPix, x, y, _srcImg, _doMasking, _maskImg, (float)_mix, _maskInvert, dstPix);
1130             // copy back original values from unprocessed channels
1131             if (nComponents == 1) {
1132                 if (!_processA) {
1133                     const PIX *srcPix = (const PIX *)  (_srcImg ? _srcImg->getPixelAddress(x, y) : 0);
1134                     dstPix[0] = srcPix ? srcPix[0] : PIX();
1135                 }
1136             } else if ( (nComponents == 3) || (nComponents == 4) ) {
1137                 const PIX *srcPix = 0;
1138                 if ( !_processR || !_processG || !_processB || ( !_processA && (nComponents == 4) ) ) {
1139                     srcPix = (const PIX *)  (_srcImg ? _srcImg->getPixelAddress(x, y) : 0);
1140                 }
1141                 if (!_processR) {
1142                     dstPix[0] = srcPix ? srcPix[0] : PIX();
1143                 }
1144                 if (!_processG) {
1145                     dstPix[1] = srcPix ? srcPix[1] : PIX();
1146                 }
1147                 if (!_processB) {
1148                     dstPix[2] = srcPix ? srcPix[2] : PIX();
1149                 }
1150                 if ( !_processA && (nComponents == 4) ) {
1151                     dstPix[3] = srcPix ? srcPix[3] : PIX();
1152                 }
1153             }
1154             // increment the dst pixel
1155             dstPix += nComponents;
1156         }
1157     }
1158 } // multiThreadProcessImages
1159 
1160 
1161 ////////////////////////////////////////////////////////////////////////////////
1162 /** @brief The plugin that does our work */
1163 class DistortionPlugin
1164     : public MultiPlane::MultiPlaneEffect
1165 {
1166 public:
1167     /** @brief ctor */
DistortionPlugin(OfxImageEffectHandle handle,int majorVersion,DistortionPluginEnum plugin)1168     DistortionPlugin(OfxImageEffectHandle handle,
1169                      int majorVersion,
1170                      DistortionPluginEnum plugin)
1171         : MultiPlane::MultiPlaneEffect(handle)
1172         , _majorVersion(majorVersion)
1173         , _dstClip(NULL)
1174         , _srcClip(NULL)
1175         , _uvClip(NULL)
1176         , _maskClip(NULL)
1177         , _processR(NULL)
1178         , _processG(NULL)
1179         , _processB(NULL)
1180         , _processA(NULL)
1181         , _uvChannels()
1182         , _unpremultUV(NULL)
1183         , _uvOffset(NULL)
1184         , _uvScale(NULL)
1185         , _uWrap(NULL)
1186         , _vWrap(NULL)
1187         , _extent(NULL)
1188         , _format(NULL)
1189         , _formatSize(NULL)
1190         , _formatPar(NULL)
1191         , _btmLeft(NULL)
1192         , _size(NULL)
1193         , _recenter(NULL)
1194         , _distortionModel(NULL)
1195         , _direction(NULL)
1196         , _outputMode(NULL)
1197         , _k1(NULL)
1198         , _k2(NULL)
1199         , _center(NULL)
1200         , _squeeze(NULL)
1201         , _asymmetric(NULL)
1202         , _pfFile(NULL)
1203         , _pfReload(NULL)
1204         , _pfC3(NULL)
1205         , _pfC5(NULL)
1206         , _pfSqueeze(NULL)
1207         , _pfP(NULL)
1208         , _xa_fov_unit(NULL)
1209         , _ya_fov_unit(NULL)
1210         , _xb_fov_unit(NULL)
1211         , _yb_fov_unit(NULL)
1212         , _fl_cm(NULL)
1213         , _fd_cm(NULL)
1214         , _w_fb_cm(NULL)
1215         , _h_fb_cm(NULL)
1216         , _x_lco_cm(NULL)
1217         , _y_lco_cm(NULL)
1218         , _pa(NULL)
1219         , _ld(NULL)
1220         , _sq(NULL)
1221         , _cx(NULL)
1222         , _cy(NULL)
1223         , _qu(NULL)
1224         , _c2(NULL)
1225         , _u1(NULL)
1226         , _v1(NULL)
1227         , _c4(NULL)
1228         , _u3(NULL)
1229         , _v3(NULL)
1230         , _phi(NULL)
1231         , _b(NULL)
1232         , _cx02(NULL)
1233         , _cy02(NULL)
1234         , _cx22(NULL)
1235         , _cy22(NULL)
1236         , _cx04(NULL)
1237         , _cy04(NULL)
1238         , _cx24(NULL)
1239         , _cy24(NULL)
1240         , _cx44(NULL)
1241         , _cy44(NULL)
1242         , _a4phi(NULL)
1243         , _a4sqx(NULL)
1244         , _a4sqy(NULL)
1245         , _cx06(NULL)
1246         , _cy06(NULL)
1247         , _cx26(NULL)
1248         , _cy26(NULL)
1249         , _cx46(NULL)
1250         , _cy46(NULL)
1251         , _cx66(NULL)
1252         , _cy66(NULL)
1253         , _c6(NULL)
1254         , _c8(NULL)
1255         , _pta(NULL)
1256         , _ptb(NULL)
1257         , _ptc(NULL)
1258         , _ptd(NULL)
1259         , _pte(NULL)
1260         , _ptg(NULL)
1261         , _ptt(NULL)
1262         , _filter(NULL)
1263         , _clamp(NULL)
1264         , _blackOutside(NULL)
1265         , _cropToFormat(NULL)
1266         , _mix(NULL)
1267         , _maskApply(NULL)
1268         , _maskInvert(NULL)
1269         , _plugin(plugin)
1270     {
1271         _dstClip = fetchClip(kOfxImageEffectOutputClipName);
1272         assert( _dstClip && (!_dstClip->isConnected() || _dstClip->getPixelComponents() == ePixelComponentRGB ||
1273                              _dstClip->getPixelComponents() == ePixelComponentRGBA ||
1274                              _dstClip->getPixelComponents() == ePixelComponentAlpha) );
1275         _srcClip = getContext() == eContextGenerator ? NULL : fetchClip(kOfxImageEffectSimpleSourceClipName);
1276         assert( (!_srcClip && getContext() == eContextGenerator) ||
1277                 ( _srcClip && (!_srcClip->isConnected() || _srcClip->getPixelComponents() ==  ePixelComponentRGB ||
1278                                _srcClip->getPixelComponents() == ePixelComponentRGBA ||
1279                                _srcClip->getPixelComponents() == ePixelComponentAlpha) ) );
1280         if ( (_plugin == eDistortionPluginIDistort) || (_plugin == eDistortionPluginSTMap) ) {
1281             _uvClip = fetchClip(kClipUV);
1282             assert( _uvClip && (_uvClip->getPixelComponents() == ePixelComponentRGB || _uvClip->getPixelComponents() == ePixelComponentRGBA || _uvClip->getPixelComponents() == ePixelComponentAlpha) );
1283         }
1284         _maskClip = fetchClip(getContext() == eContextPaint ? "Brush" : "Mask");
1285         assert(!_maskClip || !_maskClip->isConnected() || _maskClip->getPixelComponents() == ePixelComponentAlpha);
1286         _processR = fetchBooleanParam(kParamProcessR);
1287         _processG = fetchBooleanParam(kParamProcessG);
1288         _processB = fetchBooleanParam(kParamProcessB);
1289         _processA = fetchBooleanParam(kParamProcessA);
1290         assert(_processR && _processG && _processB && _processA);
1291         if ( (plugin == eDistortionPluginIDistort) || (plugin == eDistortionPluginSTMap) ) {
1292             _uvChannels[0] = fetchChoiceParam(kParamChannelU);
1293             _uvChannels[1] = fetchChoiceParam(kParamChannelV);
1294             _uvChannels[2] = fetchChoiceParam(kParamChannelA);
1295             if (gIsMultiPlaneV1 || gIsMultiPlaneV2) {
1296 
1297                 {
1298                     FetchChoiceParamOptions args = FetchChoiceParamOptions::createFetchChoiceParamOptionsForInputChannel();
1299                     args.dependsClips.push_back(_uvClip);
1300                     fetchDynamicMultiplaneChoiceParameter(kParamChannelU, args);
1301                 }
1302                 {
1303                     FetchChoiceParamOptions args = FetchChoiceParamOptions::createFetchChoiceParamOptionsForInputChannel();
1304                     args.dependsClips.push_back(_uvClip);
1305                     fetchDynamicMultiplaneChoiceParameter(kParamChannelV, args);
1306                 }
1307                 {
1308                     FetchChoiceParamOptions args = FetchChoiceParamOptions::createFetchChoiceParamOptionsForInputChannel();
1309                     args.dependsClips.push_back(_uvClip);
1310                     fetchDynamicMultiplaneChoiceParameter(kParamChannelA, args);
1311                 }
1312 
1313                 onAllParametersFetched();
1314             }
1315             _unpremultUV = fetchBooleanParam(kParamChannelUnpremultUV);
1316             _uvOffset = fetchDouble2DParam(kParamUVOffset);
1317             _uvScale = fetchDouble2DParam(kParamUVScale);
1318             assert(_uvChannels[0] && _uvChannels[1] && _uvChannels[2] && _uvOffset && _uvScale);
1319             if (plugin == eDistortionPluginSTMap) {
1320                 _uWrap = fetchChoiceParam(kParamWrapU);
1321                 _vWrap = fetchChoiceParam(kParamWrapV);
1322                 assert(_uWrap && _vWrap);
1323             }
1324         } else {
1325             _uvChannels[0] = _uvChannels[1] = _uvChannels[2] = NULL;
1326         }
1327 
1328         /* if (gIsMultiPlane) {
1329              _outputLayer = fetchChoiceParam(kParamOutputChannels);
1330              _outputLayerStr = fetchStringParam(kParamOutputChannelsChoice);
1331              assert(_outputLayer && _outputLayerStr);
1332            }*/
1333         if (_plugin == eDistortionPluginLensDistortion) {
1334             _extent = fetchChoiceParam(kParamGeneratorExtent);
1335             _format = fetchChoiceParam(kParamGeneratorFormat);
1336             _formatSize = fetchInt2DParam(kParamGeneratorSize);
1337             _formatPar= fetchDoubleParam(kParamGeneratorPAR);
1338             _btmLeft = fetchDouble2DParam(kParamRectangleInteractBtmLeft);
1339             _size = fetchDouble2DParam(kParamRectangleInteractSize);
1340             _recenter = fetchPushButtonParam(kParamGeneratorCenter);
1341 
1342             _distortionModel = fetchChoiceParam(kParamDistortionModel);
1343             _direction = fetchChoiceParam(kParamDistortionDirection);
1344             _outputMode = fetchChoiceParam(kParamDistortionOutputMode);
1345 
1346             // Nuke
1347             _k1 = fetchDoubleParam(kParamK1);
1348             _k2 = fetchDoubleParam(kParamK2);
1349             _center = fetchDouble2DParam(kParamCenter);
1350             _squeeze = fetchDoubleParam(kParamSqueeze);
1351             _asymmetric = fetchDouble2DParam(kParamAsymmetric);
1352             assert(_k1 && _k2 && _center && _squeeze && _asymmetric);
1353 
1354             // PFBarrel
1355             _pfFile = fetchStringParam(kParamPFFile);
1356             if ( paramExists(kParamPFFileReload) ) {
1357                 _pfReload = fetchPushButtonParam(kParamPFFileReload);
1358             }
1359             _pfC3 = fetchDoubleParam(kParamPFC3);
1360             _pfC5 = fetchDoubleParam(kParamPFC5);
1361             _pfSqueeze = fetchDoubleParam(kParamPFSqueeze);
1362             _pfP = fetchDouble2DParam(kParamPFP);
1363 
1364             // 3DEqualizer
1365             _xa_fov_unit = fetchDoubleParam(kParam3DE4_xa_fov_unit);
1366             _ya_fov_unit = fetchDoubleParam(kParam3DE4_ya_fov_unit);
1367             _xb_fov_unit = fetchDoubleParam(kParam3DE4_xb_fov_unit);
1368             _yb_fov_unit = fetchDoubleParam(kParam3DE4_yb_fov_unit);
1369             _fl_cm = fetchDoubleParam(kParam3DE4_focal_length_cm);
1370             _fd_cm = fetchDoubleParam(kParam3DE4_custom_focus_distance_cm);
1371             _w_fb_cm = fetchDoubleParam(kParam3DE4_filmback_width_cm);
1372             _h_fb_cm = fetchDoubleParam(kParam3DE4_filmback_height_cm);
1373             _x_lco_cm = fetchDoubleParam(kParam3DE4_lens_center_offset_x_cm);
1374             _y_lco_cm = fetchDoubleParam(kParam3DE4_lens_center_offset_y_cm);
1375             _pa = fetchDoubleParam(kParam3DE4_pixel_aspect);
1376 
1377             // 3DEclassic
1378             _ld = fetchDoubleParam(kParam3DEDistortion);
1379             _sq = fetchDoubleParam(kParam3DEAnamorphicSqueeze);
1380             _cx = fetchDoubleParam(kParam3DECurvatureX);
1381             _cy = fetchDoubleParam(kParam3DECurvatureY);
1382             _qu = fetchDoubleParam(kParam3DEQuarticDistortion);
1383 
1384             // 3DEStandard
1385             _c2 = fetchDoubleParam(kParam3DEDistortionDegree2);
1386             _u1 = fetchDoubleParam(kParam3DEUDegree2);
1387             _v1 = fetchDoubleParam(kParam3DEVDegree2);
1388             _c4 = fetchDoubleParam(kParam3DEQuarticDistortionDegree4);
1389             _u3 = fetchDoubleParam(kParam3DEUDegree4);
1390             _v3 = fetchDoubleParam(kParam3DEVDegree4);
1391             _phi = fetchDoubleParam(kParam3DEPhiCylindricDirection);
1392             _b = fetchDoubleParam(kParam3DEBCylindricBending);
1393 
1394             // 3DEAnamorphic4
1395             _cx02 = fetchDoubleParam(kParam3DECx02Degree2);
1396             _cy02 = fetchDoubleParam(kParam3DECy02Degree2);
1397             _cx22 = fetchDoubleParam(kParam3DECx22Degree2);
1398             _cy22 = fetchDoubleParam(kParam3DECy22Degree2);
1399             _cx04 = fetchDoubleParam(kParam3DECx04Degree4);
1400             _cy04 = fetchDoubleParam(kParam3DECy04Degree4);
1401             _cx24 = fetchDoubleParam(kParam3DECx24Degree4);
1402             _cy24 = fetchDoubleParam(kParam3DECy24Degree4);
1403             _cx44 = fetchDoubleParam(kParam3DECx44Degree4);
1404             _cy44 = fetchDoubleParam(kParam3DECy44Degree4);
1405             _a4phi = fetchDoubleParam(kParam3DELensRotation);
1406             _a4sqx = fetchDoubleParam(kParam3DESqueezeX);
1407             _a4sqy = fetchDoubleParam(kParam3DESqueezeY);
1408 
1409             // 3DEAnamorphic6
1410             _cx06 = fetchDoubleParam(kParam3DECx06Degree6);
1411             _cy06 = fetchDoubleParam(kParam3DECy06Degree6);
1412             _cx26 = fetchDoubleParam(kParam3DECx26Degree6);
1413             _cy26 = fetchDoubleParam(kParam3DECy26Degree6);
1414             _cx46 = fetchDoubleParam(kParam3DECx46Degree6);
1415             _cy46 = fetchDoubleParam(kParam3DECy46Degree6);
1416             _cx66 = fetchDoubleParam(kParam3DECx66Degree6);
1417             _cy66 = fetchDoubleParam(kParam3DECy66Degree6);
1418 
1419             // 3DEFishEye8
1420             _c6 = fetchDoubleParam(kParam3DEDegree6);
1421             _c8 = fetchDoubleParam(kParam3DEDegree8);
1422 
1423             // PanoTools
1424             _pta = fetchDoubleParam(kParamPanoToolsA);
1425             _ptb = fetchDoubleParam(kParamPanoToolsB);
1426             _ptc = fetchDoubleParam(kParamPanoToolsC);
1427             _ptd = fetchDoubleParam(kParamPanoToolsD);
1428             _pte = fetchDoubleParam(kParamPanoToolsE);
1429             _ptg = fetchDoubleParam(kParamPanoToolsG);
1430             _ptt = fetchDoubleParam(kParamPanoToolsT);
1431             assert(_pta && _ptb && _ptc && _ptd && _pte && _ptg && _ptt);
1432         }
1433         _filter = fetchChoiceParam(kParamFilterType);
1434         _clamp = fetchBooleanParam(kParamFilterClamp);
1435         _blackOutside = fetchBooleanParam(kParamFilterBlackOutside);
1436         assert(_filter && _clamp && _blackOutside);
1437         if ( paramExists(kParamCropToFormat) ) {
1438             _cropToFormat = fetchBooleanParam(kParamCropToFormat);
1439             assert(_cropToFormat);
1440         } else {
1441             assert( !paramExists(kParamCropToFormat) );
1442         }
1443         _mix = fetchDoubleParam(kParamMix);
1444         _maskApply = ( ofxsMaskIsAlwaysConnected( OFX::getImageEffectHostDescription() ) && paramExists(kParamMaskApply) ) ? fetchBooleanParam(kParamMaskApply) : 0;
1445         _maskInvert = fetchBooleanParam(kParamMaskInvert);
1446         assert(_mix && _maskInvert);
1447 
1448         // honor kParamDefaultsNormalised
1449         if ( _plugin == eDistortionPluginLensDistortion && paramExists(kParamDefaultsNormalised) ) {
1450             // Some hosts (e.g. Resolve) may not support normalized defaults (setDefaultCoordinateSystem(eCoordinatesNormalised))
1451             // handle these ourselves!
1452             BooleanParam* param = fetchBooleanParam(kParamDefaultsNormalised);
1453             assert(param);
1454             bool normalised = param->getValue();
1455             if (normalised) {
1456                 OfxPointD size = getProjectExtent();
1457                 OfxPointD origin = getProjectOffset();
1458                 OfxPointD p;
1459                 // we must denormalise all parameters for which setDefaultCoordinateSystem(eCoordinatesNormalised) couldn't be done
1460                 beginEditBlock(kParamDefaultsNormalised);
1461                 p = _btmLeft->getValue();
1462                 _btmLeft->setValue(p.x * size.x + origin.x, p.y * size.y + origin.y);
1463                 p = _size->getValue();
1464                 _size->setValue(p.x * size.x, p.y * size.y);
1465                 param->setValue(false);
1466                 endEditBlock();
1467             }
1468         }
1469 
1470         // finally
1471         syncPrivateData();
1472     }
1473 
1474 
1475 private:
1476     // override the roi call
1477     virtual void getRegionsOfInterest(const RegionsOfInterestArguments &args, RegionOfInterestSetter &rois) OVERRIDE FINAL;
1478     virtual bool getRegionOfDefinition(const RegionOfDefinitionArguments &args, OfxRectD &rod) OVERRIDE FINAL;
1479 #ifdef OFX_EXTENSIONS_NUKE
1480     virtual OfxStatus getClipComponents(const ClipComponentsArguments& args, ClipComponentsSetter& clipComponents) OVERRIDE FINAL;
1481 #endif
1482 
1483     /* Override the render */
1484     virtual void render(const RenderArguments &args) OVERRIDE FINAL;
1485 
1486     /* internal render function */
1487     template <class PIX, int nComponents, int maxValue, DistortionPluginEnum plugin>
1488     void renderInternalForBitDepth(const RenderArguments &args);
1489 
1490     template <int nComponents, DistortionPluginEnum plugin>
1491     void renderInternal(const RenderArguments &args, BitDepthEnum dstBitDepth);
1492 
1493     /* set up and run a processor */
1494     void setupAndProcess(DistortionProcessorBase &, const RenderArguments &args);
1495 
1496     virtual bool isIdentity(const IsIdentityArguments &args, Clip * &identityClip, double &identityTime, int& view, std::string& plane) OVERRIDE FINAL;
1497     virtual void getClipPreferences(ClipPreferencesSetter &clipPreferences) OVERRIDE FINAL;
1498 
1499     /** @brief called when a param has just had its value changed */
1500     void changedParam(const InstanceChangedArgs &args, const std::string &paramName) OVERRIDE FINAL;
1501 
1502     /** @brief The sync private data action, called when the effect needs to sync any private data to persistent parameters */
syncPrivateData(void)1503     virtual void syncPrivateData(void) OVERRIDE FINAL
1504     {
1505         updateVisibility();
1506     }
1507 
1508     void updateVisibility();
1509 
1510     DistortionModel* getDistortionModel(const OfxRectD& format, const OfxPointD& renderScale, double time);
1511 
1512     bool getLensDistortionFormat(double time, const OfxPointD& renderScale, OfxRectD *format, double *par);
1513 
1514 private:
1515     int _majorVersion;
1516 
1517     // do not need to delete these, the ImageEffect is managing them for us
1518     Clip *_dstClip;
1519     Clip *_srcClip;
1520     Clip *_uvClip;
1521     Clip *_maskClip;
1522     BooleanParam* _processR;
1523     BooleanParam* _processG;
1524     BooleanParam* _processB;
1525     BooleanParam* _processA;
1526     ChoiceParam* _uvChannels[3];
1527     BooleanParam* _unpremultUV;
1528     Double2DParam *_uvOffset;
1529     Double2DParam *_uvScale;
1530     ChoiceParam* _uWrap;
1531     ChoiceParam* _vWrap;
1532 
1533     ///////////////////
1534     // LensDistortion
1535 
1536     // format params
1537     ChoiceParam* _extent;
1538     ChoiceParam* _format;
1539     Int2DParam* _formatSize;
1540     DoubleParam* _formatPar;
1541     Double2DParam* _btmLeft;
1542     Double2DParam* _size;
1543     PushButtonParam *_recenter;
1544 
1545     ChoiceParam* _distortionModel;
1546     ChoiceParam* _direction;
1547     ChoiceParam* _outputMode;
1548 
1549     // Nuke
1550     DoubleParam* _k1;
1551     DoubleParam* _k2;
1552     Double2DParam* _center;
1553     DoubleParam* _squeeze;
1554     Double2DParam* _asymmetric;
1555 
1556     // PFBarrel
1557     StringParam* _pfFile;
1558     PushButtonParam* _pfReload;
1559     DoubleParam* _pfC3;
1560     DoubleParam* _pfC5;
1561     DoubleParam* _pfSqueeze;
1562     Double2DParam* _pfP;
1563 
1564     // 3DEqualizer
1565     // fov parameters
1566     DoubleParam* _xa_fov_unit;
1567     DoubleParam* _ya_fov_unit;
1568     DoubleParam* _xb_fov_unit;
1569     DoubleParam* _yb_fov_unit;
1570     // seven builtin parameters
1571     DoubleParam* _fl_cm;
1572     DoubleParam* _fd_cm;
1573     DoubleParam* _w_fb_cm;
1574     DoubleParam* _h_fb_cm;
1575     DoubleParam* _x_lco_cm;
1576     DoubleParam* _y_lco_cm;
1577     DoubleParam* _pa;
1578     // 3DEClassic model
1579     DoubleParam* _ld;
1580     DoubleParam* _sq;
1581     DoubleParam* _cx;
1582     DoubleParam* _cy;
1583     DoubleParam* _qu;
1584     // 3DEStandard model
1585     DoubleParam* _c2;
1586     DoubleParam* _u1;
1587     DoubleParam* _v1;
1588     DoubleParam* _c4;
1589     DoubleParam* _u3;
1590     DoubleParam* _v3;
1591     DoubleParam* _phi;
1592     DoubleParam* _b;
1593     // 3DEAnamorphic4 model
1594     DoubleParam* _cx02;
1595     DoubleParam* _cy02;
1596     DoubleParam* _cx22;
1597     DoubleParam* _cy22;
1598     DoubleParam* _cx04;
1599     DoubleParam* _cy04;
1600     DoubleParam* _cx24;
1601     DoubleParam* _cy24;
1602     DoubleParam* _cx44;
1603     DoubleParam* _cy44;
1604     DoubleParam* _a4phi;
1605     DoubleParam* _a4sqx;
1606     DoubleParam* _a4sqy;
1607 
1608     // 3DEAnamorphic6 model
1609     DoubleParam* _cx06;
1610     DoubleParam* _cy06;
1611     DoubleParam* _cx26;
1612     DoubleParam* _cy26;
1613     DoubleParam* _cx46;
1614     DoubleParam* _cy46;
1615     DoubleParam* _cx66;
1616     DoubleParam* _cy66;
1617 
1618     // 3DEFishEye8 model
1619     DoubleParam* _c6;
1620     DoubleParam* _c8;
1621 
1622     // PanoTools model
1623     DoubleParam* _pta;
1624     DoubleParam* _ptb;
1625     DoubleParam* _ptc;
1626     DoubleParam* _ptd;
1627     DoubleParam* _pte;
1628     DoubleParam* _ptg;
1629     DoubleParam* _ptt;
1630 
1631     ChoiceParam* _filter;
1632     BooleanParam* _clamp;
1633     BooleanParam* _blackOutside;
1634     BooleanParam* _cropToFormat;
1635     DoubleParam* _mix;
1636     BooleanParam* _maskApply;
1637     BooleanParam* _maskInvert;
1638     DistortionPluginEnum _plugin;
1639 };
1640 
1641 
1642 void
getClipPreferences(ClipPreferencesSetter & clipPreferences)1643 DistortionPlugin::getClipPreferences(ClipPreferencesSetter &clipPreferences)
1644 {
1645     //We have to do this because the processing code does not support varying components for uvClip and srcClip
1646     PixelComponentEnum dstPixelComps = getDefaultOutputClipComponents();
1647 
1648     if (_srcClip) {
1649         clipPreferences.setClipComponents(*_srcClip, dstPixelComps);
1650     }
1651     if (gIsMultiPlaneV2 && _uvClip) {
1652         MultiPlaneEffect::getClipPreferences(clipPreferences);
1653     }
1654     if (_plugin == eDistortionPluginLensDistortion) {
1655         OfxRectD format;
1656         double par;
1657         OfxPointD rs1 = {1., 1.};
1658 
1659         // we pass 0 as time, since anyway the input RoD is never used, thanks to the test on the return value
1660         bool setFormat = getLensDistortionFormat(0, rs1, &format, &par);
1661         if (setFormat) {
1662             OfxRectI formatI;
1663             // round to nearest
1664             formatI.x1 = std::floor(format.x1 + 0.5);
1665             formatI.y1 = std::floor(format.y1 + 0.5);
1666             formatI.x2 = std::floor(format.x2 + 0.5);
1667             formatI.y2 = std::floor(format.y2 + 0.5);
1668             clipPreferences.setOutputFormat(formatI);
1669             clipPreferences.setPixelAspectRatio(*_dstClip, par);
1670         }
1671     } else if ( _plugin == eDistortionPluginSTMap && _uvClip && _uvClip->isConnected() ) {
1672         OfxRectI uvFormat;
1673         _uvClip->getFormat(uvFormat);
1674         double par = _uvClip->getPixelAspectRatio();
1675         if ( OFX::Coords::rectIsEmpty(uvFormat) ) {
1676             // no format is available, use the RoD instead
1677             const OfxRectD& srcRod = _uvClip->getRegionOfDefinition(0.);
1678             const OfxPointD rs1 = {1., 1.};
1679             Coords::toPixelNearest(srcRod, rs1, par, &uvFormat);
1680         }
1681         clipPreferences.setOutputFormat(uvFormat);
1682         clipPreferences.setPixelAspectRatio(*_dstClip, par);
1683     }
1684 }
1685 
1686 ////////////////////////////////////////////////////////////////////////////////
1687 /** @brief render for the filter */
1688 
1689 
1690 class InputImagesHolder_RAII
1691 {
1692     std::vector<Image*> images;
1693 
1694 public:
1695 
InputImagesHolder_RAII()1696     InputImagesHolder_RAII()
1697         : images()
1698     {
1699     }
1700 
appendImage(Image * img)1701     void appendImage(Image* img)
1702     {
1703         images.push_back(img);
1704     }
1705 
~InputImagesHolder_RAII()1706     ~InputImagesHolder_RAII()
1707     {
1708         for (std::size_t i = 0; i < images.size(); ++i) {
1709             delete images[i];
1710         }
1711     }
1712 };
1713 
1714 
1715 ////////////////////////////////////////////////////////////////////////////////
1716 // basic plugin render function, just a skelington to instantiate templates from
1717 static
1718 int
getChannelIndex(InputChannelEnum e,PixelComponentEnum comps)1719 getChannelIndex(InputChannelEnum e,
1720                 PixelComponentEnum comps)
1721 {
1722     int retval;
1723 
1724     switch (e) {
1725     case eInputChannelR: {
1726         if (
1727 #ifdef OFX_EXTENSIONS_NATRON
1728             comps == ePixelComponentXY ||
1729 #endif
1730             comps == ePixelComponentRGB || comps == ePixelComponentRGBA) {
1731             retval = 0;
1732         } else {
1733             retval = -1;
1734         }
1735         break;
1736     }
1737     case eInputChannelG: {
1738         if (
1739 #ifdef OFX_EXTENSIONS_NATRON
1740             comps == ePixelComponentXY ||
1741 #endif
1742             comps == ePixelComponentRGB || comps == ePixelComponentRGBA) {
1743             retval = 1;
1744         } else {
1745             retval = -1;
1746         }
1747         break;
1748     }
1749     case eInputChannelB: {
1750         if ( ( comps == ePixelComponentRGB) || ( comps == ePixelComponentRGBA) ) {
1751             retval = 2;
1752         } else {
1753             retval = -1;
1754         }
1755         break;
1756     }
1757     case eInputChannelA: {
1758         if (comps == ePixelComponentAlpha) {
1759             return 0;
1760         } else if (comps == ePixelComponentRGBA) {
1761             retval = 3;
1762         } else {
1763             retval = -1;
1764         }
1765         break;
1766     }
1767     case eInputChannel0:
1768     case eInputChannel1:
1769     default: {
1770         retval = -1;
1771         break;
1772     }
1773     } // switch
1774 
1775     return retval;
1776 } // getChannelIndex
1777 
1778 // renderScale should be:
1779 // 1,1 when calling from getRoD
1780 // rs when calling from getRoI
1781 // rs when calling from render
1782 // format is in pixels (but may be non-integer)
1783 DistortionModel*
getDistortionModel(const OfxRectD & format,const OfxPointD & renderScale,double time)1784 DistortionPlugin::getDistortionModel(const OfxRectD& format, const OfxPointD& renderScale, double time)
1785 {
1786     if (_plugin != eDistortionPluginLensDistortion) {
1787         return NULL;
1788     }
1789     DistortionModelEnum distortionModelE = (DistortionModelEnum)_distortionModel->getValueAtTime(time);
1790     switch (distortionModelE) {
1791     case eDistortionModelNuke: {
1792         double par = 1.;
1793         if (_srcClip) {
1794             par = _srcClip->getPixelAspectRatio();
1795         }
1796         double k1 = _k1->getValueAtTime(time);
1797         double k2 = _k2->getValueAtTime(time);
1798         double cx, cy;
1799         _center->getValueAtTime(time, cx, cy);
1800         double squeeze = std::max(0.001, _squeeze->getValueAtTime(time));
1801         double ax, ay;
1802         _asymmetric->getValueAtTime(time, ax, ay);
1803         return new DistortionModelNuke(format,
1804                                        par,
1805                                        k1,
1806                                        k2,
1807                                        cx,
1808                                        cy,
1809                                        squeeze,
1810                                        ax,
1811                                        ay);
1812         break;
1813     }
1814     case eDistortionModelPFBarrel: {
1815         //double par = 1.;
1816         //if (_srcClip) {
1817         //    par = _srcClip->getPixelAspectRatio();
1818         //}
1819         double c3 = _pfC3->getValueAtTime(time);
1820         double c5 = _pfC5->getValueAtTime(time);
1821         double xp, yp;
1822         _pfP->getValueAtTime(time, xp, yp);
1823         double squeeze = _pfSqueeze->getValueAtTime(time);
1824         return new DistortionModelPFBarrel(format,
1825                                            renderScale,
1826                                            //par,
1827                                            c3,
1828                                            c5,
1829                                            xp,
1830                                            yp,
1831                                            squeeze);
1832         break;
1833     }
1834     case eDistortionModel3DEClassic: {
1835         //double pa = 1.;
1836         //if (_srcClip) {
1837         //    pa = _srcClip->getPixelAspectRatio();
1838         //}
1839         double xa_fov_unit = _xa_fov_unit->getValueAtTime(time);
1840         double ya_fov_unit = _ya_fov_unit->getValueAtTime(time);
1841         double xb_fov_unit = _xb_fov_unit->getValueAtTime(time);
1842         double yb_fov_unit = _yb_fov_unit->getValueAtTime(time);
1843         double fl_cm = _fl_cm->getValueAtTime(time);
1844         double fd_cm = _fd_cm->getValueAtTime(time);
1845         double w_fb_cm = _w_fb_cm->getValueAtTime(time);
1846         double h_fb_cm = _h_fb_cm->getValueAtTime(time);
1847         double x_lco_cm = _x_lco_cm->getValueAtTime(time);
1848         double y_lco_cm = _y_lco_cm->getValueAtTime(time);
1849         double pa = _pa->getValueAtTime(time);
1850 
1851         double ld = _ld->getValueAtTime(time);
1852         double sq = _sq->getValueAtTime(time);
1853         double cx = _cx->getValueAtTime(time);
1854         double cy = _cy->getValueAtTime(time);
1855         double qu = _qu->getValueAtTime(time);
1856         return new DistortionModel3DEClassic(format,
1857                                              renderScale,
1858                                              xa_fov_unit,
1859                                              ya_fov_unit,
1860                                              xb_fov_unit,
1861                                              yb_fov_unit,
1862                                              fl_cm,
1863                                              fd_cm,
1864                                              w_fb_cm,
1865                                              h_fb_cm,
1866                                              x_lco_cm,
1867                                              y_lco_cm,
1868                                              pa,
1869                                              ld,
1870                                              sq,
1871                                              cx,
1872                                              cy,
1873                                              qu);
1874         break;
1875     }
1876     case eDistortionModel3DEAnamorphic6: {
1877         //double pa = 1.;
1878         //if (_srcClip) {
1879         //    pa = _srcClip->getPixelAspectRatio();
1880         //}
1881         double xa_fov_unit = _xa_fov_unit->getValueAtTime(time);
1882         double ya_fov_unit = _ya_fov_unit->getValueAtTime(time);
1883         double xb_fov_unit = _xb_fov_unit->getValueAtTime(time);
1884         double yb_fov_unit = _yb_fov_unit->getValueAtTime(time);
1885         double fl_cm = _fl_cm->getValueAtTime(time);
1886         double fd_cm = _fd_cm->getValueAtTime(time);
1887         double w_fb_cm = _w_fb_cm->getValueAtTime(time);
1888         double h_fb_cm = _h_fb_cm->getValueAtTime(time);
1889         double x_lco_cm = _x_lco_cm->getValueAtTime(time);
1890         double y_lco_cm = _y_lco_cm->getValueAtTime(time);
1891         double pa = _pa->getValueAtTime(time);
1892 
1893         double cx02 = _cx02->getValueAtTime(time);
1894         double cy02 = _cy02->getValueAtTime(time);
1895         double cx22 = _cx22->getValueAtTime(time);
1896         double cy22 = _cy22->getValueAtTime(time);
1897         double cx04 = _cx04->getValueAtTime(time);
1898         double cy04 = _cy04->getValueAtTime(time);
1899         double cx24 = _cx24->getValueAtTime(time);
1900         double cy24 = _cy24->getValueAtTime(time);
1901         double cx44 = _cx44->getValueAtTime(time);
1902         double cy44 = _cy44->getValueAtTime(time);
1903         double cx06 = _cx06->getValueAtTime(time);
1904         double cy06 = _cy06->getValueAtTime(time);
1905         double cx26 = _cx26->getValueAtTime(time);
1906         double cy26 = _cy26->getValueAtTime(time);
1907         double cx46 = _cx46->getValueAtTime(time);
1908         double cy46 = _cy46->getValueAtTime(time);
1909         double cx66 = _cx66->getValueAtTime(time);
1910         double cy66 = _cy66->getValueAtTime(time);
1911         return new DistortionModel3DEAnamorphic6(format,
1912                                                  renderScale,
1913                                                  xa_fov_unit,
1914                                                  ya_fov_unit,
1915                                                  xb_fov_unit,
1916                                                  yb_fov_unit,
1917                                                  fl_cm,
1918                                                  fd_cm,
1919                                                  w_fb_cm,
1920                                                  h_fb_cm,
1921                                                  x_lco_cm,
1922                                                  y_lco_cm,
1923                                                  pa,
1924                                                  cx02,
1925                                                  cy02,
1926                                                  cx22,
1927                                                  cy22,
1928                                                  cx04,
1929                                                  cy04,
1930                                                  cx24,
1931                                                  cy24,
1932                                                  cx44,
1933                                                  cy44,
1934                                                  cx06,
1935                                                  cy06,
1936                                                  cx26,
1937                                                  cy26,
1938                                                  cx46,
1939                                                  cy46,
1940                                                  cx66,
1941                                                  cy66);
1942         break;
1943     }
1944     case eDistortionModel3DEFishEye8: {
1945         //double pa = 1.;
1946         //if (_srcClip) {
1947         //    pa = _srcClip->getPixelAspectRatio();
1948         //}
1949         double xa_fov_unit = _xa_fov_unit->getValueAtTime(time);
1950         double ya_fov_unit = _ya_fov_unit->getValueAtTime(time);
1951         double xb_fov_unit = _xb_fov_unit->getValueAtTime(time);
1952         double yb_fov_unit = _yb_fov_unit->getValueAtTime(time);
1953         double fl_cm = _fl_cm->getValueAtTime(time);
1954         double fd_cm = _fd_cm->getValueAtTime(time);
1955         double w_fb_cm = _w_fb_cm->getValueAtTime(time);
1956         double h_fb_cm = _h_fb_cm->getValueAtTime(time);
1957         double x_lco_cm = _x_lco_cm->getValueAtTime(time);
1958         double y_lco_cm = _y_lco_cm->getValueAtTime(time);
1959         double pa = _pa->getValueAtTime(time);
1960 
1961         double c2 = _c2->getValueAtTime(time);
1962         double c4 = _c4->getValueAtTime(time);
1963         double c6 = _c6->getValueAtTime(time);
1964         double c8 = _c8->getValueAtTime(time);
1965         return new DistortionModel3DEFishEye8(format,
1966                                               renderScale,
1967                                               xa_fov_unit,
1968                                               ya_fov_unit,
1969                                               xb_fov_unit,
1970                                               yb_fov_unit,
1971                                               fl_cm,
1972                                               fd_cm,
1973                                               w_fb_cm,
1974                                               h_fb_cm,
1975                                               x_lco_cm,
1976                                               y_lco_cm,
1977                                               pa,
1978                                               c2,
1979                                               c4,
1980                                               c6,
1981                                               c8);
1982         break;
1983     }
1984     case eDistortionModel3DEStandard: {
1985         //double pa = 1.;
1986         //if (_srcClip) {
1987         //    pa = _srcClip->getPixelAspectRatio();
1988         //}
1989         double xa_fov_unit = _xa_fov_unit->getValueAtTime(time);
1990         double ya_fov_unit = _ya_fov_unit->getValueAtTime(time);
1991         double xb_fov_unit = _xb_fov_unit->getValueAtTime(time);
1992         double yb_fov_unit = _yb_fov_unit->getValueAtTime(time);
1993         double fl_cm = _fl_cm->getValueAtTime(time);
1994         double fd_cm = _fd_cm->getValueAtTime(time);
1995         double w_fb_cm = _w_fb_cm->getValueAtTime(time);
1996         double h_fb_cm = _h_fb_cm->getValueAtTime(time);
1997         double x_lco_cm = _x_lco_cm->getValueAtTime(time);
1998         double y_lco_cm = _y_lco_cm->getValueAtTime(time);
1999         double pa = _pa->getValueAtTime(time);
2000 
2001         double c2 = _c2->getValueAtTime(time);
2002         double u1 = _u1->getValueAtTime(time);
2003         double v1 = _v1->getValueAtTime(time);
2004         double c4 = _c4->getValueAtTime(time);
2005         double u3 = _u3->getValueAtTime(time);
2006         double v3 = _v3->getValueAtTime(time);
2007         double phi = _phi->getValueAtTime(time);
2008         double b = _b->getValueAtTime(time);
2009         return new DistortionModel3DEStandard(format,
2010                                               renderScale,
2011                                               xa_fov_unit,
2012                                               ya_fov_unit,
2013                                               xb_fov_unit,
2014                                               yb_fov_unit,
2015                                               fl_cm,
2016                                               fd_cm,
2017                                               w_fb_cm,
2018                                               h_fb_cm,
2019                                               x_lco_cm,
2020                                               y_lco_cm,
2021                                               pa,
2022                                               c2,
2023                                               u1,
2024                                               v1,
2025                                               c4,
2026                                               u3,
2027                                               v3,
2028                                               phi,
2029                                               b);
2030         break;
2031     }
2032     case eDistortionModel3DEAnamorphic4: {
2033         //double pa = 1.;
2034         //if (_srcClip) {
2035         //    pa = _srcClip->getPixelAspectRatio();
2036         //}
2037         double xa_fov_unit = _xa_fov_unit->getValueAtTime(time);
2038         double ya_fov_unit = _ya_fov_unit->getValueAtTime(time);
2039         double xb_fov_unit = _xb_fov_unit->getValueAtTime(time);
2040         double yb_fov_unit = _yb_fov_unit->getValueAtTime(time);
2041         double fl_cm = _fl_cm->getValueAtTime(time);
2042         double fd_cm = _fd_cm->getValueAtTime(time);
2043         double w_fb_cm = _w_fb_cm->getValueAtTime(time);
2044         double h_fb_cm = _h_fb_cm->getValueAtTime(time);
2045         double x_lco_cm = _x_lco_cm->getValueAtTime(time);
2046         double y_lco_cm = _y_lco_cm->getValueAtTime(time);
2047         double pa = _pa->getValueAtTime(time);
2048 
2049         double cx02 = _cx02->getValueAtTime(time);
2050         double cy02 = _cy02->getValueAtTime(time);
2051         double cx22 = _cx22->getValueAtTime(time);
2052         double cy22 = _cy22->getValueAtTime(time);
2053         double cx04 = _cx04->getValueAtTime(time);
2054         double cy04 = _cy04->getValueAtTime(time);
2055         double cx24 = _cx24->getValueAtTime(time);
2056         double cy24 = _cy24->getValueAtTime(time);
2057         double cx44 = _cx44->getValueAtTime(time);
2058         double cy44 = _cy44->getValueAtTime(time);
2059         double phi = _a4phi->getValueAtTime(time);
2060         double sqx = _a4sqx->getValueAtTime(time);
2061         double sqy = _a4sqy->getValueAtTime(time);
2062         return new DistortionModel3DEAnamorphic4(format,
2063                                                  renderScale,
2064                                                  xa_fov_unit,
2065                                                  ya_fov_unit,
2066                                                  xb_fov_unit,
2067                                                  yb_fov_unit,
2068                                                  fl_cm,
2069                                                  fd_cm,
2070                                                  w_fb_cm,
2071                                                  h_fb_cm,
2072                                                  x_lco_cm,
2073                                                  y_lco_cm,
2074                                                  pa,
2075                                                  cx02,
2076                                                  cy02,
2077                                                  cx22,
2078                                                  cy22,
2079                                                  cx04,
2080                                                  cy04,
2081                                                  cx24,
2082                                                  cy24,
2083                                                  cx44,
2084                                                  cy44,
2085                                                  phi,
2086                                                  sqx,
2087                                                  sqy);
2088         break;
2089     }
2090     case eDistortionModelPanoTools: {
2091         double par = 1.;
2092         if (_srcClip) {
2093             par = _srcClip->getPixelAspectRatio();
2094         }
2095         double a = _pta->getValueAtTime(time);
2096         double b = _ptb->getValueAtTime(time);
2097         double c = _ptc->getValueAtTime(time);
2098         double d = _ptd->getValueAtTime(time);
2099         double e = _pte->getValueAtTime(time);
2100         double g = _ptg->getValueAtTime(time);
2101         double t = _ptt->getValueAtTime(time);
2102         return new DistortionModelPanoTools(format,
2103                                             renderScale,
2104                                             par,
2105                                             a, b, c,
2106                                             d, e,
2107                                             g, t);
2108         break;
2109     }
2110 
2111     }
2112     assert(false);
2113 }
2114 
2115 // returns true if fixed format (i.e. not the input RoD) and setFormat can be called in getClipPrefs
2116 bool
getLensDistortionFormat(double time,const OfxPointD & renderScale,OfxRectD * format,double * par)2117 DistortionPlugin::getLensDistortionFormat(double time,
2118                                           const OfxPointD& renderScale,
2119                                           OfxRectD *format,
2120                                           double *par)
2121 {
2122     assert(_plugin == eDistortionPluginLensDistortion);
2123 
2124     GeneratorExtentEnum extent = (GeneratorExtentEnum)_extent->getValue();
2125 
2126     switch (extent) {
2127         case eGeneratorExtentFormat: {
2128             int w, h;
2129             _formatSize->getValue(w, h);
2130             *par = _formatPar->getValue();
2131             format->x1 = format->y1 = 0;
2132             format->x2 = w * renderScale.x;
2133             format->y2 = h * renderScale.y;
2134 
2135             return true;
2136             break;
2137         }
2138         case eGeneratorExtentSize: {
2139             OfxRectD rod;
2140             _size->getValue(rod.x2, rod.y2);
2141             _btmLeft->getValue(rod.x1, rod.y1);
2142             rod.x2 += rod.x1;
2143             rod.y2 += rod.y1;
2144             *par = _srcClip ? _srcClip->getPixelAspectRatio() : 1.;
2145             // Coords::toPixelNearest(rod, renderScale, *par, format);
2146             format->x1 = rod.x1 * renderScale.x / *par;
2147             format->y1 = rod.y1 * renderScale.y;
2148             format->x2 = rod.x2 * renderScale.x / *par;
2149             format->y2 = rod.y2 * renderScale.y;
2150 
2151             return true;
2152             break;
2153         }
2154         case eGeneratorExtentProject: {
2155             OfxRectD rod;
2156             OfxPointD siz = getProjectSize();
2157             OfxPointD off = getProjectOffset();
2158             rod.x1 = off.x;
2159             rod.x2 = off.x + siz.x;
2160             rod.y1 = off.y;
2161             rod.y2 = off.y + siz.y;
2162             *par = getProjectPixelAspectRatio();
2163             // Coords::toPixelNearest(rod, renderScale, *par, format);
2164             format->x1 = rod.x1 * renderScale.x / *par;
2165             format->y1 = rod.y1 * renderScale.y;
2166             format->x2 = rod.x2 * renderScale.x / *par;
2167             format->y2 = rod.y2 * renderScale.y;
2168 
2169             return true;
2170             break;
2171         }
2172         case eGeneratorExtentDefault:
2173             if ( _srcClip && _srcClip->isConnected() ) {
2174                 OfxRectI formatI;
2175                 formatI.x1 = formatI.y1 = formatI.x2 = formatI.y2 = 0; // default value
2176                 if (_majorVersion >= 3) {
2177                     // before version 3, LensDistortion was only using RoD
2178                     _srcClip->getFormat(formatI);
2179                 }
2180                 *par = _srcClip->getPixelAspectRatio();
2181                 if ( OFX::Coords::rectIsEmpty(formatI) ) {
2182                     // no format is available, use the RoD instead
2183                     const OfxRectD& srcRod = _srcClip->getRegionOfDefinition(time);
2184                     // Coords::toPixelNearest(srcRod, renderScale, *par, format);
2185                     format->x1 = srcRod.x1 * renderScale.x / *par;
2186                     format->y1 = srcRod.y1 * renderScale.y;
2187                     format->x2 = srcRod.x2 * renderScale.x / *par;
2188                     format->y2 = srcRod.y2 * renderScale.y;
2189                 } else {
2190                     format->x1 = formatI.x1 * renderScale.x;
2191                     format->y1 = formatI.y1 * renderScale.y;
2192                     format->x2 = formatI.x2 * renderScale.x;
2193                     format->y2 = formatI.y2 * renderScale.y;
2194                 }
2195             } else {
2196                 // default to Project Size
2197                 OfxRectD srcRod;
2198                 OfxPointD siz = getProjectSize();
2199                 OfxPointD off = getProjectOffset();
2200                 srcRod.x1 = off.x;
2201                 srcRod.x2 = off.x + siz.x;
2202                 srcRod.y1 = off.y;
2203                 srcRod.y2 = off.y + siz.y;
2204                 *par = getProjectPixelAspectRatio();
2205                 // Coords::toPixelNearest(srcRod, renderScale, *par, format);
2206                 format->x1 = srcRod.x1 * renderScale.x / *par;
2207                 format->y1 = srcRod.y1 * renderScale.y;
2208                 format->x2 = srcRod.x2 * renderScale.x / *par;
2209                 format->y2 = srcRod.y2 * renderScale.y;
2210             }
2211 
2212             return false;
2213             break;
2214     }
2215     return false;
2216 }
2217 
2218 /* set up and run a processor */
2219 void
setupAndProcess(DistortionProcessorBase & processor,const RenderArguments & args)2220 DistortionPlugin::setupAndProcess(DistortionProcessorBase &processor,
2221                                   const RenderArguments &args)
2222 {
2223     const double time = args.time;
2224 
2225     auto_ptr<Image> dst( _dstClip->fetchImage(time) );
2226 
2227     if ( !dst.get() ) {
2228         throwSuiteStatusException(kOfxStatFailed);
2229     }
2230     BitDepthEnum dstBitDepth    = dst->getPixelDepth();
2231     PixelComponentEnum dstComponents  = dst->getPixelComponents();
2232     if ( ( dstBitDepth != _dstClip->getPixelDepth() ) ||
2233          ( dstComponents != _dstClip->getPixelComponents() ) ) {
2234         setPersistentMessage(Message::eMessageError, "", "OFX Host gave image with wrong depth or components");
2235         throwSuiteStatusException(kOfxStatFailed);
2236     }
2237     if ( (dst->getRenderScale().x != args.renderScale.x) ||
2238          ( dst->getRenderScale().y != args.renderScale.y) ||
2239          ( ( dst->getField() != eFieldNone) /* for DaVinci Resolve */ && ( dst->getField() != args.fieldToRender) ) ) {
2240         setPersistentMessage(Message::eMessageError, "", "OFX Host gave image with wrong scale or field properties");
2241         throwSuiteStatusException(kOfxStatFailed);
2242     }
2243 
2244     OutputModeEnum outputMode = _outputMode ? (OutputModeEnum)_outputMode->getValue() : eOutputModeImage;
2245 
2246     auto_ptr<const Image> src( ( (outputMode == eOutputModeImage) && _srcClip && _srcClip->isConnected() ) ?
2247                                     _srcClip->fetchImage(time) : 0 );
2248     if ( src.get() ) {
2249         if ( (src->getRenderScale().x != args.renderScale.x) ||
2250             ( src->getRenderScale().y != args.renderScale.y) ||
2251             ( ( src->getField() != eFieldNone) /* for DaVinci Resolve */ && ( src->getField() != args.fieldToRender) ) ) {
2252             setPersistentMessage(Message::eMessageError, "", "OFX Host gave image with wrong scale or field properties");
2253             throwSuiteStatusException(kOfxStatFailed);
2254         }
2255         BitDepthEnum srcBitDepth      = src->getPixelDepth();
2256         PixelComponentEnum srcComponents = src->getPixelComponents();
2257         if ( (srcBitDepth != dstBitDepth) || (srcComponents != dstComponents) ) {
2258             throwSuiteStatusException(kOfxStatErrImageFormat);
2259         }
2260     }
2261 
2262     InputImagesHolder_RAII imagesHolder;
2263     std::vector<InputPlaneChannel> planeChannels;
2264 
2265     if (_uvClip) {
2266         if (gIsMultiPlaneV1 || gIsMultiPlaneV2) {
2267             BitDepthEnum srcBitDepth = eBitDepthNone;
2268             std::map<Clip*, std::map<std::string, Image*> > fetchedPlanes;
2269             for (int i = 0; i < 3; ++i) {
2270                 InputPlaneChannel p;
2271                 p.channelIndex = i;
2272                 p.fillZero = false;
2273                 //if (_uvClip) {
2274                 Clip* clip = 0;
2275                 MultiPlane::ImagePlaneDesc plane;
2276                 MultiPlane::MultiPlaneEffect::GetPlaneNeededRetCodeEnum stat = getPlaneNeeded(_uvChannels[i]->getName(), &clip, &plane, &p.channelIndex);
2277                 if (stat == MultiPlane::MultiPlaneEffect::eGetPlaneNeededRetCodeFailed) {
2278                     setPersistentMessage(Message::eMessageError, "", "Cannot find requested channels in input");
2279                     throwSuiteStatusException(kOfxStatFailed);
2280                 }
2281 
2282                 p.img = 0;
2283                 if (stat == MultiPlane::MultiPlaneEffect::eGetPlaneNeededRetCodeReturnedConstant0 ||
2284                     (stat == MultiPlane::MultiPlaneEffect::eGetPlaneNeededRetCodeReturnedPlane && plane.getNumComponents() == 0)) {
2285                     p.fillZero = true;
2286                 } else if (stat == MultiPlane::MultiPlaneEffect::eGetPlaneNeededRetCodeReturnedConstant1) {
2287                     p.fillZero = false;
2288                 } else {
2289                     std::map<std::string, Image*>& clipPlanes = fetchedPlanes[clip];
2290                     std::map<std::string, Image*>::iterator foundPlane = clipPlanes.find(plane.getPlaneID());
2291                     if ( foundPlane != clipPlanes.end() ) {
2292                         p.img = foundPlane->second;
2293                     } else {
2294 #ifdef OFX_EXTENSIONS_NUKE
2295                         p.img = clip->fetchImagePlane( time, args.renderView, plane.getPlaneID().c_str() );
2296 #else
2297                         p.img = ( clip && clip->isConnected() ) ? clip->fetchImage(time) : 0;
2298 #endif
2299                         if (p.img) {
2300                             clipPlanes.insert( std::make_pair(plane.getPlaneID(), p.img) );
2301                             imagesHolder.appendImage(p.img);
2302                         }
2303                     }
2304                 }
2305 
2306                 if (p.img) {
2307                     if ( (p.img->getRenderScale().x != args.renderScale.x) ||
2308                         ( p.img->getRenderScale().y != args.renderScale.y) ||
2309                         ( ( p.img->getField() != eFieldNone) /* for DaVinci Resolve */ && ( p.img->getField() != args.fieldToRender) ) ) {
2310                         setPersistentMessage(Message::eMessageError, "", "OFX Host gave image with wrong scale or field properties");
2311                         throwSuiteStatusException(kOfxStatFailed);
2312                     }
2313                     if (srcBitDepth == eBitDepthNone) {
2314                         srcBitDepth = p.img->getPixelDepth();
2315                     } else {
2316                         // both input must have the same bit depth and components
2317                         if ( (srcBitDepth != eBitDepthNone) && ( srcBitDepth != p.img->getPixelDepth() ) ) {
2318                             throwSuiteStatusException(kOfxStatErrImageFormat);
2319                         }
2320                     }
2321                     // If the channel is unavailabe in the image, fill with 0 (1 for Alpha)
2322                     // This may happen if the user selected  the hard-coded Alpha channel and the input is RGB
2323                     if (p.channelIndex >= p.img->getPixelComponentCount()) {
2324                         p.img = 0;
2325                         p.fillZero = p.channelIndex != 3;
2326                     }
2327                 }
2328 
2329 
2330                 //}
2331                 planeChannels.push_back(p);
2332             }
2333         } else { //!gIsMultiPlane
2334             InputChannelEnum uChannel = eInputChannelR;
2335             InputChannelEnum vChannel = eInputChannelG;
2336             InputChannelEnum aChannel = eInputChannelA;
2337             if (_uvChannels[0]) {
2338                 uChannel = (InputChannelEnum)_uvChannels[0]->getValueAtTime(time);
2339             }
2340             if (_uvChannels[1]) {
2341                 vChannel = (InputChannelEnum)_uvChannels[1]->getValueAtTime(time);
2342             }
2343             if (_uvChannels[2]) {
2344                 aChannel = (InputChannelEnum)_uvChannels[2]->getValueAtTime(time);
2345             }
2346 
2347             Image* uv = NULL;
2348             if ( ( ( (uChannel != eInputChannel0) && (uChannel != eInputChannel1) ) ||
2349                   ( (vChannel != eInputChannel0) && (vChannel != eInputChannel1) ) ||
2350                   ( (aChannel != eInputChannel0) && (aChannel != eInputChannel1) ) ) &&
2351                 ( _uvClip && _uvClip->isConnected() ) ) {
2352                 uv =  _uvClip->fetchImage(time);
2353             }
2354 
2355             PixelComponentEnum uvComponents = ePixelComponentNone;
2356             if (uv) {
2357                 imagesHolder.appendImage(uv);
2358                 if ( (uv->getRenderScale().x != args.renderScale.x) ||
2359                     ( uv->getRenderScale().y != args.renderScale.y) ||
2360                     ( ( uv->getField() != eFieldNone) /* for DaVinci Resolve */ && ( uv->getField() != args.fieldToRender) ) ) {
2361                     setPersistentMessage(Message::eMessageError, "", "OFX Host gave image with wrong scale or field properties");
2362                     throwSuiteStatusException(kOfxStatFailed);
2363                 }
2364                 BitDepthEnum uvBitDepth      = uv->getPixelDepth();
2365                 uvComponents = uv->getPixelComponents();
2366                 // only eBitDepthFloat is supported for now (other types require special processing for uv values)
2367                 if ( (uvBitDepth != eBitDepthFloat) /*|| (uvComponents != dstComponents)*/ ) {
2368                     throwSuiteStatusException(kOfxStatErrImageFormat);
2369                 }
2370             }
2371 
2372             // fillZero is only used when the channelIndex is -1 (i.e. it does not exist), and in this case:
2373             // - it is true if the inputchannel is 0, R, G or B
2374             // - it is false if the inputchannel is 1, A (images without alpha are considered opaque)
2375             {
2376                 InputPlaneChannel u;
2377                 u.channelIndex = getChannelIndex(uChannel, uvComponents);
2378                 u.img = (u.channelIndex >= 0) ? uv : NULL;
2379                 u.fillZero = (u.channelIndex >= 0) ? false : !(uChannel == eInputChannel1 || uChannel == eInputChannelA);
2380                 planeChannels.push_back(u);
2381             }
2382             {
2383                 InputPlaneChannel v;
2384                 v.channelIndex = getChannelIndex(vChannel, uvComponents);
2385                 v.img = (v.channelIndex >= 0) ? uv : NULL;
2386                 v.fillZero = (v.channelIndex >= 0) ? false : !(vChannel == eInputChannel1 || vChannel == eInputChannelA);
2387                 planeChannels.push_back(v);
2388             }
2389             {
2390                 InputPlaneChannel a;
2391                 a.channelIndex = getChannelIndex(aChannel, uvComponents);
2392                 a.img = (a.channelIndex >= 0) ? uv : NULL;
2393                 a.fillZero = (a.channelIndex >= 0) ? false : !(aChannel == eInputChannel1 || aChannel == eInputChannelA);
2394                 planeChannels.push_back(a);
2395             }
2396         }
2397     } // if (_uvClip)"
2398 
2399 
2400     // auto ptr for the mask.
2401     bool doMasking = ( ( !_maskApply || _maskApply->getValueAtTime(time) ) && _maskClip && _maskClip->isConnected() );
2402     auto_ptr<const Image> mask(doMasking ? _maskClip->fetchImage(time) : 0);
2403     // do we do masking
2404     if (doMasking) {
2405         if ( mask.get() ) {
2406             if ( (mask->getRenderScale().x != args.renderScale.x) ||
2407                  ( mask->getRenderScale().y != args.renderScale.y) ||
2408                  ( ( mask->getField() != eFieldNone) /* for DaVinci Resolve */ && ( mask->getField() != args.fieldToRender) ) ) {
2409                 setPersistentMessage(Message::eMessageError, "", "OFX Host gave image with wrong scale or field properties");
2410                 throwSuiteStatusException(kOfxStatFailed);
2411             }
2412         }
2413         bool maskInvert = _maskInvert->getValueAtTime(time);
2414         processor.doMasking(true);
2415         processor.setMaskImg(mask.get(), maskInvert);
2416     }
2417 
2418     // set the images
2419     processor.setDstImg( dst.get() );
2420     processor.setSrcImgs( src.get() );
2421     // set the render window
2422     processor.setRenderWindow(args.renderWindow);
2423 
2424     bool processR = _processR->getValueAtTime(time);
2425     bool processG = _processG->getValueAtTime(time);
2426     bool processB = _processB->getValueAtTime(time);
2427     bool processA = _processA->getValueAtTime(time);
2428     bool unpremultUV = false;
2429     double uScale = 1., vScale = 1.;
2430     double uOffset = 0., vOffset = 0.;
2431     WrapEnum uWrap = eWrapClamp;
2432     WrapEnum vWrap = eWrapClamp;
2433     if ( (_plugin == eDistortionPluginIDistort) || (_plugin == eDistortionPluginSTMap) ) {
2434         unpremultUV = _unpremultUV->getValueAtTime(time);
2435         _uvOffset->getValueAtTime(time, uOffset, vOffset);
2436         _uvScale->getValueAtTime(time, uScale, vScale);
2437         if (_plugin == eDistortionPluginSTMap) {
2438             uWrap = (WrapEnum)_uWrap->getValueAtTime(time);
2439             vWrap = (WrapEnum)_vWrap->getValueAtTime(time);
2440         }
2441     }
2442     bool blackOutside = _blackOutside->getValueAtTime(time);
2443     double mix = _mix->getValueAtTime(time);
2444 
2445     bool transformIsIdentity = true;
2446     Matrix3x3 srcTransformInverse;
2447 #ifdef OFX_EXTENSIONS_NUKE
2448     if ( src.get() ) {
2449         transformIsIdentity = src->getTransformIsIdentity();
2450     }
2451     if (!transformIsIdentity) {
2452         double srcTransform[9]; // transform to apply to the source image, in pixel coordinates, from source to destination
2453         src->getTransform(srcTransform);
2454         Matrix3x3 srcTransformMat;
2455         srcTransformMat(0,0) = srcTransform[0];
2456         srcTransformMat(0,1) = srcTransform[1];
2457         srcTransformMat(0,2) = srcTransform[2];
2458         srcTransformMat(1,0) = srcTransform[3];
2459         srcTransformMat(1,1) = srcTransform[4];
2460         srcTransformMat(1,2) = srcTransform[5];
2461         srcTransformMat(2,0) = srcTransform[6];
2462         srcTransformMat(2,1) = srcTransform[7];
2463         srcTransformMat(2,2) = srcTransform[8];
2464         // invert it
2465         if ( !srcTransformMat.inverse(&srcTransformInverse) ) {
2466             transformIsIdentity = true; // no transform
2467         }
2468     }
2469 #endif
2470     if (_plugin == eDistortionPluginIDistort) {
2471         // in IDistort, displacement is given in full-scale pixels
2472         uScale *= args.renderScale.x;
2473         vScale *= args.renderScale.y;
2474     }
2475     OfxRectD format = {0, 0, 1, 1};
2476     if (_plugin == eDistortionPluginLensDistortion) {
2477         double par = 1.;
2478         getLensDistortionFormat(time, args.renderScale, &format, &par);
2479     } else if (_srcClip && _srcClip->isConnected()) {
2480         OfxRectI formatI;
2481         formatI.x1 = formatI.y1 = formatI.x2 = formatI.y2 = 0; // default value
2482         _srcClip->getFormat(formatI);
2483         double par = _srcClip->getPixelAspectRatio();
2484         if ( OFX::Coords::rectIsEmpty(formatI) ) {
2485             // no format is available, use the RoD instead
2486             const OfxRectD& srcRod = _srcClip->getRegionOfDefinition(time);
2487             // Coords::toPixelNearest(srcRod, renderScale, *par, format);
2488             format.x1 = srcRod.x1 * args.renderScale.x / par;
2489             format.y1 = srcRod.y1 * args.renderScale.y;
2490             format.x2 = srcRod.x2 * args.renderScale.x / par;
2491             format.y2 = srcRod.y2 * args.renderScale.y;
2492         } else {
2493             format.x1 = formatI.x1 * args.renderScale.x;
2494             format.y1 = formatI.y1 * args.renderScale.y;
2495             format.x2 = formatI.x2 * args.renderScale.x;
2496             format.y2 = formatI.y2 * args.renderScale.y;
2497         }
2498     }
2499 
2500     DirectionEnum direction = _direction ? (DirectionEnum)_direction->getValue() : eDirectionDistort;
2501     auto_ptr<DistortionModel> distortionModel( getDistortionModel(format, args.renderScale, time) );
2502     processor.setValues(processR, processG, processB, processA,
2503                         transformIsIdentity, srcTransformInverse,
2504                         format,
2505                         planeChannels,
2506                         unpremultUV,
2507                         uOffset, vOffset,
2508                         uScale, vScale,
2509                         uWrap, vWrap,
2510                         args.renderScale,
2511                         distortionModel.get(),
2512                         direction,
2513                         outputMode,
2514                         blackOutside, mix);
2515 
2516     // Call the base class process member, this will call the derived templated process code
2517     processor.process();
2518 } // DistortionPlugin::setupAndProcess
2519 
2520 template <class PIX, int nComponents, int maxValue, DistortionPluginEnum plugin>
2521 void
renderInternalForBitDepth(const RenderArguments & args)2522 DistortionPlugin::renderInternalForBitDepth(const RenderArguments &args)
2523 {
2524     const double time = args.time;
2525     FilterEnum filter = args.renderQualityDraft ? eFilterImpulse : eFilterCubic;
2526 
2527     if (!args.renderQualityDraft && _filter) {
2528         filter = (FilterEnum)_filter->getValueAtTime(time);
2529     }
2530     bool clamp = false;
2531     if (_clamp) {
2532         clamp = _clamp->getValueAtTime(time);
2533     }
2534 
2535     // as you may see below, some filters don't need explicit clamping, since they are
2536     // "clamped" by construction.
2537     switch (filter) {
2538     case eFilterImpulse: {
2539         DistortionProcessor<PIX, nComponents, maxValue, plugin, eFilterImpulse, false> fred(*this);
2540         setupAndProcess(fred, args);
2541         break;
2542     }
2543     case eFilterBox: {
2544         DistortionProcessor<PIX, nComponents, maxValue, plugin, eFilterBox, false> fred(*this);
2545         setupAndProcess(fred, args);
2546         break;
2547     }
2548     case eFilterBilinear: {
2549         DistortionProcessor<PIX, nComponents, maxValue, plugin, eFilterBilinear, false> fred(*this);
2550         setupAndProcess(fred, args);
2551         break;
2552     }
2553     case eFilterCubic: {
2554         DistortionProcessor<PIX, nComponents, maxValue, plugin, eFilterCubic, false> fred(*this);
2555         setupAndProcess(fred, args);
2556         break;
2557     }
2558     case eFilterKeys:
2559         if (clamp) {
2560             DistortionProcessor<PIX, nComponents, maxValue, plugin, eFilterKeys, true> fred(*this);
2561             setupAndProcess(fred, args);
2562         } else {
2563             DistortionProcessor<PIX, nComponents, maxValue, plugin, eFilterKeys, false> fred(*this);
2564             setupAndProcess(fred, args);
2565         }
2566         break;
2567     case eFilterSimon:
2568         if (clamp) {
2569             DistortionProcessor<PIX, nComponents, maxValue, plugin, eFilterSimon, true> fred(*this);
2570             setupAndProcess(fred, args);
2571         } else {
2572             DistortionProcessor<PIX, nComponents, maxValue, plugin, eFilterSimon, false> fred(*this);
2573             setupAndProcess(fred, args);
2574         }
2575         break;
2576     case eFilterRifman:
2577         if (clamp) {
2578             DistortionProcessor<PIX, nComponents, maxValue, plugin, eFilterRifman, true> fred(*this);
2579             setupAndProcess(fred, args);
2580         } else {
2581             DistortionProcessor<PIX, nComponents, maxValue, plugin, eFilterRifman, false> fred(*this);
2582             setupAndProcess(fred, args);
2583         }
2584         break;
2585     case eFilterMitchell:
2586         if (clamp) {
2587             DistortionProcessor<PIX, nComponents, maxValue, plugin, eFilterMitchell, true> fred(*this);
2588             setupAndProcess(fred, args);
2589         } else {
2590             DistortionProcessor<PIX, nComponents, maxValue, plugin, eFilterMitchell, false> fred(*this);
2591             setupAndProcess(fred, args);
2592         }
2593         break;
2594     case eFilterParzen: {
2595         DistortionProcessor<PIX, nComponents, maxValue, plugin, eFilterParzen, false> fred(*this);
2596         setupAndProcess(fred, args);
2597         break;
2598     }
2599     case eFilterNotch: {
2600         DistortionProcessor<PIX, nComponents, maxValue, plugin, eFilterNotch, false> fred(*this);
2601         setupAndProcess(fred, args);
2602         break;
2603     }
2604     } // switch
2605 } // renderInternalForBitDepth
2606 
2607 // the internal render function
2608 template <int nComponents, DistortionPluginEnum plugin>
2609 void
renderInternal(const RenderArguments & args,BitDepthEnum dstBitDepth)2610 DistortionPlugin::renderInternal(const RenderArguments &args,
2611                                  BitDepthEnum dstBitDepth)
2612 {
2613     switch (dstBitDepth) {
2614     case eBitDepthUByte:
2615         renderInternalForBitDepth<unsigned char, nComponents, 255, plugin>(args);
2616         break;
2617     case eBitDepthUShort:
2618         renderInternalForBitDepth<unsigned short, nComponents, 65535, plugin>(args);
2619         break;
2620     case eBitDepthFloat:
2621         renderInternalForBitDepth<float, nComponents, 1, plugin>(args);
2622         break;
2623     default:
2624         throwSuiteStatusException(kOfxStatErrUnsupported);
2625     }
2626 }
2627 
2628 // the overridden render function
2629 void
render(const RenderArguments & args)2630 DistortionPlugin::render(const RenderArguments &args)
2631 {
2632     // instantiate the render code based on the pixel depth of the dst clip
2633     BitDepthEnum dstBitDepth    = _dstClip->getPixelDepth();
2634     PixelComponentEnum dstComponents  = _dstClip->getPixelComponents();
2635 
2636     assert( kSupportsMultipleClipPARs   || !_srcClip || _srcClip->getPixelAspectRatio() == _dstClip->getPixelAspectRatio() );
2637     assert( kSupportsMultipleClipDepths || !_srcClip || _srcClip->getPixelDepth()       == _dstClip->getPixelDepth() );
2638     assert(OFX_COMPONENTS_OK(dstComponents));
2639     if (dstComponents == ePixelComponentRGBA) {
2640         switch (_plugin) {
2641         case eDistortionPluginSTMap:
2642             renderInternal<4, eDistortionPluginSTMap>(args, dstBitDepth);
2643             break;
2644         case eDistortionPluginIDistort:
2645             renderInternal<4, eDistortionPluginIDistort>(args, dstBitDepth);
2646             break;
2647         case eDistortionPluginLensDistortion:
2648             renderInternal<4, eDistortionPluginLensDistortion>(args, dstBitDepth);
2649             break;
2650         }
2651     } else if (dstComponents == ePixelComponentRGB) {
2652         switch (_plugin) {
2653         case eDistortionPluginSTMap:
2654             renderInternal<3, eDistortionPluginSTMap>(args, dstBitDepth);
2655             break;
2656         case eDistortionPluginIDistort:
2657             renderInternal<3, eDistortionPluginIDistort>(args, dstBitDepth);
2658             break;
2659         case eDistortionPluginLensDistortion:
2660             renderInternal<3, eDistortionPluginLensDistortion>(args, dstBitDepth);
2661             break;
2662         }
2663 #ifdef OFX_EXTENSIONS_NATRON
2664     } else if (dstComponents == ePixelComponentXY) {
2665         switch (_plugin) {
2666         case eDistortionPluginSTMap:
2667             renderInternal<2, eDistortionPluginSTMap>(args, dstBitDepth);
2668             break;
2669         case eDistortionPluginIDistort:
2670             renderInternal<2, eDistortionPluginIDistort>(args, dstBitDepth);
2671             break;
2672         case eDistortionPluginLensDistortion:
2673             renderInternal<2, eDistortionPluginLensDistortion>(args, dstBitDepth);
2674             break;
2675         }
2676 #endif
2677     } else {
2678         assert(dstComponents == ePixelComponentAlpha);
2679         switch (_plugin) {
2680         case eDistortionPluginSTMap:
2681             renderInternal<1, eDistortionPluginSTMap>(args, dstBitDepth);
2682             break;
2683         case eDistortionPluginIDistort:
2684             renderInternal<1, eDistortionPluginIDistort>(args, dstBitDepth);
2685             break;
2686         case eDistortionPluginLensDistortion:
2687             renderInternal<1, eDistortionPluginLensDistortion>(args, dstBitDepth);
2688             break;
2689         }
2690     }
2691 } // DistortionPlugin::render
2692 
2693 bool
isIdentity(const IsIdentityArguments & args,Clip * & identityClip,double &,int &,std::string &)2694 DistortionPlugin::isIdentity(const IsIdentityArguments &args,
2695                              Clip * &identityClip,
2696                              double & /*identityTime*/
2697                              , int& /*view*/, std::string& /*plane*/)
2698 {
2699     const double time = args.time;
2700 
2701     if ( (_plugin == eDistortionPluginIDistort) || (_plugin == eDistortionPluginSTMap) ) {
2702         if ( !_uvClip || !_uvClip->isConnected() ) {
2703             identityClip = _srcClip;
2704 
2705             return true;
2706         }
2707     }
2708     if (_plugin == eDistortionPluginLensDistortion) {
2709         OutputModeEnum outputMode = _outputMode ? (OutputModeEnum)_outputMode->getValue() : eOutputModeImage;
2710         if (outputMode == eOutputModeSTMap) {
2711             return false;
2712         }
2713         bool identity = false;
2714         DistortionModelEnum distortionModel = (DistortionModelEnum)_distortionModel->getValueAtTime(time);
2715         switch (distortionModel) {
2716         case eDistortionModelNuke: {
2717             double k1 = _k1->getValueAtTime(time);
2718             double k2 = _k2->getValueAtTime(time);
2719             double ax, ay;
2720             _asymmetric->getValueAtTime(time, ax, ay);
2721             identity = (k1 == 0.) && (k2 == 0.) && (ax == 0.) && (ay == 0.);
2722             break;
2723         }
2724         case eDistortionModelPFBarrel: {
2725             double pfC3 = _pfC3->getValueAtTime(time);
2726             double pfC5 = _pfC5->getValueAtTime(time);
2727             identity = (pfC3 == 0.) && (pfC5 == 0.);
2728             break;
2729         }
2730         case eDistortionModel3DEClassic: {
2731             double ld = _ld->getValueAtTime(time);
2732             double cx = _cx->getValueAtTime(time);
2733             double cy = _cy->getValueAtTime(time);
2734             double qu = _qu->getValueAtTime(time);
2735             identity = (ld == 0.) && (cx == 0.) && (cy == 0.) && (qu == 0.);
2736             break;
2737         }
2738         case eDistortionModel3DEAnamorphic6: {
2739             double cx02 = _cx02->getValueAtTime(time);
2740             double cy02 = _cy02->getValueAtTime(time);
2741             double cx22 = _cx22->getValueAtTime(time);
2742             double cy22 = _cy22->getValueAtTime(time);
2743             double cx04 = _cx04->getValueAtTime(time);
2744             double cy04 = _cy04->getValueAtTime(time);
2745             double cx24 = _cx24->getValueAtTime(time);
2746             double cy24 = _cy24->getValueAtTime(time);
2747             double cx44 = _cx44->getValueAtTime(time);
2748             double cy44 = _cy44->getValueAtTime(time);
2749             double cx06 = _cx06->getValueAtTime(time);
2750             double cy06 = _cy06->getValueAtTime(time);
2751             double cx26 = _cx26->getValueAtTime(time);
2752             double cy26 = _cy26->getValueAtTime(time);
2753             double cx46 = _cx46->getValueAtTime(time);
2754             double cy46 = _cy46->getValueAtTime(time);
2755             double cx66 = _cx66->getValueAtTime(time);
2756             double cy66 = _cy66->getValueAtTime(time);
2757             identity = ( (cx02 == 0.) && (cy02 == 0.) && (cx22 == 0.) && (cy22 == 0.) &&
2758                          (cx04 == 0.) && (cy04 == 0.) && (cx24 == 0.) && (cy24 == 0.) && (cx44 == 0.) && (cy44 == 0.) &&
2759                          (cx06 == 0.) && (cy06 == 0.) && (cx26 == 0.) && (cy26 == 0.) && (cx46 == 0.) && (cy46 == 0.) && (cx66 == 0.) && (cy66 == 0.) );
2760             break;
2761         }
2762         case eDistortionModel3DEFishEye8: {
2763             // fisheye is never identity
2764             break;
2765         }
2766         case eDistortionModel3DEStandard: {
2767             double c2 = _c2->getValueAtTime(time);
2768             double u1 = _u1->getValueAtTime(time);
2769             double v1 = _v1->getValueAtTime(time);
2770             double c4 = _c4->getValueAtTime(time);
2771             double u3 = _u3->getValueAtTime(time);
2772             double v3 = _v3->getValueAtTime(time);
2773             double b = _b->getValueAtTime(time);
2774             identity = ( (c2 == 0.) && (u1 == 0.) && (v1 == 0.) && (c4 == 0.) &&
2775                          (u3 == 0.) && (v3 == 0.) && (b == 0.) );
2776             break;
2777         }
2778         case eDistortionModel3DEAnamorphic4: {
2779             double cx02 = _cx02->getValueAtTime(time);
2780             double cy02 = _cy02->getValueAtTime(time);
2781             double cx22 = _cx22->getValueAtTime(time);
2782             double cy22 = _cy22->getValueAtTime(time);
2783             double cx04 = _cx04->getValueAtTime(time);
2784             double cy04 = _cy04->getValueAtTime(time);
2785             double cx24 = _cx24->getValueAtTime(time);
2786             double cy24 = _cy24->getValueAtTime(time);
2787             double cx44 = _cx44->getValueAtTime(time);
2788             double cy44 = _cy44->getValueAtTime(time);
2789             double phi = _a4phi->getValueAtTime(time);
2790             double sqx = _a4sqx->getValueAtTime(time);
2791             double sqy = _a4sqy->getValueAtTime(time);
2792             identity = ( (cx02 == 0.) && (cy02 == 0.) && (cx22 == 0.) && (cy22 == 0.) &&
2793                          (cx04 == 0.) && (cy04 == 0.) && (cx24 == 0.) && (cy24 == 0.) && (cx44 == 0.) && (cy44 == 0.) &&
2794                          (phi == 0.) && (sqx == 0.) && (sqy == 0.) );
2795             break;
2796         }
2797         case eDistortionModelPanoTools: {
2798             double a = _pta->getValueAtTime(time);
2799             double b = _ptb->getValueAtTime(time);
2800             double c = _ptc->getValueAtTime(time);
2801             double d = _ptd->getValueAtTime(time);
2802             double e = _pte->getValueAtTime(time);
2803             double g = _ptg->getValueAtTime(time);
2804             double t = _ptt->getValueAtTime(time);
2805             identity = (a == 0.) && (b == 0.) && (c == 0.) && (d == 0.) && (e == 0.) && (g == 0.) && (t == 0.);
2806             break;
2807         }
2808         } // switch (distortionModel) {
2809 
2810         if (identity) {
2811             identityClip = _srcClip;
2812 
2813             return true;
2814         }
2815     }
2816     double mix;
2817     _mix->getValueAtTime(time, mix);
2818 
2819     if (mix == 0. /*|| (!processR && !processG && !processB && !processA)*/) {
2820         identityClip = _srcClip;
2821 
2822         return true;
2823     }
2824 
2825     {
2826         bool processR, processG, processB, processA;
2827         _processR->getValueAtTime(time, processR);
2828         _processG->getValueAtTime(time, processG);
2829         _processB->getValueAtTime(time, processB);
2830         _processA->getValueAtTime(time, processA);
2831         if (!processR && !processG && !processB && !processA) {
2832             identityClip = _srcClip;
2833 
2834             return true;
2835         }
2836     }
2837 
2838     bool doMasking = ( ( !_maskApply || _maskApply->getValueAtTime(time) ) && _maskClip && _maskClip->isConnected() );
2839     if (doMasking) {
2840         bool maskInvert;
2841         _maskInvert->getValueAtTime(time, maskInvert);
2842         if (!maskInvert) {
2843             OfxRectI maskRoD;
2844             if (getImageEffectHostDescription()->supportsMultiResolution) {
2845                 // In Sony Catalyst Edit, clipGetRegionOfDefinition returns the RoD in pixels instead of canonical coordinates.
2846                 // In hosts that do not support multiResolution (e.g. Sony Catalyst Edit), all inputs have the same RoD anyway.
2847                 Coords::toPixelEnclosing(_maskClip->getRegionOfDefinition(time), args.renderScale, _maskClip->getPixelAspectRatio(), &maskRoD);
2848                 // effect is identity if the renderWindow doesn't intersect the mask RoD
2849                 if ( !Coords::rectIntersection<OfxRectI>(args.renderWindow, maskRoD, 0) ) {
2850                     identityClip = _srcClip;
2851 
2852                     return true;
2853                 }
2854             }
2855         }
2856     }
2857 
2858     return false;
2859 } // DistortionPlugin::isIdentity
2860 
2861 // override the roi call
2862 // Required if the plugin requires a region from the inputs which is different from the rendered region of the output.
2863 // (this is the case here)
2864 void
getRegionsOfInterest(const RegionsOfInterestArguments & args,RegionOfInterestSetter & rois)2865 DistortionPlugin::getRegionsOfInterest(const RegionsOfInterestArguments &args,
2866                                        RegionOfInterestSetter &rois)
2867 {
2868     const double time = args.time;
2869 
2870     if (!_srcClip || !_srcClip->isConnected()) {
2871         return;
2872     }
2873 
2874     if (_plugin == eDistortionPluginLensDistortion) {
2875         OfxRectD format = {0, 1, 0, 1};
2876         double par = 1.;
2877         getLensDistortionFormat(time, args.renderScale, &format, &par);
2878 
2879         DirectionEnum direction = _direction ? (DirectionEnum)_direction->getValue() : eDirectionDistort;
2880         auto_ptr<DistortionModel> distortionModel( getDistortionModel(format, args.renderScale, time) );
2881 
2882         OfxRectD roiPixel = { std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity() };
2883         assert( OFX::Coords::rectIsEmpty(roiPixel) );
2884 
2885         OfxRectI renderWinPixel;
2886         OFX::Coords::toPixelEnclosing(args.regionOfInterest, args.renderScale, par, &renderWinPixel);
2887         OfxRectD renderWin;
2888         renderWin.x1 = renderWinPixel.x1;
2889         renderWin.y1 = renderWinPixel.y1;
2890         renderWin.x2 = renderWinPixel.x2;
2891         renderWin.y2 = renderWinPixel.y2;
2892 
2893         const int step = 10;
2894         const double w= (renderWin.x2 - renderWin.x1);
2895         const double h= (renderWin.y2 - renderWin.y1);
2896         const double xstep = w / step;
2897         const double ystep= h / step;
2898         for (int i = 0; i <= step; ++i) {
2899             for (int j = 0; j < 2; ++j) {
2900                 double x = renderWin.x1 + xstep * i;
2901                 double y = renderWin.y1 + h * j;
2902                 double xi, yi;
2903                 // undistort/distort take pixel coordinates, do not divide by renderScale
2904                 if (direction == eDirectionDistort) {
2905                     distortionModel->undistort(x, y, &xi, &yi); // inverse of getRoD
2906                 } else {
2907                     distortionModel->distort(x, y, &xi, &yi); // inverse of getRoD
2908                 }
2909                 roiPixel.x1 = std::min(roiPixel.x1, xi);
2910                 roiPixel.x2 = std::max(roiPixel.x2, xi);
2911                 roiPixel.y1 = std::min(roiPixel.y1, yi);
2912                 roiPixel.y2 = std::max(roiPixel.y2, yi);
2913             }
2914         }
2915         for (int i = 0; i < 2; ++i) {
2916             for (int j = 1; j < step; ++j) {
2917                 double x = renderWin.x1 + w * i;
2918                 double y = renderWin.y1 + ystep * j;
2919                 double xi, yi;
2920                 // undistort/distort take pixel coordinates, do not divide by renderScale
2921                 if (direction == eDirectionDistort) {
2922                     distortionModel->undistort(x, y, &xi, &yi); // inverse of getRoD
2923                 } else {
2924                     distortionModel->distort(x, y, &xi, &yi); // inverse of getRoD
2925                 }
2926                 roiPixel.x1 = std::min(roiPixel.x1, xi);
2927                 roiPixel.x2 = std::max(roiPixel.x2, xi);
2928                 roiPixel.y1 = std::min(roiPixel.y1, yi);
2929                 roiPixel.y2 = std::max(roiPixel.y2, yi);
2930             }
2931         }
2932         assert( !OFX::Coords::rectIsEmpty(roiPixel) );
2933         // Slight extra margin, just in case.
2934         roiPixel.x1 -= 2;
2935         roiPixel.x2 += 2;
2936         roiPixel.y1 -= 2;
2937         roiPixel.y2 += 2;
2938 
2939         OfxRectD roi;
2940         OFX::Coords::toCanonical(roiPixel, args.renderScale, par, &roi);
2941         assert( !OFX::Coords::rectIsEmpty(roi) );
2942         rois.setRegionOfInterest(*_srcClip, roi);
2943         /*
2944         printf("getRegionsOfInterest: rs=(%g,%g) rw=(%g,%g,%g,%g) rwp=(%d,%d,%d,%d) -> roiPixel(%g,%g,%g,%g) roiCanonical=(%g,%g,%g,%g)\n",
2945                args.renderScale.x, args.renderScale.y,
2946                renderWin.x1, renderWin.y1, renderWin.x2, renderWin.y2,
2947                renderWinPixel.x1, renderWinPixel.y1, renderWinPixel.x2, renderWinPixel.y2,
2948                roiPixel.x1, roiPixel.y1, roiPixel.x2, roiPixel.y2,
2949                roi.x1, roi.y1, roi.x2, roi.y2);
2950         */
2951 
2952         return;
2953     }
2954 
2955     // ask for full RoD of srcClip
2956     const OfxRectD& srcRod = _srcClip->getRegionOfDefinition(time);
2957     rois.setRegionOfInterest(*_srcClip, srcRod);
2958     // only ask for the renderWindow (intersected with the RoD) from uvClip
2959     if (_uvClip) {
2960         OfxRectD uvRoI = _uvClip->getRegionOfDefinition(time);
2961         Coords::rectIntersection(uvRoI, args.regionOfInterest, &uvRoI);
2962         rois.setRegionOfInterest(*_uvClip, uvRoI);
2963     }
2964 }
2965 
2966 bool
getRegionOfDefinition(const RegionOfDefinitionArguments & args,OfxRectD & rod)2967 DistortionPlugin::getRegionOfDefinition(const RegionOfDefinitionArguments &args,
2968                                         OfxRectD &rod)
2969 {
2970     const double time = args.time;
2971 
2972     switch (_plugin) {
2973     case eDistortionPluginSTMap: {
2974         if (_uvClip) {
2975             // IDistort: RoD is the same as uv map
2976             rod = _uvClip->getRegionOfDefinition(time);
2977 
2978             return true;
2979         }
2980         break;
2981     }
2982     case eDistortionPluginIDistort: {
2983         if (_srcClip) {
2984             // IDistort: RoD is the same as srcClip
2985             rod = _srcClip->getRegionOfDefinition(time);
2986 
2987             return true;
2988         }
2989         break;
2990     }
2991     case eDistortionPluginLensDistortion: {
2992         if (_majorVersion < 3) {
2993              return false; // use source RoD
2994         }
2995         OfxRectD format = {0, 1, 0, 1};
2996         double par = 1.;
2997         getLensDistortionFormat(time, args.renderScale, &format, &par);
2998 
2999         DirectionEnum direction = _direction ? (DirectionEnum)_direction->getValue() : eDirectionDistort;
3000         auto_ptr<DistortionModel> distortionModel( getDistortionModel(format, args.renderScale, time) );
3001 
3002         OfxRectD rodPixel = { std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity() };
3003         assert( OFX::Coords::rectIsEmpty(rodPixel) );
3004 
3005         OfxRectD srcRodPixel;
3006         if (_srcClip && _srcClip->isConnected()) {
3007             OfxRectD srcRod = _srcClip->getRegionOfDefinition(time);
3008             double srcPar = _srcClip->getPixelAspectRatio();
3009             srcRodPixel.x1 = srcRod.x1 * args.renderScale.x / srcPar;
3010             srcRodPixel.y1 = srcRod.y1 * args.renderScale.y;
3011             srcRodPixel.x2 = srcRod.x2 * args.renderScale.x / srcPar;
3012             srcRodPixel.y2 = srcRod.y2 * args.renderScale.y;
3013         } else {
3014             srcRodPixel.x1 = format.x1;
3015             srcRodPixel.y1 = format.y1;
3016             srcRodPixel.x2 = format.x2;
3017             srcRodPixel.y2 = format.y2;
3018         }
3019         if (OFX::Coords::rectIsEmpty(srcRodPixel)) {
3020             return false;
3021         }
3022         const int step = 10;
3023         const double w= (srcRodPixel.x2 - srcRodPixel.x1);
3024         const double h= (srcRodPixel.y2 - srcRodPixel.y1);
3025         const double xstep = w / step;
3026         const double ystep= h / step;
3027         for (int i = 0; i <= step; ++i) {
3028             for (int j = 0; j < 2; ++j) {
3029                 double x = srcRodPixel.x1 + xstep * i;
3030                 double y = srcRodPixel.y1 + h * j;
3031                 double xo, yo;
3032                 if (direction == eDirectionDistort) {
3033                     distortionModel->distort(x, y, &xo, &yo);
3034                 } else {
3035                     distortionModel->undistort(x, y, &xo, &yo);
3036                 }
3037                 rodPixel.x1 = std::min(rodPixel.x1, xo);
3038                 rodPixel.x2 = std::max(rodPixel.x2, xo);
3039                 rodPixel.y1 = std::min(rodPixel.y1, yo);
3040                 rodPixel.y2 = std::max(rodPixel.y2, yo);
3041             }
3042         }
3043         for (int i = 0; i < 2; ++i) {
3044             for (int j = 1; j < step; ++j) {
3045                 double x = srcRodPixel.x1 + w * i;
3046                 double y = srcRodPixel.y1 + ystep * j;
3047                 double xo, yo;
3048                 if (direction == eDirectionDistort) {
3049                     distortionModel->distort(x, y, &xo, &yo);
3050                 } else {
3051                     distortionModel->undistort(x, y, &xo, &yo);
3052                 }
3053                 rodPixel.x1 = std::min(rodPixel.x1, xo);
3054                 rodPixel.x2 = std::max(rodPixel.x2, xo);
3055                 rodPixel.y1 = std::min(rodPixel.y1, yo);
3056                 rodPixel.y2 = std::max(rodPixel.y2, yo);
3057             }
3058         }
3059         assert( !OFX::Coords::rectIsEmpty(rodPixel) );
3060         // extra margin for blackOutside
3061         if ( _blackOutside->getValueAtTime(time) ) {
3062             rodPixel.x1 -= 1;
3063             rodPixel.x2 += 1;
3064             rodPixel.y1 -= 1;
3065             rodPixel.y2 += 1;
3066         }
3067         // Slight extra margin, just in case.
3068         rodPixel.x1 -= 2;
3069         rodPixel.x2 += 2;
3070         rodPixel.y1 -= 2;
3071         rodPixel.y2 += 2;
3072 
3073         OFX::Coords::toCanonical(rodPixel, args.renderScale, par, &rod);
3074         assert( !OFX::Coords::rectIsEmpty(rod) );
3075         /*
3076         printf("getRegionOfDefinition: rs=(%g,%g) srcrodp=(%g,%g,%g,%g) -> rodPixel(%g,%g,%g,%g) rodCanonical=(%g,%g,%g,%g)\n",
3077                args.renderScale.x, args.renderScale.y,
3078                srcRod.x1, srcRod.y1, srcRod.x2, srcRod.y2,
3079                rodPixel.x1, rodPixel.y1, rodPixel.x2, rodPixel.y2,
3080                rod.x1, rod.y1, rod.x2, rod.y2);
3081          */
3082 
3083         if ( _cropToFormat && _cropToFormat->getValueAtTime(time) ) {
3084             // crop to format works by clamping the output rod to the format wherever the input rod was inside the format
3085             // this avoids unwanted crops (cropToFormat is checked by default)
3086             OfxRectI srcFormat;
3087             _srcClip->getFormat(srcFormat);
3088             OfxRectI srcFormatEnclosing;
3089             srcFormatEnclosing.x1 = std::floor(srcFormat.x1 * args.renderScale.x);
3090             srcFormatEnclosing.x2 = std::ceil(srcFormat.x2 * args.renderScale.x);
3091             srcFormatEnclosing.y1 = std::floor(srcFormat.y1 * args.renderScale.y);
3092             srcFormatEnclosing.y2 = std::ceil(srcFormat.y2 * args.renderScale.y);
3093             srcFormat.x1 = std::ceil(srcFormat.x1 * args.renderScale.x);
3094             srcFormat.x2 = std::floor(srcFormat.x2 * args.renderScale.x);
3095             srcFormat.y1 = std::ceil(srcFormat.y1 * args.renderScale.y);
3096             srcFormat.y2 = std::floor(srcFormat.y2 * args.renderScale.y);
3097             if (! Coords::rectIsEmpty(srcFormat) ) {
3098                 if (rodPixel.x1 < srcFormat.x1 && srcRodPixel.x1 >= srcFormatEnclosing.x1) {
3099                     rod.x1 = srcFormat.x1 / args.renderScale.x * par;
3100                 }
3101                 if (rodPixel.x2 > srcFormat.x2 && srcRodPixel.x2 <= srcFormatEnclosing.x2) {
3102                     rod.x2 = srcFormat.x2 / args.renderScale.x * par;
3103                 }
3104                 if (rodPixel.y1 < srcFormat.y1 && srcRodPixel.y1 >= srcFormatEnclosing.y1) {
3105                     rod.y1 = srcFormat.y1 / args.renderScale.y;
3106                 }
3107                 if (rodPixel.y2 > srcFormat.y2 && srcRodPixel.y2 <= srcFormatEnclosing.y2) {
3108                     rod.y2 = srcFormat.y2 / args.renderScale.y;
3109                 }
3110             }
3111         }
3112         return true;
3113         //return false;     // use source RoD
3114         break;
3115     }
3116     } // switch (_plugin)
3117 
3118     return false;
3119 }
3120 
3121 #ifdef OFX_EXTENSIONS_NUKE
3122 OfxStatus
getClipComponents(const ClipComponentsArguments & args,ClipComponentsSetter & clipComponents)3123 DistortionPlugin::getClipComponents(const ClipComponentsArguments& args,
3124                                     ClipComponentsSetter& clipComponents)
3125 {
3126     assert(gIsMultiPlaneV2);
3127 
3128     OfxStatus stat = kOfxStatReplyDefault;
3129     if (_uvClip) {
3130         stat = MultiPlaneEffect::getClipComponents(args, clipComponents);
3131         clipComponents.setPassThroughClip(_srcClip, args.time, args.view);
3132     }
3133     return stat;
3134 } // getClipComponents
3135 
3136 #endif
3137 
3138 void
updateVisibility()3139 DistortionPlugin::updateVisibility()
3140 {
3141     if (_plugin == eDistortionPluginLensDistortion) {
3142         {
3143             GeneratorExtentEnum extent = (GeneratorExtentEnum)_extent->getValue();
3144             bool hasFormat = (extent == eGeneratorExtentFormat);
3145             bool hasSize = (extent == eGeneratorExtentSize);
3146 
3147             _format->setIsSecretAndDisabled(!hasFormat);
3148             _size->setIsSecretAndDisabled(!hasSize);
3149             _recenter->setIsSecretAndDisabled(!hasSize);
3150             _btmLeft->setIsSecretAndDisabled(!hasSize);
3151         }
3152 
3153         DistortionModelEnum distortionModel = (DistortionModelEnum)_distortionModel->getValue();
3154 
3155         _k1->setIsSecretAndDisabled(distortionModel != eDistortionModelNuke);
3156         _k2->setIsSecretAndDisabled(distortionModel != eDistortionModelNuke);
3157         _center->setIsSecretAndDisabled(distortionModel != eDistortionModelNuke);
3158         _squeeze->setIsSecretAndDisabled(distortionModel != eDistortionModelNuke);
3159         _asymmetric->setIsSecretAndDisabled(distortionModel != eDistortionModelNuke);
3160 
3161         _pfFile->setIsSecretAndDisabled(distortionModel != eDistortionModelPFBarrel);
3162         if (_pfReload) {
3163             _pfReload->setIsSecretAndDisabled(distortionModel != eDistortionModelPFBarrel);
3164         }
3165         _pfC3->setIsSecretAndDisabled(distortionModel != eDistortionModelPFBarrel);
3166         _pfC5->setIsSecretAndDisabled(distortionModel != eDistortionModelPFBarrel);
3167         _pfSqueeze->setIsSecretAndDisabled(distortionModel != eDistortionModelPFBarrel);
3168         _pfP->setIsSecretAndDisabled(distortionModel != eDistortionModelPFBarrel);
3169 
3170         bool distortionModel3DE = (distortionModel == eDistortionModel3DEClassic ||
3171                                    distortionModel == eDistortionModel3DEAnamorphic6 ||
3172                                    distortionModel == eDistortionModel3DEFishEye8 ||
3173                                    distortionModel == eDistortionModel3DEStandard ||
3174                                    distortionModel == eDistortionModel3DEAnamorphic4);
3175         _xa_fov_unit->setIsSecretAndDisabled(!distortionModel3DE);
3176         _ya_fov_unit->setIsSecretAndDisabled(!distortionModel3DE);
3177         _xb_fov_unit->setIsSecretAndDisabled(!distortionModel3DE);
3178         _yb_fov_unit->setIsSecretAndDisabled(!distortionModel3DE);
3179         _fl_cm->setIsSecretAndDisabled(!distortionModel3DE);
3180         _fd_cm->setIsSecretAndDisabled(!distortionModel3DE);
3181         _w_fb_cm->setIsSecretAndDisabled(!distortionModel3DE);
3182         _h_fb_cm->setIsSecretAndDisabled(!distortionModel3DE);
3183         _x_lco_cm->setIsSecretAndDisabled(!distortionModel3DE);
3184         _y_lco_cm->setIsSecretAndDisabled(!distortionModel3DE);
3185         _pa->setIsSecretAndDisabled(!distortionModel3DE);
3186 
3187         _ld->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEClassic);
3188         _sq->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEClassic);
3189         _cx->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEClassic);
3190         _cy->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEClassic);
3191         _qu->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEClassic);
3192 
3193         _c2->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEStandard &&
3194                                     distortionModel != eDistortionModel3DEFishEye8);
3195         _u1->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEStandard);
3196         _v1->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEStandard);
3197         _c4->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEStandard &&
3198                                     distortionModel != eDistortionModel3DEFishEye8);
3199         _u3->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEStandard);
3200         _v3->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEStandard);
3201         _phi->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEStandard);
3202         _b->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEStandard);
3203 
3204         _cx02->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic4 &&
3205                                       distortionModel != eDistortionModel3DEAnamorphic6);
3206         _cy02->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic4 &&
3207                                       distortionModel != eDistortionModel3DEAnamorphic6);
3208         _cx22->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic4 &&
3209                                       distortionModel != eDistortionModel3DEAnamorphic6);
3210         _cy22->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic4 &&
3211                                       distortionModel != eDistortionModel3DEAnamorphic6);
3212         _cx04->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic4 &&
3213                                       distortionModel != eDistortionModel3DEAnamorphic6);
3214         _cy04->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic4 &&
3215                                       distortionModel != eDistortionModel3DEAnamorphic6);
3216         _cx24->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic4 &&
3217                                       distortionModel != eDistortionModel3DEAnamorphic6);
3218         _cy24->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic4 &&
3219                                       distortionModel != eDistortionModel3DEAnamorphic6);
3220         _cx44->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic4 &&
3221                                       distortionModel != eDistortionModel3DEAnamorphic6);
3222         _cy44->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic4 &&
3223                                       distortionModel != eDistortionModel3DEAnamorphic6);
3224         _cx06->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic6);
3225         _cy06->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic6);
3226         _cx26->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic6);
3227         _cy26->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic6);
3228         _cx46->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic6);
3229         _cy46->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic6);
3230         _cx66->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic6);
3231         _cy66->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic6);
3232         _a4phi->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic4);
3233         _a4sqx->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic4);
3234         _a4sqy->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEAnamorphic4);
3235 
3236         _c6->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEFishEye8);
3237         _c8->setIsSecretAndDisabled(distortionModel != eDistortionModel3DEFishEye8);
3238 
3239         _pta->setIsSecretAndDisabled(distortionModel != eDistortionModelPanoTools);
3240         _ptb->setIsSecretAndDisabled(distortionModel != eDistortionModelPanoTools);
3241         _ptc->setIsSecretAndDisabled(distortionModel != eDistortionModelPanoTools);
3242         _ptd->setIsSecretAndDisabled(distortionModel != eDistortionModelPanoTools);
3243         _pte->setIsSecretAndDisabled(distortionModel != eDistortionModelPanoTools);
3244         _ptg->setIsSecretAndDisabled(distortionModel != eDistortionModelPanoTools);
3245         _ptt->setIsSecretAndDisabled(distortionModel != eDistortionModelPanoTools);
3246     }
3247 }
3248 
3249 void
changedParam(const InstanceChangedArgs & args,const std::string & paramName)3250 DistortionPlugin::changedParam(const InstanceChangedArgs &args,
3251                                const std::string &paramName)
3252 {
3253     if (_plugin == eDistortionPluginLensDistortion) {
3254         if ( (paramName == kParamDistortionModel) && (args.reason == eChangeUserEdit) ) {
3255             updateVisibility();
3256         } else if ( (paramName == kParamPFFileReload) ||
3257             ( (paramName == kParamPFFile) && (args.reason == eChangeUserEdit) ) ) {
3258             std::string filename;
3259             PFBarrelCommon::FileReader f(filename);
3260 
3261             beginEditBlock(kParamPFFile);
3262             _pfC3->deleteAllKeys();
3263             _pfC5->deleteAllKeys();
3264             _pfP->deleteAllKeys();
3265             if (f.model_ == 0) {
3266                 _pfC5->setValue(0.);
3267             }
3268             if (f.nkeys_ == 1) {
3269                 _pfC3->setValue(f.c3_[0]);
3270                 _pfC5->setValue(f.c5_[0]);
3271                 _pfP->setValue(f.xp_[0], f.yp_[0]);
3272             } else {
3273                 for (int i = 0; i < f.nkeys_; ++i) {
3274                     _pfC3->setValueAtTime(f.frame_[i], f.c3_[0]);
3275                     if (f.model_ == 1) {
3276                         _pfC5->setValueAtTime(f.frame_[i], f.c5_[0]);
3277                     }
3278                     _pfP->setValueAtTime(f.frame_[i], f.xp_[0], f.yp_[0]);
3279                 }
3280             }
3281             endEditBlock();
3282         } else if (paramName == kParamGeneratorExtent) {
3283             updateVisibility();
3284         } else if (paramName == kParamGeneratorFormat) {
3285             //the host does not handle the format itself, do it ourselves
3286             EParamFormat format = (EParamFormat)_format->getValue();
3287             int w = 0, h = 0;
3288             double par = -1;
3289             getFormatResolution(format, &w, &h, &par);
3290             assert(par != -1);
3291             _formatPar->setValue(par);
3292             _formatSize->setValue(w, h);
3293         } else if (paramName == kParamGeneratorCenter) {
3294             Clip* srcClip = _srcClip;
3295             OfxRectD srcRoD;
3296             if ( srcClip && srcClip->isConnected() ) {
3297                 srcRoD = srcClip->getRegionOfDefinition(args.time);
3298             } else {
3299                 OfxPointD siz = getProjectSize();
3300                 OfxPointD off = getProjectOffset();
3301                 srcRoD.x1 = off.x;
3302                 srcRoD.x2 = off.x + siz.x;
3303                 srcRoD.y1 = off.y;
3304                 srcRoD.y2 = off.y + siz.y;
3305             }
3306             OfxPointD center;
3307             center.x = (srcRoD.x2 + srcRoD.x1) / 2.;
3308             center.y = (srcRoD.y2 + srcRoD.y1) / 2.;
3309 
3310             OfxRectD rectangle;
3311             _size->getValue(rectangle.x2, rectangle.y2);
3312             _btmLeft->getValue(rectangle.x1, rectangle.y1);
3313             rectangle.x2 += rectangle.x1;
3314             rectangle.y2 += rectangle.y1;
3315 
3316             OfxRectD newRectangle;
3317             newRectangle.x1 = center.x - (rectangle.x2 - rectangle.x1) / 2.;
3318             newRectangle.y1 = center.y - (rectangle.y2 - rectangle.y1) / 2.;
3319             newRectangle.x2 = newRectangle.x1 + (rectangle.x2 - rectangle.x1);
3320             newRectangle.y2 = newRectangle.y1 + (rectangle.y2 - rectangle.y1);
3321 
3322             _size->setValue(newRectangle.x2 - newRectangle.x1, newRectangle.y2 - newRectangle.y1);
3323             _btmLeft->setValue(newRectangle.x1, newRectangle.y1);
3324         }
3325         return;
3326     }
3327     if (_plugin == eDistortionPluginIDistort ||
3328         _plugin == eDistortionPluginSTMap) {
3329         if (gIsMultiPlaneV2) {
3330             MultiPlaneEffect::changedParam(args, paramName);
3331         }
3332     }
3333 }
3334 
3335 //mDeclarePluginFactory(DistortionPluginFactory, {ofxsThreadSuiteCheck();}, {});
3336 template<DistortionPluginEnum plugin, int majorVersion>
3337 class DistortionPluginFactory
3338     : public PluginFactoryHelper<DistortionPluginFactory<plugin, majorVersion> >
3339 {
3340 public:
3341     DistortionPluginFactory<plugin, majorVersion>(const std::string & id, unsigned int verMaj, unsigned int verMin)
3342     : PluginFactoryHelper<DistortionPluginFactory>(id, verMaj, verMin)
3343     {
3344     }
load()3345     virtual void load() OVERRIDE FINAL {ofxsThreadSuiteCheck();}
3346     virtual void describe(ImageEffectDescriptor &desc) OVERRIDE FINAL;
3347     virtual void describeInContext(ImageEffectDescriptor &desc, ContextEnum context) OVERRIDE FINAL;
3348     virtual ImageEffect* createInstance(OfxImageEffectHandle handle, ContextEnum context) OVERRIDE FINAL;
3349 };
3350 
3351 template<DistortionPluginEnum plugin, int majorVersion>
3352 void
describe(ImageEffectDescriptor & desc)3353 DistortionPluginFactory<plugin, majorVersion>::describe(ImageEffectDescriptor &desc)
3354 {
3355     // basic labels
3356     switch (plugin) {
3357     case eDistortionPluginSTMap:
3358         desc.setLabel(kPluginSTMapName);
3359         desc.setPluginGrouping(kPluginSTMapGrouping);
3360         desc.setPluginDescription(kPluginSTMapDescription);
3361         break;
3362     case eDistortionPluginIDistort:
3363         desc.setLabel(kPluginIDistortName);
3364         desc.setPluginGrouping(kPluginIDistortGrouping);
3365         desc.setPluginDescription(kPluginIDistortDescription);
3366         break;
3367     case eDistortionPluginLensDistortion:
3368         desc.setLabel(kPluginLensDistortionName);
3369         desc.setPluginGrouping(kPluginLensDistortionGrouping);
3370         desc.setPluginDescription(kPluginLensDistortionDescription);
3371         break;
3372     }
3373 
3374     //desc.addSupportedContext(eContextFilter);
3375     desc.addSupportedContext(eContextGeneral);
3376     //desc.addSupportedContext(eContextPaint);
3377     switch (plugin) {
3378     case eDistortionPluginSTMap:
3379         //desc.addSupportedBitDepth(eBitDepthUByte); // not yet supported (requires special processing for uv clip values)
3380         //desc.addSupportedBitDepth(eBitDepthUShort);
3381         desc.addSupportedBitDepth(eBitDepthFloat);
3382         break;
3383     case eDistortionPluginIDistort:
3384         //desc.addSupportedBitDepth(eBitDepthUByte); // not yet supported (requires special processing for uv clip values)
3385         //desc.addSupportedBitDepth(eBitDepthUShort);
3386         desc.addSupportedBitDepth(eBitDepthFloat);
3387         break;
3388     case eDistortionPluginLensDistortion:
3389         desc.addSupportedBitDepth(eBitDepthUByte);
3390         desc.addSupportedBitDepth(eBitDepthUShort);
3391         desc.addSupportedBitDepth(eBitDepthFloat);
3392         break;
3393     }
3394 
3395 
3396     // set a few flags
3397     desc.setSingleInstance(false);
3398     desc.setHostFrameThreading(false);
3399     desc.setSupportsMultiResolution(kSupportsMultiResolution);
3400     desc.setSupportsTiles(kSupportsTiles);
3401     desc.setTemporalClipAccess(false);
3402     desc.setRenderTwiceAlways(false);
3403     desc.setSupportsMultipleClipPARs(kSupportsMultipleClipPARs);
3404     desc.setSupportsMultipleClipDepths(kSupportsMultipleClipDepths);
3405     desc.setRenderThreadSafety(kRenderThreadSafety);
3406     desc.setSupportsRenderQuality(true);
3407 #ifdef OFX_EXTENSIONS_NUKE
3408     // ask the host to render all planes
3409     desc.setPassThroughForNotProcessedPlanes(ePassThroughLevelRenderAllRequestedPlanes);
3410 #endif
3411 
3412 #ifdef OFX_EXTENSIONS_NATRON
3413     desc.setChannelSelector(ePixelComponentNone); // we have our own channel selector
3414 #endif
3415 
3416 
3417     gIsMultiPlaneV1 = false;
3418     gIsMultiPlaneV2 = false;
3419 #if defined(OFX_EXTENSIONS_NUKE)
3420     gIsMultiPlaneV1 = getImageEffectHostDescription()->isMultiPlanar;
3421 #endif
3422 #if defined(OFX_EXTENSIONS_NUKE) && defined(OFX_EXTENSIONS_NATRON)
3423     gIsMultiPlaneV2 = getImageEffectHostDescription()->supportsDynamicChoices && gIsMultiPlaneV1;
3424     if ((gIsMultiPlaneV1 || gIsMultiPlaneV2) && (plugin == eDistortionPluginSTMap || plugin == eDistortionPluginIDistort)) {
3425         // This enables fetching different planes from the input.
3426         // Generally the user will read a multi-layered EXR file in the Reader node and then use the shuffle
3427         // to redirect the plane's channels into RGBA color plane.
3428 
3429         desc.setIsMultiPlanar(true);
3430     }
3431 #endif
3432     if (plugin == eDistortionPluginLensDistortion) {
3433         desc.setOverlayInteractDescriptor(new GeneratorOverlayDescriptor);
3434     }
3435     if ( (plugin == eDistortionPluginLensDistortion && this->getMajorVersion() < kPluginVersionLensDistortionMajor) ||
3436          (plugin != eDistortionPluginLensDistortion && this->getMajorVersion() < kPluginVersionMajor) ) {
3437         desc.setIsDeprecated(true);
3438     }
3439 } // >::describe
3440 
3441 static void
addWrapOptions(ChoiceParamDescriptor * channel,WrapEnum def)3442 addWrapOptions(ChoiceParamDescriptor* channel,
3443                WrapEnum def)
3444 {
3445     assert(channel->getNOptions() == eWrapClamp);
3446     channel->appendOption(kParamWrapOptionClamp);
3447     assert(channel->getNOptions() == eWrapRepeat);
3448     channel->appendOption(kParamWrapOptionRepeat);
3449     assert(channel->getNOptions() == eWrapMirror);
3450     channel->appendOption(kParamWrapOptionMirror);
3451     channel->setDefault(def);
3452 }
3453 
3454 template<DistortionPluginEnum plugin, int majorVersion>
3455 void
describeInContext(ImageEffectDescriptor & desc,ContextEnum context)3456 DistortionPluginFactory<plugin, majorVersion>::describeInContext(ImageEffectDescriptor &desc,
3457                                                    ContextEnum context)
3458 {
3459 #ifdef OFX_EXTENSIONS_NUKE
3460     if ( gIsMultiPlaneV2 && !fetchSuite(kFnOfxImageEffectPlaneSuite, 2, true) ) {
3461         throwHostMissingSuiteException(kFnOfxImageEffectPlaneSuite);
3462     } else if (gIsMultiPlaneV1 && !fetchSuite(kFnOfxImageEffectPlaneSuite, 1, true) ) {
3463         throwHostMissingSuiteException(kFnOfxImageEffectPlaneSuite);
3464     }
3465 #endif
3466 
3467     if (plugin ==  eDistortionPluginSTMap) {
3468         // create the uv clip
3469         // uv clip is defined first, because the output format is taken from the RoD of the first clip in Nuke
3470         ClipDescriptor *uvClip = desc.defineClip(kClipUV);
3471         uvClip->addSupportedComponent(ePixelComponentRGBA);
3472         uvClip->addSupportedComponent(ePixelComponentRGB);
3473 #ifdef OFX_EXTENSIONS_NUKE
3474         uvClip->addSupportedComponent(ePixelComponentXY);
3475 #endif
3476         uvClip->addSupportedComponent(ePixelComponentAlpha);
3477         uvClip->setTemporalClipAccess(false);
3478         uvClip->setSupportsTiles(kSupportsTiles);
3479         uvClip->setIsMask(false);
3480         uvClip->setOptional(false);
3481     }
3482     // create the mandated source clip
3483     ClipDescriptor *srcClip = desc.defineClip(kOfxImageEffectSimpleSourceClipName);
3484     srcClip->addSupportedComponent(ePixelComponentRGBA);
3485     srcClip->addSupportedComponent(ePixelComponentRGB);
3486 #ifdef OFX_EXTENSIONS_NUKE
3487     srcClip->addSupportedComponent(ePixelComponentXY);
3488 #endif
3489     srcClip->addSupportedComponent(ePixelComponentAlpha);
3490     srcClip->setTemporalClipAccess(false);
3491     srcClip->setSupportsTiles(kSupportsTiles);
3492 #ifdef OFX_EXTENSIONS_NUKE
3493     srcClip->setCanTransform(true); // we can concatenate transforms upwards on srcClip only
3494 #endif
3495     srcClip->setIsMask(false);
3496     if (plugin == eDistortionPluginLensDistortion) {
3497         // in LensDistrortion, if Output Mode is set to STMap, the size is taken from the project size
3498         srcClip->setOptional(true);
3499     }
3500     if (plugin == eDistortionPluginIDistort) {
3501         // create the uv clip
3502         ClipDescriptor *uvClip = desc.defineClip(kClipUV);
3503         uvClip->addSupportedComponent(ePixelComponentRGBA);
3504         uvClip->addSupportedComponent(ePixelComponentRGB);
3505 #ifdef OFX_EXTENSIONS_NUKE
3506         uvClip->addSupportedComponent(ePixelComponentXY);
3507 #endif
3508         uvClip->addSupportedComponent(ePixelComponentAlpha);
3509         uvClip->setTemporalClipAccess(false);
3510         uvClip->setSupportsTiles(kSupportsTiles);
3511         uvClip->setIsMask(false);
3512     }
3513 
3514     // create the mandated output clip
3515     ClipDescriptor *dstClip = desc.defineClip(kOfxImageEffectOutputClipName);
3516     dstClip->addSupportedComponent(ePixelComponentRGBA);
3517     dstClip->addSupportedComponent(ePixelComponentRGB);
3518 #ifdef OFX_EXTENSIONS_NUKE
3519     dstClip->addSupportedComponent(ePixelComponentXY);
3520 #endif
3521     dstClip->addSupportedComponent(ePixelComponentAlpha);
3522     dstClip->setSupportsTiles(kSupportsTiles);
3523 
3524     ClipDescriptor *maskClip = (context == eContextPaint) ? desc.defineClip("Brush") : desc.defineClip("Mask");
3525     maskClip->addSupportedComponent(ePixelComponentAlpha);
3526     maskClip->setTemporalClipAccess(false);
3527     if (context != eContextPaint) {
3528         maskClip->setOptional(true);
3529     }
3530     maskClip->setSupportsTiles(kSupportsTiles);
3531     maskClip->setIsMask(true);
3532 
3533     // make some pages and to things in
3534     PageParamDescriptor *page = desc.definePageParam("Controls");
3535 
3536     {
3537         BooleanParamDescriptor* param = desc.defineBooleanParam(kParamProcessR);
3538         param->setLabelAndHint(kParamProcessRLabel);
3539         param->setDefault(true);
3540 #ifdef OFX_EXTENSIONS_NUKE
3541         param->setLayoutHint(eLayoutHintNoNewLine, 1);
3542 #endif
3543         if (page) {
3544             page->addChild(*param);
3545         }
3546     }
3547     {
3548         BooleanParamDescriptor* param = desc.defineBooleanParam(kParamProcessG);
3549         param->setLabelAndHint(kParamProcessGLabel);
3550         param->setDefault(true);
3551 #ifdef OFX_EXTENSIONS_NUKE
3552         param->setLayoutHint(eLayoutHintNoNewLine, 1);
3553 #endif
3554         if (page) {
3555             page->addChild(*param);
3556         }
3557     }
3558     {
3559         BooleanParamDescriptor* param = desc.defineBooleanParam(kParamProcessB);
3560         param->setLabelAndHint(kParamProcessBLabel);
3561         param->setDefault(true);
3562 #ifdef OFX_EXTENSIONS_NUKE
3563         param->setLayoutHint(eLayoutHintNoNewLine, 1);
3564 #endif
3565         if (page) {
3566             page->addChild(*param);
3567         }
3568     }
3569     {
3570         BooleanParamDescriptor* param = desc.defineBooleanParam(kParamProcessA);
3571         param->setLabelAndHint(kParamProcessALabel);
3572         param->setDefault(true);
3573         if (page) {
3574             page->addChild(*param);
3575         }
3576     }
3577 
3578     if ( (plugin == eDistortionPluginIDistort) ||
3579          ( plugin == eDistortionPluginSTMap) ) {
3580         std::vector<std::string> clipsForChannels(1);
3581         clipsForChannels[0] = kClipUV;
3582 
3583         if (gIsMultiPlaneV2) {
3584             {
3585                 ChoiceParamDescriptor* param = MultiPlane::Factory::describeInContextAddPlaneChannelChoice(desc, page, clipsForChannels, kParamChannelU, kParamChannelULabel);
3586 #ifdef OFX_EXTENSIONS_NUKE
3587                 param->setLayoutHint(eLayoutHintNoNewLine, 1);
3588 #endif
3589                 param->setDefault(eInputChannelR);
3590             }
3591             {
3592                 ChoiceParamDescriptor* param = MultiPlane::Factory::describeInContextAddPlaneChannelChoice(desc, page, clipsForChannels, kParamChannelV, kParamChannelVLabel);
3593                 param->setDefault(eInputChannelG);
3594             }
3595             {
3596                 ChoiceParamDescriptor* param = MultiPlane::Factory::describeInContextAddPlaneChannelChoice(desc, page, clipsForChannels, kParamChannelA, kParamChannelALabel);
3597                 param->setDefault(eInputChannelA);
3598             }
3599         } else {
3600             {
3601                 ChoiceParamDescriptor *param = desc.defineChoiceParam(kParamChannelU);
3602                 param->setLabelAndHint(kParamChannelULabel);
3603 #ifdef OFX_EXTENSIONS_NUKE
3604                 param->setLayoutHint(eLayoutHintNoNewLine, 1);
3605 #endif
3606                 MultiPlane::Factory::addInputChannelOptionsRGBA(param, clipsForChannels, true /*addConstants*/, !gIsMultiPlaneV1 /*addOnlyColorPlane*/);
3607                 param->setDefault(eInputChannelR);
3608                 if (page) {
3609                     page->addChild(*param);
3610                 }
3611             }
3612             {
3613                 ChoiceParamDescriptor *param = desc.defineChoiceParam(kParamChannelV);
3614                 param->setLabelAndHint(kParamChannelVLabel);
3615                 MultiPlane::Factory::addInputChannelOptionsRGBA(param, clipsForChannels, true /*addConstants*/, !gIsMultiPlaneV1 /*addOnlyColorPlane*/);
3616                 param->setDefault(eInputChannelG);
3617                 if (page) {
3618                     page->addChild(*param);
3619                 }
3620             }
3621             {
3622                 ChoiceParamDescriptor *param = desc.defineChoiceParam(kParamChannelA);
3623                 param->setLabelAndHint(kParamChannelALabel);
3624                 MultiPlane::Factory::addInputChannelOptionsRGBA(param, clipsForChannels, true /*addConstants*/, !gIsMultiPlaneV1 /*addOnlyColorPlane*/);
3625                 param->setDefault(eInputChannelA);
3626                 if (page) {
3627                     page->addChild(*param);
3628                 }
3629             }
3630         }
3631         {
3632             BooleanParamDescriptor* param = desc.defineBooleanParam(kParamChannelUnpremultUV);
3633             param->setLabelAndHint(kParamChannelUnpremultUVLabel);
3634             param->setDefault(false);
3635             if (page) {
3636                 page->addChild(*param);
3637             }
3638         }
3639         {
3640             Double2DParamDescriptor *param = desc.defineDouble2DParam(kParamUVOffset);
3641             param->setLabelAndHint(kParamUVOffsetLabel);
3642             param->setDefault(0., 0.);
3643             param->setDoubleType(eDoubleTypePlain);
3644             param->setRange(-DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
3645             param->setDisplayRange(0., 0., 1., 1.);
3646             param->setDimensionLabels("U", "V");
3647             param->setUseHostNativeOverlayHandle(false);
3648             if (page) {
3649                 page->addChild(*param);
3650             }
3651         }
3652         {
3653             Double2DParamDescriptor *param = desc.defineDouble2DParam(kParamUVScale);
3654             param->setLabelAndHint(kParamUVScaleLabel);
3655             param->setDoubleType(eDoubleTypeScale);
3656             param->setDefault(1., 1.);
3657             param->setRange(-DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
3658             param->setDisplayRange(0., 0., 100., 100.);
3659             param->setDimensionLabels("U", "V");
3660             param->setUseHostNativeOverlayHandle(false);
3661             if (page) {
3662                 page->addChild(*param);
3663             }
3664         }
3665 
3666         if (plugin == eDistortionPluginSTMap) {
3667             {
3668                 ChoiceParamDescriptor *param = desc.defineChoiceParam(kParamWrapU);
3669                 param->setLabelAndHint(kParamWrapULabel);
3670 #ifdef OFX_EXTENSIONS_NUKE
3671                 param->setLayoutHint(eLayoutHintNoNewLine, 1);
3672 #endif
3673                 addWrapOptions(param, eWrapClamp);
3674                 if (page) {
3675                     page->addChild(*param);
3676                 }
3677             }
3678             {
3679                 ChoiceParamDescriptor *param = desc.defineChoiceParam(kParamWrapV);
3680                 param->setLabelAndHint(kParamWrapVLabel);
3681                 addWrapOptions(param, eWrapClamp);
3682                 if (page) {
3683                     page->addChild(*param);
3684                 }
3685             }
3686         }
3687     }
3688 
3689     if (plugin == eDistortionPluginLensDistortion) {
3690         { // Frame format
3691             // extent
3692             {
3693                 ChoiceParamDescriptor* param = desc.defineChoiceParam(kParamFormat);
3694                 param->setLabelAndHint(kParamFormatLabel);
3695                 assert(param->getNOptions() == eGeneratorExtentFormat);
3696                 param->appendOption(kParamGeneratorExtentOptionFormat);
3697                 assert(param->getNOptions() == eGeneratorExtentSize);
3698                 param->appendOption(kParamGeneratorExtentOptionSize);
3699                 assert(param->getNOptions() == eGeneratorExtentProject);
3700                 param->appendOption(kParamGeneratorExtentOptionProject);
3701                 assert(param->getNOptions() == eGeneratorExtentDefault);
3702                 param->appendOption(kParamGeneratorExtentOptionDefault);
3703                 param->setDefault(eGeneratorExtentDefault);
3704                 param->setLayoutHint(eLayoutHintNoNewLine, 1);
3705                 param->setAnimates(false);
3706                 desc.addClipPreferencesSlaveParam(*param);
3707                 if (page) {
3708                     page->addChild(*param);
3709                 }
3710             }
3711 
3712             // recenter
3713             {
3714                 PushButtonParamDescriptor* param = desc.definePushButtonParam(kParamGeneratorCenter);
3715                 param->setLabel(kParamGeneratorCenterLabel);
3716                 param->setHint(kParamGeneratorCenterHint);
3717                 param->setLayoutHint(eLayoutHintNoNewLine, 1);
3718                 if (page) {
3719                     page->addChild(*param);
3720                 }
3721             }
3722 
3723             // format
3724             {
3725                 ChoiceParamDescriptor* param = desc.defineChoiceParam(kParamGeneratorFormat);
3726                 param->setLabel(kParamGeneratorFormatLabel);
3727                 assert(param->getNOptions() == eParamFormatPCVideo);
3728                 param->appendOption(kParamFormatPCVideoLabel, "", kParamFormatPCVideo);
3729                 assert(param->getNOptions() == eParamFormatNTSC);
3730                 param->appendOption(kParamFormatNTSCLabel, "", kParamFormatNTSC);
3731                 assert(param->getNOptions() == eParamFormatPAL);
3732                 param->appendOption(kParamFormatPALLabel, "", kParamFormatPAL);
3733                 assert(param->getNOptions() == eParamFormatNTSC169);
3734                 param->appendOption(kParamFormatNTSC169Label, "", kParamFormatNTSC169);
3735                 assert(param->getNOptions() == eParamFormatPAL169);
3736                 param->appendOption(kParamFormatPAL169Label, "", kParamFormatPAL169);
3737                 assert(param->getNOptions() == eParamFormatHD720);
3738                 param->appendOption(kParamFormatHD720Label, "", kParamFormatHD720);
3739                 assert(param->getNOptions() == eParamFormatHD);
3740                 param->appendOption(kParamFormatHDLabel, "", kParamFormatHD);
3741                 assert(param->getNOptions() == eParamFormatUHD4K);
3742                 param->appendOption(kParamFormatUHD4KLabel, "", kParamFormatUHD4K);
3743                 assert(param->getNOptions() == eParamFormat1kSuper35);
3744                 param->appendOption(kParamFormat1kSuper35Label, "", kParamFormat1kSuper35);
3745                 assert(param->getNOptions() == eParamFormat1kCinemascope);
3746                 param->appendOption(kParamFormat1kCinemascopeLabel, "", kParamFormat1kCinemascope);
3747                 assert(param->getNOptions() == eParamFormat2kSuper35);
3748                 param->appendOption(kParamFormat2kSuper35Label, "", kParamFormat2kSuper35);
3749                 assert(param->getNOptions() == eParamFormat2kCinemascope);
3750                 param->appendOption(kParamFormat2kCinemascopeLabel, "", kParamFormat2kCinemascope);
3751                 assert(param->getNOptions() == eParamFormat2kDCP);
3752                 param->appendOption(kParamFormat2kDCPLabel, "", kParamFormat2kDCP);
3753                 assert(param->getNOptions() == eParamFormat4kSuper35);
3754                 param->appendOption(kParamFormat4kSuper35Label, "", kParamFormat4kSuper35);
3755                 assert(param->getNOptions() == eParamFormat4kCinemascope);
3756                 param->appendOption(kParamFormat4kCinemascopeLabel, "", kParamFormat4kCinemascope);
3757                 assert(param->getNOptions() == eParamFormat4kDCP);
3758                 param->appendOption(kParamFormat4kDCPLabel, "", kParamFormat4kDCP);
3759                 assert(param->getNOptions() == eParamFormatSquare256);
3760                 param->appendOption(kParamFormatSquare256Label, "", kParamFormatSquare256);
3761                 assert(param->getNOptions() == eParamFormatSquare512);
3762                 param->appendOption(kParamFormatSquare512Label, "", kParamFormatSquare512);
3763                 assert(param->getNOptions() == eParamFormatSquare1k);
3764                 param->appendOption(kParamFormatSquare1kLabel, "", kParamFormatSquare1k);
3765                 assert(param->getNOptions() == eParamFormatSquare2k);
3766                 param->appendOption(kParamFormatSquare2kLabel, "", kParamFormatSquare2k);
3767                 param->setDefault(eParamFormatPCVideo);
3768                 param->setHint(kParamGeneratorFormatHint);
3769                 param->setAnimates(false);
3770                 desc.addClipPreferencesSlaveParam(*param);
3771                 if (page) {
3772                     page->addChild(*param);
3773                 }
3774             }
3775 
3776             {
3777                 int w = 0, h = 0;
3778                 double par = -1.;
3779                 getFormatResolution(eParamFormatPCVideo, &w, &h, &par);
3780                 assert(par != -1);
3781                 {
3782                     Int2DParamDescriptor* param = desc.defineInt2DParam(kParamGeneratorSize);
3783                     param->setLabelAndHint(kParamGeneratorSizeLabel, kParamGeneratorSizeHint);
3784                     param->setIsSecretAndDisabled(true);
3785                     param->setDefault(w, h);
3786                     if (page) {
3787                         page->addChild(*param);
3788                     }
3789                 }
3790 
3791                 {
3792                     DoubleParamDescriptor* param = desc.defineDoubleParam(kParamGeneratorPAR);
3793                     param->setLabelAndHint(kParamGeneratorPARLabel, kParamGeneratorPARHint);
3794                     param->setIsSecretAndDisabled(true);
3795                     param->setRange(0., DBL_MAX);
3796                     param->setDisplayRange(0.5, 2.);
3797                     param->setDefault(par);
3798                     if (page) {
3799                         page->addChild(*param);
3800                     }
3801                 }
3802             }
3803 
3804             // btmLeft
3805             {
3806                 Double2DParamDescriptor* param = desc.defineDouble2DParam(kParamRectangleInteractBtmLeft);
3807                 param->setLabelAndHint(kParamRectangleInteractBtmLeftLabel, "Coordinates of the bottom left corner of the size rectangle.");
3808                 param->setDoubleType(eDoubleTypeXYAbsolute);
3809                 if ( param->supportsDefaultCoordinateSystem() ) {
3810                     param->setDefaultCoordinateSystem(eCoordinatesNormalised); // no need of kParamDefaultsNormalised
3811                 } else {
3812                     gHostSupportsDefaultCoordinateSystem = false; // no multithread here, see kParamDefaultsNormalised
3813                 }
3814                 param->setDefault(0., 0.);
3815                 param->setRange(-DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
3816                 param->setDisplayRange(-10000, -10000, 10000, 10000); // Resolve requires display range or values are clamped to (-1,1)
3817                 param->setIncrement(1.);
3818                 param->setLayoutHint(eLayoutHintNoNewLine, 1);
3819                 param->setDigits(0);
3820                 if (page) {
3821                     page->addChild(*param);
3822                 }
3823             }
3824 
3825             // size
3826             {
3827                 Double2DParamDescriptor* param = desc.defineDouble2DParam(kParamRectangleInteractSize);
3828                 param->setLabelAndHint(kParamRectangleInteractSizeLabel, "Width and height of the size rectangle.");
3829                 param->setDoubleType(eDoubleTypeXY);
3830                 if ( param->supportsDefaultCoordinateSystem() ) {
3831                     param->setDefaultCoordinateSystem(eCoordinatesNormalised); // no need of kParamDefaultsNormalised
3832                 } else {
3833                     gHostSupportsDefaultCoordinateSystem = false; // no multithread here, see kParamDefaultsNormalised
3834                 }
3835                 param->setDefault(1., 1.);
3836                 param->setRange(0., 0., DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
3837                 param->setDisplayRange(0, 0, 10000, 10000); // Resolve requires display range or values are clamped to (-1,1)
3838                 param->setIncrement(1.);
3839                 param->setDimensionLabels(kParamRectangleInteractSizeDim1, kParamRectangleInteractSizeDim2);
3840                 param->setIncrement(1.);
3841                 param->setDigits(0);
3842                 if (page) {
3843                     page->addChild(*param);
3844                 }
3845             }
3846 
3847         }
3848 
3849         {
3850             ChoiceParamDescriptor *param = desc.defineChoiceParam(kParamDistortionModel);
3851             param->setLabelAndHint(kParamDistortionModelLabel);
3852             assert(param->getNOptions() == eDistortionModelNuke);
3853             param->appendOption(kParamDistortionModelOptionNuke);
3854             assert(param->getNOptions() == eDistortionModelPFBarrel);
3855             param->appendOption(kParamDistortionModelOptionPFBarrel);
3856             assert(param->getNOptions() == eDistortionModel3DEClassic);
3857             param->appendOption(kParamDistortionModelOption3DEClassic);
3858             assert(param->getNOptions() == eDistortionModel3DEAnamorphic6);
3859             param->appendOption(kParamDistortionModelOption3DEAnamorphic6);
3860             assert(param->getNOptions() == eDistortionModel3DEFishEye8);
3861             param->appendOption(kParamDistortionModelOption3DEFishEye8);
3862             assert(param->getNOptions() == eDistortionModel3DEStandard);
3863             param->appendOption(kParamDistortionModelOption3DEStandard);
3864             assert(param->getNOptions() == eDistortionModel3DEAnamorphic4);
3865             param->appendOption(kParamDistortionModelOption3DEAnamorphic4);
3866             assert(param->getNOptions() == eDistortionModelPanoTools);
3867             param->appendOption(kParamDistortionModelOptionPanoTools);
3868             if (page) {
3869                 page->addChild(*param);
3870             }
3871         }
3872         {
3873             ChoiceParamDescriptor *param = desc.defineChoiceParam(kParamDistortionDirection);
3874             param->setLabelAndHint(kParamDistortionDirectionLabel);
3875             assert(param->getNOptions() == eDirectionDistort);
3876             param->appendOption(kParamDistortionDirectionOptionDistort);
3877             assert(param->getNOptions() == eDirectionUndistort);
3878             param->appendOption(kParamDistortionDirectionOptionUndistort);
3879             param->setDefault((int)eDirectionDistort); // same default as in Nuke, and also the most straightforward (non-iterative) transform for most models
3880             if (page) {
3881                 page->addChild(*param);
3882             }
3883         }
3884         {
3885             ChoiceParamDescriptor *param = desc.defineChoiceParam(kParamDistortionOutputMode);
3886             param->setLabelAndHint(kParamDistortionOutputModeLabel);
3887             assert(param->getNOptions() == eOutputModeImage);
3888             param->appendOption(kParamDistortionOutputModeOptionImage);
3889             assert(param->getNOptions() == eOutputModeSTMap);
3890             param->appendOption(kParamDistortionOutputModeOptionSTMap);
3891             if (page) {
3892                 page->addChild(*param);
3893             }
3894         }
3895 
3896         // Nuke
3897         {
3898             DoubleParamDescriptor *param = desc.defineDoubleParam(kParamK1);
3899             param->setLabelAndHint(kParamK1Label);
3900             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
3901             param->setDisplayRange(-0.3, 0.3);
3902             if (page) {
3903                 page->addChild(*param);
3904             }
3905         }
3906         {
3907             DoubleParamDescriptor *param = desc.defineDoubleParam(kParamK2);
3908             param->setLabelAndHint(kParamK2Label);
3909             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
3910             param->setDisplayRange(-0.1, 0.1);
3911             if (page) {
3912                 page->addChild(*param);
3913             }
3914         }
3915         {
3916             Double2DParamDescriptor *param = desc.defineDouble2DParam(kParamCenter);
3917             param->setLabelAndHint(kParamCenterLabel);
3918             param->setRange(-DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
3919             param->setUseHostNativeOverlayHandle(false);
3920             param->setDoubleType(eDoubleTypePlain);
3921             param->setDisplayRange(-1, -1, 1, 1);
3922             if (page) {
3923                 page->addChild(*param);
3924             }
3925         }
3926         {
3927             DoubleParamDescriptor *param = desc.defineDoubleParam(kParamSqueeze);
3928             param->setLabelAndHint(kParamSqueezeLabel);
3929             param->setDefault(1.);
3930             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
3931             param->setDisplayRange(0., 1.);
3932             if (page) {
3933                 page->addChild(*param);
3934             }
3935         }
3936         {
3937             Double2DParamDescriptor *param = desc.defineDouble2DParam(kParamAsymmetric);
3938             param->setLabelAndHint(kParamAsymmetricLabel);
3939             param->setRange(-DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
3940             param->setDoubleType(eDoubleTypePlain);
3941             param->setDisplayRange(-0.5, -0.5, 0.5, 0.5);
3942             param->setUseHostNativeOverlayHandle(false);
3943             if (page) {
3944                 page->addChild(*param);
3945             }
3946         }
3947 
3948         ////////////
3949         // PFBarrel
3950         {
3951             StringParamDescriptor *param = desc.defineStringParam(kParamPFFile);
3952             param->setLabelAndHint(kParamPFFileLabel);
3953             param->setStringType(eStringTypeFilePath);
3954             param->setFilePathExists(true);
3955 #ifdef OFX_EXTENSIONS_NUKE
3956             if (!getImageEffectHostDescription()->isNatron) {
3957                 param->setLayoutHint(eLayoutHintNoNewLine, 1);
3958             }
3959 #endif
3960             if (page) {
3961                 page->addChild(*param);
3962             }
3963         }
3964         if (!getImageEffectHostDescription()->isNatron) {
3965             // Natron has its own reload button
3966             PushButtonParamDescriptor *param = desc.definePushButtonParam(kParamPFFileReload);
3967             param->setLabelAndHint(kParamPFFileReloadLabel);
3968             if (page) {
3969                 page->addChild(*param);
3970             }
3971         }
3972         {
3973             DoubleParamDescriptor *param = desc.defineDoubleParam(kParamPFC3);
3974             param->setLabelAndHint(kParamPFC3Label);
3975             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
3976             param->setDisplayRange(-0.5, 0.5);
3977             if (page) {
3978                 page->addChild(*param);
3979             }
3980         }
3981         {
3982             DoubleParamDescriptor *param = desc.defineDoubleParam(kParamPFC5);
3983             param->setLabelAndHint(kParamPFC5Label);
3984             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
3985             param->setDisplayRange(-0.5, 0.5);
3986             if (page) {
3987                 page->addChild(*param);
3988             }
3989         }
3990         {
3991             Double2DParamDescriptor *param = desc.defineDouble2DParam(kParamPFP);
3992             param->setLabelAndHint(kParamPFPLabel);
3993             param->setRange(-DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
3994             param->setDisplayRange(0., 0., 1., 1.);
3995             param->setDefault(0.5, 0.5);
3996             param->setDoubleType(eDoubleTypePlain);
3997             param->setUseHostNativeOverlayHandle(false);
3998             if (page) {
3999                 page->addChild(*param);
4000             }
4001         }
4002         {
4003             DoubleParamDescriptor *param = desc.defineDoubleParam(kParamPFSqueeze);
4004             param->setLabelAndHint(kParamPFSqueezeLabel);
4005             param->setRange(0.0, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4006             param->setDisplayRange(0.1, 3.);
4007             param->setDefault(1.);
4008             if (page) {
4009                 page->addChild(*param);
4010             }
4011         }
4012 
4013         ////////////////
4014         // 3DEqualizer
4015         // fov parameters
4016         {
4017             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DE4_xa_fov_unit);
4018             param->setLabelAndHint(kParam3DE4_xa_fov_unitLabel);
4019             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4020             param->setDisplayRange(0., 1.);
4021             param->setDefault(0.);
4022             if (page) {
4023                 page->addChild(*param);
4024             }
4025         }
4026         {
4027             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DE4_ya_fov_unit);
4028             param->setLabelAndHint(kParam3DE4_ya_fov_unitLabel);
4029             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4030             param->setDisplayRange(0., 1.);
4031             param->setDefault(0.);
4032             if (page) {
4033                 page->addChild(*param);
4034             }
4035         }
4036         {
4037             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DE4_xb_fov_unit);
4038             param->setLabelAndHint(kParam3DE4_xb_fov_unitLabel);
4039             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4040             param->setDisplayRange(0., 1.);
4041             param->setDefault(1.);
4042             if (page) {
4043                 page->addChild(*param);
4044             }
4045         }
4046         {
4047             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DE4_yb_fov_unit);
4048             param->setLabelAndHint(kParam3DE4_yb_fov_unitLabel);
4049             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4050             param->setDisplayRange(0., 1.);
4051             param->setDefault(1.);
4052             if (page) {
4053                 page->addChild(*param);
4054             }
4055         }
4056         // seven builtin parameters
4057         {
4058             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DE4_focal_length_cm);
4059             param->setLabelAndHint(kParam3DE4_focal_length_cmLabel);
4060             param->setRange(0, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4061             param->setDisplayRange(0.5, 50.);
4062             param->setDefault(1.);
4063             if (page) {
4064                 page->addChild(*param);
4065             }
4066         }
4067         {
4068             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DE4_custom_focus_distance_cm);
4069             param->setLabelAndHint(kParam3DE4_custom_focus_distance_cmLabel);
4070             param->setRange(0, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4071             param->setDisplayRange(10, 1000.);
4072             param->setDefault(100.);
4073             if (page) {
4074                 page->addChild(*param);
4075             }
4076         }
4077         {
4078             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DE4_filmback_width_cm);
4079             param->setLabelAndHint(kParam3DE4_filmback_width_cmLabel);
4080             param->setRange(0, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4081             param->setDisplayRange(0.1, 10.);
4082             param->setDefault(0.8);
4083             if (page) {
4084                 page->addChild(*param);
4085             }
4086         }
4087         {
4088             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DE4_filmback_height_cm);
4089             param->setLabelAndHint(kParam3DE4_filmback_height_cmLabel);
4090             param->setRange(0, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4091             param->setDisplayRange(0.1, 10.);
4092             param->setDefault(0.6);
4093             if (page) {
4094                 page->addChild(*param);
4095             }
4096         }
4097         {
4098             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DE4_lens_center_offset_x_cm);
4099             param->setLabelAndHint(kParam3DE4_lens_center_offset_x_cmLabel);
4100             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4101             param->setDisplayRange(-5., 5.);
4102             param->setDefault(0.);
4103             if (page) {
4104                 page->addChild(*param);
4105             }
4106         }
4107         {
4108             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DE4_lens_center_offset_y_cm);
4109             param->setLabelAndHint(kParam3DE4_lens_center_offset_y_cmLabel);
4110             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4111             param->setDisplayRange(-5., 5.);
4112             param->setDefault(0.);
4113             if (page) {
4114                 page->addChild(*param);
4115             }
4116         }
4117         {
4118             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DE4_pixel_aspect);
4119             param->setLabelAndHint(kParam3DE4_pixel_aspectLabel);
4120             param->setRange(0., DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4121             param->setDisplayRange(0.25, 4.);
4122             param->setDefault(1.);
4123             if (page) {
4124                 page->addChild(*param);
4125             }
4126         }
4127         // 3DE Classic model
4128         {
4129             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DEDistortion);
4130             param->setLabelAndHint(kParam3DEDistortionLabel);
4131             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4132             param->setDisplayRange(-0.5, 0.5);
4133             param->setDefault(0.);
4134             if (page) {
4135                 page->addChild(*param);
4136             }
4137         }
4138         {
4139             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DEAnamorphicSqueeze);
4140             param->setLabelAndHint(kParam3DEAnamorphicSqueezeLabel);
4141             param->setRange(0., DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4142             param->setDisplayRange(0.25, 4.);
4143             param->setDefault(1.);
4144             if (page) {
4145                 page->addChild(*param);
4146             }
4147         }
4148         {
4149             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECurvatureX);
4150             param->setLabelAndHint(kParam3DECurvatureXLabel);
4151             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4152             param->setDisplayRange(-0.5, 0.5);
4153             param->setDefault(0.);
4154             if (page) {
4155                 page->addChild(*param);
4156             }
4157         }
4158         {
4159             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECurvatureY);
4160             param->setLabelAndHint(kParam3DECurvatureYLabel);
4161             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4162             param->setDisplayRange(-0.5, 0.5);
4163             param->setDefault(0.);
4164             if (page) {
4165                 page->addChild(*param);
4166             }
4167         }
4168         {
4169             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DEQuarticDistortion);
4170             param->setLabelAndHint(kParam3DEQuarticDistortionLabel);
4171             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4172             param->setDisplayRange(-0.5, 0.5);
4173             param->setDefault(0.);
4174             if (page) {
4175                 page->addChild(*param);
4176             }
4177         }
4178         // 3DE Radial Standard Degree 4
4179         {
4180             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DEDistortionDegree2);
4181             param->setLabelAndHint(kParam3DEDistortionDegree2Label);
4182             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4183             param->setDisplayRange(-0.5, 0.5);
4184             param->setDefault(0.);
4185             if (page) {
4186                 page->addChild(*param);
4187             }
4188         }
4189         {
4190             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DEUDegree2);
4191             param->setLabelAndHint(kParam3DEUDegree2Label);
4192             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4193             param->setDisplayRange(-0.5, 0.5);
4194             param->setDefault(0.);
4195             if (page) {
4196                 page->addChild(*param);
4197             }
4198         }
4199         {
4200             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DEVDegree2);
4201             param->setLabelAndHint(kParam3DEVDegree2Label);
4202             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4203             param->setDisplayRange(-0.5, 0.5);
4204             param->setDefault(0.);
4205             if (page) {
4206                 page->addChild(*param);
4207             }
4208         }
4209         {
4210             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DEQuarticDistortionDegree4);
4211             param->setLabelAndHint(kParam3DEQuarticDistortionDegree4Label);
4212             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4213             param->setDisplayRange(-0.5, 0.5);
4214             param->setDefault(0.);
4215             if (page) {
4216                 page->addChild(*param);
4217             }
4218         }
4219         {
4220             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DEUDegree4);
4221             param->setLabelAndHint(kParam3DEUDegree4Label);
4222             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4223             param->setDisplayRange(-0.5, 0.5);
4224             param->setDefault(0.);
4225             if (page) {
4226                 page->addChild(*param);
4227             }
4228         }
4229         {
4230             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DEVDegree4);
4231             param->setLabelAndHint(kParam3DEVDegree4Label);
4232             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4233             param->setDisplayRange(-0.5, 0.5);
4234             param->setDefault(0.);
4235             if (page) {
4236                 page->addChild(*param);
4237             }
4238         }
4239         {
4240             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DEPhiCylindricDirection);
4241             param->setLabelAndHint(kParam3DEPhiCylindricDirectionLabel);
4242             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4243             param->setDisplayRange(-90., 90.);
4244             param->setDefault(0.);
4245             if (page) {
4246                 page->addChild(*param);
4247             }
4248         }
4249         {
4250             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DEBCylindricBending);
4251             param->setLabelAndHint(kParam3DEBCylindricBendingLabel);
4252             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4253             param->setDisplayRange(-0.1, 0.1);
4254             param->setDefault(0.);
4255             if (page) {
4256                 page->addChild(*param);
4257             }
4258         }
4259         // 3DE4_Anamorphic_Standard_Degree_4 and 3DE4_Anamorphic_Degree_6
4260         {
4261             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECx02Degree2);
4262             param->setLabelAndHint(kParam3DECx02Degree2Label);
4263             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4264             param->setDisplayRange(-0.5, 0.5);
4265             param->setDefault(0.);
4266             if (page) {
4267                 page->addChild(*param);
4268             }
4269         }
4270         {
4271             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECy02Degree2);
4272             param->setLabelAndHint(kParam3DECy02Degree2Label);
4273             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4274             param->setDisplayRange(-0.5, 0.5);
4275             param->setDefault(0.);
4276             if (page) {
4277                 page->addChild(*param);
4278             }
4279         }
4280         {
4281             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECx22Degree2);
4282             param->setLabelAndHint(kParam3DECx22Degree2Label);
4283             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4284             param->setDisplayRange(-0.5, 0.5);
4285             param->setDefault(0.);
4286             if (page) {
4287                 page->addChild(*param);
4288             }
4289         }
4290         {
4291             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECy22Degree2);
4292             param->setLabelAndHint(kParam3DECy22Degree2Label);
4293             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4294             param->setDisplayRange(-0.5, 0.5);
4295             param->setDefault(0.);
4296             if (page) {
4297                 page->addChild(*param);
4298             }
4299         }
4300         {
4301             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECx04Degree4);
4302             param->setLabelAndHint(kParam3DECx04Degree4Label);
4303             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4304             param->setDisplayRange(-0.5, 0.5);
4305             param->setDefault(0.);
4306             if (page) {
4307                 page->addChild(*param);
4308             }
4309         }
4310         {
4311             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECy04Degree4);
4312             param->setLabelAndHint(kParam3DECy04Degree4Label);
4313             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4314             param->setDisplayRange(-0.5, 0.5);
4315             param->setDefault(0.);
4316             if (page) {
4317                 page->addChild(*param);
4318             }
4319         }
4320         {
4321             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECx24Degree4);
4322             param->setLabelAndHint(kParam3DECx24Degree4Label);
4323             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4324             param->setDisplayRange(-0.5, 0.5);
4325             param->setDefault(0.);
4326             if (page) {
4327                 page->addChild(*param);
4328             }
4329         }
4330         {
4331             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECy24Degree4);
4332             param->setLabelAndHint(kParam3DECy24Degree4Label);
4333             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4334             param->setDisplayRange(-0.5, 0.5);
4335             param->setDefault(0.);
4336             if (page) {
4337                 page->addChild(*param);
4338             }
4339         }
4340         {
4341             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECx44Degree4);
4342             param->setLabelAndHint(kParam3DECx44Degree4Label);
4343             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4344             param->setDisplayRange(-0.5, 0.5);
4345             param->setDefault(0.);
4346             if (page) {
4347                 page->addChild(*param);
4348             }
4349         }
4350         {
4351             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECy44Degree4);
4352             param->setLabelAndHint(kParam3DECy44Degree4Label);
4353             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4354             param->setDisplayRange(-0.5, 0.5);
4355             param->setDefault(0.);
4356             if (page) {
4357                 page->addChild(*param);
4358             }
4359         }
4360         {
4361             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECx06Degree6);
4362             param->setLabelAndHint(kParam3DECx06Degree6Label);
4363             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4364             param->setDisplayRange(-0.5, 0.5);
4365             param->setDefault(0.);
4366             if (page) {
4367                 page->addChild(*param);
4368             }
4369         }
4370         {
4371             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECy06Degree6);
4372             param->setLabelAndHint(kParam3DECy06Degree6Label);
4373             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4374             param->setDisplayRange(-0.5, 0.5);
4375             param->setDefault(0.);
4376             if (page) {
4377                 page->addChild(*param);
4378             }
4379         }
4380         {
4381             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECx26Degree6);
4382             param->setLabelAndHint(kParam3DECx26Degree6Label);
4383             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4384             param->setDisplayRange(-0.5, 0.5);
4385             param->setDefault(0.);
4386             if (page) {
4387                 page->addChild(*param);
4388             }
4389         }
4390         {
4391             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECy26Degree6);
4392             param->setLabelAndHint(kParam3DECy26Degree6Label);
4393             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4394             param->setDisplayRange(-0.5, 0.5);
4395             param->setDefault(0.);
4396             if (page) {
4397                 page->addChild(*param);
4398             }
4399         }
4400         {
4401             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECx46Degree6);
4402             param->setLabelAndHint(kParam3DECx46Degree6Label);
4403             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4404             param->setDisplayRange(-0.5, 0.5);
4405             param->setDefault(0.);
4406             if (page) {
4407                 page->addChild(*param);
4408             }
4409         }
4410         {
4411             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECy46Degree6);
4412             param->setLabelAndHint(kParam3DECy46Degree6Label);
4413             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4414             param->setDisplayRange(-0.5, 0.5);
4415             param->setDefault(0.);
4416             if (page) {
4417                 page->addChild(*param);
4418             }
4419         }
4420         {
4421             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECx66Degree6);
4422             param->setLabelAndHint(kParam3DECx66Degree6Label);
4423             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4424             param->setDisplayRange(-0.5, 0.5);
4425             param->setDefault(0.);
4426             if (page) {
4427                 page->addChild(*param);
4428             }
4429         }
4430         {
4431             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DECy66Degree6);
4432             param->setLabelAndHint(kParam3DECy66Degree6Label);
4433             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4434             param->setDisplayRange(-0.5, 0.5);
4435             param->setDefault(0.);
4436             if (page) {
4437                 page->addChild(*param);
4438             }
4439         }
4440         {
4441             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DELensRotation);
4442             param->setLabelAndHint(kParam3DELensRotationLabel);
4443             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4444             param->setDisplayRange(-2., 2.);
4445             param->setDefault(0.);
4446             if (page) {
4447                 page->addChild(*param);
4448             }
4449         }
4450         {
4451             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DESqueezeX);
4452             param->setLabelAndHint(kParam3DESqueezeXLabel);
4453             param->setRange(0, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4454             param->setDisplayRange(0.9, 1.1);
4455             param->setDefault(1.);
4456             if (page) {
4457                 page->addChild(*param);
4458             }
4459         }
4460         {
4461             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DESqueezeY);
4462             param->setLabelAndHint(kParam3DESqueezeYLabel);
4463             param->setRange(0, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4464             param->setDisplayRange(0.9, 1.1);
4465             param->setDefault(1.);
4466             if (page) {
4467                 page->addChild(*param);
4468             }
4469         }
4470         // 3DEFishEye8
4471         {
4472             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DEDegree6);
4473             param->setLabelAndHint(kParam3DEDegree6Label);
4474             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4475             param->setDisplayRange(-0.5, 0.5);
4476             param->setDefault(0.);
4477             if (page) {
4478                 page->addChild(*param);
4479             }
4480         }
4481         {
4482             DoubleParamDescriptor *param = desc.defineDoubleParam(kParam3DEDegree8);
4483             param->setLabelAndHint(kParam3DEDegree8Label);
4484             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4485             param->setDisplayRange(-0.5, 0.5);
4486             param->setDefault(0.);
4487             if (page) {
4488                 page->addChild(*param);
4489             }
4490         }
4491 
4492         // PanoTools
4493         {
4494             DoubleParamDescriptor *param = desc.defineDoubleParam(kParamPanoToolsA);
4495             param->setLabelAndHint(kParamPanoToolsALabel);
4496             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4497             param->setDisplayRange(-0.2, 0.2);
4498             if (page) {
4499                 page->addChild(*param);
4500             }
4501         }
4502         {
4503             DoubleParamDescriptor *param = desc.defineDoubleParam(kParamPanoToolsB);
4504             param->setLabelAndHint(kParamPanoToolsBLabel);
4505             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4506             param->setDisplayRange(-0.2, 0.2);
4507             if (page) {
4508                 page->addChild(*param);
4509             }
4510         }
4511         {
4512             DoubleParamDescriptor *param = desc.defineDoubleParam(kParamPanoToolsC);
4513             param->setLabelAndHint(kParamPanoToolsCLabel);
4514             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4515             param->setDisplayRange(-0.2, 0.2);
4516             if (page) {
4517                 page->addChild(*param);
4518             }
4519         }
4520         {
4521             DoubleParamDescriptor *param = desc.defineDoubleParam(kParamPanoToolsD);
4522             param->setLabelAndHint(kParamPanoToolsDLabel);
4523             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4524             param->setDisplayRange(-1000., 1000.);
4525             if (page) {
4526                 page->addChild(*param);
4527             }
4528         }
4529         {
4530             DoubleParamDescriptor *param = desc.defineDoubleParam(kParamPanoToolsE);
4531             param->setLabelAndHint(kParamPanoToolsELabel);
4532             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4533             param->setDisplayRange(-1000., 1000.);
4534             if (page) {
4535                 page->addChild(*param);
4536             }
4537         }
4538         {
4539             DoubleParamDescriptor *param = desc.defineDoubleParam(kParamPanoToolsG);
4540             param->setLabelAndHint(kParamPanoToolsGLabel);
4541             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4542             param->setDisplayRange(-1000., 1000.);
4543             if (page) {
4544                 page->addChild(*param);
4545             }
4546         }
4547         {
4548             DoubleParamDescriptor *param = desc.defineDoubleParam(kParamPanoToolsT);
4549             param->setLabelAndHint(kParamPanoToolsTLabel);
4550             param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
4551             param->setDisplayRange(-1000., 1000.);
4552             if (page) {
4553                 page->addChild(*param);
4554             }
4555         }
4556 
4557     }
4558 
4559     ofxsFilterDescribeParamsInterpolate2D( desc, page, (plugin == eDistortionPluginSTMap) );
4560 #ifdef OFX_EXTENSIONS_NATRON
4561     if (plugin == eDistortionPluginLensDistortion && majorVersion >= 4 && getImageEffectHostDescription()->isNatron) {
4562         BooleanParamDescriptor *param = desc.defineBooleanParam(kParamCropToFormat);
4563         param->setLabel(kParamCropToFormatLabel);
4564         param->setHint(kParamCropToFormatHint);
4565         param->setDefault(true);
4566         if (page) {
4567             page->addChild(*param);
4568         }
4569     }
4570 #endif
4571     ofxsPremultDescribeParams(desc, page);
4572     ofxsMaskMixDescribeParams(desc, page);
4573 } // >::describeInContext
4574 
4575 template<DistortionPluginEnum plugin, int majorVersion>
4576 ImageEffect*
createInstance(OfxImageEffectHandle handle,ContextEnum)4577 DistortionPluginFactory<plugin, majorVersion>::createInstance(OfxImageEffectHandle handle,
4578                                                 ContextEnum /*context*/)
4579 {
4580     return new DistortionPlugin(handle, this->getMajorVersion(), plugin);
4581 }
4582 
4583 static DistortionPluginFactory<eDistortionPluginIDistort,kPluginVersionMajor> pIDistort(kPluginIDistortIdentifier, kPluginVersionMajor, kPluginVersionMinor);
4584 static DistortionPluginFactory<eDistortionPluginSTMap,kPluginVersionMajor> pSTMap(kPluginSTMapIdentifier, kPluginVersionMajor, kPluginVersionMinor);
4585 static DistortionPluginFactory<eDistortionPluginLensDistortion,2> pLensDistortion2(kPluginLensDistortionIdentifier, 2, kPluginVersionMinor);
4586 static DistortionPluginFactory<eDistortionPluginLensDistortion,3> pLensDistortion3(kPluginLensDistortionIdentifier, 3, kPluginVersionMinor);
4587 static DistortionPluginFactory<eDistortionPluginLensDistortion,kPluginVersionLensDistortionMajor> pLensDistortion4(kPluginLensDistortionIdentifier, kPluginVersionLensDistortionMajor, kPluginVersionLensDistortionMinor);
4588 mRegisterPluginFactoryInstance(pIDistort)
4589 mRegisterPluginFactoryInstance(pSTMap)
4590 mRegisterPluginFactoryInstance(pLensDistortion2)
4591 mRegisterPluginFactoryInstance(pLensDistortion3)
4592 mRegisterPluginFactoryInstance(pLensDistortion4)
4593 
4594 OFXS_NAMESPACE_ANONYMOUS_EXIT
4595