diff --git a/app/core.cpp b/app/core.cpp index 07684d63c..72d84f5f5 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -824,7 +824,8 @@ void Core::SaveProjectInternal(Project* project, const QString& override_filenam return; #endif } else { - psm = new ProjectSaveTask(project); + bool use_compression = !project->filename().endsWith(QStringLiteral(".ovexml"), Qt::CaseInsensitive); + psm = new ProjectSaveTask(project, use_compression); if (!override_filename.isEmpty()) { // Set override filename if provided @@ -1170,24 +1171,35 @@ bool Core::CloseAllExceptActiveProject() QString Core::GetProjectFilter(bool include_any_filter) { - QString filters; + static const QVector< QPair > FILTERS = { + // Standard compressed Olive project + {tr("Olive Project"), QStringLiteral("ove")}, + // Uncompressed XML Olive project + {tr("Olive Project (Uncompressed XML)"), QStringLiteral("ovexml")}, + + // OpenTimelineIO project, if available #ifdef USE_OTIO + {tr("OpenTimelineIO"), QStringLiteral("otio")} +#endif + }; + + QStringList filters; + filters.reserve(FILTERS.size() + 1); + if (include_any_filter) { - filters.append(QStringLiteral("All Supported Projects (*.ove *.otio);;")); + QStringList combined; + for (auto it=FILTERS.cbegin(); it!=FILTERS.cend(); it++) { + combined.append(QStringLiteral("*.%1").arg(it->second)); + } + filters.append(QStringLiteral("%1 (%2)").arg(tr("All Supported Projects"), combined.join(' '))); } -#else - Q_UNUSED(include_any_filter) -#endif - // Append standard filter - filters.append(QStringLiteral("%1 (*.ove)").arg(tr("Olive Project"))); + for (auto it=FILTERS.cbegin(); it!=FILTERS.cend(); it++) { + filters.append(QStringLiteral("%1 (*.%2)").arg(it->first, it->second)); + } -#ifdef USE_OTIO - filters.append(QStringLiteral(";;%2 (*.otio)").arg(tr("OpenTimelineIO"))); -#endif - - return filters; + return filters.join(QStringLiteral(";;")); } QString Core::GetRecentProjectsFilePath() diff --git a/app/node/project/serializer/serializer.cpp b/app/node/project/serializer/serializer.cpp index 60ab3521b..aa4de715b 100644 --- a/app/node/project/serializer/serializer.cpp +++ b/app/node/project/serializer/serializer.cpp @@ -59,10 +59,21 @@ ProjectSerializer::Result ProjectSerializer::Load(Project *project, const QStrin { QFile project_file(filename); - if (project_file.open(QFile::ReadOnly | QFile::Text)) { - QXmlStreamReader reader(&project_file); + if (project_file.open(QFile::ReadOnly)) { + // Some project files are compressed, marked with "OVEC" at the beginning of the file. Check for + // that signature now. + std::unique_ptr reader; + QByteArray b = project_file.read(4); + if (!memcmp(b.data(), "OVEC", 4)) { + // File is compressed, decompress into memory + b = qUncompress(project_file.readAll()); + reader.reset(new QXmlStreamReader(b)); + } else { + project_file.seek(0); + reader.reset(new QXmlStreamReader(&project_file)); + } - Result inner_result = Load(project, &reader, type); + Result inner_result = Load(project, reader.get(), type); project_file.close(); @@ -70,9 +81,9 @@ ProjectSerializer::Result ProjectSerializer::Load(Project *project, const QStrin return inner_result; } - if (reader.hasError()) { + if (reader->hasError()) { Result r(kXmlError); - r.SetDetails(reader.errorString()); + r.SetDetails(reader->errorString()); return r; } else { return kSuccess; @@ -148,14 +159,15 @@ ProjectSerializer::Result ProjectSerializer::Paste(const QString &type) return res; } -ProjectSerializer::Result ProjectSerializer::Save(const SaveData &data, const QString &type) +ProjectSerializer::Result ProjectSerializer::Save(const SaveData &data, const QString &type, bool compress) { QString temp_save = FileFunctions::GetSafeTemporaryFilename(data.GetFilename()); QFile project_file(temp_save); - if (project_file.open(QFile::WriteOnly | QFile::Text)) { - QXmlStreamWriter writer(&project_file); + if (project_file.open(QFile::WriteOnly)) { + QByteArray b; + QXmlStreamWriter writer(&b); Result inner_result = Save(&writer, data, type); @@ -164,6 +176,13 @@ ProjectSerializer::Result ProjectSerializer::Save(const SaveData &data, const QS return r; } + if (compress) { + project_file.write("OVEC"); + project_file.write(qCompress(b)); + } else { + project_file.write(b); + } + project_file.close(); if (inner_result != kSuccess) { diff --git a/app/node/project/serializer/serializer.h b/app/node/project/serializer/serializer.h index 831c98d44..3847ba062 100644 --- a/app/node/project/serializer/serializer.h +++ b/app/node/project/serializer/serializer.h @@ -160,7 +160,7 @@ public: static Result Load(Project *project, QXmlStreamReader *read_device, const QString &type); static Result Paste(const QString &type); - static Result Save(const SaveData &data, const QString &type); + static Result Save(const SaveData &data, const QString &type, bool compress); static Result Save(QXmlStreamWriter *write_device, const SaveData &data, const QString &type); static Result Copy(const SaveData &data, const QString &type); diff --git a/app/shaders/yuv2rgb.frag b/app/shaders/yuv2rgb.frag index 7f0d87a16..49da773b7 100644 --- a/app/shaders/yuv2rgb.frag +++ b/app/shaders/yuv2rgb.frag @@ -27,15 +27,17 @@ void main() // Convert 0.0-1.0 to -0.5-0.5 yuv.gb -= (128.0/255.0); } else if (bits_per_pixel == 10) { + // Scale from 10-bit to 16-bit + yuv *= (65535.0/1023.0); + // Convert 0.0-1.0 to -0.5-0.5 yuv.gb -= (512.0/1023.0); - - yuv *= 64.0; } else if (bits_per_pixel == 12) { + // Scale from 12-bit to 16-bit + yuv *= (65535.0/4095.0); + // Convert 0.0-1.0 to -0.5-0.5 yuv.gb -= (2048.0/4095.0); - - yuv *= 16.0; } // Convert YUV limited range from 16-235 to 0-255 diff --git a/app/task/project/save/save.cpp b/app/task/project/save/save.cpp index 10446e8f1..0283c02f7 100644 --- a/app/task/project/save/save.cpp +++ b/app/task/project/save/save.cpp @@ -30,8 +30,9 @@ namespace olive { -ProjectSaveTask::ProjectSaveTask(Project *project) : - project_(project) +ProjectSaveTask::ProjectSaveTask(Project *project, bool use_compression) : + project_(project), + use_compression_(use_compression) { SetTitle(tr("Saving '%1'").arg(project->filename())); } @@ -42,7 +43,7 @@ bool ProjectSaveTask::Run() ProjectSerializer::SaveData data(project_, using_filename); - ProjectSerializer::Result result = ProjectSerializer::Save(data, QStringLiteral("project")); + ProjectSerializer::Result result = ProjectSerializer::Save(data, QStringLiteral("project"), use_compression_); bool success = false; diff --git a/app/task/project/save/save.h b/app/task/project/save/save.h index 6157b2d67..642d00e7f 100644 --- a/app/task/project/save/save.h +++ b/app/task/project/save/save.h @@ -30,7 +30,7 @@ class ProjectSaveTask : public Task { Q_OBJECT public: - ProjectSaveTask(Project* project); + ProjectSaveTask(Project* project, bool use_compression); Project* GetProject() const { @@ -50,6 +50,8 @@ private: QString override_filename_; + bool use_compression_; + }; } diff --git a/app/widget/manageddisplay/manageddisplay.cpp b/app/widget/manageddisplay/manageddisplay.cpp index 2e26be7c8..6a7886242 100644 --- a/app/widget/manageddisplay/manageddisplay.cpp +++ b/app/widget/manageddisplay/manageddisplay.cpp @@ -147,7 +147,7 @@ void ManagedDisplayWidget::ColorConfigChanged() return; } - SetColorTransform(color_manager_->GetCompliantColorSpace(color_transform_, true)); + SetColorTransform(color_manager_->GetCompliantColorSpace(color_transform_, false)); } ColorProcessorPtr ManagedDisplayWidget::color_service() diff --git a/app/widget/timelinewidget/view/timelineview.cpp b/app/widget/timelinewidget/view/timelineview.cpp index 5554a3677..d086fb657 100644 --- a/app/widget/timelinewidget/view/timelineview.cpp +++ b/app/widget/timelinewidget/view/timelineview.cpp @@ -475,45 +475,46 @@ void TimelineView::DrawBlock(QPainter *painter, bool foreground, Block *block, q // Draw clip thumbnails if (clip->GetTrackType() == Track::kVideo - && OLIVE_CONFIG("TimelineThumbnailMode").toInt() != Timeline::kThumbnailOff - && preview_rect.height() > r.height()/3) { - if (const FrameHashCache *thumbs = clip->thumbnails()) { - // Start thumbnails underneath clip name - preview_rect.adjust(0, text_total_height, 0, 0); + && OLIVE_CONFIG("TimelineThumbnailMode").toInt() != Timeline::kThumbnailOff) { + // Start thumbnails underneath clip name + preview_rect.adjust(0, text_total_height, 0, 0); - QRect thumb_rect; - painter->setRenderHint(QPainter::SmoothPixmapTransform); - painter->setClipRect(preview_rect); + if (preview_rect.height() > r.height()/3) { + if (const FrameHashCache *thumbs = clip->thumbnails()) { + QRect thumb_rect; + painter->setRenderHint(QPainter::SmoothPixmapTransform); + painter->setClipRect(preview_rect); - if (OLIVE_CONFIG("TimelineThumbnailMode") == Timeline::kThumbnailOn) { + if (OLIVE_CONFIG("TimelineThumbnailMode") == Timeline::kThumbnailOn) { + + Sequence *s = clip->track()->sequence(); + int width = s->GetVideoParams().width(); + int height = s->GetVideoParams().height(); + int start; + if (height > 0) { // Prevent divide by zero/invalid params + double scale = double(preview_rect.height())/double(height); + thumb_rect.setWidth(width * scale); + start = (((preview_rect.left() - int(qFloor(block_in))) / thumb_rect.width()) * thumb_rect.width()) + qFloor(block_in); + } else { + start = preview_rect.left(); + } + + for (int i=start; iparent()->GetVideoParams().frame_rate_as_time_base()) + media_in; + DrawThumbnail(painter, thumbs, time_here, i, preview_rect, &thumb_rect); + } - Sequence *s = clip->track()->sequence(); - int width = s->GetVideoParams().width(); - int height = s->GetVideoParams().height(); - int start; - if (height > 0) { // Prevent divide by zero/invalid params - double scale = double(preview_rect.height())/double(height); - thumb_rect.setWidth(width * scale); - start = (((preview_rect.left() - int(qFloor(block_in))) / thumb_rect.width()) * thumb_rect.width()) + qFloor(block_in); } else { - start = preview_rect.left(); + + rational time = clip->media_range().in(); + time = Timecode::snap_time_to_timebase(time, thumbs->GetTimebase(), Timecode::kFloor); + DrawThumbnail(painter, thumbs, time, block_left, preview_rect, &thumb_rect); + } - for (int i=start; iparent()->GetVideoParams().frame_rate_as_time_base()) + media_in; - DrawThumbnail(painter, thumbs, time_here, i, preview_rect, &thumb_rect); - } - - } else { - - rational time = clip->media_range().in(); - time = Timecode::snap_time_to_timebase(time, thumbs->GetTimebase(), Timecode::kFloor); - DrawThumbnail(painter, thumbs, time, block_left, preview_rect, &thumb_rect); + painter->setClipping(false); } - - painter->setClipping(false); - } } diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index c461743b9..294a98c87 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -1350,6 +1350,11 @@ void ViewerWidget::ShowContextMenu(const QPoint &pos) if (context_menu_widget_) { // Color options if (context_menu_widget_->color_manager() && color_menu_enabled_) { + { + Menu* ocio_colorspace_menu = context_menu_widget_->GetColorSpaceMenu(&menu); + menu.addMenu(ocio_colorspace_menu); + } + { Menu* ocio_display_menu = context_menu_widget_->GetDisplayMenu(&menu); menu.addMenu(ocio_display_menu);