Click here to Skip to main content
13,829,149 members
Click here to Skip to main content

Stats

730.1K views
29.9K downloads
281 bookmarked
Posted 14 Sep 2010
Licenced CPOL

PVS.AVPlayer - MCI Audio and Video Library

, 7 Aug 2018
Windows Media Control Interface (MCI) library with many added features
PVS.AVPlayer
PVS.AVPlayer .NET 2.0
PVS.AVPlayer.XML
PVS.AVPlayer .NET 3.0
PVS.AVPlayer.XML
PVS.AVPlayer .NET 3.5
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.0
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.5
PVS.AVPlayer .NET 4.5.1
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.5.2
PVS.AVPlayer.XML
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.6
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.6.1
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.6.2
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.7
PVS.AVPlayer .NET 4.7.1
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer .NET 4.7.2
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer.dll
PVS.AVPlayer.XML
PVS.AVPlayer All Source Code
AVPlayerExample
AVPlayerExample
AVPlayerExample.csproj.user
bin
Debug
PVS.AVPlayer.XML
Release
Dialogs
Display Overlays
obj
Debug
Release
x86
Debug
Release
Properties
Resources
Crystal Italic1.ttf
WingDings3a.ttf
Voice Recorder
FolderView
FolderView
bin
Debug
PVS.AVPlayer.XML
Release
FolderView.csproj.user
obj
Release
x86
Debug
Release
Properties
Resources
Crystal Italic1.ttf
PVS.AVPlayer
AVPlayerExample.csproj.user
PVS.AVPlayer.dll
PVS.AVPlayer.XML
Custom Items
Native Methods
Bob.png
Crystal Italic1.ttf
Dial Green 2.png
Dial Green 4.png
Dial Green.png
Dial Red 2.png
Dial Red.png
media7.ico
media7a.ico
Media8.ico
Media8a.ico
VU Meter.png
WingDings3a.ttf
Sound Recorder
Various
About Dialog
PVS.AVPlayer.dll
PVS.AVPlayer.XML
Custom Items
FolderView.csproj.user
Debug
Bob.png
Crystal Italic1.ttf
media7a.ico
media7b.ico
Media8a.ico
Media8b.ico
Subtitles Overlay
Various
How To (C#)
PVSAVPlayerHowTo
bin
Debug
PVS.AVPlayer.dll
PVS.AVPlayer.XML
Release
obj
Debug
Release
Properties
How To (VB.NET)
PVSAVPlayerHowToVB
bin
Debug
PVS.AVPlayer.dll
PVS.AVPlayer.XML
Release
My Project
Application.myapp
obj
Debug
Release
PVSAVPlayerHowTo.vbproj.user
PVS.AVPlayer Examples
AVPlayerExample.ex_
FolderView.ex_
AVPlayerExample.exe
FolderView.exe
PVS.AVPlayer.dll
/****************************************************************

    PVS.AVPlayer - Version 0.91
    August 2018, The Netherlands
    © Copyright 2018 PVS The Netherlands - Free to Use

    ****************

    For use with Microsoft .NET Framework version 2.0 or higher and any CPU.
    Created with Microsoft Visual Studio.

    Articles on CodeProject with information on the use of the PVS.AVPlayer library:
    About the Player: http://www.codeproject.com/Articles/109714/PVS-AVPlayer-MCI-Audio-and-Video-Library
    About the Sound Recorder: http://www.codeproject.com/Articles/1116698/PVS-AVPlayer-MCI-Sound-Recorder

    ****************

    The PVS.AVPlayer library source code is divided into 10 files:

     1. Player.cs            - Player source code
     2. MouseEvents.cs       - extension source code of Player.cs, provides player display mouse events
     3. PeakMeter.cs         - extension source code of Player.cs, provides player audio peak level values
     4. DisplayClones.cs     - extension source code of Player.cs, provides player multiple video displays 
     5. Subtitles.cs         - extension source code of Player.cs, provides player SubRip (.srt) subtitles
     6. Signals.cs           - extension source code of Player.cs, provides player media position signaling
     7. CursorHide.cs        - extension source code of Player.cs, hides the mouse cursor during inactivity
     8. Recorder.cs          - Sound Recorder source code
     9. PlayerRecorder.cs    - code used by both Player.cs (and its extension files) and Recorder.cs
    10. Infolabel.cs         - Info Label (custom ToolTip) source code

    Required references:
    System
    System.Drawing
    System.Windows.Forms

    ****************

    This file: DisplayClones.cs

    Player Class
    Extension to file 'Player.cs'.

    ****************

    About Media Control Interface (MCI)
    - for information about MCI, please see https://msdn.microsoft.com/en-us/library/vs/alm/dd757151(v=vs.85).aspx
    - you can find many articles about mci on the internet, search examples: 'c# mci', 'vb mci', 'mcisendstring'
      or the subject of your question.
    - the PVS.AVPlayer library also provides 'direct access' to MCI (e.g. 'Mci.MciSendString').

    ****************

    Thanks!

    Many thanks to Microsoft (Windows, .NET Framework, Visual Studio Express, etc.), all the people
    writing about programming on the internet (a great source for ideas and solving problems),
    the websites publishing those or other writings about programming, the people responding
    to the PVS.AVPlayer articles with comments and suggestions and, of course, CodeProject:
    thank you Deeksha, Smitha and Sean Ewington for the beautiful articles and all!

    Special thanks to Sean Ewington of CodeProject who also took care of publishing the many code
    updates and changes in the PVS.AVPlayer articles in a friendly, fast, and highly competent manner.
    Thank you very much, Sean!

    Peter Vegter
    August 2018, The Netherlands

    ****************************************************************/

#region Usings

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;

#endregion

namespace PVS.AVPlayer
{

    // ******************************** PVS.AVPlayer (Player) - Enumerations (Display Clones)

    #region PVS.AVPlayer (Player) - Enumerations (Display Clones)

    /// <summary>
    /// Specifies the axis used to flip the video image of the display clone.
    /// </summary>
    public enum CloneFlip
    {
        /// <summary>
        /// Specifies no flipping.
        /// </summary>
        FlipNone,
        /// <summary>
        /// Specifies a horizontal flip.
        /// </summary>
        FlipX,
        /// <summary>
        /// Specifies a vertical flip.
        /// </summary>
        FlipY,
        /// <summary>
        /// Specifies a horizontal and vertical flip.
        /// </summary>
        FlipXY
    }

    /// <summary>
    /// Specifies the size and location of the video image of the display clone.
    /// </summary>
    public enum CloneLayout
    {
        /// <summary>
        /// The video image is stretched across the display of the clone.
        /// </summary>
        Stretch,
        /// <summary>
        /// The video image is maximally enlarged within the display of the clone while maintaining the aspect ratio.
        /// </summary>
        Zoom
        ///// <summary>
        ///// The video image completely fills the display of the clone while maintaining the aspect ratio, but possibly with horizontal or vertical image cropping.
        ///// </summary>
        //Cover
    }

    /// <summary>
    /// Specifies the video quality of the display clone.
    /// </summary>
    public enum CloneQuality
    {
        /// <summary>
        /// Specifies normal quality video.
        /// </summary>
        Normal,
        /// <summary>
        /// Specifies high-quality video.
        /// </summary>
        High,
        /// <summary>
        /// Specifies automatic quality video: high-quality video if the video image of the display clone is smaller than the original video image, otherwise normal video quality.
        /// </summary>
        Auto
    }

    #endregion


    public partial class Player
    {
        /*
            Display Clones

            This section provides easy cloning (copying) of the video display of the player to one or more other display
            controls by using the Win32 BitBlt functions.
        */

        // ******************************** Display Clones - Fields

        #region Display Clones - Fields

        // Display Clone data
        internal class Clone
        {
            internal Control            Control;    // the display clone control
            internal CloneQuality       Quality;
            internal CloneLayout        Layout;
            internal CloneFlip          Flip;
            internal bool               Refresh;
            internal int                Errors;     // count errors because there can be 'no-true-error glitches' (?)
        }

        private const int               DC_DEFAULT_FRAMERATE        = 30;
        private const bool              DC_DEFAULT_OVERLAY_SHOW     = true;
        private const int               DC_BUSY_TIME_OUT            = 1000;

        internal int                    dc_cloneFrameRate           = DC_DEFAULT_FRAMERATE;
        internal bool                   dc_cloneOverlayShow         = DC_DEFAULT_OVERLAY_SHOW;

        internal bool                   dc_hasDisplayClones;
        internal bool                   dc_displayClonesRunning;
        private volatile bool           dc_paintBusy;
        internal volatile Clone[]       dc_displayClones;

        private volatile System.Threading.Timer  dc_timer;
        internal volatile int           dc_timerInterval            = 1000 / DC_DEFAULT_FRAMERATE;
        private volatile bool           dc_timerRestart;

        private Bitmap                  dc_backBuffer;

        private Region                  dc_refreshRegion;
        private Rectangle               dc_refreshRect;
        private bool                    dc_noSizeDisplay;

        private delegate void           RefreshCloneCallback(int index);
        private RefreshCloneCallback    dc_refreshCallback;

        private object                  dc_lock                     = new object();

        #endregion


        // ******************************** Display Clones - Main

        #region Display Clones - Main

        /// <summary>
        /// Provides access to the display clones settings of the player (e.g. Player.DisplayClones.Add).
        /// </summary>
        public DisplayClones DisplayClones
        {
            get
            {
                if (_clonesClass == null) _clonesClass = new DisplayClones(this);
                return _clonesClass;
            }
        }

        #endregion


        // ******************************** Display Clones - Add / DisplayCheck / Clear / Remove

        #region Display Clones - Add / DisplayCheck / Clear / Remove

        internal int DisplayClones_Add(Control[] clones, CloneFlip flip, CloneLayout layout, CloneQuality quality)
        {
            lock (dc_lock)
            {
                if (!_hasDisplay)
                {
                    _lastError = Global.MCIERR_NO_WINDOW;
                    return _lastError;
                }
                _lastError = Global.MCIERR_NO_ERROR;
                if (clones == null || clones.Length == 0) return _lastError;

                int addCount = 0;
                bool duplicate = false;
                bool resume = DisplayClones_Pause();

                Control[] addClones = new Control[clones.Length];
                for (int i = 0; i < clones.Length; i++)
                {
                    duplicate = false;
                    if (dc_hasDisplayClones) // check if specified clones already exist
                    {
                        for (int j = 0; j < dc_displayClones.Length; j++)
                        {
                            if (clones[i] == null || (dc_displayClones[j] != null && clones[i] == dc_displayClones[j].Control))
                            {
                                duplicate = true;
                                break;
                            }
                        }
                    }
                    if (!duplicate)
                    {
                        for (int j = i + 1; j < clones.Length; j++) // check for duplicates in specified clones
                        {
                            if (clones[i] == null || clones[i] == clones[j] || clones[i] == _display)
                            {
                                duplicate = true;
                                break;
                            }
                        }
                    }
                    if (!duplicate)
                    {
                        addClones[i] = clones[i];
                        addCount++;
                    }
                }

                if (addCount > 0)
                {
                    int oldCount = 0;
                    int index = 0;

                    if (dc_hasDisplayClones)
                    {
                        for (int i = 0; i < dc_displayClones.Length; i++) // count existing display clones (skip any 'null')
                        {
                            if (dc_displayClones[i] != null) oldCount++;
                        }
                    }

                    Clone[] newClones = new Clone[oldCount + addCount];
                    if (dc_hasDisplayClones)
                    {
                        for (int i = 0; i < dc_displayClones.Length; i++) // add existing display clones
                        {
                            if (dc_displayClones[i] != null) newClones[index++] = dc_displayClones[i];
                        }
                    }

                    for (int i = 0; i < addClones.Length; i++) // add new display clones
                    {
                        if (addClones[i] != null)
                        {
                            newClones[index]                        = new Clone();
                            newClones[index].Control                = addClones[i];
                            newClones[index].Control.SizeChanged    += DisplayClones_SizeChanged;
                            newClones[index].Control.Invalidated    += DisplayClones_SizeChanged;
                            newClones[index].Flip                   = flip;
                            newClones[index].Layout                 = layout;
                            newClones[index].Quality                = quality;
                            newClones[index++].Refresh              = true;
                        }
                    }

                    dc_displayClones = newClones;
                    dc_hasDisplayClones = true;
                    if (MediaDisplayClonesChanged != null) MediaDisplayClonesChanged(this, EventArgs.Empty);

                    if (!dc_displayClonesRunning) DisplayClones_Start(); // was not paused (resume == false)
                }
                if (resume) DisplayClones_Resume();
                return _lastError;
            }
        }

        // check if a display clone is the same as the display of the player
        private void DisplayClones_DisplayCheck()
        {
            if (_hasDisplay && dc_hasDisplayClones)
            {
                for (int i = 0; i < dc_displayClones.Length; i++)
                {
                    if (dc_displayClones[i] != null && dc_displayClones[i].Control == _display)
                    {
                        dc_displayClones[i].Control.SizeChanged -= DisplayClones_SizeChanged;
                        dc_displayClones[i].Control.Invalidated -= DisplayClones_SizeChanged;
                        dc_displayClones[i].Control             = null;
                        dc_displayClones[i]                     = null;
                    }
                }
            }
        }

        internal int DisplayClones_Clear()
        {
            lock (dc_lock)
            {
                _lastError = Global.MCIERR_NO_ERROR;

                if (dc_hasDisplayClones)
                {
                    DisplayClones_Stop(true);

                    for (int i = 0; i < dc_displayClones.Length; i++)
                    {
                        if (dc_displayClones[i] != null && dc_displayClones[i].Control != null)
                        {
                            try
                            {
                                dc_displayClones[i].Control.SizeChanged -= DisplayClones_SizeChanged;
                                dc_displayClones[i].Control.Invalidated -= DisplayClones_SizeChanged;
                            }
                            catch { /* ignore */ }
                            dc_displayClones[i].Control = null;
                            dc_displayClones[i]         = null;
                        }
                    }
                    dc_hasDisplayClones = false;
                    dc_displayClones    = null;

                    if (MediaDisplayClonesChanged != null) MediaDisplayClonesChanged(this, EventArgs.Empty);
                }
                return _lastError;
            }
        }

        internal int DisplayClones_Remove(Control[] clones)
        {
            lock (dc_lock)
            {
                _lastError = Global.MCIERR_NO_ERROR;
                if (dc_hasDisplayClones && clones != null)
                {
                    bool clonesStopped = false;
                    bool resume = DisplayClones_Pause();

                    for (int i = 0; i < clones.Length; i++)
                    {
                        if (clones[i] != null)
                        {
                            for (int j = 0; j < dc_displayClones.Length; j++)
                            {
                                if (dc_displayClones[j] != null && dc_displayClones[j].Control == clones[i])
                                {
                                    try
                                    {
                                        dc_displayClones[j].Control.SizeChanged -= DisplayClones_SizeChanged;
                                        dc_displayClones[j].Control.Invalidated -= DisplayClones_SizeChanged;
                                        dc_displayClones[j].Control.Invalidate();
                                    }
                                    catch { /* ignore */ }

                                    dc_displayClones[j].Control = null;
                                    dc_displayClones[j]         = null;
                                    clonesStopped               = true;
                                }
                            }
                        }
                    }

                    if (clonesStopped)
                    {
                        int count = 0;
                        for (int i = 0; i < dc_displayClones.Length; i++)
                        {
                            if (dc_displayClones[i] != null) count++;
                        }
                        if (count > 0)
                        {
                            Clone[] newDisplayClones = new Clone[count];
                            int index = 0;
                            for (int i = 0; i < dc_displayClones.Length; i++)
                            {
                                if (dc_displayClones[i] != null) newDisplayClones[index++] = dc_displayClones[i];
                            }
                            dc_displayClones = newDisplayClones;
                        }
                        else
                        {
                            resume = false;
                            DisplayClones_Stop(true);

                            dc_hasDisplayClones = false;
                            dc_displayClones = null;

                            if (dc_refreshRegion != null)
                            {
                                dc_refreshRegion.Dispose();
                                dc_refreshRegion = null;
                            }
                        }
                        if (MediaDisplayClonesChanged != null) MediaDisplayClonesChanged(this, EventArgs.Empty);
                    }
                    if (resume) DisplayClones_Resume();
                }
                return _lastError;
            }
        }

        // remove the clones that generated an error (because their displays were removed without first removing the clone)
        private void DisplayClones_RemoveDropOutClones()
        {
            if (dc_displayClones != null)
            {
                bool removed = false;
                int count = 0;

                for (int i = 0; i < dc_displayClones.Length; i++)
                {
                    if (dc_displayClones[i] != null) count++;
                }

                if (count == 0)
                {
                    dc_hasDisplayClones = false;
                    dc_displayClones = null;
                    removed = true;
                }
                else if (count < dc_displayClones.Length)
                {
                    Clone[] temp = new Clone[count];
                    int index = 0;

                    for (int i = 0; i < dc_displayClones.Length; i++)
                    {
                        if (dc_displayClones[i] != null)
                        {
                            temp[index++] = dc_displayClones[i];
                        }
                    }
                    dc_displayClones = temp;
                    removed = true;
                }

                if (removed && MediaDisplayClonesChanged != null) MediaDisplayClonesChanged(this, EventArgs.Empty);
            }
        }

        #endregion


        // ******************************** Display Clones - Start / Stop / Pause / Resume

        #region Display Clones - Start / Stop / Pause / Resume

        internal void DisplayClones_Start()
        {
            lock (dc_lock)
            {
                if (dc_hasDisplayClones)
                {
                    if (!dc_displayClonesRunning)
                    {
                        if (_playing || (_hasOverlay && _overlayHold && dc_cloneOverlayShow))
                        {
                            if (dc_refreshRegion == null) dc_refreshRegion = new Region();
                            dc_displayClonesRunning = true;
                            DisplayClones_StartTimer();

                            if (MediaDisplayClonesStarted != null) MediaDisplayClonesStarted(this, EventArgs.Empty);
                        }
                    }
                    else if (_playing && _hasOverlay && _overlayHold && dc_cloneOverlayShow)
                    {
                        for (int i = 0; i < dc_displayClones.Length; i++)
                        {
                            if (dc_displayClones[i] != null) dc_displayClones[i].Refresh = true;
                        }
                    }
                }
            }
        }

        internal void DisplayClones_Stop(bool force)
        {
            lock (dc_lock)
            {
                if (dc_displayClonesRunning)
                {
                    if (force || !_hasOverlay || !(_overlayHold && dc_cloneOverlayShow))
                    {
                        DisplayClones_StopTimer();

                        if (dc_refreshRegion != null)
                        {
                            dc_refreshRegion.Dispose();
                            dc_refreshRegion = null;
                        }
                        if (dc_backBuffer != null)
                        {
                            dc_backBuffer.Dispose();
                            dc_backBuffer = null;
                        }
                        dc_displayClonesRunning = false;

                        if (dc_displayClones != null)
                        {
                            for (int i = 0; i < dc_displayClones.Length; i++)
                            {
                                try
                                {
                                    if (dc_displayClones[i] != null && dc_displayClones[i].Control != null && dc_displayClones[i].Control.Visible)
                                    {
                                        dc_displayClones[i].Control.Invalidate();
                                    }
                                }
                                catch { /* ignore */ }
                            }
                        }

                        if (MediaDisplayClonesStopped != null) MediaDisplayClonesStopped(this, EventArgs.Empty);
                        DisplayClones_RemoveDropOutClones();
                    }
                }
            }
        }

        private bool DisplayClones_Pause()
        {
            bool paused = false;
            if (dc_displayClonesRunning)
            {
                if (dc_timer != null)
                {
                    dc_timerRestart = false;
                    int timeOut = DC_BUSY_TIME_OUT;
                    while (dc_paintBusy && --timeOut > 0)
                    {
                        Thread.Sleep(1);
                        Application.DoEvents();
                    }
                    paused = true;
                }
            }
            return paused;
        }

        private void DisplayClones_Resume()
        {
            if (dc_displayClonesRunning)
            {
                dc_timerRestart = true;
                dc_timer.Change(dc_timerInterval, Timeout.Infinite);
            }
        }

        #endregion


        // ******************************** Display Clones - Timer Start / Stop

        #region Display Clones - Timer Start / Stop

        private void DisplayClones_StartTimer()
        {
            if (dc_timer == null)
            {
                if (dc_refreshCallback == null) dc_refreshCallback = new RefreshCloneCallback(DisplayClones_Invalidate);
                dc_timerRestart = true;
                dc_timer = new System.Threading.Timer(DisplayClones_Paint, null, 0, Timeout.Infinite);
            }
        }

        private void DisplayClones_StopTimer()
        {
            if (dc_timer != null)
            {
                dc_timerRestart = false;
                dc_timer.Dispose();
                dc_timer = null;

                int timeOut = DC_BUSY_TIME_OUT;
                while (dc_paintBusy && --timeOut > 0)
                {
                    Thread.Sleep(1);
                    Application.DoEvents();
                }
            }
        }

        #endregion


        // ******************************** Display Clones - SizeChanged / Refresh

        #region Display Clones - SizeChanged / Refresh

        // also used with clone invalidated (to handle fully 'overlapped' forms)
        private void DisplayClones_SizeChanged(object sender, EventArgs e)
        {
            for (int i = 0; i < dc_displayClones.Length; i++)
            {
                if (dc_displayClones[i] != null && dc_displayClones[i].Control == sender)
                {
                    dc_displayClones[i].Refresh = true;
                    //DisplayClones_Paint(null);
                    break;
                }
            }
        }

        internal void DisplayClones_Refresh()
        {
            for (int i = 0; i < dc_displayClones.Length; i++)
            {
                if (dc_displayClones[i] != null)
                {
                    dc_displayClones[i].Refresh = true;
                }
            }
        }

        #endregion


        // ******************************** Display Clones - Paint

        #region Display Clones - Paint

        // Paint the display clones
        private void DisplayClones_Paint(object state)
        {
            if (dc_paintBusy || !_hasDisplay || !dc_timerRestart) return;
            dc_paintBusy = true;

            // Player display too small check
            #region Player display too small check

            try
            {
                if (_display.DisplayRectangle.Width < 4 || _display.DisplayRectangle.Height < 4)
                {
                    if (!dc_noSizeDisplay)
                    {
                        dc_noSizeDisplay = true;
                        for (int i = 0; i < dc_displayClones.Length; i++)
                        {
                            if (dc_displayClones[i] != null)
                            {
                                dc_refreshRegion.MakeEmpty();
                                dc_refreshRegion.Union(dc_displayClones[i].Control.DisplayRectangle);
                                dc_displayClones[i].Control.Invoke(dc_refreshCallback, new object[] { i });
                                dc_displayClones[i].Refresh = false;
                            }
                        }
                    }
                    if (dc_timerRestart) dc_timer.Change(dc_timerInterval, Timeout.Infinite);
                    dc_paintBusy = false;
                    return;
                }
                dc_noSizeDisplay = false;
            }
            catch
            {
                if (dc_timerRestart) dc_timer.Change(dc_timerInterval, Timeout.Infinite);
                dc_paintBusy = false;
                return;
            }

            #endregion

            // Determine whether also to paint the display overlay
            bool overlayMode = dc_cloneOverlayShow && (_hasOverlay && _overlay.Visible);

            // Paint only if there's a video image or a display overlay (hold)
            if (_hasVideo || (overlayMode && _overlayHold))
            {
                double      difX;
                double      difY;
                int         newSize;
                bool        fullSize        = false;
                //bool        reCalc          = false;

                //Bitmap      backBuffer      = null;
                IntPtr      sourceHdc       = IntPtr.Zero;
                Graphics    sourceGraphics  = null;
                Rectangle   sourceRect      = Rectangle.Empty;

                Graphics    tempGraphics    = null;
                IntPtr      tempHdc         = IntPtr.Zero;

                // Get source - video and/or overlay
                #region Get source - video and/or overlay

                if (overlayMode)
                {
                    try
                    {
                        int transparentColor = ColorTranslator.ToWin32(_overlay.TransparencyKey);

                        // create a 'screencopy' in backBuffer
                        if (!_hasVideo || _overlayMode == OverlayMode.Display)
                        {
                            sourceRect = _display.DisplayRectangle; // with overlay - same size as display
                        }
                        else
                        {
                            sourceRect = _videoBoundsClip; // with overlay - same size as video
                            if (sourceRect.Width <= 2 || sourceRect.Height <= 2)
                            {
                                sourceRect = _display.DisplayRectangle;
                                fullSize = true;
                            }
                        }

                        // create buffer
                        if (dc_backBuffer == null || sourceRect.Width != dc_backBuffer.Width || sourceRect.Height != dc_backBuffer.Height)
                        {
                            if (dc_backBuffer != null) dc_backBuffer.Dispose();
                            dc_backBuffer = new Bitmap(sourceRect.Width, sourceRect.Height);
                        }
                        sourceGraphics = Graphics.FromImage(dc_backBuffer);
                        sourceHdc = sourceGraphics.GetHdc();

                        // copy display to buffer
                        tempGraphics = _display.CreateGraphics();
                        tempHdc = tempGraphics.GetHdc();
                        SafeNativeMethods.BitBlt(sourceHdc, 0, 0, sourceRect.Width, sourceRect.Height, tempHdc, sourceRect.X, sourceRect.Y, SafeNativeMethods.SRCCOPY);
                        tempGraphics.ReleaseHdc(tempHdc);
                        tempGraphics.Dispose();
                        tempGraphics = null;

                        // copy overlay to buffer - transparent
                        tempGraphics = _overlay.CreateGraphics();
                        tempHdc = tempGraphics.GetHdc();
                        SafeNativeMethods.TransparentBlt(sourceHdc, 0, 0, sourceRect.Width, sourceRect.Height, tempHdc, 0, 0, _overlay.Width, _overlay.Height, transparentColor);
                        tempGraphics.ReleaseHdc(tempHdc);
                        tempGraphics.Dispose();
                        tempGraphics = null;

                        sourceRect.X = 0;
                        sourceRect.Y = 0; // backBuffer is source, no margins
                    }
                    catch
                    {
                        if (tempGraphics != null)
                        {
                            if (tempHdc != IntPtr.Zero) tempGraphics.ReleaseHdc(tempHdc);
                            tempGraphics.Dispose();
                            tempGraphics = null;
                        }
                        if (sourceGraphics != null)
                        {
                            if (sourceHdc != IntPtr.Zero) sourceGraphics.ReleaseHdc(sourceHdc);
                            sourceGraphics.Dispose();
                            sourceGraphics = null;
                        }

                        try
                        {
                            sourceGraphics = _display.CreateGraphics(); // player display is source
                            sourceHdc = sourceGraphics.GetHdc();
                            sourceRect = _videoBoundsClip;
                        }
                        catch
                        {
                            if (sourceGraphics != null)
                            {
                                if (sourceHdc != IntPtr.Zero) sourceGraphics.ReleaseHdc(sourceHdc);
                                sourceGraphics.Dispose();
                            }
                            if (dc_timerRestart) dc_timer.Change(dc_timerInterval, Timeout.Infinite);
                            dc_paintBusy = false;
                            return;
                        }
                    }
                }
                else
                {
                    try
                    {
                        sourceGraphics = _display.CreateGraphics(); // player display is source
                        sourceHdc = sourceGraphics.GetHdc();
                        sourceRect = _videoBoundsClip;
                    }
                    catch
                    {
                        if (sourceGraphics != null)
                        {
                            if (sourceHdc != IntPtr.Zero) sourceGraphics.ReleaseHdc(sourceHdc);
                            sourceGraphics.Dispose();
                        }
                        if (dc_timerRestart) dc_timer.Change(dc_timerInterval, Timeout.Infinite);
                        dc_paintBusy = false;
                        return;
                    }
                }

                #endregion

                // Paint the clones - one by one
                #region Paint the clones - one by one

                for (int i = 0; i < dc_displayClones.Length && dc_timerRestart; i++)
                {
                    Graphics destGraphics = null;
                    Rectangle destRect;
                    IntPtr destHdc = IntPtr.Zero;

                    try
                    {
                        if (dc_displayClones[i] != null && dc_displayClones[i].Control.Visible)
                        {
                            CloneFlip flipMode = dc_displayClones[i].Flip;
                            destGraphics = dc_displayClones[i].Control.CreateGraphics();
                            destRect = dc_displayClones[i].Control.DisplayRectangle;
                            destHdc = destGraphics.GetHdc();

                            if (_displayMode == DisplayMode.Stretch || fullSize || dc_displayClones[i].Layout == CloneLayout.Stretch)
                            {
                                dc_displayClones[i].Refresh = false;

                                // set quality
                                if (dc_displayClones[i].Quality != CloneQuality.Normal)
                                {
                                    if (dc_displayClones[i].Quality == CloneQuality.Auto)
                                    {
                                        if (destRect.Width < sourceRect.Width || destRect.Height < sourceRect.Height)
                                            SafeNativeMethods.SetStretchBltMode(destHdc, SafeNativeMethods.STRETCH_HALFTONE);
                                    }
                                    else SafeNativeMethods.SetStretchBltMode(destHdc, SafeNativeMethods.STRETCH_HALFTONE);
                                }

                                if (flipMode == CloneFlip.FlipNone)
                                {
                                    SafeNativeMethods.StretchBlt(destHdc, destRect.X, destRect.Y, destRect.Width, destRect.Height,
                                        sourceHdc, sourceRect.Left, sourceRect.Top, sourceRect.Width, sourceRect.Height, SafeNativeMethods.SRCCOPY_U);
                                }
                                else if (flipMode == CloneFlip.FlipX)
                                {
                                    SafeNativeMethods.StretchBlt(destHdc, destRect.X + destRect.Width - 1, destRect.Y, -destRect.Width, destRect.Height,
                                        sourceHdc, sourceRect.Left, sourceRect.Top, sourceRect.Width, sourceRect.Height, SafeNativeMethods.SRCCOPY_U);
                                }
                                else if (flipMode == CloneFlip.FlipY)
                                {
                                    SafeNativeMethods.StretchBlt(destHdc, destRect.X, destRect.Y + destRect.Height - 1, destRect.Width, -destRect.Height,
                                        sourceHdc, sourceRect.Left, sourceRect.Top, sourceRect.Width, sourceRect.Height, SafeNativeMethods.SRCCOPY_U);
                                }
                                else //if (flip == FlipType.FlipXY)
                                {
                                    SafeNativeMethods.StretchBlt(destHdc, destRect.X + destRect.Width - 1, destRect.Y + destRect.Height - 1, -destRect.Width, -destRect.Height,
                                        sourceHdc, sourceRect.Left, sourceRect.Top, sourceRect.Width, sourceRect.Height, SafeNativeMethods.SRCCOPY_U);
                                }
                            }
                            else
                            {
                                difX = (double)destRect.Width / sourceRect.Width;
                                difY = (double)destRect.Height / sourceRect.Height;
                                //if ((dc_displayClones[i].Layout == CloneLayout.Zoom && (difX < difY)) ||
                                //    (dc_displayClones[i].Layout == CloneLayout.Cover && (difX > difY)))
                                if (difX < difY)
                                {
                                    newSize = (int)(sourceRect.Height * difX);

                                    dc_refreshRect.X = 0;
                                    dc_refreshRect.Y = (destRect.Height - newSize) / 2;
                                    dc_refreshRect.Width = (int)(sourceRect.Width * difX);
                                    dc_refreshRect.Height = newSize;

                                    if (dc_displayClones[i].Refresh)
                                    {
                                        dc_displayClones[i].Refresh = false;

                                        dc_refreshRegion.MakeEmpty();
                                        dc_refreshRegion.Union(destRect);
                                        dc_refreshRegion.Exclude(dc_refreshRect);

                                        dc_displayClones[i].Control.Invoke(dc_refreshCallback, new object[] { i });
                                    }

                                    // set quality
                                    if (dc_displayClones[i].Quality != CloneQuality.Normal)
                                    {
                                        if (dc_displayClones[i].Quality == CloneQuality.Auto)
                                        {
                                            if (dc_refreshRect.Width < sourceRect.Width || dc_refreshRect.Height < sourceRect.Height)
                                                SafeNativeMethods.SetStretchBltMode(destHdc, SafeNativeMethods.STRETCH_HALFTONE);
                                        }
                                        else SafeNativeMethods.SetStretchBltMode(destHdc, SafeNativeMethods.STRETCH_HALFTONE);
                                    }

                                    if (flipMode == CloneFlip.FlipNone)
                                    {
                                        SafeNativeMethods.StretchBlt(destHdc, 0, dc_refreshRect.Y, dc_refreshRect.Width, newSize,
                                            sourceHdc, sourceRect.Left, sourceRect.Top, sourceRect.Width, sourceRect.Height, SafeNativeMethods.SRCCOPY_U);
                                    }
                                    else if (flipMode == CloneFlip.FlipX)
                                    {
                                        SafeNativeMethods.StretchBlt(destHdc, dc_refreshRect.Width, dc_refreshRect.Y, -dc_refreshRect.Width, newSize,
                                            sourceHdc, sourceRect.Left, sourceRect.Top, sourceRect.Width, sourceRect.Height, SafeNativeMethods.SRCCOPY_U);
                                    }
                                    else if (flipMode == CloneFlip.FlipY)
                                    {
                                        SafeNativeMethods.StretchBlt(destHdc, 0, dc_refreshRect.Y + newSize - 1, dc_refreshRect.Width, -newSize,
                                            sourceHdc, sourceRect.Left, sourceRect.Top, sourceRect.Width, sourceRect.Height, SafeNativeMethods.SRCCOPY_U);
                                    }
                                    else //if (flip == FlipType.FlipXY)
                                    {
                                        SafeNativeMethods.StretchBlt(destHdc, dc_refreshRect.Width, dc_refreshRect.Y + newSize - 1, -dc_refreshRect.Width, -newSize,
                                            sourceHdc, sourceRect.Left, sourceRect.Top, sourceRect.Width, sourceRect.Height, SafeNativeMethods.SRCCOPY_U);
                                    }
                                }
                                else
                                {
                                    newSize = (int)(sourceRect.Width * difY);

                                    dc_refreshRect.X = (destRect.Width - newSize) / 2;
                                    dc_refreshRect.Y = 0;
                                    dc_refreshRect.Width = newSize;
                                    dc_refreshRect.Height = (int)(sourceRect.Height * difY);

                                    if (dc_displayClones[i].Refresh)
                                    {
                                        dc_displayClones[i].Refresh = false;
                                        dc_refreshRegion.MakeEmpty();
                                        dc_refreshRegion.Union(destRect);
                                        dc_refreshRegion.Exclude(dc_refreshRect);

                                        dc_displayClones[i].Control.Invoke(dc_refreshCallback, new object[] { i });
                                    }

                                    // set quality
                                    if (dc_displayClones[i].Quality != CloneQuality.Normal)
                                    {
                                        if (dc_displayClones[i].Quality == CloneQuality.Auto)
                                        {
                                            if (dc_refreshRect.Width < sourceRect.Width || dc_refreshRect.Height < sourceRect.Height)
                                                SafeNativeMethods.SetStretchBltMode(destHdc, SafeNativeMethods.STRETCH_HALFTONE);
                                        }
                                        else SafeNativeMethods.SetStretchBltMode(destHdc, SafeNativeMethods.STRETCH_HALFTONE);
                                    }

                                    if (flipMode == CloneFlip.FlipNone)
                                    {
                                        SafeNativeMethods.StretchBlt(destHdc, dc_refreshRect.X, 0, newSize, dc_refreshRect.Height,
                                            sourceHdc, sourceRect.Left, sourceRect.Top, sourceRect.Width, sourceRect.Height, SafeNativeMethods.SRCCOPY_U);
                                    }
                                    else if (flipMode == CloneFlip.FlipX)
                                    {
                                        SafeNativeMethods.StretchBlt(destHdc, dc_refreshRect.X + newSize - 1, 0, -newSize, dc_refreshRect.Height,
                                            sourceHdc, sourceRect.Left, sourceRect.Top, sourceRect.Width, sourceRect.Height, SafeNativeMethods.SRCCOPY_U);
                                    }
                                    else if (flipMode == CloneFlip.FlipY)
                                    {
                                        SafeNativeMethods.StretchBlt(destHdc, dc_refreshRect.X, dc_refreshRect.Height, newSize, -dc_refreshRect.Height,
                                            sourceHdc, sourceRect.Left, sourceRect.Top, sourceRect.Width, sourceRect.Height, SafeNativeMethods.SRCCOPY_U);
                                    }
                                    else //if (flip == FlipType.FlipXY)
                                    {
                                        SafeNativeMethods.StretchBlt(destHdc, dc_refreshRect.X + newSize - 1, dc_refreshRect.Height, -newSize, -dc_refreshRect.Height,
                                            sourceHdc, sourceRect.Left, sourceRect.Top, sourceRect.Width, sourceRect.Height, SafeNativeMethods.SRCCOPY_U);
                                    }
                                }
                            }
                            dc_displayClones[i].Errors = 0;
                        }
                    }
                    catch
                    {
                        if (dc_displayClones != null && i < dc_displayClones.Length && dc_displayClones[i] != null)
                        {
                            if (++dc_displayClones[i].Errors > 5)
                            {
                                dc_displayClones[i].Control = null;
                                dc_displayClones[i] = null;
                            }
                        }
                    }

                    if (destGraphics != null)
                    {
                        if (destHdc != IntPtr.Zero) destGraphics.ReleaseHdc(destHdc);
                        destGraphics.Dispose();
                    }
                }

                #endregion

                // Release source items
                #region Release source items

                sourceGraphics.ReleaseHdc(sourceHdc);
                sourceGraphics.Dispose();

                #endregion

            }
            if (dc_timerRestart) dc_timer.Change(dc_timerInterval, Timeout.Infinite);
            dc_paintBusy = false;
        }

        // Invoke / Callback from DisplayClonesTimer_Elapsed
        private void DisplayClones_Invalidate(int index)
        {
            try
            {
                dc_displayClones[index].Control.Invalidate(dc_refreshRegion);
                ////dc_displayClones[index].Control.Update();
            }
            catch { /* ignore */ }
        }

        #endregion

    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Peter Vegter
United States United States
No Biography provided

You may also be interested in...

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web03 | 2.8.190114.1 | Last Updated 7 Aug 2018
Article Copyright 2010 by Peter Vegter
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid