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