using System; using System.ComponentModel; using System.Drawing; using System.Globalization; using System.Windows.Forms; using Handbrake.Parsing; namespace Handbrake.Controls { // TODO Custom Anamorphic /* NOT KEEPING DISPLAY ASPECT == [Complete] KEEPING DISPLAY ASPECT RATIO == [TODO] DAR = DISPLAY WIDTH / DISPLAY HEIGHT (cache after every modification) Disable editing: PIXEL WIDTH, PIXEL HEIGHT Changing DISPLAY WIDTH: Changes HEIGHT to keep DAR Changes PIXEL WIDTH to new DISPLAY WIDTH Changes PIXEL HEIGHT to STORAGE WIDTH Changing HEIGHT Changes DISPLAY WIDTH to keep DAR Changes PIXEL WIDTH to new DISPLAY WIDTH Changes PIXEL HEIGHT to STORAGE WIDTH Changing STORAGE_WIDTH: Changes PIXEL WIDTH to DISPLAY WIDTH Changes PIXEL HEIGHT to new STORAGE WIDTH * */ 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 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 { 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; } } /// /// 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; } 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; 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) { preventChangingCustom = true; updownParWidth.Value = updownDisplayWidth.Value; updownParHeight.Value = text_width.Value; preventChangingCustom = 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); } } }