1 /***
2 
3     Olive - Non-Linear Video Editor
4     Copyright (C) 2019  Olive Team
5 
6     This program is free software: you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation, either version 3 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 
19 ***/
20 
21 #include "transformeffect.h"
22 
23 #include <QWidget>
24 #include <QLabel>
25 #include <QGridLayout>
26 #include <QSpinBox>
27 #include <QCheckBox>
28 #include <QOpenGLFunctions>
29 #include <QComboBox>
30 #include <QMouseEvent>
31 
32 #include "ui/collapsiblewidget.h"
33 #include "timeline/clip.h"
34 #include "timeline/sequence.h"
35 #include "project/footage.h"
36 #include "global/math.h"
37 #include "ui/labelslider.h"
38 #include "ui/comboboxex.h"
39 #include "panels/project.h"
40 #include "global/debug.h"
41 
42 #include "panels/panels.h"
43 #include "panels/viewer.h"
44 #include "ui/viewerwidget.h"
45 
TransformEffect(Clip * c,const EffectMeta * em)46 TransformEffect::TransformEffect(Clip* c, const EffectMeta* em) : Effect(c, em) {
47   SetFlags(Effect::CoordsFlag);
48 
49   EffectRow* position_row = new EffectRow(this, tr("Position"));
50 
51   position_x = new DoubleField(position_row, "posx"); // position X
52   position_y = new DoubleField(position_row, "posy"); // position Y
53 
54   EffectRow* scale_row = new EffectRow(this, tr("Scale"));
55 
56   // scale X (and Y is uniform scale is selected)
57   scale_x = new DoubleField(scale_row, "scalex");
58   scale_x->SetMinimum(0);
59 
60   // scale Y (disabled if uniform scale is selected)
61   scale_y = new DoubleField(scale_row, "scaley");
62   scale_y->SetMinimum(0);
63 
64   EffectRow* uniform_scale_row = new EffectRow(this, tr("Uniform Scale"));
65 
66   uniform_scale_field = new BoolField(uniform_scale_row, "uniformscale"); // uniform scale option
67 
68   EffectRow* rotation_row = new EffectRow(this, tr("Rotation"));
69 
70   rotation = new DoubleField(rotation_row, "rotation");
71 
72   EffectRow* anchor_point_row = new EffectRow(this, tr("Anchor Point"));
73 
74   anchor_x_box = new DoubleField(anchor_point_row, "anchorx"); // anchor point X
75   anchor_y_box = new DoubleField(anchor_point_row, "anchory"); // anchor point Y
76 
77   EffectRow* opacity_row = new EffectRow(this, tr("Opacity"));
78 
79   // opacity
80   opacity = new DoubleField(opacity_row, "opacity");
81   opacity->SetMinimum(0);
82   opacity->SetMaximum(100);
83 
84   EffectRow* blend_mode_row = new EffectRow(this, tr("Blend Mode"));
85 
86   // blend mode
87   blend_mode_box = new ComboField(blend_mode_row, "blendmode");
88   blend_mode_box->SetColumnSpan(2);
89   blend_mode_box->AddItem(tr("Normal"), "");
90 
91   // set up gizmos
92   top_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
93   top_left_gizmo->set_cursor(Qt::SizeFDiagCursor);
94   top_left_gizmo->x_field1 = scale_x;
95 
96   top_center_gizmo = add_gizmo(GIZMO_TYPE_DOT);
97   top_center_gizmo->set_cursor(Qt::SizeVerCursor);
98   top_center_gizmo->y_field1 = scale_x;
99 
100   top_right_gizmo = add_gizmo(GIZMO_TYPE_DOT);
101   top_right_gizmo->set_cursor(Qt::SizeBDiagCursor);
102   top_right_gizmo->x_field1 = scale_x;
103 
104   bottom_left_gizmo = add_gizmo(GIZMO_TYPE_DOT);
105   bottom_left_gizmo->set_cursor(Qt::SizeBDiagCursor);
106   bottom_left_gizmo->x_field1 = scale_x;
107 
108   bottom_center_gizmo = add_gizmo(GIZMO_TYPE_DOT);
109   bottom_center_gizmo->set_cursor(Qt::SizeVerCursor);
110   bottom_center_gizmo->y_field1 = scale_x;
111 
112   bottom_right_gizmo = add_gizmo(GIZMO_TYPE_DOT);
113   bottom_right_gizmo->set_cursor(Qt::SizeFDiagCursor);
114   bottom_right_gizmo->x_field1 = scale_x;
115 
116   left_center_gizmo = add_gizmo(GIZMO_TYPE_DOT);
117   left_center_gizmo->set_cursor(Qt::SizeHorCursor);
118   left_center_gizmo->x_field1 = scale_x;
119 
120   right_center_gizmo = add_gizmo(GIZMO_TYPE_DOT);
121   right_center_gizmo->set_cursor(Qt::SizeHorCursor);
122   right_center_gizmo->x_field1 = scale_x;
123 
124   anchor_gizmo = add_gizmo(GIZMO_TYPE_TARGET);
125   anchor_gizmo->set_cursor(Qt::SizeAllCursor);
126   anchor_gizmo->x_field1 = anchor_x_box;
127   anchor_gizmo->y_field1 = anchor_y_box;
128   anchor_gizmo->x_field2 = position_x;
129   anchor_gizmo->y_field2 = position_y;
130 
131   rotate_gizmo = add_gizmo(GIZMO_TYPE_DOT);
132   rotate_gizmo->color = Qt::green;
133   rotate_gizmo->set_cursor(Qt::SizeAllCursor);
134   rotate_gizmo->x_field1 = rotation;
135 
136   rect_gizmo = add_gizmo(GIZMO_TYPE_POLY);
137   rect_gizmo->x_field1 = position_x;
138   rect_gizmo->y_field1 = position_y;
139 
140   connect(uniform_scale_field, SIGNAL(Toggled(bool)), this, SLOT(toggle_uniform_scale(bool)));
141 
142   // set defaults
143   uniform_scale_field->SetValueAt(0, true);
144   blend_mode_box->SetValueAt(0, "");
145   anchor_x_box->SetDefault(0);
146   anchor_y_box->SetDefault(0);
147   opacity->SetDefault(100);
148   scale_x->SetDefault(100);
149   scale_y->SetDefault(100);
150 
151   refresh();
152 }
153 
refresh()154 void TransformEffect::refresh() {
155   if (parent_clip != nullptr && parent_clip->sequence != nullptr) {
156 
157     position_x->SetDefault(parent_clip->sequence->width/2);
158     position_y->SetDefault(parent_clip->sequence->height/2);
159 
160     double x_percent_multipler = 200.0 / parent_clip->sequence->width;
161     double y_percent_multipler = 200.0 / parent_clip->sequence->height;
162 
163     top_left_gizmo->x_field_multi1 = -x_percent_multipler;
164     top_left_gizmo->y_field_multi1 = -y_percent_multipler;
165     top_center_gizmo->y_field_multi1 = -y_percent_multipler;
166     top_right_gizmo->x_field_multi1 = x_percent_multipler;
167     top_right_gizmo->y_field_multi1 = -y_percent_multipler;
168     bottom_left_gizmo->x_field_multi1 = -x_percent_multipler;
169     bottom_left_gizmo->y_field_multi1 = y_percent_multipler;
170     bottom_center_gizmo->y_field_multi1 = y_percent_multipler;
171     bottom_right_gizmo->x_field_multi1 = x_percent_multipler;
172     bottom_right_gizmo->y_field_multi1 = y_percent_multipler;
173     left_center_gizmo->x_field_multi1 = -x_percent_multipler;
174     right_center_gizmo->x_field_multi1 = x_percent_multipler;
175     rotate_gizmo->x_field_multi1 = x_percent_multipler;
176 
177   }
178 }
179 
toggle_uniform_scale(bool enabled)180 void TransformEffect::toggle_uniform_scale(bool enabled) {
181   scale_y->SetEnabled(!enabled);
182 
183   top_center_gizmo->y_field1 = enabled ? scale_x : scale_y;
184   bottom_center_gizmo->y_field1 = enabled ? scale_x : scale_y;
185   top_left_gizmo->y_field1 = enabled ? nullptr : scale_y;
186   top_right_gizmo->y_field1 = enabled ? nullptr : scale_y;
187   bottom_left_gizmo->y_field1 = enabled ? nullptr : scale_y;
188   bottom_right_gizmo->y_field1 = enabled ? nullptr : scale_y;
189 }
190 
process_coords(double timecode,GLTextureCoords & coords,int)191 void TransformEffect::process_coords(double timecode, GLTextureCoords& coords, int) {
192   // position
193   glTranslated(position_x->GetDoubleAt(timecode)-(parent_clip->sequence->width/2),
194                position_y->GetDoubleAt(timecode)-(parent_clip->sequence->height/2),
195                0);
196 
197   // anchor point
198   int anchor_x_offset = qRound(anchor_x_box->GetDoubleAt(timecode));
199   int anchor_y_offset = qRound(anchor_y_box->GetDoubleAt(timecode));
200   coords.vertexTopLeftX -= anchor_x_offset;
201   coords.vertexTopRightX -= anchor_x_offset;
202   coords.vertexBottomLeftX -= anchor_x_offset;
203   coords.vertexBottomRightX -= anchor_x_offset;
204   coords.vertexTopLeftY -= anchor_y_offset;
205   coords.vertexTopRightY -= anchor_y_offset;
206   coords.vertexBottomLeftY -= anchor_y_offset;
207   coords.vertexBottomRightY -= anchor_y_offset;
208 
209   // rotation
210   glRotated(rotation->GetDoubleAt(timecode), 0, 0, 1);
211 
212   // scale
213   double sx = scale_x->GetDoubleAt(timecode)*0.01;
214   double sy = (uniform_scale_field->GetBoolAt(timecode)) ? sx : scale_y->GetDoubleAt(timecode)*0.01;
215   glScaled(sx, sy, 1);
216 
217   // blend mode
218   coords.blendmode = blend_mode_box->GetValueAt(timecode).toInt();
219 
220   // opacity
221   coords.opacity *= float(opacity->GetDoubleAt(timecode)*0.01);
222 }
223 
gizmo_draw(double,GLTextureCoords & coords)224 void TransformEffect::gizmo_draw(double, GLTextureCoords& coords) {
225   top_left_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY);
226   top_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopLeftX, coords.vertexTopRightX, 0.5), lerp(coords.vertexTopLeftY, coords.vertexTopRightY, 0.5));
227   top_right_gizmo->world_pos[0] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY);
228   right_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexTopRightX, coords.vertexBottomRightX, 0.5), lerp(coords.vertexTopRightY, coords.vertexBottomRightY, 0.5));
229   bottom_right_gizmo->world_pos[0] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY);
230   bottom_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomRightX, coords.vertexBottomLeftX, 0.5), lerp(coords.vertexBottomRightY, coords.vertexBottomLeftY, 0.5));
231   bottom_left_gizmo->world_pos[0] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY);
232   left_center_gizmo->world_pos[0] = QPoint(lerp(coords.vertexBottomLeftX, coords.vertexTopLeftX, 0.5), lerp(coords.vertexBottomLeftY, coords.vertexTopLeftY, 0.5));
233 
234   rotate_gizmo->world_pos[0] = QPoint(lerp(top_center_gizmo->world_pos[0].x(), bottom_center_gizmo->world_pos[0].x(), -0.1), lerp(top_center_gizmo->world_pos[0].y(), bottom_center_gizmo->world_pos[0].y(), -0.1));
235 
236   rect_gizmo->world_pos[0] = QPoint(coords.vertexTopLeftX, coords.vertexTopLeftY);
237   rect_gizmo->world_pos[1] = QPoint(coords.vertexTopRightX, coords.vertexTopRightY);
238   rect_gizmo->world_pos[2] = QPoint(coords.vertexBottomRightX, coords.vertexBottomRightY);
239   rect_gizmo->world_pos[3] = QPoint(coords.vertexBottomLeftX, coords.vertexBottomLeftY);
240 }
241