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);
}
}
}