diff --git a/Greenshot/Helpers/AviHelper.cs b/Greenshot/Helpers/AviHelper.cs
new file mode 100644
index 000000000..224da01f0
--- /dev/null
+++ b/Greenshot/Helpers/AviHelper.cs
@@ -0,0 +1,901 @@
+// AForge Video for Windows Library
+// AForge.NET framework
+// http://www.aforgenet.com/framework/
+//
+// Copyright © Andrew Kirillov, 2007-2009
+// andrew.kirillov@aforgenet.com
+//
+//
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Runtime.InteropServices;
+
+using GreenshotPlugin.UnmanagedHelpers;
+
+namespace Greenshot.Helpers {
+
+ ///
+ /// AVI files writing using Video for Windows interface.
+ ///
+ ///
+ /// The class allows to write AVI files using Video for Windows API.
+ ///
+ /// Sample usage:
+ /// /// // instantiate AVI writer, use WMV3 codec
+ /// AVIWriter writer = new AVIWriter( "wmv3" );
+ /// // create new AVI file and open it
+ /// writer.Open( "test.avi", 320, 240 );
+ /// // create frame image
+ /// Bitmap image = new Bitmap( 320, 240 );
+ ///
+ /// for ( int i = 0; i < 240; i++ )
+ /// {
+ /// // update image
+ /// image.SetPixel( i, i, Color.Red );
+ /// // add the image as a new frame of video file
+ /// writer.AddFrame( image );
+ /// }
+ /// writer.Close( );
+ ///
+ ///
+ ///
+ public class AVIWriter : IDisposable {
+ private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(AVIWriter));
+ // AVI file
+ private IntPtr file;
+ // video stream
+ private IntPtr stream;
+ // compressed stream
+ private IntPtr streamCompressed;
+ // width of video frames
+ private int width;
+ // height of vide frames
+ private int height;
+ // length of one line
+ private int stride;
+ // quality
+ private int quality = -1;
+ // frame rate
+ private int rate = 25;
+ // current position
+ private int position;
+ // codec used for video compression
+ private string codec = null; //"DIB ";
+
+ ///
+ /// Width of video frames.
+ ///
+ ///
+ /// The property specifies the width of video frames, which are acceptable
+ /// by method for saving, which is set in
+ /// method.
+ ///
+ public int Width {
+ get { return width; }
+ }
+
+ ///
+ /// Height of video frames.
+ ///
+ ///
+ /// The property specifies the height of video frames, which are acceptable
+ /// by method for saving, which is set in
+ /// method.
+ ///
+ public int Height {
+ get { return height; }
+ }
+
+ ///
+ /// Current position in video stream.
+ ///
+ ///
+ /// The property tell current position in video stream, which actually equals
+ /// to the amount of frames added using method.
+ ///
+ public int Position
+ {
+ get { return position; }
+ }
+
+ ///
+ /// Desired playing frame rate.
+ ///
+ ///
+ /// The property sets the video frame rate, which should be use during playing
+ /// of the video to be saved.
+ ///
+ /// The property should be set befor opening new file to take effect.
+ ///
+ /// Default frame rate is set to 25.
+ ///
+ public int FrameRate
+ {
+ get { return rate; }
+ set { rate = value; }
+ }
+
+ ///
+ /// Codec used for video compression.
+ ///
+ ///
+ /// The property sets the FOURCC code of video compression codec, which needs to
+ /// be used for video encoding.
+ ///
+ /// The property should be set befor opening new file to take effect.
+ ///
+ /// Default video codec is set "DIB ", which means no compression.
+ ///
+ public string Codec
+ {
+ get { return codec; }
+ set { codec = value; }
+ }
+
+ ///
+ /// Compression video quality.
+ ///
+ ///
+ /// The property sets video quality used by codec in order to balance compression rate
+ /// and image quality. The quality is measured usually in the [0, 100] range.
+ ///
+ /// The property should be set befor opening new file to take effect.
+ ///
+ /// Default value is set to -1 - default compression quality of the codec.
+ ///
+ public int Quality
+ {
+ get { return quality; }
+ set { quality = value; }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// Initializes Video for Windows library.
+ ///
+ public AVIWriter() {
+ Avi32.AVIFileInit();
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// Codec to use for compression. eg [CVID],[IV50]
+ ///
+ /// Initializes Video for Windows library.
+ ///
+ public AVIWriter(string codec) : this() {
+ this.codec = codec;
+ }
+
+ ///
+ /// Destroys the instance of the class.
+ ///
+ ///
+ ~AVIWriter() {
+ Dispose(false);
+ }
+
+ ///
+ /// Dispose the object.
+ ///
+ ///
+ /// Frees unmanaged resources used by the object. The object becomes unusable
+ /// after that.
+ ///
+ public void Dispose() {
+ Dispose(true);
+ // remove me from the Finalization queue
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Dispose the object.
+ ///
+ ///
+ /// Indicates if disposing was initiated manually.
+ ///
+ protected virtual void Dispose(bool disposing) {
+ if (disposing) {
+ // dispose managed resources
+ }
+ // close current AVI file if any opened and uninitialize AVI library
+ Close();
+ Avi32.AVIFileExit();
+ }
+
+ ///
+ /// Create new AVI file and open it for writing.
+ ///
+ ///
+ /// AVI file name to create.
+ /// Video width.
+ /// Video height.
+ ///
+ /// The method opens (creates) a video files, configure video codec and prepares
+ /// the stream for saving video frames with a help of method.
+ ///
+ /// Failure of opening video files (the exception message
+ /// specifies the issues).
+ ///
+ public bool Open(string fileName, int width, int height) {
+ lock (this) {
+ // calculate stride
+ stride = width * 4;
+ if ((stride % 4) != 0)
+ stride += (4 - stride % 4);
+
+ // create new file
+ if (Avi32.AVIFileOpen(out file, fileName, Avi32.OpenFileMode.Create | Avi32.OpenFileMode.Write, IntPtr.Zero) != 0) {
+ throw new ApplicationException("Failed opening file");
+ }
+
+ this.width = width;
+ this.height = height;
+
+ // describe new stream
+ Avi32.AVISTREAMINFO info = new Avi32.AVISTREAMINFO();
+
+ info.type = Avi32.mmioFOURCC("vids");
+ if (codec != null) {
+ info.handler = Avi32.mmioFOURCC(codec);
+ } else {
+ info.handler = Avi32.mmioFOURCC("DIB ");
+ }
+ info.scale = 1;
+ info.rate = rate;
+ info.suggestedBufferSize = stride * height;
+
+ // create stream
+ if (Avi32.AVIFileCreateStream(file, out stream, ref info) != 0) {
+ throw new ApplicationException("Failed creating stream");
+ }
+
+ // describe compression options
+ Avi32.AVICOMPRESSOPTIONS options = new Avi32.AVICOMPRESSOPTIONS();
+
+
+ // uncomment if video settings dialog is required to show
+ if (codec == null) {
+ int retCode = Avi32.AVISaveOptions( stream, ref options );
+ if (retCode == 0) {
+ LOG.Debug("Cancel clicked!");
+ return false;
+ }
+ } else {
+ options.handler = Avi32.mmioFOURCC(codec);
+ options.quality = quality;
+ }
+
+ // create compressed stream
+ int retval = Avi32.AVIMakeCompressedStream(out streamCompressed, stream, ref options, IntPtr.Zero);
+ if (retval != 0) {
+ throw new ApplicationException("Failed creating compressed stream: " + retval);
+ }
+
+ // describe frame format
+ BitmapInfoHeader bitmapInfoHeader = new BitmapInfoHeader(width, height, 32);
+
+ // set frame format
+ retval = Avi32.AVIStreamSetFormat(streamCompressed, 0, ref bitmapInfoHeader, Marshal.SizeOf(bitmapInfoHeader.GetType()));
+ if (retval != 0) {
+ throw new ApplicationException("Failed creating compressed stream: "+ retval);
+ }
+ position = 0;
+ return true;
+ }
+ }
+
+ ///
+ /// Close video file.
+ ///
+ ///
+ public void Close() {
+ LOG.Debug("Close called");
+ lock (this) {
+ // release compressed stream
+ if (streamCompressed != IntPtr.Zero) {
+ LOG.Debug("AVIStreamRelease streamCompressed");
+ Avi32.AVIStreamRelease(streamCompressed);
+ streamCompressed = IntPtr.Zero;
+ }
+
+ // release stream
+ if (stream != IntPtr.Zero) {
+ LOG.Debug("AVIStreamRelease stream");
+ Avi32.AVIStreamRelease(stream);
+ stream = IntPtr.Zero;
+ }
+
+ // release file
+ if (file != IntPtr.Zero) {
+ LOG.Debug("AVIFileRelease file");
+ Avi32.AVIFileRelease(file);
+ file = IntPtr.Zero;
+ }
+ }
+ }
+
+ ///
+ /// Add new frame to the AVI file.
+ ///
+ /// New frame data.
+ public void AddLowLevelFrame(IntPtr frameData) {
+ lock (this) {
+ // write to stream
+ if (Avi32.AVIStreamWrite(streamCompressed, position, 1, frameData, stride * height, 0, IntPtr.Zero, IntPtr.Zero) != 0) {
+ throw new ApplicationException("Failed adding frame");
+ }
+
+ position++;
+ }
+ }
+ }
+
+ ///
+ /// Windows API functions and structures.
+ ///
+ ///
+ /// The class provides Video for Windows and some other Avi32 functions and structurs.
+ ///
+ internal static class Avi32 {
+ ///
+ /// Copy a block of memory.
+ ///
+ ///
+ /// Destination pointer.
+ /// Source pointer.
+ /// Memory block's length to copy.
+ ///
+ /// Return's the value of dst - pointer to destination.
+ ///
+ [DllImport("ntdll.dll")]
+ public static extern int memcpy(int dst, int src, int count);
+
+
+ // --- Video for Windows Functions
+
+ ///
+ /// Initialize the AVIFile library.
+ ///
+ ///
+ [DllImport("avifil32.dll")]
+ public static extern void AVIFileInit();
+
+ ///
+ /// Exit the AVIFile library.
+ ///
+ [DllImport("avifil32.dll")]
+ public static extern void AVIFileExit();
+
+ ///
+ /// Open an AVI file.
+ ///
+ ///
+ /// Opened AVI file interface.
+ /// AVI file name.
+ /// Opening mode (see ).
+ /// Handler to use (null to use default).
+ ///
+ /// Returns zero on success or error code otherwise.
+ ///
+ [DllImport("avifil32.dll", CharSet = CharSet.Unicode)]
+ public static extern int AVIFileOpen(out IntPtr aviHandler, String fileName, OpenFileMode mode, IntPtr handler);
+
+ ///
+ /// Release an open AVI stream.
+ ///
+ ///
+ /// Open AVI file interface.
+ ///
+ /// Returns the reference count of the file.
+ ///
+ [DllImport("avifil32.dll")]
+ public static extern int AVIFileRelease(IntPtr aviHandler);
+
+ ///
+ /// Get stream interface that is associated with a specified AVI file
+ ///
+ ///
+ /// Handler to an open AVI file.
+ /// Stream interface.
+ /// Stream type to open.
+ /// Count of the stream type. Identifies which occurrence of the specified stream type to access.
+ ///
+ ///
+ ///
+ [DllImport("avifil32.dll")]
+ public static extern int AVIFileGetStream(IntPtr aviHandler, out IntPtr streamHandler, int streamType, int streamNumner);
+
+ ///
+ /// Create a new stream in an existing file and creates an interface to the new stream.
+ ///
+ ///
+ /// Handler to an open AVI file.
+ /// Stream interface.
+ /// Pointer to a structure containing information about the new stream.
+ ///
+ /// Returns zero if successful or an error otherwise.
+ ///
+ [DllImport("avifil32.dll")]
+ public static extern int AVIFileCreateStream(IntPtr aviHandler, out IntPtr streamHandler, ref AVISTREAMINFO streamInfo);
+
+ ///
+ /// Release an open AVI stream.
+ ///
+ ///
+ /// Handle to an open stream.
+ ///
+ /// Returns the current reference count of the stream.
+ ///
+ [DllImport("avifil32.dll")]
+ public static extern int AVIStreamRelease(IntPtr streamHandler);
+
+ ///
+ /// Set the format of a stream at the specified position.
+ ///
+ ///
+ /// Handle to an open stream.
+ /// Position in the stream to receive the format.
+ /// Pointer to a structure containing the new format.
+ /// Size, in bytes, of the block of memory referenced by format.
+ ///
+ /// Returns zero if successful or an error otherwise.
+ ///
+ [DllImport("avifil32.dll")]
+ public static extern int AVIStreamSetFormat(IntPtr streamHandler, int position, ref BitmapInfoHeader format, int formatSize);
+
+ ///
+ /// Get the starting sample number for the stream.
+ ///
+ ///
+ /// Handle to an open stream.
+ ///
+ /// Returns the number if successful or – 1 otherwise.
+ ///
+ [DllImport("avifil32.dll")]
+ public static extern int AVIStreamStart(IntPtr streamHandler);
+
+ ///
+ /// Get the length of the stream.
+ ///
+ /// Handle to an open stream.
+ /// Returns the stream's length, in samples, if successful or -1 otherwise.
+ [DllImport("avifil32.dll")]
+ public static extern int AVIStreamLength(IntPtr streamHandler);
+
+ ///
+ /// Obtain stream header information.
+ ///
+ ///
+ /// Handle to an open stream.
+ /// Pointer to a structure to contain the stream information.
+ /// Size, in bytes, of the structure used for streamInfo.
+ ///
+ /// Returns zero if successful or an error otherwise.
+ ///
+ [DllImport("avifil32.dll", CharSet = CharSet.Unicode)]
+ public static extern int AVIStreamInfo(IntPtr streamHandler, ref AVISTREAMINFO streamInfo, int infoSize);
+
+ ///
+ /// Prepare to decompress video frames from the specified video stream
+ ///
+ /// Pointer to the video stream used as the video source.
+ /// Pointer to a structure that defines the desired video format. Specify NULL to use a default format.
+ /// Returns an object that can be used with the function.
+ [DllImport("avifil32.dll")]
+ public static extern IntPtr AVIStreamGetFrameOpen(IntPtr streamHandler, ref BitmapInfoHeader wantedFormat);
+
+ ///
+ /// Prepare to decompress video frames from the specified video stream.
+ ///
+ /// Pointer to the video stream used as the video source.
+ /// Pointer to a structure that defines the desired video format. Specify NULL to use a default format.
+ /// Returns a GetFrame object that can be used with the function.
+ [DllImport("avifil32.dll")]
+ public static extern IntPtr AVIStreamGetFrameOpen(IntPtr streamHandler, int wantedFormat);
+
+ ///
+ /// Releases resources used to decompress video frames.
+ ///
+ /// Handle returned from the function.
+ /// Returns zero if successful or an error otherwise.
+ [DllImport("avifil32.dll")]
+ public static extern int AVIStreamGetFrameClose(IntPtr getFrameObject);
+
+ ///
+ /// Return the address of a decompressed video frame.
+ ///
+ /// Pointer to a GetFrame object.
+ /// Position, in samples, within the stream of the desired frame.
+ /// Returns a pointer to the frame data if successful or NULL otherwise.
+ [DllImport("avifil32.dll")]
+ public static extern IntPtr AVIStreamGetFrame(IntPtr getFrameObject, int position);
+
+ ///
+ /// Write data to a stream.
+ ///
+ /// Handle to an open stream.
+ /// First sample to write.
+ /// Number of samples to write.
+ /// Pointer to a buffer containing the data to write.
+ /// Size of the buffer referenced by buffer.
+ /// Flag associated with this data.
+ /// Pointer to a buffer that receives the number of samples written. This can be set to NULL.
+ /// Pointer to a buffer that receives the number of bytes written. This can be set to NULL.
+ ///
+ /// Returns zero if successful or an error otherwise.
+ ///
+ [DllImport("avifil32.dll")]
+ public static extern int AVIStreamWrite(IntPtr streamHandler, int start, int samples, IntPtr buffer, int bufferSize, int flags, IntPtr samplesWritten, IntPtr bytesWritten);
+
+ ///
+ /// Retrieve the save options for a file and returns them in a buffer.
+ ///
+ /// Handle to the parent window for the Compression Options dialog box.
+ /// Flags for displaying the Compression Options dialog box.
+ /// Number of streams that have their options set by the dialog box.
+ /// Pointer to an array of stream interface pointers.
+ /// Pointer to an array of pointers to AVICOMPRESSOPTIONS structures.
+ /// Returns TRUE if the user pressed OK, FALSE for CANCEL, or an error otherwise.
+ [DllImport("avifil32.dll")]
+ public static extern int AVISaveOptions(IntPtr window, int flags, int streams, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] streamInterfaces, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] options);
+
+ ///
+ /// Free the resources allocated by the AVISaveOptions function.
+ ///
+ /// Count of the AVICOMPRESSOPTIONS structures referenced in options.
+ /// Pointer to an array of pointers to AVICOMPRESSOPTIONS structures.
+ /// Returns 0.
+ [DllImport("avifil32.dll")]
+ public static extern int AVISaveOptionsFree(int streams, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] options);
+
+ ///
+ /// Create a compressed stream from an uncompressed stream and a
+ /// compression filter, and returns the address of a pointer to
+ /// the compressed stream.
+ ///
+ /// Pointer to a buffer that receives the compressed stream pointer.
+ /// Pointer to the stream to be compressed.
+ /// Pointer to a structure that identifies the type of compression to use and the options to apply.
+ /// Pointer to a class identifier used to create the stream.
+ /// Returns 0 if successful or an error otherwise.
+ [DllImport("avifil32.dll")]
+ public static extern int AVIMakeCompressedStream(out IntPtr compressedStream, IntPtr sourceStream, ref AVICOMPRESSOPTIONS options, IntPtr clsidHandler);
+
+ // --- structures
+
+ ///
+ /// Structure, which contains information for a single stream .
+ ///
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
+ public struct AVISTREAMINFO
+ {
+ ///
+ /// Four-character code indicating the stream type.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int type;
+
+ ///
+ /// Four-character code of the compressor handler that will compress this video stream when it is saved.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int handler;
+
+ ///
+ /// Applicable flags for the stream.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int flags;
+
+ ///
+ /// Capability flags; currently unused.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int ñapabilities;
+
+ ///
+ /// Priority of the stream.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I2)]
+ public short priority;
+
+ ///
+ /// Language of the stream.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I2)]
+ public short language;
+
+ ///
+ /// Time scale applicable for the stream.
+ ///
+ ///
+ /// Dividing rate by scale gives the playback rate in number of samples per second.
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int scale;
+
+ ///
+ /// Rate in an integer format.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int rate;
+
+ ///
+ /// Sample number of the first frame of the AVI file.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int start;
+
+ ///
+ /// Length of this stream.
+ ///
+ ///
+ /// The units are defined by rate and scale.
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int length;
+
+ ///
+ /// Audio skew. This member specifies how much to skew the audio data ahead of the video frames in interleaved files.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int initialFrames;
+
+ ///
+ /// Recommended buffer size, in bytes, for the stream.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int suggestedBufferSize;
+
+ ///
+ /// Quality indicator of the video data in the stream.
+ ///
+ ///
+ /// Quality is represented as a number between 0 and 10,000.
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int quality;
+
+ ///
+ /// Size, in bytes, of a single data sample.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int sampleSize;
+
+ ///
+ /// Dimensions of the video destination rectangle.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.Struct, SizeConst = 16)]
+ public RECT rectFrame;
+
+ ///
+ /// Number of times the stream has been edited.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int editCount;
+
+ ///
+ /// Number of times the stream format has changed.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int formatChangeCount;
+
+ ///
+ /// Description of the stream.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
+ public string name;
+ }
+
+ ///
+ /// Structure, which contains information about a stream and how it is compressed and saved.
+ ///
+ ///
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ public struct AVICOMPRESSOPTIONS
+ {
+ ///
+ /// Four-character code indicating the stream type.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int type;
+
+ ///
+ /// Four-character code for the compressor handler that will compress this video stream when it is saved.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int handler;
+
+ ///
+ /// Maximum period between video key frames.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int keyFrameEvery;
+
+ ///
+ /// Quality value passed to a video compressor.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int quality;
+
+ ///
+ /// Video compressor data rate.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int bytesPerSecond;
+
+ ///
+ /// Flags used for compression.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int flags;
+
+ ///
+ /// Pointer to a structure defining the data format.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int format;
+
+ ///
+ /// Size, in bytes, of the data referenced by format.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int formatSize;
+
+ ///
+ /// Video-compressor-specific data; used internally.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int parameters;
+
+ ///
+ /// Size, in bytes, of the data referenced by parameters.
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int parametersSize;
+
+ ///
+ /// Interleave factor for interspersing stream data with data from the first stream.
+ ///
+ ///
+ [MarshalAs(UnmanagedType.I4)]
+ public int interleaveEvery;
+ }
+
+ // --- enumerations
+
+ ///
+ /// File access modes.
+ ///
+ ///
+ [Flags]
+ public enum OpenFileMode {
+ Read = 0x00000000,
+ Write = 0x00000001,
+ ReadWrite = 0x00000002,
+ ShareCompat = 0x00000000,
+ ShareExclusive = 0x00000010,
+ ShareDenyWrite = 0x00000020,
+ ShareDenyRead = 0x00000030,
+ ShareDenyNone = 0x00000040,
+ Parse = 0x00000100,
+ Delete = 0x00000200,
+ Verify = 0x00000400,
+ Cancel = 0x00000800,
+ Create = 0x00001000,
+ Prompt = 0x00002000,
+ Exist = 0x00004000,
+ Reopen = 0x00008000
+ }
+
+ ///
+ /// .NET replacement of mmioFOURCC macros. Converts four characters to code.
+ ///
+ ///
+ /// Four characters string.
+ ///
+ /// Returns the code created from provided characters.
+ ///
+ public static int mmioFOURCC(string str) {
+ return ( ((int)(byte)(str[0])) |
+ ((int)(byte)(str[1]) << 8) |
+ ((int)(byte)(str[2]) << 16) |
+ ((int)(byte)(str[3]) << 24));
+ }
+
+ ///
+ /// Inverse to . Converts code to fout characters string.
+ ///
+ ///
+ /// Code to convert.
+ ///
+ /// Returns four characters string.
+ ///
+ public static string decode_mmioFOURCC(int code) {
+ char[] chs = new char[4];
+
+ for (int i = 0; i < 4; i++) {
+ chs[i] = (char)(byte)((code >> (i << 3)) & 0xFF);
+ if (!char.IsLetterOrDigit(chs[i])) {
+ chs[i] = ' ';
+ }
+ }
+ return new string(chs);
+ }
+
+
+ ///
+ /// Version of for one stream only.
+ ///
+ ///
+ /// Stream to configure.
+ /// Stream options.
+ ///
+ /// Returns TRUE if the user pressed OK, FALSE for CANCEL, or an error otherwise.
+ ///
+ public static int AVISaveOptions(IntPtr stream, ref AVICOMPRESSOPTIONS options) {
+ IntPtr[] streams = new IntPtr[1];
+ IntPtr[] infPtrs = new IntPtr[1];
+ IntPtr mem;
+ int ret;
+
+ // alloc unmanaged memory
+ mem = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(AVICOMPRESSOPTIONS)));
+
+ // copy from managed structure to unmanaged memory
+ Marshal.StructureToPtr(options, mem, false);
+
+ streams[0] = stream;
+ infPtrs[0] = mem;
+
+ // show dialog with a list of available compresors and configuration
+ ret = AVISaveOptions(IntPtr.Zero, 0, 1, streams, infPtrs);
+
+ // copy from unmanaged memory to managed structure
+ options = (AVICOMPRESSOPTIONS)Marshal.PtrToStructure(mem, typeof(AVICOMPRESSOPTIONS));
+
+ // free AVI compression options
+ AVISaveOptionsFree(1, infPtrs);
+
+ // clear it, because the information already freed by AVISaveOptionsFree
+ options.format = 0;
+ options.parameters = 0;
+
+ // free unmanaged memory
+ Marshal.FreeHGlobal(mem);
+
+ return ret;
+ }
+ }
+}
diff --git a/Greenshot/Helpers/CaptureHelper.cs b/Greenshot/Helpers/CaptureHelper.cs
new file mode 100644
index 000000000..a286c077a
--- /dev/null
+++ b/Greenshot/Helpers/CaptureHelper.cs
@@ -0,0 +1,725 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Printing;
+using System.IO;
+using System.Windows.Forms;
+
+using Greenshot.Configuration;
+using Greenshot.Drawing;
+using Greenshot.Helpers;
+using Greenshot.Forms;
+using Greenshot.Plugin;
+using GreenshotPlugin.UnmanagedHelpers;
+using GreenshotPlugin.Core;
+using IniFile;
+
+namespace Greenshot.Helpers {
+ ///
+ /// CaptureHelper contains all the capture logic
+ ///
+ public class CaptureHelper {
+ private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(CaptureHelper));
+ private static CoreConfiguration conf = IniConfig.GetIniSection();
+ private static ScreenCaptureHelper screenCapture = null;
+ private List windows = new List();
+ private WindowDetails selectedCaptureWindow = null;
+ private Rectangle captureRect = Rectangle.Empty;
+ private bool captureMouseCursor = false;
+ private ICapture capture = null;
+ private ILanguage lang = Language.GetInstance();
+ private CaptureMode captureMode;
+
+ public static void CaptureClipboard() {
+ new CaptureHelper(CaptureMode.Clipboard).MakeCapture();
+ }
+ public static void CaptureRegion(bool captureMouse) {
+ new CaptureHelper(CaptureMode.Region, captureMouse).MakeCapture();
+ }
+ public static void CaptureRegion(bool captureMouse, IDestination destination) {
+ CaptureHelper captureHelper = new CaptureHelper(CaptureMode.Region, captureMouse, destination);
+ captureHelper.MakeCapture();
+ }
+ public static void CaptureFullscreen(bool captureMouse) {
+ new CaptureHelper(CaptureMode.FullScreen, captureMouse).MakeCapture();
+ }
+ public static void CaptureLastRegion(bool captureMouse) {
+ new CaptureHelper(CaptureMode.LastRegion, captureMouse).MakeCapture();
+ }
+ public static void CaptureIE(bool captureMouse) {
+ new CaptureHelper(CaptureMode.IE, captureMouse).MakeCapture();
+ }
+ public static void CaptureWindow(bool captureMouse) {
+ new CaptureHelper(CaptureMode.ActiveWindow, captureMouse).MakeCapture();
+ }
+ public static void CaptureWindow(WindowDetails windowToCapture) {
+ CaptureHelper captureHelper = new CaptureHelper(CaptureMode.ActiveWindow);
+ captureHelper.SelectedCaptureWindow = windowToCapture;
+ captureHelper.MakeCapture();
+ }
+ public static void CaptureWindowInteractive(bool captureMouse) {
+ new CaptureHelper(CaptureMode.Window, captureMouse).MakeCapture();
+ }
+ public static void CaptureFile(string filename) {
+ new CaptureHelper(CaptureMode.File).MakeCapture(filename);
+ }
+
+ public static void ImportCapture(ICapture captureToImport) {
+ CaptureHelper captureHelper = new CaptureHelper(CaptureMode.File);
+ captureHelper.capture = captureToImport;
+ captureHelper.HandleCapture();
+ }
+
+ public CaptureHelper(CaptureMode captureMode) {
+ this.captureMode = captureMode;
+ capture = new Capture();
+ }
+
+ public CaptureHelper(CaptureMode captureMode, bool captureMouseCursor) : this(captureMode) {
+ this.captureMouseCursor = captureMouseCursor;
+ }
+
+ public CaptureHelper(CaptureMode captureMode, bool captureMouseCursor, IDestination destination) : this(captureMode, captureMouseCursor) {
+ capture.CaptureDetails.AddDestination(destination);
+ }
+
+ public WindowDetails SelectedCaptureWindow {
+ get {
+ return selectedCaptureWindow;
+ }
+ set {
+ selectedCaptureWindow = value;
+ }
+ }
+
+ private void DoCaptureFeedback() {
+ if(conf.PlayCameraSound) {
+ SoundHelper.Play();
+ }
+ }
+
+ ///
+ /// Make Capture with file name
+ ///
+ /// filename
+ private void MakeCapture(string filename) {
+ capture.CaptureDetails.Filename = filename;
+ MakeCapture();
+ }
+
+ ///
+ /// Make Capture with specified destinations
+ ///
+ private void MakeCapture() {
+
+ // Experimental code
+ if (screenCapture != null) {
+ screenCapture.Stop();
+ screenCapture = null;
+ return;
+ }
+
+ LOG.Debug(String.Format("Capturing with mode {0} and using Cursor {1})", captureMode, captureMouseCursor));
+ capture.CaptureDetails.CaptureMode = captureMode;
+
+ // Add destinations if no-one passed a handler
+ if (capture.CaptureDetails.CaptureDestinations == null || capture.CaptureDetails.CaptureDestinations.Count == 0) {
+ AddConfiguredDestination();
+ }
+ PrepareForCaptureWithFeedback();
+
+ // Workaround for proble with DPI retrieval, the FromHwnd activates the window...
+ WindowDetails previouslyActiveWindow = WindowDetails.GetActiveWindow();
+ // Workaround for changed DPI settings in Windows 7
+ using (Graphics graphics = Graphics.FromHwnd(MainForm.instance.Handle)) {
+ capture.CaptureDetails.DpiX = graphics.DpiX;
+ capture.CaptureDetails.DpiY = graphics.DpiY;
+ }
+ if (previouslyActiveWindow != null) {
+ // Set previouslyActiveWindow as foreground window
+ previouslyActiveWindow.ToForeground();
+ }
+
+ // Delay for the Context menu
+ if (conf.CaptureDelay > 0) {
+ System.Threading.Thread.Sleep(conf.CaptureDelay);
+ } else {
+ conf.CaptureDelay = 0;
+ }
+
+ // Allways capture Mousecursor, only show when needed
+ capture = WindowCapture.CaptureCursor(capture);
+ capture.CursorVisible = false;
+ // Check if needed
+ if (captureMouseCursor && captureMode != CaptureMode.Clipboard && captureMode != CaptureMode.File) {
+ capture.CursorVisible = conf.CaptureMousepointer;
+ }
+
+ switch(captureMode) {
+ case CaptureMode.Window:
+ capture = WindowCapture.CaptureScreen(capture);
+ capture.CaptureDetails.AddMetaData("source", "Screen");
+ CaptureWithFeedback();
+ break;
+ case CaptureMode.ActiveWindow:
+ if (CaptureActiveWindow()) {
+ // Capture worked, offset mouse according to screen bounds and capture location
+ capture.MoveMouseLocation(capture.ScreenBounds.Location.X-capture.Location.X, capture.ScreenBounds.Location.Y-capture.Location.Y);
+ capture.MoveElements(capture.ScreenBounds.Location.X-capture.Location.X, capture.ScreenBounds.Location.Y-capture.Location.Y);
+ capture.CaptureDetails.AddMetaData("source", "Window");
+ } else {
+ captureMode = CaptureMode.FullScreen;
+ capture = WindowCapture.CaptureScreen(capture);
+ capture.CaptureDetails.AddMetaData("source", "Screen");
+ capture.CaptureDetails.Title = "Screen";
+ }
+ HandleCapture();
+ break;
+ case CaptureMode.IE:
+ if (IECaptureHelper.CaptureIE(capture) != null) {
+ capture.CaptureDetails.AddMetaData("source", "Internet Explorer");
+ HandleCapture();
+ }
+ break;
+ case CaptureMode.FullScreen:
+ capture = WindowCapture.CaptureScreen(capture);
+ HandleCapture();
+ break;
+ case CaptureMode.Clipboard:
+ Image clipboardImage = null;
+ string text = "Clipboard";
+ if (ClipboardHelper.ContainsImage()) {
+ clipboardImage = ClipboardHelper.GetImage();
+ }
+ if (clipboardImage != null) {
+ if (capture != null) {
+ capture.Image = clipboardImage;
+ } else {
+ capture = new Capture(clipboardImage);
+ }
+ string title = ClipboardHelper.GetText();
+ if (title == null || title.Trim().Length == 0) {
+ title = "Clipboard";
+ }
+ capture.CaptureDetails.Title = title;
+ capture.CaptureDetails.AddMetaData("source", "Clipboard");
+ // Force Editor, keep picker
+ if (capture.CaptureDetails.HasDestination(Destinations.PickerDestination.DESIGNATION)) {
+ capture.CaptureDetails.ClearDestinations();
+ capture.CaptureDetails.AddDestination(DestinationHelper.GetDestination(Destinations.EditorDestination.DESIGNATION));
+ capture.CaptureDetails.AddDestination(DestinationHelper.GetDestination(Destinations.PickerDestination.DESIGNATION));
+ } else {
+ capture.CaptureDetails.ClearDestinations();
+ capture.CaptureDetails.AddDestination(DestinationHelper.GetDestination(Destinations.EditorDestination.DESIGNATION));
+ }
+ HandleCapture();
+ } else {
+ MessageBox.Show("Couldn't create bitmap from : " + text);
+ }
+ break;
+ case CaptureMode.File:
+ Bitmap fileBitmap = null;
+ string filename = capture.CaptureDetails.Filename;
+ if (!string.IsNullOrEmpty(filename)) {
+ try {
+ fileBitmap = ImageHelper.LoadBitmap(filename);
+ } catch (Exception e) {
+ LOG.Error(e.Message, e);
+ MessageBox.Show(lang.GetFormattedString(LangKey.error_openfile, filename));
+ }
+ }
+ if (fileBitmap != null) {
+ capture.CaptureDetails.Title = Path.GetFileNameWithoutExtension(filename);
+ capture.CaptureDetails.AddMetaData("file", filename);
+ capture.CaptureDetails.AddMetaData("source", "file");
+ if (capture != null) {
+ capture.Image = fileBitmap;
+ } else {
+ capture = new Capture(fileBitmap);
+ }
+ // Force Editor, keep picker, this is currently the only usefull destination
+ if (capture.CaptureDetails.HasDestination("Picker")) {
+ capture.CaptureDetails.ClearDestinations();
+ capture.CaptureDetails.AddDestination(DestinationHelper.GetDestination(Destinations.EditorDestination.DESIGNATION));
+ capture.CaptureDetails.AddDestination(DestinationHelper.GetDestination(Destinations.PickerDestination.DESIGNATION));
+ } else {
+ capture.CaptureDetails.ClearDestinations();
+ capture.CaptureDetails.AddDestination(DestinationHelper.GetDestination(Destinations.EditorDestination.DESIGNATION));
+ }
+ HandleCapture();
+ }
+ break;
+ case CaptureMode.LastRegion:
+ if (!RuntimeConfig.LastCapturedRegion.IsEmpty) {
+ capture = WindowCapture.CaptureScreen(capture);
+ capture.Crop(RuntimeConfig.LastCapturedRegion);
+ capture.CaptureDetails.AddMetaData("source", "screen");
+ HandleCapture();
+ }
+ break;
+ case CaptureMode.Region:
+ capture = WindowCapture.CaptureScreen(capture);
+ capture.CaptureDetails.AddMetaData("source", "screen");
+ CaptureWithFeedback();
+ break;
+ case CaptureMode.Video:
+ capture = WindowCapture.CaptureScreen(capture);
+ // Set the capturemode to be window
+ captureMode = CaptureMode.Window;
+ capture.CaptureDetails.AddMetaData("source", "Video");
+ CaptureWithFeedback();
+ break;
+ default:
+ LOG.Warn("Unknown capture mode: " + captureMode);
+ break;
+ }
+ }
+
+ ///
+ /// Pre-Initialization for CaptureWithFeedback, this will get all the windows before we change anything
+ ///
+ private void PrepareForCaptureWithFeedback() {
+ windows = new List();
+ // Start Enumeration of "active" windows
+ foreach (WindowDetails window in WindowDetails.GetAllWindows()) {
+ // Window should be visible and not ourselves
+ if (!window.Visible) {
+ continue;
+ }
+
+ // Skip empty
+ Rectangle windowRectangle = window.WindowRectangle;
+ Size windowSize = windowRectangle.Size;
+ if (windowSize.Width == 0 || windowSize.Height == 0) {
+ continue;
+ }
+
+ // Make sure the details are retrieved once
+ window.FreezeDetails();
+
+ // Force children retrieval, sometimes windows close on losing focus and this is solved by caching
+ int goLevelDeep = 3;
+ if (conf.WindowCaptureAllChildLocations) {
+ goLevelDeep = 20;
+ }
+ window.GetChildren(goLevelDeep);
+ windows.Add(window);
+
+ // Get window rectangle as capture Element
+ CaptureElement windowCaptureElement = new CaptureElement(windowRectangle);
+ capture.Elements.Add(windowCaptureElement);
+
+ if (!window.HasParent) {
+ // Get window client rectangle as capture Element, place all the other "children" in there
+ Rectangle clientRectangle = window.ClientRectangle;
+ CaptureElement windowClientCaptureElement = new CaptureElement(clientRectangle);
+ windowCaptureElement.Children.Add(windowClientCaptureElement);
+ AddCaptureElementsForWindow(windowClientCaptureElement, window, goLevelDeep);
+ } else {
+ AddCaptureElementsForWindow(windowCaptureElement, window, goLevelDeep);
+ }
+
+ }
+ windows = WindowDetails.SortByZOrder(IntPtr.Zero, windows);
+ }
+
+ private void AddCaptureElementsForWindow(ICaptureElement parentElement, WindowDetails parentWindow, int level) {
+ foreach(WindowDetails childWindow in parentWindow.Children) {
+ // Make sure the details are retrieved once
+ childWindow.FreezeDetails();
+ Rectangle childRectangle = childWindow.WindowRectangle;
+ Size s1 = childRectangle.Size;
+ childRectangle.Intersect(parentElement.Bounds);
+ if (childRectangle.Width > 0 && childRectangle.Height > 0) {
+ CaptureElement childCaptureElement = new CaptureElement(childRectangle);
+ parentElement.Children.Add(childCaptureElement);
+ if (level > 0) {
+ AddCaptureElementsForWindow(childCaptureElement, childWindow, level -1);
+ }
+ }
+ }
+ }
+
+ private void AddConfiguredDestination() {
+ foreach(string destinationDesignation in conf.OutputDestinations) {
+ IDestination destination = DestinationHelper.GetDestination(destinationDesignation);
+ if (destination != null) {
+ capture.CaptureDetails.AddDestination(destination);
+ }
+ }
+ }
+
+ private void HandleCapture() {
+ // Flag to see if the image was "exported" so the FileEditor doesn't
+ // ask to save the file as long as nothing is done.
+ bool outputMade = false;
+
+ // Make sure the user sees that the capture is made
+ if (capture.CaptureDetails.CaptureMode != CaptureMode.File && capture.CaptureDetails.CaptureMode != CaptureMode.Clipboard) {
+ DoCaptureFeedback();
+ } else {
+ // If File || Clipboard
+ // Maybe not "made" but the original is still there... somehow
+ outputMade = true;
+ }
+
+ LOG.Debug("A capture of: " + capture.CaptureDetails.Title);
+
+ // check if someone has passed a destination
+ if (capture.CaptureDetails.CaptureDestinations == null || capture.CaptureDetails.CaptureDestinations.Count == 0) {
+ AddConfiguredDestination();
+ }
+
+ // Create Surface with capture, this way elements can be added automatically (like the mouse cursor)
+ Surface surface = new Surface(capture);
+
+ // Register notify events if this is wanted
+ if (conf.ShowTrayNotification) {
+ surface.SurfaceMessage += delegate(object source, SurfaceMessageEventArgs eventArgs) {
+ switch (eventArgs.MessageType) {
+ case SurfaceMessageTyp.Error:
+ MainForm.instance.notifyIcon.ShowBalloonTip(10000, "Greenshot", eventArgs.Message, ToolTipIcon.Error);
+ break;
+ case SurfaceMessageTyp.Info:
+ MainForm.instance.notifyIcon.ShowBalloonTip(10000, "Greenshot", eventArgs.Message, ToolTipIcon.Info);
+ break;
+ case SurfaceMessageTyp.FileSaved:
+ EventHandler balloonTipClickedHandler = null;
+ EventHandler balloonTipClosedHandler = null;
+ balloonTipClosedHandler = delegate(object sender, EventArgs e) {
+ LOG.DebugFormat("Deregistering the BalloonTipClosed");
+ MainForm.instance.notifyIcon.BalloonTipClicked -= balloonTipClickedHandler;
+ MainForm.instance.notifyIcon.BalloonTipClosed -= balloonTipClosedHandler;
+ };
+
+ balloonTipClickedHandler = delegate(object sender, EventArgs e) {
+ if (surface.LastSaveFullPath != null) {
+ ProcessStartInfo psi = new ProcessStartInfo("explorer");
+ psi.Arguments = Path.GetDirectoryName(eventArgs.Surface.LastSaveFullPath);
+ psi.UseShellExecute = false;
+ Process p = new Process();
+ p.StartInfo = psi;
+ p.Start();
+ }
+ LOG.DebugFormat("Deregistering the BalloonTipClicked");
+ MainForm.instance.notifyIcon.BalloonTipClicked -= balloonTipClickedHandler;
+ MainForm.instance.notifyIcon.BalloonTipClosed -= balloonTipClosedHandler;
+ };
+ MainForm.instance.notifyIcon.BalloonTipClicked += balloonTipClickedHandler;
+ MainForm.instance.notifyIcon.BalloonTipClosed += balloonTipClosedHandler;
+ MainForm.instance.notifyIcon.ShowBalloonTip(10000, "Greenshot", eventArgs.Message, ToolTipIcon.Info);
+ break;
+ }
+ };
+
+ }
+ // Let the processors do their job
+ foreach(IProcessor processor in ProcessorHelper.GetAllProcessors()) {
+ if (processor.isActive) {
+ LOG.InfoFormat("Calling processor {0}", processor.Description);
+ processor.ProcessCapture(surface, capture.CaptureDetails);
+ }
+ }
+
+ // As the surfaces copies the reference to the image, make sure the image is not being disposed (a trick to save memory)
+ capture.Image = null;
+
+ // Get CaptureDetails as we need it even after the capture is disposed
+ ICaptureDetails captureDetails = capture.CaptureDetails;
+ bool canDisposeSurface = true;
+
+ if (captureDetails.HasDestination(Destinations.PickerDestination.DESIGNATION)) {
+ DestinationHelper.ExportCapture(Destinations.PickerDestination.DESIGNATION, surface, captureDetails);
+ captureDetails.CaptureDestinations.Clear();
+ canDisposeSurface = false;
+ }
+
+ // Disable capturing
+ captureMode = CaptureMode.None;
+ // Dispose the capture, we don't need it anymore (the surface copied all information and we got the title (if any)).
+ capture.Dispose();
+ capture = null;
+
+ int destinationCount = captureDetails.CaptureDestinations.Count;
+ if (destinationCount > 0) {
+ // Flag to detect if we need to create a temp file for the email
+ // or use the file that was written
+ foreach(IDestination destination in captureDetails.CaptureDestinations) {
+ if ("Picker".Equals(destination.Designation)) {
+ continue;
+ }
+ LOG.InfoFormat("Calling destination {0}", destination.Description);
+
+ bool destinationOk = destination.ExportCapture(surface, captureDetails);
+ if (Destinations.EditorDestination.DESIGNATION.Equals(destination.Designation) && destinationOk) {
+ canDisposeSurface = false;
+ }
+ outputMade = outputMade || destinationOk;
+ }
+ }
+ if (canDisposeSurface) {
+ surface.Dispose();
+ }
+ }
+
+ private bool CaptureActiveWindow() {
+ bool presupplied = false;
+ LOG.Debug("CaptureActiveWindow");
+ if (selectedCaptureWindow != null) {
+ LOG.Debug("Using supplied window");
+ presupplied = true;
+ } else {
+ selectedCaptureWindow = WindowDetails.GetActiveWindow();
+ if (selectedCaptureWindow != null) {
+ LOG.DebugFormat("Capturing window: {0} with {1}", selectedCaptureWindow.Text, selectedCaptureWindow.WindowRectangle);
+ }
+ }
+ if (selectedCaptureWindow == null || (!presupplied && selectedCaptureWindow.Iconic)) {
+ LOG.Warn("No window to capture!");
+ // Nothing to capture, code up in the stack will capture the full screen
+ return false;
+ }
+ if (selectedCaptureWindow != null && selectedCaptureWindow.Iconic) {
+ // Restore the window making sure it's visible!
+ // This is done mainly for a screen capture, but some applications like Excel and TOAD have weird behaviour!
+ selectedCaptureWindow.Restore();
+ }
+ selectedCaptureWindow.ToForeground();
+ selectedCaptureWindow = SelectCaptureWindow(selectedCaptureWindow);
+ if (selectedCaptureWindow == null) {
+ LOG.Warn("No window to capture, after SelectCaptureWindow!");
+ // Nothing to capture, code up in the stack will capture the full screen
+ return false;
+ }
+ // Fix for Bug #3430560
+ RuntimeConfig.LastCapturedRegion = selectedCaptureWindow.WindowRectangle;
+ bool returnValue = CaptureWindow(selectedCaptureWindow, capture, conf.WindowCaptureMode) != null;
+ return returnValue;
+ }
+
+ ///
+ /// Select the window to capture, this has logic which takes care of certain special applications
+ /// like TOAD or Excel
+ ///
+ /// WindowDetails with the target Window
+ /// WindowDetails with the target Window OR a replacement
+ public static WindowDetails SelectCaptureWindow(WindowDetails windowToCapture) {
+ Rectangle windowRectangle = windowToCapture.WindowRectangle;
+ if (windowToCapture.Iconic || windowRectangle.Width == 0 || windowRectangle.Height == 0) {
+ LOG.WarnFormat("Window {0} has nothing to capture, using workaround to find other window of same process.", windowToCapture.Text);
+ // Trying workaround, the size 0 arrises with e.g. Toad.exe, has a different Window when minimized
+ WindowDetails linkedWindow = WindowDetails.GetLinkedWindow(windowToCapture);
+ if (linkedWindow != null) {
+ windowRectangle = linkedWindow.WindowRectangle;
+ windowToCapture = linkedWindow;
+ } else {
+ return null;
+ }
+ }
+ return windowToCapture;
+ }
+
+ ///
+ /// Check if Process uses PresentationFramework.dll -> meaning it uses WPF
+ ///
+ ///
+ /// true if the process uses WPF
+ private static bool isWPF(Process process) {
+ if (process != null) {
+ foreach(ProcessModule module in process.Modules) {
+ if (module.ModuleName.StartsWith("PresentationFramework")) {
+ LOG.InfoFormat("Found that Process {0} uses {1}, assuming it's using WPF", process.ProcessName, module.FileName);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Capture the supplied Window
+ ///
+ /// Window to capture
+ /// The capture to store the details
+ /// What WindowCaptureMode to use
+ ///
+ public static ICapture CaptureWindow(WindowDetails windowToCapture, ICapture captureForWindow, WindowCaptureMode windowCaptureMode) {
+ if (captureForWindow == null) {
+ captureForWindow = new Capture();
+ }
+ Rectangle windowRectangle = windowToCapture.WindowRectangle;
+ if (windowToCapture.Iconic) {
+ // Restore the window making sure it's visible!
+ windowToCapture.Restore();
+ }
+
+ // When Vista & DWM (Aero) enabled
+ bool dwmEnabled = DWM.isDWMEnabled();
+ // get process name to be able to exclude certain processes from certain capture modes
+ Process process = windowToCapture.Process;
+ bool isAutoMode = windowCaptureMode == WindowCaptureMode.Auto;
+ // For WindowCaptureMode.Auto we check:
+ // 1) Is window IE, use IE Capture
+ // 2) Is Windows >= Vista & DWM enabled: use DWM
+ // 3) Otherwise use GDI (Screen might be also okay but might lose content)
+ if (isAutoMode) {
+ if (conf.IECapture && windowToCapture.ClassName == "IEFrame") {
+ try {
+ ICapture ieCapture = IECaptureHelper.CaptureIE(captureForWindow);
+ if (ieCapture != null) {
+ return ieCapture;
+ }
+ } catch (Exception ex) {
+ LOG.WarnFormat("Problem capturing IE, skipping to normal capture. Exception message was: {0}", ex.Message);
+ }
+ }
+
+ // Take default screen
+ windowCaptureMode = WindowCaptureMode.Screen;
+
+ // Change to GDI, if allowed
+ if (conf.isGDIAllowed(process)) {
+ if (!dwmEnabled && isWPF(process)) {
+ // do not use GDI, as DWM is not enabled and the application uses PresentationFramework.dll -> isWPF
+ LOG.InfoFormat("Not using GDI for windows of process {0}, as the process uses WPF", process.ProcessName);
+ } else {
+ windowCaptureMode = WindowCaptureMode.GDI;
+ }
+ }
+
+ // Change to DWM, if enabled and allowed
+ if (dwmEnabled) {
+ if (conf.isDWMAllowed(process)) {
+ windowCaptureMode = WindowCaptureMode.Aero;
+ }
+ }
+ } else if (windowCaptureMode == WindowCaptureMode.Aero || windowCaptureMode == WindowCaptureMode.AeroTransparent) {
+ if (!dwmEnabled || !conf.isDWMAllowed(process)) {
+ // Take default screen
+ windowCaptureMode = WindowCaptureMode.Screen;
+ // Change to GDI, if allowed
+ if (conf.isGDIAllowed(process)) {
+ windowCaptureMode = WindowCaptureMode.GDI;
+ }
+ }
+ } else if (windowCaptureMode == WindowCaptureMode.GDI && !conf.isGDIAllowed(process)) {
+ // GDI not allowed, take screen
+ windowCaptureMode = WindowCaptureMode.Screen;
+ }
+
+ LOG.InfoFormat("Capturing window with mode {0}", windowCaptureMode);
+ bool captureTaken = false;
+ // Try to capture
+ while (!captureTaken) {
+ if (windowCaptureMode == WindowCaptureMode.GDI) {
+ ICapture tmpCapture = null;
+ if (conf.isGDIAllowed(process)) {
+ tmpCapture = windowToCapture.CaptureWindow(captureForWindow);
+ }
+ if (tmpCapture != null) {
+ captureForWindow = tmpCapture;
+ captureTaken = true;
+ } else {
+ // A problem, try Screen
+ windowCaptureMode = WindowCaptureMode.Screen;
+ }
+ } else if (windowCaptureMode == WindowCaptureMode.Aero || windowCaptureMode == WindowCaptureMode.AeroTransparent) {
+ ICapture tmpCapture = null;
+ if (conf.isDWMAllowed(process)) {
+ tmpCapture = windowToCapture.CaptureDWMWindow(captureForWindow, windowCaptureMode, isAutoMode);
+ }
+ if (tmpCapture != null) {
+ captureForWindow = tmpCapture;
+ captureTaken = true;
+ } else {
+ // A problem, try GDI
+ windowCaptureMode = WindowCaptureMode.GDI;
+ }
+ } else {
+ // Screen capture
+ windowRectangle.Intersect(captureForWindow.ScreenBounds);
+ try {
+ captureForWindow = WindowCapture.CaptureRectangle(captureForWindow, windowRectangle);
+ captureTaken = true;
+ } catch (Exception e) {
+ LOG.Error("Problem capturing", e);
+ return null;
+ }
+ }
+ }
+
+ if (captureForWindow != null && windowToCapture != null) {
+ captureForWindow.CaptureDetails.Title = windowToCapture.Text;
+ ((Bitmap)captureForWindow.Image).SetResolution(captureForWindow.CaptureDetails.DpiX, captureForWindow.CaptureDetails.DpiY);
+ }
+
+ return captureForWindow;
+ }
+
+ #region capture with feedback
+ private void CaptureWithFeedback() {
+ using (CaptureForm captureForm = new CaptureForm(capture, windows)) {
+ DialogResult result = captureForm.ShowDialog();
+ if (result == DialogResult.OK) {
+ selectedCaptureWindow = captureForm.SelectedCaptureWindow;
+ captureRect = captureForm.CaptureRectangle;
+ // Get title
+ if (selectedCaptureWindow != null) {
+ capture.CaptureDetails.Title = selectedCaptureWindow.Text;
+ }
+
+ // Experimental code
+ if (capture.CaptureDetails.CaptureMode == CaptureMode.Video) {
+ if (captureForm.UsedCaptureMode == CaptureMode.Window) {
+ screenCapture = new ScreenCaptureHelper(selectedCaptureWindow);
+ } else if (captureForm.UsedCaptureMode == CaptureMode.Region) {
+ screenCapture = new ScreenCaptureHelper(captureRect);
+ }
+ if (screenCapture != null) {
+ screenCapture.RecordMouse = capture.CursorVisible;
+ if (screenCapture.Start(15)) {
+ return;
+ }
+ // User clicked cancel or a problem occured
+ screenCapture.Stop();
+ screenCapture = null;
+ return;
+ }
+ }
+
+ if (captureRect.Height > 0 && captureRect.Width > 0) {
+ // Take the captureRect, this already is specified as bitmap coordinates
+ capture.Crop(captureRect);
+ // save for re-capturing later and show recapture context menu option
+ RuntimeConfig.LastCapturedRegion = captureRect;
+ HandleCapture();
+ }
+ }
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Greenshot/Helpers/DestinationHelper.cs b/Greenshot/Helpers/DestinationHelper.cs
new file mode 100644
index 000000000..c98d45f8a
--- /dev/null
+++ b/Greenshot/Helpers/DestinationHelper.cs
@@ -0,0 +1,124 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+
+using Greenshot.Plugin;
+using GreenshotPlugin.Core;
+
+namespace Greenshot.Helpers {
+ ///
+ /// Description of DestinationHelper.
+ ///
+ public static class DestinationHelper {
+ private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(DestinationHelper));
+ private static Dictionary RegisteredDestinations = new Dictionary();
+
+ /// Initialize the destinations
+ static DestinationHelper() {
+ foreach(Type destinationType in InterfaceUtils.GetSubclassesOf(typeof(IDestination),true)) {
+ // Only take our own
+ if (!"Greenshot.Destinations".Equals(destinationType.Namespace)) {
+ continue;
+ }
+ if (!destinationType.IsAbstract) {
+ IDestination destination;
+ try {
+ destination = (IDestination)Activator.CreateInstance(destinationType);
+ } catch (Exception e) {
+ LOG.ErrorFormat("Can't create instance of {0}", destinationType);
+ LOG.Error(e);
+ continue;
+ }
+ if (destination.isActive) {
+ LOG.DebugFormat("Found destination {0} with designation {1}", destinationType.Name, destination.Designation);
+ RegisterDestination(destination);
+ } else {
+ LOG.DebugFormat("Ignoring destination {0} with designation {1}", destinationType.Name, destination.Designation);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Register your destination here, if it doesn't come from a plugin and needs to be available
+ ///
+ ///
+ public static void RegisterDestination(IDestination destination) {
+ // don't test the key, an exception should happen wenn it's not unique
+ RegisteredDestinations.Add(destination.Designation, destination);
+ }
+
+ ///
+ /// Get a list of all destinations, registered or supplied by a plugin
+ ///
+ ///
+ public static List GetAllDestinations() {
+ List destinations = new List();
+ destinations.AddRange(RegisteredDestinations.Values);
+ foreach(IGreenshotPlugin plugin in PluginHelper.instance.Plugins.Values) {
+ destinations.AddRange(plugin.Destinations());
+ }
+ destinations.Sort();
+ return destinations;
+ }
+
+ ///
+ /// Get a destination by a designation
+ ///
+ /// Designation of the destination
+ /// IDestination or null
+ public static IDestination GetDestination(string designation) {
+ if (designation == null) {
+ return null;
+ }
+ if (RegisteredDestinations.ContainsKey(designation)) {
+ return RegisteredDestinations[designation];
+ }
+ foreach(IGreenshotPlugin plugin in PluginHelper.instance.Plugins.Values) {
+ foreach(IDestination destination in plugin.Destinations()) {
+ if (designation.Equals(destination.Designation)) {
+ return destination;
+ }
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// A simple helper method which will call ExportCapture for the destination with the specified designation
+ ///
+ ///
+ ///
+ ///
+ public static void ExportCapture(string designation, ISurface surface, ICaptureDetails captureDetails) {
+ if (RegisteredDestinations.ContainsKey(designation)) {
+ IDestination destination = RegisteredDestinations[designation];
+ if (destination.isActive) {
+ if (destination.ExportCapture(surface, captureDetails)) {
+ // Export worked, set the modified flag
+ surface.Modified = false;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Greenshot/Helpers/GeometryHelper.cs b/Greenshot/Helpers/GeometryHelper.cs
new file mode 100644
index 000000000..21414a559
--- /dev/null
+++ b/Greenshot/Helpers/GeometryHelper.cs
@@ -0,0 +1,63 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+
+namespace Greenshot.Helpers {
+ ///
+ /// Description of GeometryHelper.
+ ///
+ public static class GeometryHelper {
+ ///
+ /// Finds the distance between two points on a 2D surface.
+ ///
+ /// The point on the x-axis of the first point
+ /// The point on the x-axis of the second point
+ /// The point on the y-axis of the first point
+ /// The point on the y-axis of the second point
+ ///
+ public static int Distance2D(int x1, int y1, int x2, int y2) {
+ //Our end result
+ int result = 0;
+ //Take x2-x1, then square it
+ double part1 = Math.Pow((x2 - x1), 2);
+ //Take y2-y1, then square it
+ double part2 = Math.Pow((y2 - y1), 2);
+ //Add both of the parts together
+ double underRadical = part1 + part2;
+ //Get the square root of the parts
+ result = (int)Math.Sqrt(underRadical);
+ //Return our result
+ return result;
+ }
+
+ ///
+ /// Calculates the angle of a line defined by two points on a 2D surface.
+ ///
+ /// The point on the x-axis of the first point
+ /// The point on the x-axis of the second point
+ /// The point on the y-axis of the first point
+ /// The point on the y-axis of the second point
+ ///
+ public static double Angle2D(int x1, int y1, int x2, int y2) {
+ return Math.Atan2(y2 - y1, x2 - x1) * 180 / Math.PI;
+ }
+ }
+}
diff --git a/Greenshot/Helpers/ProcessorHelper.cs b/Greenshot/Helpers/ProcessorHelper.cs
new file mode 100644
index 000000000..12da10ab6
--- /dev/null
+++ b/Greenshot/Helpers/ProcessorHelper.cs
@@ -0,0 +1,121 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+
+using Greenshot.Plugin;
+using GreenshotPlugin.Core;
+
+namespace Greenshot.Helpers {
+ ///
+ /// Description of ProcessorHelper.
+ ///
+ public static class ProcessorHelper {
+ private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ProcessorHelper));
+ private static Dictionary RegisteredProcessors = new Dictionary();
+
+ /// Initialize the Processors
+ static ProcessorHelper() {
+ foreach(Type ProcessorType in InterfaceUtils.GetSubclassesOf(typeof(IProcessor),true)) {
+ // Only take our own
+ if (!"Greenshot.Processors".Equals(ProcessorType.Namespace)) {
+ continue;
+ }
+ if (!ProcessorType.IsAbstract) {
+ IProcessor Processor;
+ try {
+ Processor = (IProcessor)Activator.CreateInstance(ProcessorType);
+ } catch (Exception e) {
+ LOG.ErrorFormat("Can't create instance of {0}", ProcessorType);
+ LOG.Error(e);
+ continue;
+ }
+ if (Processor.isActive) {
+ LOG.DebugFormat("Found Processor {0} with designation {1}", ProcessorType.Name, Processor.Designation);
+ RegisterProcessor(Processor);
+ } else {
+ LOG.DebugFormat("Ignoring Processor {0} with designation {1}", ProcessorType.Name, Processor.Designation);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Register your Processor here, if it doesn't come from a plugin and needs to be available
+ ///
+ ///
+ public static void RegisterProcessor(IProcessor Processor) {
+ // don't test the key, an exception should happen wenn it's not unique
+ RegisteredProcessors.Add(Processor.Designation, Processor);
+ }
+
+ ///
+ /// Get a list of all Processors, registered or supplied by a plugin
+ ///
+ ///
+ public static List GetAllProcessors() {
+ List Processors = new List();
+ Processors.AddRange(RegisteredProcessors.Values);
+ foreach(IGreenshotPlugin plugin in PluginHelper.instance.Plugins.Values) {
+ Processors.AddRange(plugin.Processors());
+ }
+ Processors.Sort();
+ return Processors;
+ }
+
+ ///
+ /// Get a Processor by a designation
+ ///
+ /// Designation of the Processor
+ /// IProcessor or null
+ public static IProcessor GetProcessor(string designation) {
+ if (designation == null) {
+ return null;
+ }
+ if (RegisteredProcessors.ContainsKey(designation)) {
+ return RegisteredProcessors[designation];
+ }
+ foreach(IGreenshotPlugin plugin in PluginHelper.instance.Plugins.Values) {
+ foreach(IProcessor Processor in plugin.Processors()) {
+ if (designation.Equals(Processor.Designation)) {
+ return Processor;
+ }
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// A simple helper method which will call ProcessCapture for the Processor with the specified designation
+ ///
+ ///
+ ///
+ ///
+ public static void ProcessCapture(string designation, ISurface surface, ICaptureDetails captureDetails) {
+ if (RegisteredProcessors.ContainsKey(designation)) {
+ IProcessor Processor = RegisteredProcessors[designation];
+ if (Processor.isActive) {
+ Processor.ProcessCapture(surface, captureDetails);
+ }
+ }
+ }
+ }
+}
diff --git a/Greenshot/Helpers/ScreenCaptureHelper.cs b/Greenshot/Helpers/ScreenCaptureHelper.cs
new file mode 100644
index 000000000..325dc2ab8
--- /dev/null
+++ b/Greenshot/Helpers/ScreenCaptureHelper.cs
@@ -0,0 +1,262 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Windows.Forms;
+
+using Greenshot;
+using Greenshot.Configuration;
+using Greenshot.Plugin;
+using GreenshotPlugin.UnmanagedHelpers;
+using GreenshotPlugin.Core;
+using IniFile;
+
+namespace Greenshot.Helpers {
+ ///
+ /// Description of ScreenCaptureHelper.
+ ///
+ public class ScreenCaptureHelper {
+ private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ScreenCaptureHelper));
+ private static CoreConfiguration conf = IniConfig.GetIniSection();
+ private const int MAX_FRAMES = 500;
+ private IntPtr hWndDesktop = IntPtr.Zero;
+ private IntPtr hDCDesktop = IntPtr.Zero;
+ private IntPtr hDCDest = IntPtr.Zero;
+ private IntPtr hDIBSection = IntPtr.Zero;
+ private IntPtr hOldObject = IntPtr.Zero;
+ private int framesPerSecond;
+ private Thread backgroundTask;
+ private bool stop = false;
+ private AVIWriter aviWriter;
+ private WindowDetails recordingWindow;
+ private Rectangle recordingRectangle;
+ public bool RecordMouse = false;
+ private Size recordingSize;
+ private IntPtr bits0 = IntPtr.Zero; //pointer to the raw bits that make up the bitmap.
+ private Bitmap GDIBitmap;
+ private string filename = null;
+
+ public ScreenCaptureHelper(Rectangle recordingRectangle) {
+ this.recordingRectangle = recordingRectangle;
+ }
+ public ScreenCaptureHelper(WindowDetails recordingWindow) {
+ this.recordingWindow = recordingWindow;
+ }
+
+ ///
+ /// Helper method to create an exception that might explain what is wrong while capturing
+ ///
+ /// string with current method
+ /// Rectangle of what we want to capture
+ ///
+ private static Exception CreateCaptureException(string method, Size size) {
+ Exception exceptionToThrow = User32.CreateWin32Exception(method);
+ if (size != Size.Empty) {
+ exceptionToThrow.Data.Add("Height", size.Height);
+ exceptionToThrow.Data.Add("Width", size.Width);
+ }
+ return exceptionToThrow;
+ }
+
+ public bool Start(int framesPerSecond) {
+ if (recordingWindow != null) {
+ string windowTitle = Regex.Replace(recordingWindow.Text, @"[^\x20\d\w]", "");
+ if (string.IsNullOrEmpty(windowTitle)) {
+ windowTitle = "greenshot-recording";
+ }
+ filename = Path.Combine(conf.OutputFilePath, windowTitle + ".avi");
+
+ } else {
+ filename = Path.Combine(conf.OutputFilePath, "greenshot-recording.avi");
+ }
+ if (File.Exists(filename)) {
+ try {
+ File.Delete(filename);
+ } catch {}
+ }
+ LOG.InfoFormat("Capturing to {0}", filename);
+
+ if (recordingWindow != null) {
+ LOG.InfoFormat("Starting recording Window '{0}', {1}", recordingWindow.Text, recordingWindow.WindowRectangle);
+ recordingSize = recordingWindow.WindowRectangle.Size;
+ } else {
+ LOG.InfoFormat("Starting recording rectangle {0}", recordingRectangle);
+ recordingSize = recordingRectangle.Size;
+ }
+ if (recordingSize.Width % 8 > 0) {
+ LOG.InfoFormat("Correcting width to be factor 8, {0} => {1}", recordingSize.Width, recordingSize.Width + (8-(recordingSize.Width % 8)));
+ recordingSize = new Size(recordingSize.Width + (8-(recordingSize.Width % 8)), recordingSize.Height);
+ }
+ if (recordingSize.Height % 8 > 0) {
+ LOG.InfoFormat("Correcting Height to be factor 8, {0} => {1}", recordingSize.Height, recordingSize.Height + (8-(recordingSize.Height % 8)));
+ recordingSize = new Size(recordingSize.Width, recordingSize.Height + (8-(recordingSize.Height % 8)));
+ }
+ this.framesPerSecond = framesPerSecond;
+ // "P/Invoke" Solution for capturing the screen
+ hWndDesktop = User32.GetDesktopWindow();
+ // get te hDC of the target window
+ hDCDesktop = User32.GetWindowDC(hWndDesktop);
+ // Make sure the last error is set to 0
+ Win32.SetLastError(0);
+
+ // create a device context we can copy to
+ hDCDest = GDI32.CreateCompatibleDC(hDCDesktop);
+ // Check if the device context is there, if not throw an error with as much info as possible!
+ if (hDCDest == IntPtr.Zero) {
+ // Get Exception before the error is lost
+ Exception exceptionToThrow = CreateCaptureException("CreateCompatibleDC", recordingSize);
+ // Cleanup
+ User32.ReleaseDC(hWndDesktop, hDCDesktop);
+ // throw exception
+ throw exceptionToThrow;
+ }
+
+ // Create BitmapInfoHeader for CreateDIBSection
+ BitmapInfoHeader bitmapInfoHeader = new BitmapInfoHeader(recordingSize.Width, recordingSize.Height, 32);
+
+ // Make sure the last error is set to 0
+ Win32.SetLastError(0);
+
+ // create a bitmap we can copy it to, using GetDeviceCaps to get the width/height
+ hDIBSection = GDI32.CreateDIBSection(hDCDesktop, ref bitmapInfoHeader, BitmapInfoHeader.DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0);
+
+ if (hDIBSection == IntPtr.Zero) {
+ // Get Exception before the error is lost
+ Exception exceptionToThrow = CreateCaptureException("CreateDIBSection", recordingSize);
+ exceptionToThrow.Data.Add("hdcDest", hDCDest.ToInt32());
+ exceptionToThrow.Data.Add("hdcSrc", hDCDesktop.ToInt32());
+
+ // clean up
+ GDI32.DeleteDC(hDCDest);
+ User32.ReleaseDC(hWndDesktop, hDCDesktop);
+
+ // Throw so people can report the problem
+ throw exceptionToThrow;
+ }
+ // Create a GDI Bitmap so we can use GDI and GDI+ operations on the same memory
+ GDIBitmap = new Bitmap(recordingSize.Width, recordingSize.Height, 32, PixelFormat.Format32bppArgb, bits0);
+ // select the bitmap object and store the old handle
+ hOldObject = GDI32.SelectObject(hDCDest, hDIBSection);
+ stop = false;
+
+ aviWriter = new AVIWriter();
+ // Comment the following 2 lines to make the user select it's own codec
+ //aviWriter.Codec = "msvc";
+ //aviWriter.Quality = 99;
+
+ aviWriter.FrameRate = framesPerSecond;
+ if (aviWriter.Open(filename, recordingSize.Width, recordingSize.Height)) {
+ // Start update check in the background
+ backgroundTask = new Thread (new ThreadStart(CaptureFrame));
+ backgroundTask.IsBackground = true;
+ backgroundTask.Start();
+ return true;
+ } else {
+ // Cancel
+ aviWriter.Dispose();
+ aviWriter = null;
+ }
+ return false;
+ }
+
+ private void CaptureFrame() {
+ int MSBETWEENCAPTURES = 1000/framesPerSecond;
+ int msToNextCapture = MSBETWEENCAPTURES;
+ while (!stop) {
+ DateTime nextCapture = DateTime.Now.AddMilliseconds(msToNextCapture);
+ Point captureLocation;
+ if (recordingWindow != null) {
+ recordingWindow.Reset();
+ captureLocation = recordingWindow.Location;
+ } else {
+ captureLocation = new Point(recordingRectangle.X, recordingRectangle.Y);
+ }
+ // "Capture"
+ GDI32.BitBlt(hDCDest, 0, 0, recordingSize.Width, recordingSize.Height, hDCDesktop, captureLocation.X, captureLocation.Y, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);
+
+ // Mouse
+ if (RecordMouse) {
+ CursorInfo cursorInfo = new CursorInfo();
+ cursorInfo.cbSize = Marshal.SizeOf(cursorInfo);
+ Point mouseLocation = Cursor.Position;
+ mouseLocation.Offset(-captureLocation.X, -captureLocation.Y);
+ if (User32.GetCursorInfo(out cursorInfo)) {
+ User32.DrawIcon(hDCDest, mouseLocation.X, mouseLocation.Y, cursorInfo.hCursor);
+ }
+ }
+ // add to avi
+ try {
+ aviWriter.AddLowLevelFrame(bits0);
+ } catch (Exception) {
+ LOG.Error("Error adding frame to avi, stopping capturing.");
+ break;
+ }
+ int sleeptime = (int)(nextCapture.Subtract(DateTime.Now).Ticks / TimeSpan.TicksPerMillisecond);
+ if (sleeptime > 0) {
+ Thread.Sleep(sleeptime);
+ msToNextCapture = MSBETWEENCAPTURES;
+ } else {
+ // Compensating
+ msToNextCapture = Math.Max(0, MSBETWEENCAPTURES - sleeptime);
+ }
+ }
+ Cleanup();
+ }
+
+ public void Stop() {
+ stop = true;
+ if (backgroundTask != null) {
+ backgroundTask.Join();
+ }
+ Cleanup();
+ }
+ ///
+ /// Free resources
+ ///
+ private void Cleanup() {
+ if (hOldObject != IntPtr.Zero && hDCDest != IntPtr.Zero) {
+ // restore selection (old handle)
+ GDI32.SelectObject(hDCDest, hOldObject);
+ GDI32.DeleteDC(hDCDest);
+ }
+ if (hDCDesktop != IntPtr.Zero) {
+ User32.ReleaseDC(hWndDesktop, hDCDesktop);
+ }
+ if (hDIBSection != IntPtr.Zero) {
+ // free up the Bitmap object
+ GDI32.DeleteObject(hDIBSection);
+ }
+ if (aviWriter != null) {
+ aviWriter.Dispose();
+ aviWriter = null;
+ MessageBox.Show("Recording written to " + filename);
+ }
+ }
+ }
+}
diff --git a/Greenshot/Helpers/WindowWrapper.cs b/Greenshot/Helpers/WindowWrapper.cs
new file mode 100644
index 000000000..356b336a2
--- /dev/null
+++ b/Greenshot/Helpers/WindowWrapper.cs
@@ -0,0 +1,35 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2011 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+using System;
+
+namespace Greenshot.Helpers {
+ public class WindowWrapper : System.Windows.Forms.IWin32Window {
+ public WindowWrapper(IntPtr handle) {
+ _hwnd = handle;
+ }
+
+ public IntPtr Handle {
+ get { return _hwnd; }
+ }
+
+ private IntPtr _hwnd;
+ }
+}