using System; using System.ComponentModel; using System.Drawing; using System.Globalization; using System.Windows.Forms; using Handbrake.Parsing; namespace Handbrake.Controls { public partial class PictureSettings : UserControl { private readonly CultureInfo Culture = new CultureInfo("en-US", false); public event EventHandler PictureSettingsChanged; private Boolean preventChangingWidth, preventChangingHeight, preventChangingCustom, preventChangingDisplayWidth; private int _PresetMaximumWidth, _PresetMaximumHeight; private double cachedDar; private Title _SourceTitle; public PictureSettings() { InitializeComponent(); drp_anamorphic.SelectedIndex = 1; drp_modulus.SelectedIndex = 0; } /// /// Gets or sets the source media used by this control. /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Title Source { get { return _SourceTitle; } set { _SourceTitle = value; Enabled = _SourceTitle != null; // Set the Aspect Ratio lbl_Aspect.Text = _SourceTitle.AspectRatio.ToString(Culture); lbl_src_res.Text = _SourceTitle.Resolution.Width + " x " + _SourceTitle.Resolution.Height; // Set the Recommended Cropping values crop_top.Value = GetCropMod2Clean(_SourceTitle.AutoCropDimensions[0]); crop_bottom.Value = GetCropMod2Clean(_SourceTitle.AutoCropDimensions[1]); crop_left.Value = GetCropMod2Clean(_SourceTitle.AutoCropDimensions[2]); crop_right.Value = GetCropMod2Clean(_SourceTitle.AutoCropDimensions[3]); // Set the Resolution Boxes if (drp_anamorphic.SelectedIndex == 0) { if (text_width.Value == 0) // Only update the values if the fields don't already have values. text_width.Value = _SourceTitle.Resolution.Width; check_KeepAR.Checked = true; // Forces Resolution to be correct. } else { if (text_width.Value == 0 && text_height.Value ==0)// Only update the values if the fields don't already have values. { text_width.Value = _SourceTitle.Resolution.Width; text_height.Value = _SourceTitle.Resolution.Height - (int) crop_top.Value - (int) crop_bottom.Value; } labelDisplaySize.Text = calculateAnamorphicSizes().Width + "x" + calculateAnamorphicSizes().Height; } updownDisplayWidth.Value = calculateAnamorphicSizes().Width; updownParWidth.Value = _SourceTitle.ParVal.Width; updownParHeight.Value = _SourceTitle.ParVal.Height; cachedDar = (double)updownDisplayWidth.Value / (double)text_height.Value; } } /// /// Gets or sets the maximum allowable size for the encoded resolution. Set a value to /// "0" if the maximum does not matter. /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Size PresetMaximumResolution { get { return new Size(_PresetMaximumWidth, _PresetMaximumHeight); } set { _PresetMaximumWidth = value.Width; _PresetMaximumHeight = value.Height; if (value.Width != 0 && value.Height != 0) lbl_max.Text = "Max Width / Height"; else if (value.Width != 0) lbl_max.Text = "Max Width"; else if (value.Height != 0) lbl_max.Text = "Max Height"; else lbl_max.Text = ""; } } // Picture Controls private void text_width_ValueChanged(object sender, EventArgs e) { if (preventChangingWidth) return; // Make sure the new value doesn't exceed the maximum if (Source != null) if (text_width.Value > Source.Resolution.Width) text_width.Value = Source.Resolution.Width; switch (drp_anamorphic.SelectedIndex) { case 0: if (check_KeepAR.Checked && Source != null) { preventChangingHeight = true; int width = (int)text_width.Value; double crop_width = Source.Resolution.Width - (double)crop_left.Value - (double)crop_right.Value; double crop_height = Source.Resolution.Height - (double)crop_top.Value - (double)crop_bottom.Value; double newHeight = (width * Source.Resolution.Width * sourceAspect.Height * crop_height) / (Source.Resolution.Height * sourceAspect.Width * crop_width); text_height.Value = (decimal)GetModulusValue(newHeight); preventChangingHeight = false; } break; case 3: if (check_KeepAR.CheckState == CheckState.Unchecked && Source != null) { if (preventChangingCustom) break; preventChangingDisplayWidth = true; updownDisplayWidth.Value = text_width.Value * updownParWidth.Value / updownParHeight.Value; preventChangingDisplayWidth = false; labelDisplaySize.Text = Math.Truncate(updownDisplayWidth.Value) + "x" + text_height.Value; } if (check_KeepAR.CheckState == CheckState.Checked && Source != null) { updownParWidth.Value = updownDisplayWidth.Value; updownParHeight.Value = text_width.Value; } break; default: labelDisplaySize.Text = calculateAnamorphicSizes().Width + "x" + calculateAnamorphicSizes().Height; break; } preventChangingWidth = false; } private void text_height_ValueChanged(object sender, EventArgs e) { if (preventChangingHeight) return; if (Source != null) if (text_height.Value > Source.Resolution.Height) text_height.Value = Source.Resolution.Height; switch (drp_anamorphic.SelectedIndex) { case 0: if (check_KeepAR.Checked && Source != null) { preventChangingWidth = true; double crop_width = Source.Resolution.Width - (double)crop_left.Value - (double)crop_right.Value; double crop_height = Source.Resolution.Height - (double)crop_top.Value - (double)crop_bottom.Value; double new_width = ((double)text_height.Value * Source.Resolution.Height * sourceAspect.Width * crop_width) / (Source.Resolution.Width * sourceAspect.Height * crop_height); text_width.Value = (decimal)GetModulusValue(new_width); preventChangingWidth = false; } break; case 3: labelDisplaySize.Text = Math.Truncate(updownDisplayWidth.Value) + "x" + text_height.Value; if (check_KeepAR.CheckState == CheckState.Checked && Source != null) { // - Changes DISPLAY WIDTH to keep DAR // - Changes PIXEL WIDTH to new DISPLAY WIDTH // - Changes PIXEL HEIGHT to STORAGE WIDTH // DAR = DISPLAY WIDTH / DISPLAY HEIGHT (cache after every modification) double rawCalculatedDisplayWidth = (double)text_height.Value * cachedDar; preventChangingDisplayWidth = true; // Start Guards preventChangingWidth = true; updownDisplayWidth.Value = (decimal)rawCalculatedDisplayWidth; updownParWidth.Value = updownDisplayWidth.Value; updownParHeight.Value = text_width.Value; preventChangingWidth = false; // Reset Guards preventChangingDisplayWidth = false; } break; default: labelDisplaySize.Text = calculateAnamorphicSizes().Width + "x" + calculateAnamorphicSizes().Height; break; } preventChangingHeight = false; } private void check_KeepAR_CheckedChanged(object sender, EventArgs e) { //Force TextWidth to recalc height if (check_KeepAR.Checked) text_width_ValueChanged(this, new EventArgs()); // Disable the Custom Anamorphic Par Controls if checked. if (drp_anamorphic.SelectedIndex == 3) { updownParWidth.Enabled = !check_KeepAR.Checked; updownParHeight.Enabled = !check_KeepAR.Checked; } // Raise the Picture Settings Changed Event if (PictureSettingsChanged != null) PictureSettingsChanged(this, new EventArgs()); } private void updownDisplayWidth_ValueChanged(object sender, EventArgs e) { if (preventChangingDisplayWidth == false && check_KeepAR.CheckState == CheckState.Unchecked) { preventChangingCustom = true; updownParWidth.Value = updownDisplayWidth.Value; updownParHeight.Value = text_width.Value; preventChangingCustom = false; } if (preventChangingDisplayWidth == false && check_KeepAR.CheckState == CheckState.Checked) { // - Changes HEIGHT to keep DAR // - Changes PIXEL WIDTH to new DISPLAY WIDTH // - Changes PIXEL HEIGHT to STORAGE WIDTH // DAR = DISPLAY WIDTH / DISPLAY HEIGHT (cache after every modification) // Calculate new Height Value int modulus = 16; int.TryParse(drp_modulus.SelectedItem.ToString(), out modulus); int rawCalculatedHeight = (int)((int)updownDisplayWidth.Value / cachedDar); int modulusHeight = rawCalculatedHeight - (rawCalculatedHeight % modulus); // Update value preventChangingHeight = true; text_height.Value = (decimal)modulusHeight; updownParWidth.Value = updownDisplayWidth.Value; updownParHeight.Value = text_width.Value; preventChangingHeight = false; } } // Anamorphic Controls private void drp_anamorphic_SelectedIndexChanged(object sender, EventArgs e) { switch (drp_anamorphic.SelectedIndex) { case 0: text_width.Enabled = true; text_height.Enabled = true; check_KeepAR.Enabled = true; setCustomAnamorphicOptionsVisible(false); labelStaticDisplaySize.Visible = false; labelDisplaySize.Visible = false; check_KeepAR.Checked = true; drp_modulus.SelectedIndex = 0; if (check_KeepAR.Checked) text_width_ValueChanged(this, new EventArgs()); // Don't update display size if we're not using anamorphic return; case 1: text_width.Enabled = false; text_height.Enabled = false; check_KeepAR.Enabled = false; setCustomAnamorphicOptionsVisible(false); labelStaticDisplaySize.Visible = true; labelDisplaySize.Visible = true; drp_modulus.SelectedIndex = 0; check_KeepAR.Checked = true; break; case 2: text_width.Enabled = true; text_height.Enabled = false; check_KeepAR.Enabled = false; setCustomAnamorphicOptionsVisible(false); labelStaticDisplaySize.Visible = true; labelDisplaySize.Visible = true; drp_modulus.SelectedIndex = 0; check_KeepAR.Checked = true; break; case 3: text_width.Enabled = true; text_height.Enabled = true; check_KeepAR.Enabled = true; setCustomAnamorphicOptionsVisible(true); labelStaticDisplaySize.Visible = true; labelDisplaySize.Visible = true; check_KeepAR.Checked = true; break; } labelDisplaySize.Text = calculateAnamorphicSizes().Width + "x" + calculateAnamorphicSizes().Height; if (check_KeepAR.Checked) text_width_ValueChanged(this, new EventArgs()); if (PictureSettingsChanged != null) PictureSettingsChanged(this, new EventArgs()); } private void drp_modulus_SelectedIndexChanged(object sender, EventArgs e) { preventChangingWidth = true; preventChangingHeight = true; text_width.Value = (decimal)GetModulusValue((double)text_width.Value); text_height.Value = (decimal)GetModulusValue((double)text_height.Value); preventChangingWidth = false; preventChangingHeight = false; text_width.Increment = int.Parse(drp_modulus.SelectedItem.ToString()); text_height.Increment = int.Parse(drp_modulus.SelectedItem.ToString()); if (PictureSettingsChanged != null) PictureSettingsChanged(this, new EventArgs()); } // Cropping Controls private void check_autoCrop_CheckedChanged(object sender, EventArgs e) { crop_top.Enabled = check_customCrop.Checked; crop_bottom.Enabled = check_customCrop.Checked; crop_left.Enabled = check_customCrop.Checked; crop_right.Enabled = check_customCrop.Checked; } private void crop_ValueChanged(object sender, EventArgs e) { text_width_ValueChanged(this, new EventArgs()); } // GUI Functions private void setCustomAnamorphicOptionsVisible(bool visible) { lbl_modulus.Visible = visible; lbl_displayWidth.Visible = visible; lbl_parWidth.Visible = visible; lbl_parHeight.Visible = visible; drp_modulus.Visible = visible; updownDisplayWidth.Visible = visible; updownParWidth.Visible = visible; updownParHeight.Visible = visible; } // Calculation Functions private Size sourceAspect { get { if (Source != null) { if (Source.AspectRatio == 1.78F) return new Size(16, 9); if (Source.AspectRatio == 1.33F) return new Size(4, 3); } return new Size(0, 0); } } private Size calculateAnamorphicSizes() { if (Source != null) { /* Set up some variables to make the math easier to follow. */ int cropped_width = Source.Resolution.Width - (int)crop_left.Value - (int)crop_right.Value; int cropped_height = Source.Resolution.Height - (int)crop_top.Value - (int)crop_bottom.Value; double storage_aspect = (double)cropped_width / cropped_height; /* Figure out what width the source would display at. */ double source_display_width = (double)cropped_width * Source.ParVal.Width / Source.ParVal.Height; /* 3 different ways of deciding output dimensions: - 1: Strict anamorphic, preserve source dimensions - 2: Loose anamorphic, round to mod16 and preserve storage aspect ratio - 3: Power user anamorphic, specify everything */ double width, height; switch (drp_anamorphic.SelectedIndex) { case 1: /* Strict anamorphic */ double displayWidth = ((double)cropped_width * Source.ParVal.Width / Source.ParVal.Height); displayWidth = Math.Round(displayWidth, 0); Size output = new Size((int)displayWidth, cropped_height); return output; case 2: /* "Loose" anamorphic. - Uses mod16-compliant dimensions, - Allows users to set the width */ width = (int)text_width.Value - (int)crop_left.Value - (int)crop_right.Value; width = GetModulusValue(width); /* Time to get picture width that divide cleanly.*/ height = (width / storage_aspect) + 0.5; height = GetModulusValue(height); /* Time to get picture height that divide cleanly.*/ /* The film AR is the source's display width / cropped source height. The output display width is the output height * film AR. The output PAR is the output display width / output storage width. */ double pixel_aspect_width = height * source_display_width / cropped_height; double pixel_aspect_height = width; double disWidthLoose = (width * pixel_aspect_width / pixel_aspect_height); return new Size((int)disWidthLoose, (int)height); case 3: // Get the User Interface Values double UIdisplayWidth; double.TryParse(updownDisplayWidth.Text, out UIdisplayWidth); /* Anamorphic 3: Power User Jamboree - Set everything based on specified values */ height = GetModulusValue((double)text_height.Value); if (check_KeepAR.Checked) return new Size((int)Math.Truncate(UIdisplayWidth), (int)height); return new Size((int)Math.Truncate(UIdisplayWidth), (int)height); } } // Return a default value of 0,0 to indicate failure return new Size(0, 0); } private double GetModulusValue(double value) { int mod = int.Parse(drp_modulus.SelectedItem.ToString()); double remainder = value % mod; if (remainder == 0) return value; return remainder >= ((double)mod / 2) ? value + (mod - remainder) : value - remainder; } private static int GetCropMod2Clean(int value) { int remainder = value % 2; if (remainder == 0) return value; return (value + remainder); } } }