// -------------------------------------------------------------------------------------------------------------------- // // This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License. // // // The Picture Settings View Model // // -------------------------------------------------------------------------------------------------------------------- namespace HandBrakeWPF.ViewModels { using System; using System.ComponentModel; using System.Globalization; using System.Linq; using System.Windows; using HandBrake.App.Core.Utilities; using HandBrake.Interop.Interop; using HandBrake.Interop.Interop.Interfaces.Model.Picture; using HandBrakeWPF.EventArgs; using HandBrakeWPF.Model; using HandBrakeWPF.Model.Filters; using HandBrakeWPF.Model.Picture; using HandBrakeWPF.Properties; using HandBrakeWPF.Services.Interfaces; using HandBrakeWPF.Services.Presets.Model; using HandBrakeWPF.Services.Scan.Model; using HandBrakeWPF.ViewModelItems.Filters; using HandBrakeWPF.ViewModels.Interfaces; using HandBrakeWPF.Views; using EncodeTask = Services.Encode.Model.EncodeTask; using Size = Model.Picture.Size; public class PictureSettingsViewModel : ViewModelBase, IPictureSettingsViewModel { private readonly IWindowManager windowManager; private bool heightControlEnabled = true; private string sourceInfo; private Size sourceParValues; private Size sourceResolution; private bool widthControlEnabled = true; private DelayedActionProcessor delayedPreviewprocessor = new DelayedActionProcessor(); private Title currentTitle; private Source scannedSource; private PictureSettingsResLimitModes selectedPictureSettingsResLimitMode; public PictureSettingsViewModel(IStaticPreviewViewModel staticPreviewViewModel, IWindowManager windowManager) { this.windowManager = windowManager; this.StaticPreviewViewModel = staticPreviewViewModel; this.StaticPreviewViewModel.SetPictureSettingsInstance(this); this.sourceResolution = new Size(0, 0); this.Task = new EncodeTask(); this.PaddingFilter = new PadFilter(this.Task, () => this.OnFilterChanged(null)); this.RotateFlipFilter = new RotateFlipFilter(this.Task, e => this.OnFlipRotateChanged(e)); this.Init(); } /* Events */ public event EventHandler TabStatusChanged; /* Properties */ public IStaticPreviewViewModel StaticPreviewViewModel { get; set; } public BindingList AnamorphicModes { get; } = new BindingList { AnamorphicMode.None, AnamorphicMode.Automatic, AnamorphicMode.Custom }; public PadFilter PaddingFilter { get; set; } public RotateFlipFilter RotateFlipFilter { get; set; } public string DisplaySize { get; set; } public string OutputAspect { get; set; } public bool HeightControlEnabled { get => this.heightControlEnabled; set { this.heightControlEnabled = value; this.NotifyOfPropertyChange(() => this.HeightControlEnabled); } } public string SourceInfo { get => this.sourceInfo; set { this.sourceInfo = value; this.NotifyOfPropertyChange(() => this.SourceInfo); } } public EncodeTask Task { get; set; } public bool WidthControlEnabled { get => this.widthControlEnabled; set { this.widthControlEnabled = value; this.NotifyOfPropertyChange(() => this.WidthControlEnabled); } } public int? MaxHeight { get => this.Task.MaxHeight; set { if (value != this.Task.MaxHeight) { this.Task.MaxHeight = value; this.NotifyOfPropertyChange(() => this.MaxHeight); this.OnTabStatusChanged(null); } } } public int? MaxWidth { get => this.Task.MaxWidth; set { if (value != this.Task.MaxWidth) { this.Task.MaxWidth = value; this.NotifyOfPropertyChange(() => this.MaxWidth); this.OnTabStatusChanged(null); } } } public BindingList ResolutionLimitModes => new BindingList { PictureSettingsResLimitModes.Size8K, PictureSettingsResLimitModes.Size4K, PictureSettingsResLimitModes.Size1080p, PictureSettingsResLimitModes.Size720p, PictureSettingsResLimitModes.Size576p, PictureSettingsResLimitModes.Size480p, PictureSettingsResLimitModes.Custom, }; public PictureSettingsResLimitModes SelectedPictureSettingsResLimitMode { get => this.selectedPictureSettingsResLimitMode; set { if (value == this.selectedPictureSettingsResLimitMode) { return; } this.selectedPictureSettingsResLimitMode = value; this.NotifyOfPropertyChange(() => this.SelectedPictureSettingsResLimitMode); this.IsCustomMaxRes = value == PictureSettingsResLimitModes.Custom; this.NotifyOfPropertyChange(() => this.IsCustomMaxRes); // Enforce the new limit ResLimit limit = EnumHelper.GetAttribute(value); if (value == PictureSettingsResLimitModes.Custom && this.Task.MaxWidth == null && this.Task.MaxHeight == null) { // Default to 4K if null! limit = EnumHelper.GetAttribute(PictureSettingsResLimitModes.Size4K); } if (limit != null) { this.Task.MaxWidth = limit.Width; this.Task.MaxHeight = limit.Height; } if (value == PictureSettingsResLimitModes.None) { this.Task.MaxWidth = null; this.Task.MaxHeight = null; } this.NotifyOfPropertyChange(() => this.MaxWidth); this.NotifyOfPropertyChange(() => this.MaxHeight); this.RecalculatePictureSettingsProperties(ChangedPictureField.ResolutionLimit); this.OnTabStatusChanged(null); } } public bool IsCustomMaxRes { get; private set; } public bool OptimalSize { get => this.Task.OptimalSize; set { this.Task.OptimalSize = value; this.UpdateVisibleControls(); this.Task.Width = this.sourceResolution.Width; this.NotifyOfPropertyChange(() => this.OptimalSize); this.RecalculatePictureSettingsProperties(ChangedPictureField.OptimalSize); } } public bool AllowUpscaling { get => this.Task.AllowUpscaling; set { this.Task.AllowUpscaling = value; this.UpdateVisibleControls(); this.RecalculatePictureSettingsProperties(ChangedPictureField.AllowUpscale); this.NotifyOfPropertyChange(() => this.AllowUpscaling); } } /* Task Properties */ public int CropBottom { get => this.Task.Cropping.Bottom; set { if (!ValidCropTB(value, this.CropTop)) { return; // Don't accept user input. } this.Task.Cropping.Bottom = value; this.NotifyOfPropertyChange(() => this.CropBottom); this.RecalculatePictureSettingsProperties(ChangedPictureField.Crop); } } public int CropLeft { get => this.Task.Cropping.Left; set { if (!ValidCropLR(value, this.CropRight)) { return; // Don't accept user input. } this.Task.Cropping.Left = value; this.NotifyOfPropertyChange(() => this.CropLeft); this.RecalculatePictureSettingsProperties(ChangedPictureField.Crop); } } public int CropRight { get => this.Task.Cropping.Right; set { if (!ValidCropLR(value, this.CropLeft)) { return; // Don't accept user input. } this.Task.Cropping.Right = value; this.NotifyOfPropertyChange(() => this.CropRight); this.RecalculatePictureSettingsProperties(ChangedPictureField.Crop); } } public int CropTop { get => this.Task.Cropping.Top; set { if (!ValidCropTB(value, this.CropBottom)) { return; // Don't accept user input. } this.Task.Cropping.Top = value; this.NotifyOfPropertyChange(() => this.CropTop); this.RecalculatePictureSettingsProperties(ChangedPictureField.Crop); } } public int MaxCropLR => currentTitle?.Resolution.Width - 8 ?? 0; public int MaxCropTB => currentTitle?.Resolution.Height - 8 ?? 0; public BindingList CropModes { get; } = new BindingList { CropMode.Automatic, CropMode.Loose, CropMode.None, CropMode.Custom }; public CropMode SelectedCropMode { get { return (CropMode)this.Task.Cropping.CropMode; } set { this.Task.Cropping.CropMode = (int)value; this.NotifyOfPropertyChange(() => this.SelectedCropMode); this.OnTabStatusChanged(null); if (value != CropMode.Custom && this.currentTitle != null) { if (value == CropMode.Automatic) { this.Task.Cropping.Top = currentTitle.AutoCropDimensions.Top; this.Task.Cropping.Bottom = currentTitle.AutoCropDimensions.Bottom; this.Task.Cropping.Left = currentTitle.AutoCropDimensions.Left; this.Task.Cropping.Right = currentTitle.AutoCropDimensions.Right; } else if (value == CropMode.Loose) { this.Task.Cropping.Top = currentTitle.LooseCropDimensions.Top; this.Task.Cropping.Bottom = currentTitle.LooseCropDimensions.Bottom; this.Task.Cropping.Left = currentTitle.LooseCropDimensions.Left; this.Task.Cropping.Right = currentTitle.LooseCropDimensions.Right; } else { this.Task.Cropping.Top = 0; this.Task.Cropping.Bottom = 0; this.Task.Cropping.Left = 0; this.Task.Cropping.Right = 0; } } this.NotifyOfPropertyChange(() => this.CropLeft); this.NotifyOfPropertyChange(() => this.CropRight); this.NotifyOfPropertyChange(() => this.CropTop); this.NotifyOfPropertyChange(() => this.CropBottom); this.NotifyOfPropertyChange(() => this.IsCustomCrop); this.RecalculatePictureSettingsProperties(ChangedPictureField.Crop); } } public bool IsCustomCrop => this.SelectedCropMode == CropMode.Custom; public int DisplayWidth { get { if (this.Task.DisplayWidth.HasValue && !double.IsInfinity(this.Task.DisplayWidth.Value)) { if (int.TryParse(Math.Round(this.Task.DisplayWidth.Value, 0).ToString(CultureInfo.InvariantCulture), out int value)) { return value; } } return 0; } set { if (!object.Equals(this.Task.DisplayWidth, value)) { this.Task.DisplayWidth = value; this.NotifyOfPropertyChange(() => this.DisplayWidth); this.RecalculatePictureSettingsProperties(ChangedPictureField.DisplayWidth); } } } public int DisplayHeight { get; set; } public int Width { get => this.Task.Width.HasValue ? this.Task.Width.Value : 0; set { if (!object.Equals(this.Task.Width, value)) { this.Task.Width = value; this.NotifyOfPropertyChange(() => this.Width); this.RecalculatePictureSettingsProperties(ChangedPictureField.Width); } } } public int Height { get => this.Task.Height.HasValue ? this.Task.Height.Value : 0; set { if (!object.Equals(this.Task.Height, value)) { this.Task.Height = value; this.NotifyOfPropertyChange(() => this.Height); this.RecalculatePictureSettingsProperties(ChangedPictureField.Height); } } } public bool MaintainAspectRatio { get => this.Task.KeepDisplayAspect; set { this.Task.KeepDisplayAspect = value; this.NotifyOfPropertyChange(() => this.MaintainAspectRatio); this.RecalculatePictureSettingsProperties(ChangedPictureField.MaintainAspectRatio); } } public int ParHeight { get => this.Task.PixelAspectY; set { if (!object.Equals(this.Task.PixelAspectY, value)) { this.Task.PixelAspectY = value; this.NotifyOfPropertyChange(() => this.ParHeight); this.RecalculatePictureSettingsProperties(ChangedPictureField.ParH); } } } public int ParWidth { get => this.Task.PixelAspectX; set { if (!object.Equals(this.Task.PixelAspectX, value)) { this.Task.PixelAspectX = value; this.NotifyOfPropertyChange(() => this.ParWidth); this.RecalculatePictureSettingsProperties(ChangedPictureField.ParW); } } } public AnamorphicMode SelectedAnamorphicMode { get => (AnamorphicMode)this.Task.Anamorphic; set { if (!object.Equals(this.SelectedAnamorphicMode, value)) { this.Task.Anamorphic = (Anamorphic)value; this.NotifyOfPropertyChange(() => this.SelectedAnamorphicMode); this.RecalculatePictureSettingsProperties(ChangedPictureField.Anamorphic); this.OnTabStatusChanged(null); this.IsPixelAspectSettable = value == AnamorphicMode.Custom; this.NotifyOfPropertyChange(() => this.IsPixelAspectSettable); } } } public bool IsPixelAspectSettable { get; set; } /* Public Tab API Implementation */ public void SetPreset(Preset preset, EncodeTask task) { this.Task = task; // Cropping if ((CropMode)preset.Task.Cropping.CropMode == CropMode.Custom) { this.SelectedCropMode = CropMode.Custom; this.Task.Cropping.Left = preset.Task.Cropping.Left; this.Task.Cropping.Right = preset.Task.Cropping.Right; this.Task.Cropping.Top = preset.Task.Cropping.Top; this.Task.Cropping.Bottom = preset.Task.Cropping.Bottom; this.NotifyOfPropertyChange(() => this.CropLeft); this.NotifyOfPropertyChange(() => this.CropRight); this.NotifyOfPropertyChange(() => this.CropTop); this.NotifyOfPropertyChange(() => this.CropBottom); } else { this.SelectedCropMode = (CropMode)preset.Task.Cropping.CropMode; } // Padding and Rotate Filters this.PaddingFilter.SetPreset(preset, task); this.RotateFlipFilter?.SetPreset(preset, task); // Picture Sizes and Anamorphic this.SelectedAnamorphicMode = (AnamorphicMode)preset.Task.Anamorphic; this.OptimalSize = preset.Task.OptimalSize; this.AllowUpscaling = preset.Task.AllowUpscaling; // Set the Maintain Aspect ratio. this.MaintainAspectRatio = preset.Task.KeepDisplayAspect; // Setup the Maximum Width / Height this.MaxWidth = preset.Task.MaxWidth; this.MaxHeight = preset.Task.MaxHeight; this.SetSelectedPictureSettingsResLimitMode(); // Set the width, then check the height doesn't breach the max height and correct if necessary. int width = this.GetModulusValue(this.GetRes((this.sourceResolution.Width - this.CropLeft - this.CropRight), this.MaxWidth)); int height = this.GetModulusValue(this.GetRes((this.sourceResolution.Height - this.CropTop - this.CropBottom), this.MaxHeight)); // Set the backing fields to avoid triggering re-calculation until both are set. this.Task.Width = width; this.Task.Height = height; // Trigger a calculation this.RecalculatePictureSettingsProperties(ChangedPictureField.Width); // Update the UI this.NotifyOfPropertyChange(() => this.Width); this.NotifyOfPropertyChange(() => this.Height); // Custom Anamorphic if (preset.Task.Anamorphic == Anamorphic.Custom) { this.DisplayWidth = preset.Task.DisplayWidth != null ? int.Parse(preset.Task.DisplayWidth.ToString()) : 0; this.ParWidth = preset.Task.PixelAspectX == 0 ? 1 : preset.Task.PixelAspectX; this.ParHeight = preset.Task.PixelAspectY == 0 ? 1 : preset.Task.PixelAspectY; } this.NotifyOfPropertyChange(() => this.Task); this.UpdateVisibleControls(); } public void UpdateTask(EncodeTask task) { this.Task = task; this.SetSelectedPictureSettingsResLimitMode(); this.PaddingFilter.UpdateTask(task); this.RotateFlipFilter?.UpdateTask(task); this.NotifyOfPropertyChange(() => this.Width); this.NotifyOfPropertyChange(() => this.Height); this.NotifyOfPropertyChange(() => this.SelectedAnamorphicMode); this.NotifyOfPropertyChange(() => this.CropTop); this.NotifyOfPropertyChange(() => this.CropBottom); this.NotifyOfPropertyChange(() => this.CropLeft); this.NotifyOfPropertyChange(() => this.CropRight); this.NotifyOfPropertyChange(() => this.SelectedCropMode); this.NotifyOfPropertyChange(() => this.MaintainAspectRatio); this.NotifyOfPropertyChange(() => this.DisplayWidth); this.NotifyOfPropertyChange(() => this.ParWidth); this.NotifyOfPropertyChange(() => this.ParHeight); this.NotifyOfPropertyChange(() => this.MaxWidth); this.NotifyOfPropertyChange(() => this.MaxHeight); this.NotifyOfPropertyChange(() => this.IsCustomCrop); this.UpdateVisibleControls(); } public void SetSource(Source source, Title title, Preset preset, EncodeTask task) { this.currentTitle = title; this.Task = task; this.PaddingFilter.SetSource(source, title, preset, task); this.RotateFlipFilter?.SetSource(source, title, preset, task); this.scannedSource = source; if (title != null) { // Set cached info this.sourceParValues = title.ParVal; this.sourceResolution = title.Resolution; // Update the cropping values, preferring those in the presets. if ((CropMode)preset.Task.Cropping.CropMode == CropMode.Custom) { this.Task.Cropping.Left = preset.Task.Cropping.Left; this.Task.Cropping.Right = preset.Task.Cropping.Right; this.Task.Cropping.Top = preset.Task.Cropping.Top; this.Task.Cropping.Bottom = preset.Task.Cropping.Bottom; this.SelectedCropMode = CropMode.Custom; } else { if ((CropMode)preset.Task.Cropping.CropMode == CropMode.Automatic) { this.Task.Cropping.Top = title.AutoCropDimensions.Top; this.Task.Cropping.Bottom = title.AutoCropDimensions.Bottom; this.Task.Cropping.Left = title.AutoCropDimensions.Left; this.Task.Cropping.Right = title.AutoCropDimensions.Right; } else if ((CropMode)preset.Task.Cropping.CropMode == CropMode.Loose) { this.Task.Cropping.Top = title.LooseCropDimensions.Top; this.Task.Cropping.Bottom = title.LooseCropDimensions.Bottom; this.Task.Cropping.Left = title.LooseCropDimensions.Left; this.Task.Cropping.Right = title.LooseCropDimensions.Right; } else { this.Task.Cropping.Top = 0; this.Task.Cropping.Bottom = 0; this.Task.Cropping.Left = 0; this.Task.Cropping.Right = 0; } this.SelectedCropMode = (CropMode)preset.Task.Cropping.CropMode; } // Set the W/H // Set the width, then check the height doesn't breach the max height and correct if necessary. this.Task.Width = this.GetModulusValue(this.GetRes((this.sourceResolution.Width - this.CropLeft - this.CropRight), this.MaxWidth)); this.Task.Height = this.GetModulusValue(this.GetRes((this.sourceResolution.Height - this.CropTop - this.CropBottom), this.MaxHeight)); // Set Screen Controls // "Storage Size: {0}x{1} Display Size: {2}x{3} Aspect Ratio: {4}", double sourceDisplayWidth = (double)title.Resolution.Width * title.ParVal.Width / title.ParVal.Height; this.SourceInfo = string.Format( Resources.PictureSettingsViewModel_SourceInfo, title.Resolution.Width, title.Resolution.Height, sourceDisplayWidth, title.Resolution.Height, HandBrakePictureHelpers.GetNiceDisplayAspect(sourceDisplayWidth, title.Resolution.Height)); // Force a re-calc. This will handle MaxWidth / Height corrections. this.RecalculatePictureSettingsProperties(ChangedPictureField.Width); } this.NotifyOfPropertyChange(() => this.Task); } public bool MatchesPreset(Preset preset) { if ((AnamorphicMode)preset.Task.Anamorphic != this.SelectedAnamorphicMode) { return false; } if (preset.Task.MaxHeight != this.MaxHeight) { return false; } if (preset.Task.MaxWidth != this.MaxWidth) { return false; } if ((CropMode)preset.Task.Cropping.CropMode != this.SelectedCropMode) { return false; } if (!PaddingFilter.MatchesPreset(preset)) { return false; } if (!RotateFlipFilter.MatchesPreset(preset)) { return false; } return true; } /* Protected and Private Methods */ public void OpenPreviewWindow() { if (!string.IsNullOrEmpty(this.Task.Source) && !this.StaticPreviewViewModel.IsOpen) { this.StaticPreviewViewModel.IsOpen = true; this.StaticPreviewViewModel.UpdatePreviewFrame(this.currentTitle, this.Task, this.scannedSource); this.windowManager.ShowWindow(this.StaticPreviewViewModel); } else if (this.StaticPreviewViewModel.IsOpen) { Window window = Application.Current.Windows.Cast().FirstOrDefault(x => x.GetType() == typeof(StaticPreviewView)); window?.Focus(); } } protected virtual void OnTabStatusChanged(TabStatusEventArgs e) { this.TabStatusChanged?.Invoke(this, e); } protected virtual void OnFilterChanged(TabStatusEventArgs e) { RecalculatePictureSettingsProperties(ChangedPictureField.Padding); this.TabStatusChanged?.Invoke(this, e); } protected virtual void OnFlipRotateChanged(FlipRotationCommand e) { this.HandleRotationFlipChange(e); this.TabStatusChanged?.Invoke(this, new TabStatusEventArgs(null)); } private void Init() { this.Task.KeepDisplayAspect = true; this.NotifyOfPropertyChange(() => this.MaintainAspectRatio); // Default the Max Width / Height to 4K format this.MaxHeight = 2160; this.MaxWidth = 3480; this.SetSelectedPictureSettingsResLimitMode(); } private PictureSettingsTitle GetPictureTitleInfo() { PictureSettingsTitle title = new PictureSettingsTitle { Width = this.sourceResolution.Width, Height = this.sourceResolution.Height, ParW = this.sourceParValues.Width, ParH = this.sourceParValues.Height, }; return title; } private PictureSettingsJob GetPictureSettings(ChangedPictureField changedField) { PictureSettingsJob job = new PictureSettingsJob { Width = this.Width, Height = this.Height, ItuPar = false, ParW = this.SelectedAnamorphicMode == AnamorphicMode.None ? 1 : this.ParWidth, ParH = this.SelectedAnamorphicMode == AnamorphicMode.None ? 1 : this.ParHeight, MaxWidth = this.MaxWidth.HasValue ? this.MaxWidth.Value : 0, MaxHeight = this.MaxHeight.HasValue ? this.MaxHeight.Value : 0, KeepDisplayAspect = this.MaintainAspectRatio, AnamorphicMode = (Anamorphic)this.SelectedAnamorphicMode, Crop = new Cropping(this.CropTop, this.CropBottom, this.CropLeft, this.CropRight, (int)CropMode.Custom), Pad = new Padding(this.PaddingFilter.Top, this.PaddingFilter.Bottom, this.PaddingFilter.Left, this.PaddingFilter.Right), RotateAngle = this.RotateFlipFilter.SelectedRotation, Hflip = this.RotateFlipFilter.FlipVideo ? 1 : 0, DarWidth = this.DisplayWidth, DarHeight = this.DisplayHeight }; if (this.SelectedAnamorphicMode == AnamorphicMode.Custom) { if (changedField == ChangedPictureField.DisplayWidth) { var displayWidth = this.DisplayWidth; job.ParW = (int)displayWidth; // num job.ParH = job.Width; // den } } // Reduce the Par W/H if we can. Don't do it while the user is altering the PAR controls through as it will mess with the result. if (changedField != ChangedPictureField.ParH && changedField != ChangedPictureField.ParW) { long x, y; HandBrakeUtils.Reduce(job.ParW, job.ParH, out x, out y); job.ParW = (int)y; job.ParH = (int)x; } return job; } /// /// Recalculate the picture settings when the user changes a particular field defined in the ChangedPictureField enum. /// The properties in this class are dumb. They simply call this method if there is a change. /// It is the job of this method to update all affected private fields and raise change notifications. /// /// /// The changed field. /// private void RecalculatePictureSettingsProperties(ChangedPictureField changedField) { // Sanity Check if (this.currentTitle == null) { return; } // Step 1, Update what controls are visible. this.UpdateVisibleControls(); // Step 2, Set sensible defaults if (changedField == ChangedPictureField.Anamorphic && (this.SelectedAnamorphicMode == AnamorphicMode.None)) { this.Task.Width = this.sourceResolution.Width > this.MaxWidth ? this.MaxWidth : this.sourceResolution.Width; this.Task.KeepDisplayAspect = true; } // Choose which setting to keep. HandBrakePictureHelpers.KeepSetting setting = 0; switch (changedField) { case ChangedPictureField.Width: setting = HandBrakePictureHelpers.KeepSetting.HB_KEEP_WIDTH; break; case ChangedPictureField.Height: setting = HandBrakePictureHelpers.KeepSetting.HB_KEEP_HEIGHT; break; case ChangedPictureField.DisplayWidth: setting = HandBrakePictureHelpers.KeepSetting.HB_KEEP_DISPLAY_WIDTH; break; case ChangedPictureField.ParW: case ChangedPictureField.ParH: setting = HandBrakePictureHelpers.KeepSetting.HB_KEEP_HEIGHT; break; case ChangedPictureField.Padding: setting = HandBrakePictureHelpers.KeepSetting.HB_KEEP_PAD; break; } if (this.MaintainAspectRatio) { setting |= HandBrakePictureHelpers.KeepSetting.HB_KEEP_DISPLAY_ASPECT; } // Step 2, For the changed field, call hb_set_anamorphic_size and process the results. HandBrakePictureHelpers.FlagsSetting flag = 0; if (this.AllowUpscaling) { flag = HandBrakePictureHelpers.FlagsSetting.HB_GEO_SCALE_UP; } if (this.OptimalSize) { flag |= HandBrakePictureHelpers.FlagsSetting.HB_GEO_SCALE_BEST; } AnamorphicResult result = HandBrakePictureHelpers.GetAnamorphicSize(this.GetPictureSettings(changedField), this.GetPictureTitleInfo(), setting, flag); this.Task.Width = result.OutputWidth; this.Task.Height = result.OutputHeight; this.Task.PixelAspectX = result.OutputParWidth; this.Task.PixelAspectY = result.OutputParHeight; if (this.Task.PixelAspectX == 0) { this.Task.PixelAspectX = 1; } if (this.Task.PixelAspectY == 0) { this.Task.PixelAspectY = 1; } this.ApplyPad(this.PaddingFilter.Mode, result); // Update for display purposes. double dispWidth = Math.Round((double)result.OutputWidth * result.OutputParWidth / result.OutputParHeight, 0); this.Task.DisplayWidth = dispWidth; this.DisplayHeight = result.OutputHeight; // Step 3, Set the display width label to indicate the output. this.DisplaySize = this.sourceResolution == null || this.sourceResolution.IsEmpty ? string.Empty : string.Format(Resources.PictureSettingsViewModel_StorageDisplayLabel, result.OutputWidth, result.OutputHeight); this.NotifyOfPropertyChange(() => this.DisplaySize); this.OutputAspect = string.Format(Resources.PictureSettingsViewModel_AspectRatioLabel, HandBrakePictureHelpers.GetNiceDisplayAspect(dispWidth, result.OutputHeight)); this.NotifyOfPropertyChange(() => this.OutputAspect); // Step 4, Force an update on all the UI elements. this.NotifyOfPropertyChange(() => this.Width); this.NotifyOfPropertyChange(() => this.Height); this.NotifyOfPropertyChange(() => this.DisplayWidth); this.NotifyOfPropertyChange(() => this.ParWidth); this.NotifyOfPropertyChange(() => this.ParHeight); this.NotifyOfPropertyChange(() => this.CropTop); this.NotifyOfPropertyChange(() => this.CropBottom); this.NotifyOfPropertyChange(() => this.CropLeft); this.NotifyOfPropertyChange(() => this.CropRight); this.NotifyOfPropertyChange(() => this.MaintainAspectRatio); // Step 5, Update the Preview if (delayedPreviewprocessor != null && this.Task != null && this.StaticPreviewViewModel != null && this.StaticPreviewViewModel.IsOpen) { delayedPreviewprocessor.PerformTask(() => this.StaticPreviewViewModel.UpdatePreviewFrame(this.currentTitle, this.Task, this.scannedSource), 800); } this.OnTabStatusChanged(new TabStatusEventArgs("picture", ChangedOption.Dimensions)); } private void ApplyPad(PaddingMode mode, AnamorphicResult result) { if (mode == PaddingMode.Custom) { result.OutputWidth = result.OutputWidth + this.PaddingFilter.Left + this.PaddingFilter.Right; result.OutputHeight = result.OutputHeight + this.PaddingFilter.Top + this.PaddingFilter.Bottom; } } private void UpdateVisibleControls() { this.WidthControlEnabled = true; this.HeightControlEnabled = true; if (OptimalSize) { this.WidthControlEnabled = false; this.HeightControlEnabled = false; } } private int GetModulusValue(double value) { double remainder = value % 2; if (remainder.Equals(0.0d)) { return (int)Math.Abs(value); } double result = remainder >= 1 ? value + (2 - remainder) : value - remainder; return (int)Math.Abs(result); } private int GetRes(int value, int? max) { return max.HasValue ? (value > max.Value ? max.Value : value) : value; } private void SetSelectedPictureSettingsResLimitMode() { if (this.MaxWidth == null && this.MaxHeight == null) { this.SelectedPictureSettingsResLimitMode = PictureSettingsResLimitModes.None; return; } // Look for a matching resolution. foreach (PictureSettingsResLimitModes limit in EnumHelper.GetEnumList()) { ResLimit resLimit = EnumHelper.GetAttribute(limit); if (resLimit != null) { if (resLimit.Width == this.MaxWidth && resLimit.Height == this.MaxHeight) { this.SelectedPictureSettingsResLimitMode = limit; return; } } } if (this.MaxWidth.HasValue || this.MaxHeight.HasValue) { this.SelectedPictureSettingsResLimitMode = PictureSettingsResLimitModes.Custom; } } private bool ValidCropTB(int value, int value2) { int totalCrop = value + value2; if (totalCrop >= this.currentTitle.Resolution.Height) { return false; } return true; } private bool ValidCropLR(int value, int value2) { int totalCrop = value + value2; if (totalCrop >= this.currentTitle.Resolution.Width) { return false; } return true; } private void HandleRotationFlipChange(FlipRotationCommand command) { if (this.currentTitle == null) { return; } PictureSettingsJob job = this.GetPictureSettings(command.ChangedField); job.PreviousHflip = command.PreviousHflip; job.PreviousRotation = command.PreviousRotation; RotateResult result = HandBrakePictureHelpers.RotateGeometry(job); this.Task.Cropping.Top = result.CropTop; this.Task.Cropping.Bottom = result.CropBottom; this.Task.Cropping.Left = result.CropLeft; this.Task.Cropping.Right = result.CropRight; this.NotifyOfPropertyChange(() => this.CropTop); this.NotifyOfPropertyChange(() => this.CropBottom); this.NotifyOfPropertyChange(() => this.CropLeft); this.NotifyOfPropertyChange(() => this.CropRight); this.PaddingFilter.SetRotationValues(result.PadTop, result.PadBottom, result.PadLeft, result.PadRight); this.Task.Width = result.Width; this.Task.Height = result.Height; this.Task.PixelAspectX = result.ParNum; this.Task.PixelAspectY = result.ParDen; this.NotifyOfPropertyChange(() => this.Width); this.NotifyOfPropertyChange(() => this.Height); this.NotifyOfPropertyChange(() => this.ParWidth); this.NotifyOfPropertyChange(() => this.ParHeight); RecalculatePictureSettingsProperties(ChangedPictureField.Rotate); } } }