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
{
private 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;
Size croppedDar = CalculateAnamorphicSizes();
_cachedDar = (double) croppedDar.Width/croppedDar.Height;
updownDisplayWidth.Value = croppedDar.Width;
}
}
///
/// 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 (Properties.Settings.Default.disableResCalc)
return;
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 - (int)crop_left.Value - (int)crop_right.Value;
double crop_height = Source.Resolution.Height - (int)crop_top.Value - (int)crop_bottom.Value;
if (SourceAspect.Width == 0 && SourceAspect.Height == 0)
break;
double newHeight = ((double)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 (Properties.Settings.Default.disableResCalc)
return;
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 - (int)crop_left.Value - (int)crop_right.Value;
double crop_height = Source.Resolution.Height - (int)crop_top.Value - (int)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)
{
if (Properties.Settings.Default.disableResCalc)
return;
//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 (Properties.Settings.Default.disableResCalc)
return;
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;
if(!int.TryParse(drp_modulus.SelectedItem.ToString(), out modulus))
modulus = 16;
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;
updownParWidth.Enabled = !check_KeepAR.Checked;
updownParHeight.Enabled = !check_KeepAR.Checked;
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;
crop_top.Value = Source.AutoCropDimensions[0];
crop_bottom.Value = Source.AutoCropDimensions[1];
crop_left.Value = Source.AutoCropDimensions[2];
crop_right.Value = Source.AutoCropDimensions[3];
}
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) // display aspect = (width * par_width) / (height * par_height)
return new Size((Source.ParVal.Width * Source.Resolution.Width), (Source.ParVal.Height * Source.Resolution.Height));
return new Size(0, 0); // Fall over to 16:9 and hope for the best
}
}
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)
{
default:
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;
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);
if (double.IsNaN(disWidthLoose))
disWidthLoose = 0;
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);
}
// Hidden UI feature to drop the MaxWidth / Height with the MaxWidth/Height label is double clicked
private void lbl_max_DoubleClick(object sender, EventArgs e)
{
PresetMaximumResolution = new Size(0,0);
if (PictureSettingsChanged != null)
PictureSettingsChanged(this, new EventArgs());
}
}
}