示例#1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="ThumbnailWriter"/> class.
        /// </summary>
        /// <param name="creator">The <see cref="ThumbnailCreator"/>
        /// (only used to get a jpeg compression encoder).</param>
        /// <param name="tgrid">The <see cref="ThumbnailGrid"/>.</param>
        /// <param name="directory">The directory to write the thumbnail page.</param>
        /// <param name="displayFilename">The display name of the <see cref="AVFileSet"/>
        /// from which the thumbnails are generated.</param>
        /// <param name="outTemplate">The template used to generate page filename.</param>
        /// <param name="nFiles">The number of files in set (>0 for multi-part videos).</param>
        /// <param name="stats">The stats of the <see cref="AVFileSet"/> to display 
        /// in header.</param>
        /// <param name="duration">The duration of the <see cref="AVFileSet"/>.</param>
        public ThumbnailWriter(ThumbnailCreator creator,
            ThumbnailGrid tgrid,
            string directory,
            string displayFilename,
            string outTemplate,
            int nFiles,
            string stats,
            TimeSpan duration)
        {
            this._creator = creator;
            this._tgrid = tgrid;
            this._directory = directory;
            this._displayFilename = displayFilename;
            this._outTemplate = outTemplate;
            this._nFiles = nFiles;
            this._stats = stats;
            this._duration = duration;

            _thumbnailPage = null;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ThumbnailMultiWriter"/> class.
        /// </summary>
        /// <param name="creator">The <see cref="ThumbnailCreator"/>
        /// (only used to get a jpeg compression encoder).</param>
        /// <param name="tgrid">The <see cref="ThumbnailGrid"/>.</param>
        /// <param name="directory">The directory to write thumbnail pages.</param>
        /// <param name="displayFilename">The display name of the <see cref="AVFileSet"/>
        /// from which the thumbnails are generated.</param>
        /// <param name="outTemplate">The template used to generate page filenames.</param>
        /// <param name="nFiles">The number of files in set (>0 for multi-part videos).</param>
        /// <param name="interval">The interval between thumbnails.</param>
        /// <param name="stats">The stats of the <see cref="AVFileSet"/> to display
        /// in header.</param>
        /// <param name="duration">The duration of the <see cref="AVFileSet"/>.</param>
        /// <param name="nPages">The total number of thumbnail pages.</param>
        public ThumbnailMultiWriter(ThumbnailCreator creator,
            ThumbnailGrid tgrid,
            string directory,
            string displayFilename,
            string outTemplate,
            int nFiles,
            TimeSpan interval,
            string stats, TimeSpan duration, int nPages)
        {
            this._creator = creator;
            this._tgrid = tgrid;
            this._directory = directory;
            this._displayFilename = displayFilename;
            this._outTemplate = outTemplate;
            this._nFiles = nFiles;
            this._interval = interval;
            this._stats = stats;
            this._duration = duration;
            this._nPages = nPages;

            this._pageNum = 1;
            _thumbnailPage = null;
        }
示例#3
0
        /// <summary>
        /// Initializes a new instance of the <see cref="ThumbnailPage"/> class.
        /// </summary>
        /// <param name="creator">The <see cref="ThumbnailCreator"/>
        /// (only used to get a jpeg compression encoder).</param>
        /// <param name="tgrid">The <see cref="ThumbnailGrid"/> that specifies
        /// the page layout.</param>
        /// <param name="displayFilename">The display name of the <see cref="AVFileSet"/>
        /// from which the thumbnails are generated.</param>
        /// <param name="filename">The fullpath of thumbnail page to create.</param>
        /// <param name="nFiles">The number of files in set (>0 for multi-part videos).</param>
        /// <param name="time">The time of first thumbnail on page.</param>
        /// <param name="pageNum">The page number (0 for overview page).</param>
        /// <param name="duration">The duration of the <see cref="AVFileSet"/>.</param>
        /// <param name="nPages">The total number of thumbnail pages.</param>
        /// <param name="stats">The stats of the <see cref="AVFileSet"/> to display
        /// in header.</param>
        public ThumbnailPage(ThumbnailCreator creator, 
            ThumbnailGrid tgrid,
            string displayFilename, string filename, int nFiles, TimeSpan time,
            int pageNum, TimeSpan duration, int nPages, string stats)
        {
            this._creator = creator;
            this._tgrid = tgrid;
            this._filename = filename;

            _pageBitmap = new System.Drawing.Bitmap (tgrid.Layout.Width,
                                                    tgrid.Layout.Height);
            _graphics = System.Drawing.Graphics.FromImage (_pageBitmap);

            _graphics.PageUnit = System.Drawing.GraphicsUnit.Pixel;

            _graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
            //_graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
            _graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
            //_graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
            _graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.None;

            // can't use TextRenderingHint.ClearTypeGridFit with CompositingMode.SourceCopy
            //_graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
            //_graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
            _graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;

            // Affects image resizing
            _graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

            // Affects anti-aliasing of filled edges
            //_graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            _graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;

            _font = new System.Drawing.Font ("Arial", (float) (7 * creator.TNSettings.ScaleFactor),
                                             System.Drawing.FontStyle.Bold);
            _brushWhite = new System.Drawing.SolidBrush (System.Drawing.Color.White);
            _brushBlack = new System.Drawing.SolidBrush (System.Drawing.Color.Black);
            _borderPen = new System.Drawing.Pen (System.Drawing.Color.Wheat,
                                                 tgrid.Layout.Border <= 1 ? 0 : tgrid.Layout.Border);
            _borderHilightPen = new System.Drawing.Pen (System.Drawing.Color.Red,
                                                 tgrid.Layout.Border <= 1 ? 0 : tgrid.Layout.Border);
            //_borderPen.Alignment = System.Drawing.Drawing2D.PenAlignment.Inset;
            //_borderPen.LineJoin = System.Drawing.Drawing2D.LineJoin.Miter;

            _graphics.FillRectangle (_brushBlack, 0, 0,
                                    tgrid.Layout.Width,
                                    tgrid.Layout.Height);

            _thumbFormat = new System.Drawing.StringFormat ();
            switch (creator.TNSettings.LabelPosition)
                {
                case ThumbnailSettings.LabelPositions.None:
                case ThumbnailSettings.LabelPositions.LowerRight:
                    {
                    _thumbFormat.LineAlignment = System.Drawing.StringAlignment.Far;
                    _thumbFormat.Alignment = System.Drawing.StringAlignment.Far;
                    break;
                    }
                case ThumbnailSettings.LabelPositions.LowerLeft:
                    {
                    _thumbFormat.LineAlignment = System.Drawing.StringAlignment.Far;
                    _thumbFormat.Alignment = System.Drawing.StringAlignment.Near;
                    break;
                    }
                case ThumbnailSettings.LabelPositions.UpperRight:
                    {
                    _thumbFormat.LineAlignment = System.Drawing.StringAlignment.Near;
                    _thumbFormat.Alignment = System.Drawing.StringAlignment.Far;
                    break;
                    }
                case ThumbnailSettings.LabelPositions.UpperLeft:
                    {
                    _thumbFormat.LineAlignment = System.Drawing.StringAlignment.Near;
                    _thumbFormat.Alignment = System.Drawing.StringAlignment.Near;
                    break;
                    }
                }

            _headerFormat = new System.Drawing.StringFormat ();
            _headerFormat.LineAlignment = System.Drawing.StringAlignment.Center;
            _headerFormat.Alignment = System.Drawing.StringAlignment.Near;

            float inset = 2*tgrid.Layout.Margin;
            System.Drawing.RectangleF headerRectF =
                new System.Drawing.RectangleF (inset, 0,
                    this._tgrid.Layout.Width - 2*inset,
                    this._tgrid.Layout.HeaderHeight);
            //_graphics.DrawRectangle (_borderPen,
            //                         headerRectF.X,
            //                         headerRectF.Y,
            //                         headerRectF.Width,
            //                         headerRectF.Height);

            string leftSide;
            string timeformat = @"h\:mm\:ss";
            if (time.Milliseconds > 0 && creator.TNSettings.AlwaysShowMilliseconds)
                timeformat = @"h\:mm\:ss\.ffff";

            if (pageNum > 0)
                leftSide = String.Format ("{0} / {1} ({2} of {3})",
                                         time.ToString (timeformat),
                                         duration.ToString (@"h\:mm\:ss"),
                                         pageNum, nPages);
            else
                leftSide = String.Format ("{0}",
                                          duration.ToString (@"h\:mm\:ss"));
            if (nFiles > 0)
                leftSide += String.Format (" {0} files", nFiles);

            using (System.Drawing.Font headerFont = new System.Drawing.Font (
                ThumbnailPageLayout.FONT_NAME,
                (float) (ThumbnailPageLayout.FONT_SIZE * creator.TNSettings.ScaleFactor),
                System.Drawing.FontStyle.Bold))
                {
                _graphics.DrawString (leftSide,
                            headerFont, _brushWhite, headerRectF, _headerFormat);

                _headerFormat.Alignment = System.Drawing.StringAlignment.Far;
                _graphics.DrawString (stats,
                                      headerFont, _brushWhite, headerRectF, _headerFormat);
                float left = headerRectF.Left +
                                _graphics.MeasureString(leftSide, headerFont).Width;
                float right = headerRectF.Right -
                                _graphics.MeasureString (stats, headerFont).Width;
                headerRectF = new System.Drawing.RectangleF (
                    left,
                    headerRectF.Top,
                    right - left,
                    headerRectF.Height);

                _headerFormat.Alignment = System.Drawing.StringAlignment.Center;
                _graphics.DrawString (displayFilename,
                                      headerFont, _brushWhite, headerRectF, _headerFormat);
                }

            _pageNum = pageNum;
            _currentCol = 0;
            _currentRow = 0;
        }
        /// <summary>
        /// Dumps the Column/Rows determination (based on aspect ratio) Plot.
        /// </summary>
        /// <param name="filename">name of the image file to write to.</param>
        /// <param name="plotThresholds">if set to <c>true</c> plot threshold info.</param>
        private void DumpColRowsPlot(string filename, bool plotThresholds)
        {
            bool detailsPage;
            int nThumbs, nCols, nRows;
            double crossoverThreshold;

            filename = System.IO.Path.GetFileNameWithoutExtension (filename) + ".png";
            if (System.IO.File.Exists (filename))
                {
                Console.Write ("'{0}' already exists. Overwrite (Y/N) [N]?", filename);
                string answer = Console.ReadLine ();
                answer = answer.Trim ().ToLower ();
                if (answer != "y" && answer != "yes")
                    {
                    Console.Write ("Aborting operation.");
                    return;
                    }
                }

            if (_tnSettings.Interval.TotalSeconds > 0)
                {
                detailsPage = true;
                nThumbs = _tnSettings.DetailThumbs;
                nCols = _tnSettings.DetailColumns;
                nRows = _tnSettings.DetailRows;
                crossoverThreshold = _tnSettings.DetailThreshold;
                }
            else
                {
                detailsPage = false;
                nThumbs = _tnSettings.OverviewThumbs;
                nCols = _tnSettings.OverviewColumns;
                nRows = _tnSettings.OverviewRows;
                crossoverThreshold = _tnSettings.OverviewThreshold;
                }

            THelper.Information ("");
            THelper.Information ("Dumping Column/Rows Determination Plot");
            THelper.Information ("Page:             {0}  {1}x{2} ({3:F2}:1)",
                                 detailsPage ? "Detail" : "Overview",
                                 _tnSettings.Width, _tnSettings.Height,
                                 _tnSettings.AspectRatio);
            THelper.Information ("Layout Mode:      {0}", _tnSettings.LayoutMode);
            if (_tnSettings.LayoutMode == ThumbnailSettings.LayoutModes.Auto)
                THelper.Information ("Threshold:        {0:F2}", crossoverThreshold);
            if (_tnSettings.LayoutMode == ThumbnailSettings.LayoutModes.Actual)
                {
                THelper.Information ("Actual:           {1}x{2}", nCols, nRows);
                }
            else
                {
                THelper.Information ("RC Optimization:  {0}", _tnSettings.RCOptimization);
                THelper.Information ("Max RC Opt Steps: {0}", _tnSettings.MaxOptimizationSteps);
                THelper.Information ("Desired:          {0} thumbs", nThumbs);
                THelper.Information ("Minimum:          {0} columns, {1} rows",
                                     _tnSettings.MinColumns, _tnSettings.MinRows);
                }
            THelper.Information ("");

            System.Drawing.Font titleFont =
                new System.Drawing.Font ("Ariel", 24, System.Drawing.FontStyle.Bold);
            System.Drawing.Font subTitleFont =
                new System.Drawing.Font ("Ariel", 13, System.Drawing.FontStyle.Bold);
            System.Drawing.Font axisLabelFont =
                new System.Drawing.Font ("Ariel", 20, System.Drawing.FontStyle.Bold);
            System.Drawing.Font axisFont =
                new System.Drawing.Font ("Ariel", 12, System.Drawing.FontStyle.Bold);
            System.Drawing.Font annotationFont =
                new System.Drawing.Font ("Ariel", 10, System.Drawing.FontStyle.Regular);
            System.Drawing.Font annotationItFont =
                new System.Drawing.Font ("Ariel", 10, System.Drawing.FontStyle.Italic);

            Charting.Chart chart = new Charting.Chart ();
            Charting.ChartArea chartArea = chart.ChartAreas.Add ("Wasted");
            Charting.Legend legend = new Charting.Legend ("Wasted");
            legend.DockedToChartArea = "Wasted";
            legend.Font = axisFont;
            legend.Docking = Charting.Docking.Bottom;
            legend.Alignment = System.Drawing.StringAlignment.Far;
            legend.LegendStyle = Charting.LegendStyle.Column;
            chart.Legends.Add (legend);

            Charting.LabelStyle labelStyle1 = new Charting.LabelStyle();
            labelStyle1.Font = axisFont;
            Charting.LabelStyle labelStyle2 = new Charting.LabelStyle ();
            labelStyle2.Font = axisFont;
            Charting.LabelStyle labelStyle3 = new Charting.LabelStyle ();
            labelStyle3.Font = axisFont;

            chart.BackColor = System.Drawing.Color.Wheat;

            chartArea.BorderWidth = 3;
            chartArea.BorderDashStyle = Charting.ChartDashStyle.Solid;
            //chartArea.BorderColor = System.Drawing.Color.Violet;

            legend.BackColor = System.Drawing.Color.Wheat;

            string titleStr = "Optimum Number of Columns & Rows";
            if (plotThresholds)
                titleStr += "\nUsing % Wasted Thumbnail Width & Height";
            Charting.Title title = chart.Titles.Add (titleStr);
            title.Font = titleFont;
            //subTitle.DockingOffset = -2;

            Charting.TextAnnotation desired = new Charting.TextAnnotation ();
            desired.Font = subTitleFont;
            switch (_tnSettings.LayoutMode)
                {
                case ThumbnailSettings.LayoutModes.Auto:
                    chartArea.BackColor = System.Drawing.Color.Beige;
                    desired.Text = String.Format (
                        "{0} Cols or Rows; Min {1} Cols, {2} Rows; {3} Max Opt Steps",
                        nThumbs, _tnSettings.MinColumns, _tnSettings.MinRows,
                        _tnSettings.MaxOptimizationSteps);
                    break;

                case ThumbnailSettings.LayoutModes.Actual:
                    chartArea.BackColor = System.Drawing.Color.Ivory;
                    desired.Text = String.Format ("{0} Columns and {1} Rows",
                                                  nCols, nRows);
                    break;

                case ThumbnailSettings.LayoutModes.RowPriority:
                    chartArea.BackColor = System.Drawing.Color.Beige;
                    desired.Text = String.Format (
                        "{0} Rows; Min {1} Columns; {2} Max Opt Steps",
                        nThumbs, _tnSettings.MinColumns, _tnSettings.MaxOptimizationSteps);
                    break;

                case ThumbnailSettings.LayoutModes.ColumnPriority:
                    chartArea.BackColor = System.Drawing.Color.AliceBlue;
                    desired.Text = String.Format (
                        "{0} Columns; Min {1} Rows; {2} Max Opt Steps",
                        nThumbs, _tnSettings.MinRows, _tnSettings.MaxOptimizationSteps);
                    break;
                }
            desired.Text += detailsPage ? "\nDetail Page" : "\nOverview Page";
            desired.Text += String.Format ("  {0}x{1} ({2:F2}:1)",
                                           _tnSettings.Width, _tnSettings.Height,
                                           _tnSettings.AspectRatio);

            desired.Alignment = System.Drawing.ContentAlignment.BottomLeft;

            desired.X = 1;
            desired.Y = 95;
            chart.Annotations.Add (desired);

            Charting.TextAnnotation layout = new Charting.TextAnnotation ();
            layout.Font = subTitleFont;
            layout.Text = String.Format("{0} Layout Mode", _tnSettings.LayoutMode);
            if (_tnSettings.LayoutMode != ThumbnailSettings.LayoutModes.Actual)
                layout.Text += String.Format ("\nRow/Column Optimization {0}",
                                              _tnSettings.RCOptimization ? "enabled" : "disabled");
            layout.Alignment = System.Drawing.ContentAlignment.BottomRight;
            layout.X = 77;
            layout.Y = 95;
            chart.Annotations.Add (layout);

            chart.Width = 1280;
            chart.Height = 1024;
            int lineWidth = 5;
            int dotsWidth = 2;

            chartArea.AxisX.Title = "Video Aspect Ratio";
            chartArea.AxisX.TitleFont = axisLabelFont;
            chartArea.AxisX.MajorGrid.Enabled = false;
            chartArea.AxisX.MajorTickMark.Interval = 0.10;
            chartArea.AxisX.Minimum = 1.0;
            chartArea.AxisX.Maximum = 3.0;
            chartArea.AxisX.Interval = 0.5;
            chartArea.AxisX.LineWidth = 3;
            chartArea.AxisX.MajorTickMark.LineWidth = 3;
            chartArea.AxisX.LabelStyle = labelStyle1;
            chartArea.AxisX.LabelStyle.Format = "F2";
            chartArea.AxisX.IsMarginVisible = true;

            if (_tnSettings.LayoutMode == ThumbnailSettings.LayoutModes.Auto &&
                crossoverThreshold > 1.0)
                {
                Charting.StripLine stripLine = new Charting.StripLine ();
                stripLine.IntervalOffset = 0.0;
                stripLine.StripWidth = crossoverThreshold - 1.0;
                stripLine.Interval = 10000;
                stripLine.BackColor = System.Drawing.Color.AliceBlue;
                chartArea.AxisX.StripLines.Add (stripLine);
                }

            chartArea.AxisY.Title = "# of Columns or Rows";
            chartArea.AxisY.TitleFont = axisLabelFont;
            chartArea.AxisY.MajorGrid.Enabled = false;
            chartArea.AxisY.MajorTickMark.Interval = 1.0;
            chartArea.AxisY.LineWidth = 3;
            chartArea.AxisY.MajorTickMark.LineWidth = 3;
            chartArea.AxisY.LabelStyle = labelStyle2;
            //chartArea.AxisY.LabelStyle.IsEndLabelVisible = false;
            chartArea.AxisY.IsMarginVisible = true;

            if (!plotThresholds)
                {
                chartArea.AxisY2.Enabled = Charting.AxisEnabled.True;
                //chartArea.AxisY2.Title = "# of Columns or Rows";
                chartArea.AxisY2.TitleFont = axisLabelFont;
                chartArea.AxisY2.MajorGrid.Enabled = false;
                chartArea.AxisY2.MajorTickMark.Interval = 1.0;
                chartArea.AxisY2.LineWidth = 3;
                chartArea.AxisY2.MajorTickMark.LineWidth = 3;
                chartArea.AxisY2.LabelStyle = labelStyle3;
                //chartArea.AxisY2.LabelStyle.IsEndLabelVisible = false;
                chartArea.AxisY2.IsMarginVisible = true;
                }

            Charting.Series seriesNCols = chart.Series.Add ("# of Columns");
            seriesNCols.ChartType = Charting.SeriesChartType.StepLine;
            seriesNCols.ChartArea = "Wasted";
            seriesNCols.Legend = "Wasted";
            seriesNCols.IsVisibleInLegend = true;
            seriesNCols.BorderWidth = lineWidth;

            Charting.Series seriesNRows = chart.Series.Add ("# of Rows");
            seriesNRows.ChartType = Charting.SeriesChartType.StepLine;
            seriesNRows.ChartArea = "Wasted";
            seriesNRows.Legend = "Wasted";
            seriesNRows.IsVisibleInLegend = true;
            seriesNRows.BorderWidth = lineWidth;

            Charting.Series seriesWW = null;
            Charting.Series seriesWH = null;

            if (plotThresholds)
                {
                chartArea.AxisY2.Title = "% Wasted Thumbnail Width or Height";
                chartArea.AxisY2.TitleFont = axisLabelFont;
                chartArea.AxisY2.MajorGrid.Enabled = false;
                chartArea.AxisY2.LineWidth = 3;
                chartArea.AxisY2.MajorTickMark.LineWidth = 3;
                chartArea.AxisY2.LabelStyle = labelStyle3;
                //chartArea.AxisY2.LabelStyle.IsEndLabelVisible = false;
                chartArea.AxisY2.Maximum = 100.0;

                seriesWW = chart.Series.Add ("%Wasted Width");
                seriesWW.ChartType = Charting.SeriesChartType.Line;
                seriesWW.ChartArea = "Wasted";
                seriesWW.Legend = "Wasted";
                seriesWW.IsVisibleInLegend = true;
                seriesWW.BorderWidth = dotsWidth;
                seriesWW.BorderDashStyle = Charting.ChartDashStyle.Dot;
                seriesWW.YAxisType = Charting.AxisType.Secondary;

                seriesWH = chart.Series.Add ("%Wasted Height");
                seriesWH.ChartType = Charting.SeriesChartType.Line;
                seriesWH.ChartArea = "Wasted";
                seriesWH.Legend = "Wasted";
                seriesWH.IsVisibleInLegend = true;
                seriesWH.BorderWidth = dotsWidth;
                seriesWH.BorderDashStyle = Charting.ChartDashStyle.Dot;
                seriesWH.YAxisType = Charting.AxisType.Secondary;
                }

            ThumbnailCreator creator = new ThumbnailCreator (_tnSettings, null);
            ThumbnailPageLayout container = new ThumbnailPageLayout (_tnSettings);
            ThumbnailPageLayout newContainer;

            double wastedWidth, wastedHeight;
            int extraWidth, extraHeight;
            double extraWidthPercent, extraHeightPercent;

            for (double aspectRatio=1.0; aspectRatio <= 3.0; aspectRatio += 0.01)
                {
                _tnSettings.ThumbAspectRatio = aspectRatio;

                ThumbnailGrid tg = creator.CreateThumbnailGrid(_tnSettings.LayoutMode,
                                                               nThumbs,
                                                               nCols, nRows,
                                                               crossoverThreshold);

                newContainer = tg.Layout;
                container.CalcWasted (_tnSettings, tg, out wastedWidth, out wastedHeight);

                extraWidth = (int) (wastedWidth * tg.ThumbWidth);
                extraHeight = (int) (wastedHeight * tg.ThumbHeight);
                extraWidthPercent = 100.0 * extraWidth / newContainer.Width;
                extraHeightPercent = 100.0 * extraHeight / newContainer.Height;

                THelper.Information (String.Format (
                    "{9,4}x{10,4} ({13,4:F2})  {0,4:F2} {1,2}x{2,2} {3,3:D}x{4,3:D} " +
                    "{5,6:F2}x{6,6:F2} {7,3:D}x{8,3:D}  {11:F1}x{12:F1}",
                    aspectRatio,
                    tg.NColumns, tg.NRows, tg.ThumbWidth, tg.ThumbHeight,
                    wastedWidth, wastedHeight,
                    extraWidth, extraHeight,
                    newContainer.Width, newContainer.Height,
                    extraWidthPercent, extraHeightPercent,
                    newContainer.AspectRatio
                    ));

                int index;

                if (plotThresholds)
                    {
                    index = seriesWW.Points.AddXY (aspectRatio, wastedWidth * 100.0);
                    if (wastedWidth == 0.0)
                        seriesWW.Points[index].IsEmpty = true;

                    index = seriesWH.Points.AddXY (aspectRatio, wastedHeight * 100.0);
                    if (wastedHeight == 0.0)
                        seriesWH.Points[index].IsEmpty = true;
                    }

                seriesNCols.Points.AddXY (aspectRatio, tg.NColumns);
                seriesNRows.Points.AddXY (aspectRatio, tg.NRows);
                }
            chartArea.RecalculateAxesScale ();

            AddARAnnotation (chart, "Fullscreen 4:3\n(1.33)", 1.33, annotationFont, plotThresholds);
            AddARAnnotation (chart, "HD 16:9\n(1.78)", 1.78, annotationFont, plotThresholds);
            AddARAnnotation (chart, "Widescreen\n(1.85)", 1.85, annotationFont, plotThresholds);
            AddARAnnotation (chart, "CinemaScope\n(2.35)", 2.35, annotationFont, plotThresholds);
            AddARAnnotation (chart, "Ultra-Panavision\n(2.76)", 2.76, annotationFont, plotThresholds);

            AddARAnnotation (chart,
                    String.Format ("Layout Threshold\n({0:F2})",
                                crossoverThreshold),
                    crossoverThreshold,
                    _tnSettings.LayoutMode == ThumbnailSettings.LayoutModes.Auto ?
                        annotationFont : annotationItFont,
                    plotThresholds,
                    true);

            if (_tnSettings.RCOptimization && plotThresholds)
                {
                switch (_tnSettings.LayoutMode)
                    {
                    case ThumbnailSettings.LayoutModes.Auto:

                        if (_tnSettings.WidthThreshold == _tnSettings.HeightThreshold)
                            AddThresholdAnnotation (chart,
                                                   String.Format ("Width & Height Threshold\n" +
                                                                 "({0:F2})", _tnSettings.WidthThreshold),
                                                   _tnSettings.WidthThreshold,
                                                   annotationFont);
                        else
                            {
                            AddThresholdAnnotation (chart,
                                                   String.Format ("Width Threshold\n" +
                                                                 "({0:F2})", _tnSettings.WidthThreshold),
                                                   _tnSettings.WidthThreshold,
                                                   annotationFont);
                            AddThresholdAnnotation (chart,
                                                   String.Format ("Height Threshold\n" +
                                                                 "({0:F2})", _tnSettings.HeightThreshold),
                                                   _tnSettings.HeightThreshold,
                                                   annotationFont);
                            }
                        break;

                    case ThumbnailSettings.LayoutModes.RowPriority:
                        AddThresholdAnnotation (chart,
                                               String.Format ("Width Threshold\n" +
                                                             "({0:F2})", _tnSettings.WidthThreshold),
                                               _tnSettings.WidthThreshold,
                                               annotationFont);
                        break;

                    case ThumbnailSettings.LayoutModes.ColumnPriority:
                        AddThresholdAnnotation (chart,
                                               String.Format ("Height Threshold\n" +
                                                             "({0:F2})", _tnSettings.HeightThreshold),
                                               _tnSettings.HeightThreshold,
                                               annotationFont);
                        break;
                    }
                }

            chart.SaveImage (filename, Charting.ChartImageFormat.Png);
            THelper.Information ("'{0}' created.", filename);

            labelStyle1.Dispose ();
            labelStyle2.Dispose ();
            labelStyle3.Dispose ();

            titleFont.Dispose ();
            subTitleFont.Dispose ();
            axisFont.Dispose ();
            annotationFont.Dispose ();
            annotationItFont.Dispose ();
            chart.Dispose ();
        }
        /// <summary>
        /// Processes the file as media item. OBSOLETE
        /// </summary>
        /// <param name="filename">The filename.</param>
        /// <param name="outputDirectory">The output directory.</param>
        private void processFile(string filename, string outputDirectory)
        {
            THelper.Information ("Processing {0} ...", filename);

            DateTime startTime = DateTime.Now;

            MSEEncoder.MediaItem mediaItem;
            try
                {
                mediaItem = new MSEEncoder.MediaItem (filename);
                }
            catch (MSEEncoder.InvalidMediaFileException)
                {
                THelper.Critical("\"" + filename + "\" isn't a video file.");
                return;
                }
            bool hasVideo = (mediaItem.OriginalFileType &
                MSEEncoder.FileType.Video) == MSEEncoder.FileType.Video;
            if (!hasVideo)
                {
                THelper.Critical ("\"" + filename + "\" isn't a video file.");
                return;
                }

            if (_tnSettings.End == TimeSpan.Zero)
                _tnSettings.End = mediaItem.FileDuration - new TimeSpan (0, 0, 5);
            else if (_tnSettings.End < TimeSpan.Zero)
                _tnSettings.End = mediaItem.FileDuration + _tnSettings.End;
            else if (_tnSettings.End > mediaItem.FileDuration)
                _tnSettings.End = mediaItem.FileDuration;

            DateTime mediaItemCreated = DateTime.Now;
            THelper.Information ("{0} to create Microsoft.Encoder.MediaItem.",
                                (mediaItemCreated - startTime).ToString(@"h\:mm\:ss"));

            ThumbnailCreator tg;
            DateTime overviewCreated;
            int nThumbs;
            int originalBorder = _tnSettings.Border;
            _tnSettings.SrcRect =
                new System.Drawing.Rectangle (0, 0,
                                              mediaItem.OriginalVideoSize.Width,
                                              mediaItem.OriginalVideoSize.Height);
            double videoAspect = (double) mediaItem.OriginalVideoSize.Width /
                mediaItem.OriginalVideoSize.Height;
            _tnSettings.ThumbAspectRatio = videoAspect;

            int x, y, newWidth, newHeight;

            if (_cropAspect != 0.0)
                {
                if (_cropAspect > videoAspect)
                    {
                    x = 0;
                    newWidth = mediaItem.OriginalVideoSize.Width;

                    newHeight = (int) (mediaItem.OriginalVideoSize.Width /
                                     _cropAspect + 0.5);
                    y = (int) ((mediaItem.OriginalVideoSize.Height - newHeight)
                        / 2.0);
                    }
                else
                    {
                    y = 0;
                    newHeight = mediaItem.OriginalVideoSize.Height;

                    newWidth = (int) (mediaItem.OriginalVideoSize.Height *
                                      _cropAspect + 0.5);
                    x = (int) ((mediaItem.OriginalVideoSize.Width - newWidth)
                        / 2.0);
                    }
                _tnSettings.SrcRect = new System.Drawing.Rectangle (x, y,
                                                                   newWidth,
                                                                   newHeight);
                _tnSettings.ThumbAspectRatio = (double) newWidth / newHeight;
                }

            if (_stretchAspect != 0.0)
                {
                _tnSettings.ThumbAspectRatio = _stretchAspect;
                }

            if (!_skipOverview)
                {
                _tnSettings.Border = 1;
                tg = new ThumbnailCreator (_tnSettings, _debug);

                nThumbs = tg.GenerateOverviewThumbs (mediaItem, filename, outputDirectory);
                overviewCreated = DateTime.Now;
                THelper.Information ("{0} to create overview thumbnails.",
                                     (overviewCreated - mediaItemCreated).ToString(@"h\:mm\:ss"));
                THelper.Information ("{0} thumbs created. {1:F2} seconds / thumb.",
                       nThumbs, (overviewCreated - mediaItemCreated).TotalSeconds / nThumbs);
                }
            else
                {
                overviewCreated = DateTime.Now;
                THelper.Information ("Overview page skipped.");
                }

            _tnSettings.Border = originalBorder;

            tg = new ThumbnailCreator (_tnSettings, _debug);
            nThumbs = 0;
            if (_tnSettings.Interval.TotalSeconds > 0)
                {
                TimeSpan originalInterval = _tnSettings.Interval;
                if (mediaItem.FileDuration.TotalMinutes < 45 && originalInterval.TotalSeconds > 2)
                    _tnSettings.Interval = new TimeSpan (0, 0, 2);

                nThumbs = tg.GenerateMultiThumbs (mediaItem, filename, outputDirectory);

                _tnSettings.Interval = originalInterval;
                }
            DateTime multiCreated = DateTime.Now;
            if (nThumbs > 0)
                {
                THelper.Information ("{0} to generate multi-page thumbnails.",
                                    (multiCreated - overviewCreated).ToString(@"h\:mm\:ss"));
                THelper.Information ("{0} thumbs created. {1:F2} seconds / thumb.",
                                     nThumbs, (multiCreated - overviewCreated).TotalSeconds / nThumbs);
                }
            else
                {
                THelper.Information ("Multi-page thumbnails skipped.");
                }

            THelper.Information ("{0} overall time to process {1}.",
                                (multiCreated - startTime).ToString(@"h\:mm\:ss"), filename);
            THelper.Information ();
        }
        /// <summary>
        /// Generate thumbnail pages for the set of video files in list.
        /// </summary>
        /// <param name="filenames">A list of video files. Only a single
        /// thumbnail set is generated. Multiple files are 
        /// treated as one long video.</param>
        /// <param name="displayFilename">The display filename to use for the video file
        /// set.</param>
        /// <param name="outputDirectory">The output directory.</param>
        private void ProcessFiles(List<string> filenames, 
            string displayFilename,
            string outputDirectory)
        {
            DateTime startTime = DateTime.Now;

            AVFileSet avFiles = new AVFileSet ();
            MSEEncoder.AudioVideoFile avFile;
            DateTime tempStart, tempEnd;

            foreach (string filename in filenames)
                {
                try
                    {
                    THelper.Information ("Processing {0} ...", filename);
                    tempStart = DateTime.Now;
                    avFile = new MSEEncoder.AudioVideoFile (filename);
                    tempEnd = DateTime.Now;
                    THelper.Debug ("{0} to create Microsoft.Encoder.AudioVideoFile.",
                                         (tempEnd - tempStart).ToString (@"h\:mm\:ss\.ff"));
                    }
                catch (MSEEncoder.InvalidMediaFileException)
                    {
                    THelper.Critical ("\"" + filename + "\" isn't a video file.");
                    return;
                    }

                tempStart = DateTime.Now;
                MSEEncoder.MediaItem mediaItem = new MSEEncoder.MediaItem (avFile);
                tempEnd = DateTime.Now;
                THelper.Debug ("{0} to create Microsoft.Encoder.MediaItem.",
                                     (tempEnd - tempStart).ToString (@"h\:mm\:ss\.ff"));

                bool hasVideo = (mediaItem.OriginalFileType &
                    MSEEncoder.FileType.Video) == MSEEncoder.FileType.Video;
                if (!hasVideo)
                    {
                    THelper.Critical ("\"" + filename + "\" isn't a video file.");
                    return;
                    }

                tempStart = DateTime.Now;
                avFile.CalculateDuration (AVFileSet.IndexProgessHandler);
                Console.Write ("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
                tempEnd = DateTime.Now;
                THelper.Debug ("{0} to run CalculateDuration.",
                                     (tempEnd - tempStart).ToString (@"h\:mm\:ss\.ff"));
                THelper.Debug ("AudioVideoFile.Duration {0} {1}.",
                                     avFile.Duration.ToString (@"h\:mm\:ss\.ffff"),
                                     filename);
                THelper.Debug ("VideoStream.Duration    {0} {1}.",
                        AVFileSet.GetVideoDuration(avFile).ToString (@"h\:mm\:ss\.ffff"),
                        filename);
                THelper.Debug ("AudioStream.Duration    {0} {1}.",
                        AVFileSet.GetAudioDuration(avFile).ToString (@"h\:mm\:ss\.ffff"),
                        filename);
                THelper.Debug ("AudioStream channels =  {0} {1}.",
                                     AVFileSet.GetAudioChannels(avFile),
                                     filename);
                avFiles.Add (avFile);
                }
            avFiles.UpdateTotals ();

            DateTime avFileSetCreated = DateTime.Now;
            THelper.Information ("{0} Total time to create AVFileSet.",
                                (avFileSetCreated - startTime).ToString (@"h\:mm\:ss"));

            THelper.Debug ("Using {0} Layout Mode.", _tnSettings.LayoutMode);

            if (_tnSettings.End == TimeSpan.Zero)
                _tnSettings.End = avFiles.TotalDuration;
            else if (_tnSettings.End < TimeSpan.Zero)
                _tnSettings.End = avFiles.TotalDuration + _tnSettings.End;
            else if (_tnSettings.End > avFiles.TotalDuration)
                _tnSettings.End = avFiles.TotalDuration;
            THelper.Information (
                "Thumbnails Range   {0} -> {1}",
                _tnSettings.Start.ToString(@"h\:mm\:ss\.fff"),
                _tnSettings.End.ToString (@"h\:mm\:ss\.fff"));
            THelper.Information ("Thumbnail Duration {0} (Total {1})",
                                 (_tnSettings.End - _tnSettings.Start).ToString(@"h\:mm\:ss\.fff"),
                                 avFiles.TotalDuration.ToString (@"h\:mm\:ss\.fff"));

            avFile = avFiles[0];
            System.Drawing.Size videoSize = AVFileSet.GetVideoSize(avFile);
            if (outputDirectory != null)
                THelper.Information ("OutputDirectory is \"{0}\"", outputDirectory);

            string baseDirectory = outputDirectory;
            if (outputDirectory == null)
                {
                outputDirectory = ThumbnailCreator.GetDirectoryName (avFile.FileName);
                baseDirectory = outputDirectory;
                if (_tnSettings.SubDirectory.Length > 0)
                    {
                    outputDirectory = System.IO.Path.Combine (outputDirectory, _tnSettings.SubDirectory);
                    if (!System.IO.Directory.Exists (outputDirectory))
                        System.IO.Directory.CreateDirectory (outputDirectory);
                    }
                }

            if (displayFilename == null)
                displayFilename = avFiles.GetDisplayName (baseDirectory);

            ThumbnailCreator creator;
            DateTime overviewCreated;
            int nThumbs;
            int originalBorder = _tnSettings.Border;
            _tnSettings.SrcRect =
                new System.Drawing.Rectangle (0, 0, videoSize.Width, videoSize.Height);
            double videoAspect = (double) videoSize.Width / videoSize.Height;
            _tnSettings.ThumbAspectRatio = videoAspect;
            THelper.Debug ("Video Frame Size   {0}x{1} ({2:F2})",
                           videoSize.Width, videoSize.Height, videoAspect);

            bool manualSrcRect = false;

            if (_srcRect.Width != 0 && _srcRect.Height != 0)
                {
                _tnSettings.SetSrcRect (_srcRect);
                manualSrcRect = true;
                THelper.Information ("Changing video rectangle from 0,0+{0}x{1} to {2},{3}+{4}x{5}",
                    videoSize.Width, videoSize.Height,
                    _srcRect.X, _srcRect.Y, _srcRect.Width, _srcRect.Height);
                }
            else if (_cropAspect != 0.0)
                {
                _tnSettings.AdjustThumbAspectRatio (_cropAspect, videoAspect, videoSize);
                manualSrcRect = true;
                THelper.Information ("Cropping aspect ratio from {0:F3} to {1:F3}",
                    videoAspect, _cropAspect);
                }

            if (_stretchAspect != 0.0)
                {
                _tnSettings.ThumbAspectRatio = _stretchAspect;
                manualSrcRect = true;
                THelper.Information ("Stretching aspect ratio from {0:F3} to {1:F3}",
                    videoAspect, _stretchAspect);
                }

            if (!manualSrcRect && _autoAspectRatio)
                {
                double displayAspect = AVFileSet.GetAspectRatio(avFile);
                if (Math.Abs(videoAspect - displayAspect) > 0.05)
                    {
                    //if (displayAspect > videoAspect)
                    //    displayAspect -= 0.01;
                    //else
                    //    displayAspect += 0.01;
                    _tnSettings.AdjustThumbAspectRatio (displayAspect, videoAspect, videoSize);

                    THelper.Information ("Auto adjusting aspect ratio from {0:F3} to {1:F3}",
                        videoAspect, displayAspect);
                    }
                }

            string outTemplate;
            if (_createOverview)
                {
                //_tnSettings.Border = 1;
                creator = new ThumbnailCreator (_tnSettings, null);

                outTemplate = System.IO.Path.GetFileNameWithoutExtension (displayFilename) +
                              "_overview.jpg";

                nThumbs = creator.GenerateOverviewThumbs (avFiles, displayFilename,
                                                          outTemplate, outputDirectory);
                overviewCreated = DateTime.Now;
                THelper.Information ("{0} to create Overview thumbnails.",
                                     (overviewCreated - avFileSetCreated).ToString (@"h\:mm\:ss"));
                THelper.Information ("{0} thumbnails created. {1:F2} seconds / thumbnail.",
                       nThumbs, (overviewCreated - avFileSetCreated).TotalSeconds / nThumbs);
                }
            else
                {
                overviewCreated = DateTime.Now;
                THelper.Information ("Overview page skipped.");
                }

            _tnSettings.Border = originalBorder;

            if (_tnSettings.Interval.TotalSeconds > 0)
                {
                nThumbs = 0;
                creator = new ThumbnailCreator (_tnSettings, null);

                TimeSpan originalInterval = _tnSettings.Interval;
                if (_autoInterval)
                    {
                    IEnumerator<KeyValuePair<int,int>> ranges = _autoIntervals.GetEnumerator ();
                    ranges.MoveNext ();
                    KeyValuePair<int,int> range = ranges.Current;
                    int interval = range.Value;
                    int maxInterval = interval;
                    double totalMinutes = (_tnSettings.End - _tnSettings.Start).TotalMinutes;

                    while (ranges.MoveNext ())
                        {
                        range = ranges.Current;
                        if (totalMinutes < range.Key)
                            {
                            THelper.Information ("Duration {0:F2} < {1} minutes. AutoInterval is {2} seconds.",
                                totalMinutes, range.Key, range.Value);
                            interval = range.Value;
                            break;
                            }
                        }
                    _tnSettings.Interval = new TimeSpan (0, 0, interval);

                    if (interval == maxInterval)
                        THelper.Information ("Duration {0:F2} > {1} minutes. AutoInterval is {2} seconds",
                        totalMinutes, range.Key, interval);
                    }

                outTemplate = System.IO.Path.GetFileNameWithoutExtension (displayFilename);
                outTemplate = outTemplate.Replace ("{", "(");
                outTemplate = outTemplate.Replace ("}", ")");
                outTemplate = outTemplate + "_page{0:D4}.jpg";

                nThumbs = creator.GenerateDetailThumbs (avFiles, displayFilename,
                                                       outTemplate, outputDirectory);

                _tnSettings.Interval = originalInterval;

                DateTime detailsCreated = DateTime.Now;
                if (nThumbs > 0)
                    {
                    THelper.Information ("{0} to generate Detail page thumbnails.",
                                        (detailsCreated - overviewCreated).ToString (@"h\:mm\:ss"));
                    THelper.Information ("{0} thumbnails created. {1:F2} seconds / thumbnail.",
                                         nThumbs, (detailsCreated - overviewCreated).TotalSeconds / nThumbs);
                    }
                }
            else
                {
                THelper.Information ("Detail page thumbnails skipped.");
                }

            DateTime overall = DateTime.Now;
            THelper.Information ("{0} overall time to process {1}.",
                                (overall - startTime).ToString (@"h\:mm\:ss"), displayFilename);
            THelper.Information ();
        }