30 #include <QPainterPath>
32 #ifdef USE_IMAGEMAGICK
43 struct CompositeChoice {
const char* name;
CompositeType value; };
44 const CompositeChoice composite_choices[] = {
67 const int composite_choices_count =
sizeof(composite_choices)/
sizeof(CompositeChoice);
86 reader_orientation_mode = ReaderOrientationMode::Reader;
108 wave_color =
Color((
unsigned char)0, (
unsigned char)123, (
unsigned char)255, (
unsigned char)255);
133 parentTrackedObject =
nullptr;
134 parentClipObject = NULL;
164 const auto rotate_meta = reader->
info.
metadata.find(
"rotate");
171 float rotate_angle = 0.0f;
173 rotate_angle = strtof(rotate_meta->second.c_str(),
nullptr);
174 }
catch (
const std::exception& e) {
181 auto has_default_scale = [](
const Keyframe& kf) {
182 return kf.GetCount() == 1 && fabs(kf.GetPoint(0).co.Y - 1.0) < 0.00001;
188 if (fabs(rotate_angle) < 0.0001f)
191 float w =
static_cast<float>(reader->
info.
width);
192 float h =
static_cast<float>(reader->
info.
height);
193 if (w <= 0.0f || h <= 0.0f)
196 float rad = rotate_angle *
static_cast<float>(M_PI) / 180.0f;
198 float new_width = fabs(w * cos(rad)) + fabs(h * sin(rad));
199 float new_height = fabs(w * sin(rad)) + fabs(h * cos(rad));
200 if (new_width <= 0.0f || new_height <= 0.0f)
203 float uniform_scale = std::min(w / new_width, h / new_height);
210 Clip::Clip() : resampler(NULL), reader(NULL), allocated_reader(NULL), is_open(false)
217 Clip::Clip(
ReaderBase* new_reader) : resampler(NULL), reader(new_reader), allocated_reader(NULL), is_open(false)
236 Clip::Clip(std::string
path) : resampler(NULL), reader(NULL), allocated_reader(NULL), is_open(false)
246 allocated_reader = reader;
255 std::string ext = get_file_extension(
path);
256 std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
259 if (ext==
"avi" || ext==
"flac" || ext==
"mov" || ext==
"mkv" || ext==
"mpg" || ext==
"mpeg" || ext==
"mp3" || ext==
"mp4" || ext==
"mts" ||
260 ext==
"ogg" || ext==
"wav" || ext==
"wmv" || ext==
"webm" || ext==
"vob" || ext==
"gif" ||
path.find(
"%") != std::string::npos)
293 if (allocated_reader) {
294 delete allocated_reader;
295 allocated_reader = NULL;
315 if (parentTimeline) {
317 std::shared_ptr<openshot::TrackedObjectBase> trackedObject = parentTimeline->
GetTrackedObject(object_id);
318 Clip* clipObject = parentTimeline->
GetClip(object_id);
323 parentClipObject = NULL;
325 else if (clipObject) {
327 parentTrackedObject =
nullptr;
334 parentTrackedObject = trackedObject;
339 parentClipObject = clipObject;
347 bool is_same_reader =
false;
348 if (new_reader && allocated_reader) {
349 if (new_reader->
Name() ==
"FrameMapper") {
352 if (allocated_reader == clip_mapped_reader->
Reader()) {
353 is_same_reader =
true;
358 if (allocated_reader && !is_same_reader) {
360 allocated_reader->
Close();
361 delete allocated_reader;
363 allocated_reader = NULL;
386 throw ReaderClosed(
"No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
407 throw ReaderClosed(
"No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
413 if (is_open && reader) {
438 throw ReaderClosed(
"No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
464 return GetFrame(NULL, clip_frame_number, NULL);
469 std::shared_ptr<Frame>
Clip::GetFrame(std::shared_ptr<openshot::Frame> background_frame, int64_t clip_frame_number)
472 return GetFrame(background_frame, clip_frame_number, NULL);
480 throw ReaderClosed(
"The Clip is closed. Call Open() before calling this method.");
485 std::shared_ptr<Frame> frame = NULL;
488 frame = GetOrCreateFrame(clip_frame_number);
491 int64_t timeline_frame_number = clip_frame_number;
492 QSize timeline_size(frame->GetWidth(), frame->GetHeight());
493 if (background_frame) {
495 timeline_frame_number = background_frame->number;
496 timeline_size.setWidth(background_frame->GetWidth());
497 timeline_size.setHeight(background_frame->GetHeight());
501 apply_timemapping(frame);
504 apply_waveform(frame, timeline_size);
507 apply_effects(frame, timeline_frame_number, options,
true);
510 apply_keyframes(frame, timeline_size);
513 apply_effects(frame, timeline_frame_number, options,
false);
518 if (!background_frame) {
519 background_frame = std::make_shared<Frame>(frame->number, frame->GetWidth(), frame->GetHeight(),
520 "#00000000", frame->GetAudioSamplesCount(),
521 frame->GetAudioChannelsCount());
523 apply_background(frame, background_frame,
false);
528 if (!background_frame) {
533 auto output = std::make_shared<Frame>(*frame.get());
534 apply_background(output, background_frame,
true);
539 throw ReaderClosed(
"No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
546 for (
const auto& effect : effects) {
547 if (effect->Id() ==
id) {
556 if (!parentObjectId.empty() && (!parentClipObject && !parentTrackedObject)) {
560 return parentClipObject;
565 if (!parentObjectId.empty() && (!parentClipObject && !parentTrackedObject)) {
569 return parentTrackedObject;
573 std::string Clip::get_file_extension(std::string
path)
576 const auto dot_pos =
path.find_last_of(
'.');
577 if (dot_pos == std::string::npos || dot_pos + 1 >=
path.size()) {
578 return std::string();
581 return path.substr(dot_pos + 1);
585 void Clip::apply_timemapping(std::shared_ptr<Frame> frame)
590 throw ReaderClosed(
"No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
595 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
597 int64_t clip_frame_number = frame->number;
598 int64_t new_frame_number = adjust_frame_number_minimum(
time.
GetLong(clip_frame_number));
615 int source_sample_count = round(target_sample_count * fabs(delta));
621 location.
frame = new_frame_number;
636 init_samples.clear();
637 resampler->
SetBuffer(&init_samples, 1.0);
645 if (source_sample_count <= 0) {
647 frame->AddAudioSilence(target_sample_count);
653 source_samples->clear();
656 int remaining_samples = source_sample_count;
658 while (remaining_samples > 0) {
659 std::shared_ptr<Frame> source_frame = GetOrCreateFrame(location.
frame,
false);
660 int frame_sample_count = source_frame->GetAudioSamplesCount() - location.
sample_start;
663 if (
auto *fm =
dynamic_cast<FrameMapper*
>(reader)) {
664 fm->SetDirectionHint(is_increasing);
666 source_frame->SetAudioDirection(is_increasing);
668 if (frame_sample_count == 0) {
678 if (remaining_samples - frame_sample_count >= 0) {
680 for (
int channel = 0; channel < source_frame->GetAudioChannelsCount(); channel++) {
681 source_samples->addFrom(channel, source_pos, source_frame->GetAudioSamples(channel) + location.
sample_start, frame_sample_count, 1.0f);
689 remaining_samples -= frame_sample_count;
690 source_pos += frame_sample_count;
694 for (
int channel = 0; channel < source_frame->GetAudioChannelsCount(); channel++) {
695 source_samples->addFrom(channel, source_pos, source_frame->GetAudioSamples(channel) + location.
sample_start, remaining_samples, 1.0f);
698 remaining_samples = 0;
699 source_pos += remaining_samples;
706 frame->AddAudioSilence(target_sample_count);
708 if (source_sample_count != target_sample_count) {
710 double resample_ratio = double(source_sample_count) / double(target_sample_count);
711 resampler->
SetBuffer(source_samples, resample_ratio);
719 frame->AddAudio(
true, channel, 0, resampled_buffer->getReadPointer(channel, 0), std::min(resampled_buffer->getNumSamples(), target_sample_count), 1.0f);
725 frame->AddAudio(
true, channel, 0, source_samples->getReadPointer(channel, 0), target_sample_count, 1.0f);
730 delete source_samples;
738 int64_t Clip::adjust_frame_number_minimum(int64_t frame_number)
741 if (frame_number < 1)
749 std::shared_ptr<Frame> Clip::GetOrCreateFrame(int64_t number,
bool enable_time)
753 int64_t clip_frame_number = adjust_frame_number_minimum(number);
754 bool is_increasing =
true;
759 const int64_t time_frame_number = adjust_frame_number_minimum(
time.
GetLong(clip_frame_number));
760 if (
auto *fm =
dynamic_cast<FrameMapper*
>(reader)) {
762 fm->SetDirectionHint(is_increasing);
764 clip_frame_number = time_frame_number;
769 "Clip::GetOrCreateFrame (from reader)",
770 "number", number,
"clip_frame_number", clip_frame_number);
773 auto reader_frame = reader->
GetFrame(clip_frame_number);
776 reader_frame->number = number;
777 reader_frame->SetAudioDirection(is_increasing);
783 auto reader_copy = std::make_shared<Frame>(*reader_frame.get());
786 reader_copy->AddColor(QColor(Qt::transparent));
790 reader_copy->AddAudioSilence(reader_copy->GetAudioSamplesCount());
806 "Clip::GetOrCreateFrame (create blank)",
808 "estimated_samples_in_frame", estimated_samples_in_frame);
811 auto new_frame = std::make_shared<Frame>(
813 "#000000", estimated_samples_in_frame, reader->
info.
channels);
816 new_frame->AddAudioSilence(estimated_samples_in_frame);
832 root[
"id"] =
add_property_json(
"ID", 0.0,
"string",
Id(), NULL, -1, -1,
true, requested_frame);
833 root[
"position"] =
add_property_json(
"Position",
Position(),
"float",
"", NULL, 0, 30 * 60 * 60 * 48,
false, requested_frame);
835 root[
"start"] =
add_property_json(
"Start",
Start(),
"float",
"", NULL, 0, 30 * 60 * 60 * 48,
false, requested_frame);
836 root[
"end"] =
add_property_json(
"End",
End(),
"float",
"", NULL, 0, 30 * 60 * 60 * 48,
false, requested_frame);
837 root[
"duration"] =
add_property_json(
"Duration", Duration(),
"float",
"", NULL, 0, 30 * 60 * 60 * 48,
true, requested_frame);
842 root[
"composite"] =
add_property_json(
"Composite",
composite,
"int",
"", NULL, 0, composite_choices_count - 1,
false, requested_frame);
843 root[
"waveform"] =
add_property_json(
"Waveform", waveform,
"int",
"", NULL, 0, 1,
false, requested_frame);
845 root[
"parentObjectId"] =
add_property_json(
"Parent", 0.0,
"string", parentObjectId, NULL, -1, -1,
false, requested_frame);
876 for (
int i = 0; i < composite_choices_count; ++i)
895 if (parentClipObject)
900 double timeline_frame_number = requested_frame + clip_start_position - clip_start_frame;
903 float parentObject_location_x = parentClipObject->
location_x.
GetValue(timeline_frame_number);
904 float parentObject_location_y = parentClipObject->
location_y.
GetValue(timeline_frame_number);
905 float parentObject_scale_x = parentClipObject->
scale_x.
GetValue(timeline_frame_number);
906 float parentObject_scale_y = parentClipObject->
scale_y.
GetValue(timeline_frame_number);
907 float parentObject_shear_x = parentClipObject->
shear_x.
GetValue(timeline_frame_number);
908 float parentObject_shear_y = parentClipObject->
shear_y.
GetValue(timeline_frame_number);
909 float parentObject_rotation = parentClipObject->
rotation.
GetValue(timeline_frame_number);
912 root[
"location_x"] =
add_property_json(
"Location X", parentObject_location_x,
"float",
"", &
location_x, -1.0, 1.0,
false, requested_frame);
913 root[
"location_y"] =
add_property_json(
"Location Y", parentObject_location_y,
"float",
"", &
location_y, -1.0, 1.0,
false, requested_frame);
914 root[
"scale_x"] =
add_property_json(
"Scale X", parentObject_scale_x,
"float",
"", &
scale_x, 0.0, 1.0,
false, requested_frame);
915 root[
"scale_y"] =
add_property_json(
"Scale Y", parentObject_scale_y,
"float",
"", &
scale_y, 0.0, 1.0,
false, requested_frame);
916 root[
"rotation"] =
add_property_json(
"Rotation", parentObject_rotation,
"float",
"", &
rotation, -360, 360,
false, requested_frame);
917 root[
"shear_x"] =
add_property_json(
"Shear X", parentObject_shear_x,
"float",
"", &
shear_x, -1.0, 1.0,
false, requested_frame);
918 root[
"shear_y"] =
add_property_json(
"Shear Y", parentObject_shear_y,
"float",
"", &
shear_y, -1.0, 1.0,
false, requested_frame);
960 return root.toStyledString();
968 root[
"parentObjectId"] = parentObjectId;
970 root[
"scale"] =
scale;
975 root[
"waveform"] = waveform;
976 root[
"waveform_mode"] = waveform_mode;
977 switch (reader_orientation_mode) {
978 case ReaderOrientationMode::LegacyClipTransform:
979 root[
"reader_orientation_mode"] =
"legacy_clip_transform";
981 case ReaderOrientationMode::Reader:
983 root[
"reader_orientation_mode"] =
"reader";
1015 root[
"effects"] = Json::Value(Json::arrayValue);
1018 for (
auto existing_effect : effects)
1020 root[
"effects"].append(existing_effect->JsonValue());
1026 root[
"reader"] = Json::Value(Json::objectValue);
1042 catch (
const std::exception& e)
1045 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
1051 auto ensure_default_keyframe = [](
Keyframe& kf,
double default_value) {
1062 if (root[
"reader_orientation_mode"].isNull()) {
1063 reader_orientation_mode = ReaderOrientationMode::LegacyClipTransform;
1065 const std::string mode = root[
"reader_orientation_mode"].asString();
1066 if (mode ==
"legacy_clip_transform") {
1067 reader_orientation_mode = ReaderOrientationMode::LegacyClipTransform;
1069 reader_orientation_mode = ReaderOrientationMode::Reader;
1074 if (!root[
"parentObjectId"].isNull()){
1075 parentObjectId = root[
"parentObjectId"].asString();
1076 if (parentObjectId.size() > 0 && parentObjectId !=
""){
1079 parentTrackedObject =
nullptr;
1080 parentClipObject = NULL;
1083 if (!root[
"gravity"].isNull())
1085 if (!root[
"scale"].isNull())
1087 if (!root[
"anchor"].isNull())
1089 if (!root[
"display"].isNull())
1091 if (!root[
"mixing"].isNull())
1093 if (!root[
"composite"].isNull())
1095 if (!root[
"waveform"].isNull())
1096 waveform = root[
"waveform"].asBool();
1097 if (!root[
"waveform_mode"].isNull())
1098 waveform_mode = root[
"waveform_mode"].asInt();
1099 if (!root[
"scale_x"].isNull())
1101 if (!root[
"scale_y"].isNull())
1103 if (!root[
"location_x"].isNull())
1105 if (!root[
"location_y"].isNull())
1107 if (!root[
"alpha"].isNull())
1109 if (!root[
"corner_radius"].isNull())
1111 if (!root[
"margin"].isNull())
1113 if (!root[
"rotation"].isNull())
1115 if (!root[
"time"].isNull())
1117 if (!root[
"volume"].isNull())
1119 if (!root[
"wave_color"].isNull())
1121 if (!root[
"shear_x"].isNull())
1123 if (!root[
"shear_y"].isNull())
1125 if (!root[
"origin_x"].isNull())
1127 if (!root[
"origin_y"].isNull())
1129 if (!root[
"channel_filter"].isNull())
1131 if (!root[
"channel_mapping"].isNull())
1133 if (!root[
"has_audio"].isNull())
1135 if (!root[
"has_video"].isNull())
1137 if (!root[
"perspective_c1_x"].isNull())
1139 if (!root[
"perspective_c1_y"].isNull())
1141 if (!root[
"perspective_c2_x"].isNull())
1143 if (!root[
"perspective_c2_y"].isNull())
1145 if (!root[
"perspective_c3_x"].isNull())
1147 if (!root[
"perspective_c3_y"].isNull())
1149 if (!root[
"perspective_c4_x"].isNull())
1151 if (!root[
"perspective_c4_y"].isNull())
1156 ensure_default_keyframe(
scale_x, 1.0);
1157 ensure_default_keyframe(
scale_y, 1.0);
1160 ensure_default_keyframe(
origin_x, 0.5);
1161 ensure_default_keyframe(
origin_y, 0.5);
1162 ensure_default_keyframe(
rotation, 0.0);
1164 ensure_default_keyframe(
margin, 0.0);
1165 if (!root[
"effects"].isNull()) {
1171 for (
const auto existing_effect : root[
"effects"]) {
1173 if (existing_effect.isNull()) {
1179 if (!existing_effect[
"type"].isNull()) {
1182 if ( (e =
EffectInfo().CreateEffect(existing_effect[
"type"].asString()))) {
1193 if (!root[
"reader"].isNull())
1195 if (!root[
"reader"][
"type"].isNull())
1198 bool already_open =
false;
1202 already_open = reader->
IsOpen();
1209 std::string type = root[
"reader"][
"type"].asString();
1211 if (type ==
"FFmpegReader") {
1217 }
else if (type ==
"QtImageReader") {
1223 #ifdef USE_IMAGEMAGICK
1224 }
else if (type ==
"ImageReader") {
1227 reader =
new ImageReader(root[
"reader"][
"path"].asString(),
false);
1230 }
else if (type ==
"TextReader") {
1237 }
else if (type ==
"ChunkReader") {
1243 }
else if (type ==
"DummyReader") {
1249 }
else if (type ==
"Timeline") {
1260 allocated_reader = reader;
1271 final_cache.
Clear();
1275 void Clip::sort_effects()
1288 effects.push_back(effect);
1297 effect->ParentTimeline(parentTimeline);
1304 if (parentTimeline){
1306 effect->ParentTimeline(parentTimeline);
1312 std::shared_ptr<TrackedObjectBBox> trackedObjectBBox = std::static_pointer_cast<TrackedObjectBBox>(trackedObject.second);
1315 trackedObjectBBox->ParentClip(
this);
1325 final_cache.
Clear();
1331 effects.remove(effect);
1334 final_cache.
Clear();
1338 void Clip::apply_background(std::shared_ptr<openshot::Frame> frame,
1339 std::shared_ptr<openshot::Frame> background_frame,
1340 bool update_frame_image) {
1342 std::shared_ptr<QImage> background_canvas = background_frame->GetImage();
1343 QPainter painter(background_canvas.get());
1346 painter.setCompositionMode(
static_cast<QPainter::CompositionMode
>(
composite));
1347 painter.drawImage(0, 0, *frame->GetImage());
1352 if (update_frame_image)
1353 frame->AddImage(background_canvas);
1357 void Clip::apply_effects(std::shared_ptr<Frame> frame, int64_t timeline_frame_number,
TimelineInfoStruct* options,
bool before_keyframes)
1359 for (
auto effect : effects)
1362 if (effect->info.apply_before_clip && before_keyframes) {
1363 effect->ProcessFrame(frame, frame->number);
1364 }
else if (!effect->info.apply_before_clip && !before_keyframes) {
1365 effect->ProcessFrame(frame, frame->number);
1369 if (
timeline != NULL && options != NULL) {
1378 bool Clip::isNear(
double a,
double b)
1380 return fabs(a - b) < 0.000001;
1384 void Clip::apply_keyframes(std::shared_ptr<Frame> frame, QSize timeline_size) {
1386 if (!frame->has_image_data) {
1392 std::shared_ptr<QImage> source_image = frame->GetImage();
1393 std::shared_ptr<QImage> background_canvas = std::make_shared<QImage>(timeline_size.width(),
1394 timeline_size.height(),
1395 QImage::Format_RGBA8888_Premultiplied);
1396 background_canvas->fill(QColor(Qt::transparent));
1399 QTransform transform = get_transform(frame, background_canvas->width(), background_canvas->height());
1402 QPainter painter(background_canvas.get());
1403 painter.setRenderHint(QPainter::TextAntialiasing,
true);
1404 if (!transform.isIdentity()) {
1405 painter.setRenderHint(QPainter::SmoothPixmapTransform,
true);
1408 painter.setTransform(transform);
1411 painter.setCompositionMode(
static_cast<QPainter::CompositionMode
>(
composite));
1415 const double corner_radius_value = std::max(0.0, std::min(0.5,
corner_radius.
GetValue(frame->number)));
1416 if (corner_radius_value > 0.0f) {
1417 painter.setRenderHint(QPainter::Antialiasing,
true);
1418 const double radius_pixels = corner_radius_value
1419 * std::min(source_image->width(), source_image->height());
1420 QPainterPath clip_path;
1421 clip_path.addRoundedRect(
1422 QRectF(0, 0, source_image->width(), source_image->height()),
1425 painter.setClipPath(clip_path);
1427 if (alpha_value != 1.0f) {
1428 painter.setOpacity(alpha_value);
1429 painter.drawImage(0, 0, *source_image);
1431 painter.setOpacity(1.0);
1433 painter.drawImage(0, 0, *source_image);
1441 std::stringstream frame_number_str;
1448 frame_number_str << frame->number;
1461 painter.setPen(QColor(
"#ffffff"));
1462 painter.drawText(20, 20, QString(frame_number_str.str().c_str()));
1468 frame->AddImage(background_canvas);
1472 void Clip::apply_waveform(std::shared_ptr<Frame> frame, QSize timeline_size) {
1481 "frame->number", frame->number,
1482 "Waveform()", Waveform(),
1483 "width", timeline_size.width(),
1484 "height", timeline_size.height());
1494 auto visual_frame = std::make_shared<Frame>(*frame.get());
1495 visual_frame->AddImage(std::make_shared<QImage>(
1496 timeline_size.width(), timeline_size.height(), QImage::Format_RGBA8888_Premultiplied));
1497 visual_frame->GetImage()->fill(Qt::transparent);
1503 static_cast<unsigned char>(red),
1504 static_cast<unsigned char>(green),
1505 static_cast<unsigned char>(blue),
1506 static_cast<unsigned char>(
alpha));
1517 visualization.
GetFrame(visual_frame, frame->number);
1519 frame->AddImage(visual_frame->GetImage());
1523 QSize Clip::scale_size(QSize source_size,
ScaleType source_scale,
int target_width,
int target_height) {
1524 switch (source_scale)
1527 source_size.scale(target_width, target_height, Qt::KeepAspectRatio);
1531 source_size.scale(target_width, target_height, Qt::IgnoreAspectRatio);
1535 source_size.scale(target_width, target_height, Qt::KeepAspectRatioByExpanding);;
1544 QTransform Clip::get_transform(std::shared_ptr<Frame> frame,
int width,
int height)
1547 std::shared_ptr<QImage> source_image = frame->GetImage();
1549 const double margin_value = std::max(0.0, std::min(0.5,
margin.
GetValue(frame->number)));
1550 const double margin_pixels = margin_value * std::min(width, height);
1551 const double layout_x = margin_pixels;
1552 const double layout_y = margin_pixels;
1553 const double layout_width = std::max(1.0, width - (margin_pixels * 2.0));
1554 const double layout_height = std::max(1.0, height - (margin_pixels * 2.0));
1557 QSize source_size = scale_size(source_image->size(),
scale, layout_width, layout_height);
1560 float parentObject_location_x = 0.0;
1561 float parentObject_location_y = 0.0;
1562 float parentObject_scale_x = 1.0;
1563 float parentObject_scale_y = 1.0;
1564 float parentObject_shear_x = 0.0;
1565 float parentObject_shear_y = 0.0;
1566 float parentObject_rotation = 0.0;
1572 long parent_frame_number = frame->number + parent_start_offset;
1575 parentObject_location_x = parentClipObject->
location_x.
GetValue(parent_frame_number);
1576 parentObject_location_y = parentClipObject->
location_y.
GetValue(parent_frame_number);
1577 parentObject_scale_x = parentClipObject->
scale_x.
GetValue(parent_frame_number);
1578 parentObject_scale_y = parentClipObject->
scale_y.
GetValue(parent_frame_number);
1579 parentObject_shear_x = parentClipObject->
shear_x.
GetValue(parent_frame_number);
1580 parentObject_shear_y = parentClipObject->
shear_y.
GetValue(parent_frame_number);
1581 parentObject_rotation = parentClipObject->
rotation.
GetValue(parent_frame_number);
1587 Clip* parentClip = (
Clip*) parentTrackedObject->ParentClip();
1592 long parent_frame_number = frame->number + parent_start_offset;
1595 std::map<std::string, float> trackedObjectProperties = parentTrackedObject->GetBoxValues(parent_frame_number);
1599 parentClip->
scale, width, height);
1602 int trackedWidth = trackedObjectProperties[
"w"] * trackedObjectProperties[
"sx"] * parent_size.width() *
1604 int trackedHeight = trackedObjectProperties[
"h"] * trackedObjectProperties[
"sy"] * parent_size.height() *
1608 source_size = scale_size(source_size,
scale, trackedWidth, trackedHeight);
1611 parentObject_location_x = parentClip->
location_x.
GetValue(parent_frame_number) + ((trackedObjectProperties[
"cx"] - 0.5) * parentClip->
scale_x.
GetValue(parent_frame_number));
1612 parentObject_location_y = parentClip->
location_y.
GetValue(parent_frame_number) + ((trackedObjectProperties[
"cy"] - 0.5) * parentClip->
scale_y.
GetValue(parent_frame_number));
1613 parentObject_rotation = trackedObjectProperties[
"r"] + parentClip->
rotation.
GetValue(parent_frame_number);
1626 if(parentObject_scale_x != 0.0 && parentObject_scale_y != 0.0){
1627 sx*= parentObject_scale_x;
1628 sy*= parentObject_scale_y;
1631 float scaled_source_width = source_size.width() * sx;
1632 float scaled_source_height = source_size.height() * sy;
1642 x = layout_x + ((layout_width - scaled_source_width) / 2.0);
1646 x = layout_x + layout_width - scaled_source_width;
1651 y = layout_y + ((layout_height - scaled_source_height) / 2.0);
1654 x = layout_x + ((layout_width - scaled_source_width) / 2.0);
1655 y = layout_y + ((layout_height - scaled_source_height) / 2.0);
1658 x = layout_x + layout_width - scaled_source_width;
1659 y = layout_y + ((layout_height - scaled_source_height) / 2.0);
1663 y = layout_y + (layout_height - scaled_source_height);
1666 x = layout_x + ((layout_width - scaled_source_width) / 2.0);
1667 y = layout_y + (layout_height - scaled_source_height);
1670 x = layout_x + layout_width - scaled_source_width;
1671 y = layout_y + (layout_height - scaled_source_height);
1677 "Clip::get_transform (Gravity)",
1678 "frame->number", frame->number,
1679 "source_clip->gravity",
gravity,
1680 "scaled_source_width", scaled_source_width,
1681 "scaled_source_height", scaled_source_height);
1683 QTransform transform;
1687 float location_x_value =
location_x.
GetValue(frame->number) + parentObject_location_x;
1688 float location_y_value =
location_y.
GetValue(frame->number) + parentObject_location_y;
1689 auto location_offset = [](
float location,
float anchored_position,
float canvas_size,
float clip_size) {
1690 if (location < 0.0f) {
1691 return location * (anchored_position + clip_size);
1693 return location * (canvas_size - anchored_position);
1695 x += location_offset(location_x_value, x - layout_x, layout_width, scaled_source_width);
1696 y += location_offset(location_y_value, y - layout_y, layout_height, scaled_source_height);
1697 float shear_x_value =
shear_x.
GetValue(frame->number) + parentObject_shear_x;
1698 float shear_y_value =
shear_y.
GetValue(frame->number) + parentObject_shear_y;
1704 "Clip::get_transform (Build QTransform - if needed)",
1705 "frame->number", frame->number,
1708 "sx", sx,
"sy", sy);
1710 if (!isNear(x, 0) || !isNear(y, 0)) {
1712 transform.translate(x, y);
1714 if (!isNear(r, 0) || !isNear(shear_x_value, 0) || !isNear(shear_y_value, 0)) {
1716 float origin_x_offset = (scaled_source_width * origin_x_value);
1717 float origin_y_offset = (scaled_source_height * origin_y_value);
1718 transform.translate(origin_x_offset, origin_y_offset);
1719 transform.rotate(r);
1720 transform.shear(shear_x_value, shear_y_value);
1721 transform.translate(-origin_x_offset,-origin_y_offset);
1724 float source_width_scale = (float(source_size.width()) / float(source_image->width())) * sx;
1725 float source_height_scale = (float(source_size.height()) / float(source_image->height())) * sy;
1726 if (!isNear(source_width_scale, 1.0) || !isNear(source_height_scale, 1.0)) {
1727 transform.scale(source_width_scale, source_height_scale);
1734 int64_t Clip::adjust_timeline_framenumber(int64_t clip_frame_number) {
1751 int64_t frame_number = clip_frame_number + clip_start_position - clip_start_frame;
1753 return frame_number;