--- /dev/null
+// --------------------------------------------------------------------------------------------------------------------\r
+// <copyright file="PictureSize.cs" company="HandBrake Project (http://handbrake.fr)">\r
+// This file is part of the HandBrake source code - It may be used under the terms of the GNU General Public License.\r
+// </copyright>\r
+// <summary>\r
+// Defines the PictureSize type.\r
+// </summary>\r
+// --------------------------------------------------------------------------------------------------------------------\r
+\r
+namespace HandBrakeWPF.Helpers\r
+{\r
+ using System;\r
+ using System.Runtime.InteropServices;\r
+\r
+ using HandBrake.Interop.HbLib;\r
+ using HandBrake.Interop.Model;\r
+ using HandBrake.Interop.Model.Encoding;\r
+\r
+ /// <summary>\r
+ /// The picture size Helpers\r
+ /// </summary>\r
+ public class PictureSize\r
+ {\r
+ /// <summary>\r
+ /// The picture settings job.\r
+ /// </summary>\r
+ public class PictureSettingsJob\r
+ {\r
+ /// <summary>\r
+ /// Gets or sets the crop.\r
+ /// </summary>\r
+ public Cropping Crop { get; set; }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the modulus.\r
+ /// </summary>\r
+ public int? Modulus { get; set; }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the par w.\r
+ /// </summary>\r
+ public int ParW { get; set; }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the par h.\r
+ /// </summary>\r
+ public int ParH { get; set; }\r
+\r
+ /// <summary>\r
+ /// Gets or sets a value indicating whether itu par.\r
+ /// </summary>\r
+ public bool ItuPar { get; set; }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the width.\r
+ /// </summary>\r
+ public int Width { get; set; }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the height.\r
+ /// </summary>\r
+ public int Height { get; set; }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the anamorphic mode.\r
+ /// </summary>\r
+ public Anamorphic AnamorphicMode { get; set; }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the max width.\r
+ /// </summary>\r
+ public int MaxWidth { get; set; }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the max height.\r
+ /// </summary>\r
+ public int MaxHeight { get; set; }\r
+\r
+ /// <summary>\r
+ /// Gets or sets a value indicating whether keep display aspect.\r
+ /// </summary>\r
+ public bool KeepDisplayAspect { get; set; }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the dar width.\r
+ /// </summary>\r
+ public int DarWidth { get; set; }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the dar height.\r
+ /// </summary>\r
+ public int DarHeight { get; set; }\r
+ }\r
+\r
+ /// <summary>\r
+ /// The picture settings title.\r
+ /// </summary>\r
+ public class PictureSettingsTitle\r
+ {\r
+ /// <summary>\r
+ /// Gets or sets the width.\r
+ /// </summary>\r
+ public int Width { get; set; }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the height.\r
+ /// </summary>\r
+ public int Height { get; set; }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the par w.\r
+ /// </summary>\r
+ public int ParW { get; set; }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the par h.\r
+ /// </summary>\r
+ public int ParH { get; set; }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the aspect.\r
+ /// </summary>\r
+ public double Aspect { get; set; }\r
+ }\r
+\r
+ /// <summary>\r
+ /// The anamorphic result.\r
+ /// </summary>\r
+ public class AnamorphicResult\r
+ {\r
+ /// <summary>\r
+ /// Gets or sets the output width.\r
+ /// </summary>\r
+ public int OutputWidth { get; set; }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the output height.\r
+ /// </summary>\r
+ public double OutputHeight { get; set; }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the output par width.\r
+ /// </summary>\r
+ public double OutputParWidth { get; set; }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the output par height.\r
+ /// </summary>\r
+ public double OutputParHeight { get; set; }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Calculates job width and height for anamorphic content,\r
+ /// * @param output_width Pointer to returned storage width\r
+ /// * @param output_height Pointer to returned storage height\r
+ /// * @param output_par_width Pointer to returned pixel width\r
+ /// * @param output_par_height Pointer to returned pixel height\r
+ /// </summary>\r
+ /// <param name="job">\r
+ /// The job.\r
+ /// </param>\r
+ /// <param name="title">\r
+ /// The title.\r
+ /// </param>\r
+ /// <param name="output_width">\r
+ /// The output_width.\r
+ /// </param>\r
+ /// <param name="output_height">\r
+ /// The output_height.\r
+ /// </param>\r
+ /// <param name="output_par_width">\r
+ /// The output_par_width.\r
+ /// </param>\r
+ /// <param name="output_par_height">\r
+ /// The output_par_height.\r
+ /// </param>\r
+ public static void hb_set_anamorphic_size(PictureSettingsJob job, PictureSettingsTitle title,\r
+ out int output_width, out int output_height,\r
+ out int output_par_width, out int output_par_height)\r
+ {\r
+ /* Initialise the Output Width / Height */\r
+ output_width = 0;\r
+ output_height = 0;\r
+\r
+ /* Set up some variables to make the math easier to follow. */\r
+ int cropped_width = title.Width - job.Crop.Left - job.Crop.Right;\r
+ int cropped_height = title.Height - job.Crop.Top - job.Crop.Bottom;\r
+ double storage_aspect = cropped_width / (double)cropped_height;\r
+ int mod = job.Modulus.HasValue ? job.Modulus.Value : 16;\r
+ double aspect = title.Aspect;\r
+\r
+ long pixel_aspect_width = job.ParW;\r
+ long pixel_aspect_height = job.ParH;\r
+\r
+ /* If a source was really NTSC or PAL and the user specified ITU PAR
+ values, replace the standard PAR values with the ITU broadcast ones. */\r
+ if (title.Width == 720 && job.ItuPar)\r
+ {\r
+ // convert aspect to a scaled integer so we can test for 16:9 & 4:3\r
+ // aspect ratios ignoring insignificant differences in the LSBs of\r
+ // the floating point representation.\r
+ int iaspect = (int)Math.Round(aspect * 9.0, 0); // int iaspect = aspect * 9.; TODO CHECK THIS\r
+\r
+ /* Handle ITU PARs */\r
+ if (title.Height == 480)\r
+ {\r
+ /* It's NTSC */\r
+ if (iaspect == 16)\r
+ {\r
+ /* It's widescreen */\r
+ pixel_aspect_width = 40;\r
+ pixel_aspect_height = 33;\r
+ }\r
+ else if (iaspect == 12)\r
+ {\r
+ /* It's 4:3 */\r
+ pixel_aspect_width = 10;\r
+ pixel_aspect_height = 11;\r
+ }\r
+ }\r
+ else if (title.Height == 576)\r
+ {\r
+ /* It's PAL */\r
+ if (iaspect == 16)\r
+ {\r
+ /* It's widescreen */\r
+ pixel_aspect_width = 16;\r
+ pixel_aspect_height = 11;\r
+ }\r
+ else if (iaspect == 12)\r
+ {\r
+ /* It's 4:3 */\r
+ pixel_aspect_width = 12;\r
+ pixel_aspect_height = 11;\r
+ }\r
+ }\r
+ }\r
+\r
+ /* Figure out what width the source would display at. */\r
+ int source_display_width = (int)(cropped_width * (double)pixel_aspect_width / (double)pixel_aspect_height); // TODO Check casting\r
+\r
+ /*
+ 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
+ */\r
+ int width, height;\r
+ int maxWidth, maxHeight;\r
+\r
+ maxWidth = Macros.MultipleModDown(job.MaxWidth, mod);\r
+ maxHeight = Macros.MultipleModDown(job.MaxHeight, mod);\r
+\r
+ switch (job.AnamorphicMode)\r
+ {\r
+ case Anamorphic.Strict:\r
+ /* Strict anamorphic */\r
+ output_width = Macros.MultipleMod(cropped_width, 2);\r
+ output_height = Macros.MultipleMod(cropped_height, 2);\r
+ // adjust the source PAR for new width/height\r
+ // new PAR = source PAR * ( old width / new_width ) * ( new_height / old_height )\r
+ pixel_aspect_width = (long)title.ParW * cropped_width * (output_height);\r
+ pixel_aspect_height = (long)title.ParH * (output_width) * cropped_height;\r
+ break;\r
+\r
+ case Anamorphic.Loose:\r
+ /* "Loose" anamorphic.
+ - Uses mod16-compliant dimensions,
+ - Allows users to set the width
+ */\r
+ width = job.Width;\r
+ // height: Gets set later, ignore user job->height value\r
+\r
+ /* Gotta handle bounding dimensions.
+ If the width is too big, just reset it with no rescaling.
+ Instead of using the aspect-scaled job height,
+ we need to see if the job width divided by the storage aspect
+ is bigger than the max. If so, set it to the max (this is sloppy).
+ If not, set job height to job width divided by storage aspect.
+ */\r
+\r
+ /* Time to get picture width that divide cleanly.*/\r
+ width = Macros.MultipleMod(width, mod);\r
+\r
+ if (maxWidth != 0 && (maxWidth < job.Width))\r
+ width = maxWidth;\r
+\r
+ /* Verify these new dimensions don't violate max height and width settings */\r
+ height = (int)Math.Round(((double)width / storage_aspect) + 0.5, 0); // TODO Check Casting and rounding\r
+\r
+ /* Time to get picture height that divide cleanly.*/\r
+ height = Macros.MultipleMod(height, mod);\r
+\r
+ if (maxHeight != 0 && (maxHeight < height))\r
+ {\r
+ height = maxHeight;\r
+ width = (int)Math.Round(((double)height * storage_aspect) + 0.5, 0); // TODO Check asting and arounding.\r
+ width = Macros.MultipleMod(width, mod);\r
+ }\r
+\r
+ /* 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. */\r
+ pixel_aspect_width = (long)height * cropped_width * pixel_aspect_width;\r
+ pixel_aspect_height = (long)width * cropped_height * pixel_aspect_height;\r
+\r
+ /* Pass the results back to the caller */\r
+ output_width = width;\r
+ output_height = height;\r
+ break;\r
+\r
+ case Anamorphic.Custom:\r
+ /* Anamorphic 3: Power User Jamboree
+ - Set everything based on specified values */\r
+\r
+ /* Use specified storage dimensions */\r
+ storage_aspect = job.Width / (double)job.Height;\r
+ width = job.Width;\r
+ height = job.Height;\r
+\r
+ /* Time to get picture dimensions that divide cleanly.*/\r
+ width = Macros.MultipleMod(width, mod);\r
+ height = Macros.MultipleMod(height, mod);\r
+\r
+ /* Bind to max dimensions */\r
+ if (maxWidth != 0 && width > maxWidth)\r
+ {\r
+ width = maxWidth;\r
+ // If we are keeping the display aspect, then we are going\r
+ // to be modifying the PAR anyway. So it's preferred\r
+ // to let the width/height stray some from the original\r
+ // requested storage aspect.\r
+ //\r
+ // But otherwise, PAR and DAR will change the least\r
+ // if we stay as close as possible to the requested\r
+ // storage aspect.\r
+ if (!job.KeepDisplayAspect)\r
+ {\r
+ height = (int)Math.Round(((double)width / storage_aspect) + 0.5, 0); // TODO Check rounding and casting\r
+ height = Macros.MultipleMod(height, mod);\r
+ }\r
+ }\r
+ if (maxHeight != 0 && height > maxHeight)\r
+ {\r
+ height = maxHeight;\r
+ // Ditto, see comment above\r
+ if (!job.KeepDisplayAspect)\r
+ {\r
+ width = (int)Math.Round(((double)height * storage_aspect) + 0.5, 0); // TODO check Rounding and casting\r
+ width = Macros.MultipleMod(width, mod);\r
+ }\r
+ }\r
+\r
+ /* That finishes the storage dimensions. On to display. */\r
+ if (job.DarWidth != 0 && job.DarHeight != 0)\r
+ {\r
+ /* We need to adjust the PAR to produce this aspect. */\r
+ pixel_aspect_width = (long)height * job.DarWidth / job.DarHeight;\r
+ pixel_aspect_height = width;\r
+ }\r
+ else\r
+ {\r
+ /* If we're doing ana 3 and not specifying a DAR, care needs to be taken.
+ This indicates a PAR is potentially being set by the interface. But
+ this is an output PAR, to correct a source, and it should not be assumed
+ that it properly creates a display aspect ratio when applied to the source,
+ which could easily be stored in a different resolution. */\r
+ if (job.KeepDisplayAspect)\r
+ {\r
+ /* We can ignore the possibility of a PAR change */\r
+ pixel_aspect_width = (int)Math.Round((long)height * ((double)source_display_width / (double)cropped_height), 0); // TODO Check rounding and casting\r
+ pixel_aspect_height = width;\r
+ }\r
+ else\r
+ {\r
+ int output_display_width = (int)Math.Round(width * (double)pixel_aspect_width / (double)pixel_aspect_height, 0); // TODO Check rounding and casting.\r
+ pixel_aspect_width = output_display_width;\r
+ pixel_aspect_height = width;\r
+ }\r
+ }\r
+\r
+ /* Back to caller */\r
+ output_width = width;\r
+ output_height = height;\r
+ break;\r
+ }\r
+\r
+ /* While x264 is smart enough to reduce fractions on its own, libavcodec
+ * needs some help with the math, so lose superfluous factors. */\r
+ hb_limit_rational64(out pixel_aspect_width, out pixel_aspect_height,\r
+ pixel_aspect_width, pixel_aspect_height, 65535);\r
+ hb_reduce(out output_par_width, out output_par_height,\r
+ (int)pixel_aspect_width, (int)pixel_aspect_height);\r
+ }\r
+\r
+ /// <summary>\r
+ /// The hb_set_anamorphic_size_native.\r
+ /// </summary>\r
+ /// <param name="job">\r
+ /// The job.\r
+ /// </param>\r
+ /// <param name="title">\r
+ /// The title.\r
+ /// </param>\r
+ /// <returns>\r
+ /// The <see cref="AnamorphicResult"/> object.\r
+ /// </returns>\r
+ public static AnamorphicResult hb_set_anamorphic_size_native(PictureSettingsJob job, PictureSettingsTitle title)\r
+ {\r
+ int outputHeight = 0;\r
+ int outputParHeight = 0;\r
+ int outputParWidth = 0;\r
+ int outputWidth = 0;\r
+\r
+ hb_job_s nativeJob = new hb_job_s\r
+ {\r
+ modulus = job.Modulus.HasValue ? job.Modulus.Value : 16,\r
+ anamorphic =\r
+ new hb_anamorphic_substruct\r
+ {\r
+ par_width = job.ParW,\r
+ par_height = job.ParH,\r
+ itu_par = 0,\r
+ mode = (int)job.AnamorphicMode,\r
+ dar_width = 0,\r
+ dar_height = 0,\r
+ keep_display_aspect = job.KeepDisplayAspect ? 1 : 0\r
+ },\r
+ maxWidth = title.Width,\r
+ maxHeight = title.Height,\r
+ keep_ratio = job.KeepDisplayAspect ? 1 : 0,\r
+ width = job.Width,\r
+ height = job.Height,\r
+ crop = new[] { job.Crop.Top, job.Crop.Bottom, job.Crop.Left, job.Crop.Right }\r
+ };\r
+\r
+ hb_title_s title_s = new hb_title_s\r
+ {\r
+ crop = new[] { job.Crop.Top, job.Crop.Bottom, job.Crop.Left, job.Crop.Right },\r
+ width = title.Width,\r
+ height = title.Height,\r
+ pixel_aspect_width = title.ParW,\r
+ pixel_aspect_height = title.ParH,\r
+ aspect = 0\r
+ };\r
+\r
+ IntPtr pointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(hb_title_s)));\r
+ Marshal.StructureToPtr(title_s, pointer, false);\r
+ nativeJob.title = pointer;\r
+\r
+ HBFunctions.hb_set_anamorphic_size(\r
+ ref nativeJob, ref outputWidth, ref outputHeight, ref outputParWidth, ref outputParHeight);\r
+\r
+ return new AnamorphicResult { OutputWidth = outputWidth, OutputHeight = outputHeight, OutputParWidth = outputParWidth, OutputParHeight = outputParHeight };\r
+ }\r
+\r
+ /// <summary>\r
+ /// The hb_limit_rational 64.\r
+ /// </summary>\r
+ /// <param name="x">\r
+ /// The x.\r
+ /// </param>\r
+ /// <param name="y">\r
+ /// The y.\r
+ /// </param>\r
+ /// <param name="num">\r
+ /// The num.\r
+ /// </param>\r
+ /// <param name="den">\r
+ /// The den.\r
+ /// </param>\r
+ /// <param name="limit">\r
+ /// The limit.\r
+ /// </param>\r
+ private static void hb_limit_rational64(out long x, out long y, long num, long den, long limit)\r
+ {\r
+ hb_reduce64(out num, out den, num, den);\r
+ if (num < limit && den < limit)\r
+ {\r
+ x = num;\r
+ y = den;\r
+ return;\r
+ }\r
+\r
+ if (num > den)\r
+ {\r
+ double div = (double)limit / num;\r
+ num = limit;\r
+ den *= (int)Math.Round(div, 0); // TODO Check cast and rounding.\r
+ }\r
+ else\r
+ {\r
+ double div = (double)limit / den;\r
+ den = limit;\r
+ num *= (int)Math.Round(div, 0); // TODO Check cast and rounding.\r
+ }\r
+\r
+ x = num;\r
+ y = den;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Given a numerator (num) and a denominator (den), reduce them to an\r
+ /// equivalent fraction and store the result in x and y.\r
+ /// </summary>\r
+ /// <param name="x">\r
+ /// The x.\r
+ /// </param>\r
+ /// <param name="y">\r
+ /// The y.\r
+ /// </param>\r
+ /// <param name="num">\r
+ /// The num.\r
+ /// </param>\r
+ /// <param name="den">\r
+ /// The den.\r
+ /// </param>\r
+ private static void hb_reduce64(out long x, out long y, long num, long den)\r
+ {\r
+ // find the greatest common divisor of num & den by Euclid's algorithm\r
+ long n = num, d = den;\r
+ while (d != 0)\r
+ {\r
+ long t = d;\r
+ d = n % d;\r
+ n = t;\r
+ }\r
+\r
+ // at this point n is the gcd. if it's non-zero remove it from num\r
+ // and den. Otherwise just return the original values.\r
+ if (n != 0)\r
+ {\r
+ num /= n;\r
+ den /= n;\r
+ }\r
+\r
+ x = num;\r
+ y = den;\r
+ }\r
+\r
+ /// <summary>\r
+ /// Given a numerator (num) and a denominator (den), reduce them to an\r
+ /// equivalent fraction and store the result in x and y.\r
+ /// </summary>\r
+ /// <param name="x">\r
+ /// The x.\r
+ /// </param>\r
+ /// <param name="y">\r
+ /// The y.\r
+ /// </param>\r
+ /// <param name="num">\r
+ /// The num.\r
+ /// </param>\r
+ /// <param name="den">\r
+ /// The den.\r
+ /// </param>\r
+ private static void hb_reduce(out int x, out int y, int num, int den)\r
+ {\r
+ // find the greatest common divisor of num & den by Euclid's algorithm\r
+ int n = num, d = den;\r
+ while (d != 0)\r
+ {\r
+ int t = d;\r
+ d = n % d;\r
+ n = t;\r
+ }\r
+\r
+ // at this point n is the gcd. if it's non-zero remove it from num\r
+ // and den. Otherwise just return the original values.\r
+ if (n != 0)\r
+ {\r
+ x = num / n;\r
+ y = den / n;\r
+ }\r
+ else\r
+ {\r
+ x = num;\r
+ y = den;\r
+ }\r
+ }\r
+ }\r
+}\r