mirror of
https://github.com/greenshot/greenshot
synced 2025-08-19 21:13:23 -07:00
Added some missing files.
git-svn-id: http://svn.code.sf.net/p/greenshot/code/trunk@1603 7dccd23d-a4a3-4e1f-8c07-b4c1b4018ab4
This commit is contained in:
parent
8d458998a1
commit
e860872ae0
7 changed files with 2231 additions and 0 deletions
901
Greenshot/Helpers/AviHelper.cs
Normal file
901
Greenshot/Helpers/AviHelper.cs
Normal file
|
@ -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 {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AVI files writing using Video for Windows interface.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <remarks><para>The class allows to write AVI files using Video for Windows API.</para>
|
||||||
|
///
|
||||||
|
/// <para>Sample usage:</para>
|
||||||
|
/// /// // 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( );
|
||||||
|
///
|
||||||
|
/// </remarks>
|
||||||
|
///
|
||||||
|
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 ";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Width of video frames.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <remarks><para>The property specifies the width of video frames, which are acceptable
|
||||||
|
/// by <see cref="AddFrame"/> method for saving, which is set in <see cref="Open"/>
|
||||||
|
/// method.</para></remarks>
|
||||||
|
///
|
||||||
|
public int Width {
|
||||||
|
get { return width; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Height of video frames.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <remarks><para>The property specifies the height of video frames, which are acceptable
|
||||||
|
/// by <see cref="AddFrame"/> method for saving, which is set in <see cref="Open"/>
|
||||||
|
/// method.</para></remarks>
|
||||||
|
///
|
||||||
|
public int Height {
|
||||||
|
get { return height; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current position in video stream.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <remarks><para>The property tell current position in video stream, which actually equals
|
||||||
|
/// to the amount of frames added using <see cref="AddFrame"/> method.</para></remarks>
|
||||||
|
///
|
||||||
|
public int Position
|
||||||
|
{
|
||||||
|
get { return position; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Desired playing frame rate.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <remarks><para>The property sets the video frame rate, which should be use during playing
|
||||||
|
/// of the video to be saved.</para>
|
||||||
|
///
|
||||||
|
/// <para><note>The property should be set befor opening new file to take effect.</note></para>
|
||||||
|
///
|
||||||
|
/// <para>Default frame rate is set to <b>25</b>.</para></remarks>
|
||||||
|
///
|
||||||
|
public int FrameRate
|
||||||
|
{
|
||||||
|
get { return rate; }
|
||||||
|
set { rate = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Codec used for video compression.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <remarks><para>The property sets the FOURCC code of video compression codec, which needs to
|
||||||
|
/// be used for video encoding.</para>
|
||||||
|
///
|
||||||
|
/// <para><note>The property should be set befor opening new file to take effect.</note></para>
|
||||||
|
///
|
||||||
|
/// <para>Default video codec is set <b>"DIB "</b>, which means no compression.</para></remarks>
|
||||||
|
///
|
||||||
|
public string Codec
|
||||||
|
{
|
||||||
|
get { return codec; }
|
||||||
|
set { codec = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compression video quality.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <remarks><para>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.</para>
|
||||||
|
///
|
||||||
|
/// <para><note>The property should be set befor opening new file to take effect.</note></para>
|
||||||
|
///
|
||||||
|
/// <para>Default value is set to <b>-1</b> - default compression quality of the codec.</para></remarks>
|
||||||
|
///
|
||||||
|
public int Quality
|
||||||
|
{
|
||||||
|
get { return quality; }
|
||||||
|
set { quality = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="AVIWriter"/> class.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <remarks>Initializes Video for Windows library.</remarks>
|
||||||
|
///
|
||||||
|
public AVIWriter() {
|
||||||
|
Avi32.AVIFileInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="AVIWriter"/> class.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <param name="codec">Codec to use for compression. eg [CVID],[IV50]</param>
|
||||||
|
///
|
||||||
|
/// <remarks>Initializes Video for Windows library.</remarks>
|
||||||
|
///
|
||||||
|
public AVIWriter(string codec) : this() {
|
||||||
|
this.codec = codec;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Destroys the instance of the <see cref="AVIWriter"/> class.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
~AVIWriter() {
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose the object.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <remarks>Frees unmanaged resources used by the object. The object becomes unusable
|
||||||
|
/// after that.</remarks>
|
||||||
|
///
|
||||||
|
public void Dispose() {
|
||||||
|
Dispose(true);
|
||||||
|
// remove me from the Finalization queue
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dispose the object.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <param name="disposing">Indicates if disposing was initiated manually.</param>
|
||||||
|
///
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create new AVI file and open it for writing.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <param name="fileName">AVI file name to create.</param>
|
||||||
|
/// <param name="width">Video width.</param>
|
||||||
|
/// <param name="height">Video height.</param>
|
||||||
|
///
|
||||||
|
/// <remarks><para>The method opens (creates) a video files, configure video codec and prepares
|
||||||
|
/// the stream for saving video frames with a help of <see cref="AddFrame"/> method.</para></remarks>
|
||||||
|
///
|
||||||
|
/// <exception cref="ApplicationException">Failure of opening video files (the exception message
|
||||||
|
/// specifies the issues).</exception>
|
||||||
|
///
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Close video file.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add new frame to the AVI file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="frameData">New frame data.</param>
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Windows API functions and structures.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <remarks>The class provides Video for Windows and some other Avi32 functions and structurs.</remarks>
|
||||||
|
///
|
||||||
|
internal static class Avi32 {
|
||||||
|
/// <summary>
|
||||||
|
/// Copy a block of memory.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <param name="dst">Destination pointer.</param>
|
||||||
|
/// <param name="src">Source pointer.</param>
|
||||||
|
/// <param name="count">Memory block's length to copy.</param>
|
||||||
|
///
|
||||||
|
/// <returns>Return's the value of <b>dst</b> - pointer to destination.</returns>
|
||||||
|
///
|
||||||
|
[DllImport("ntdll.dll")]
|
||||||
|
public static extern int memcpy(int dst, int src, int count);
|
||||||
|
|
||||||
|
|
||||||
|
// --- Video for Windows Functions
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize the AVIFile library.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern void AVIFileInit();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exit the AVIFile library.
|
||||||
|
/// </summary>
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern void AVIFileExit();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Open an AVI file.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <param name="aviHandler">Opened AVI file interface.</param>
|
||||||
|
/// <param name="fileName">AVI file name.</param>
|
||||||
|
/// <param name="mode">Opening mode (see <see cref="OpenFileMode"/>).</param>
|
||||||
|
/// <param name="handler">Handler to use (<b>null</b> to use default).</param>
|
||||||
|
///
|
||||||
|
/// <returns>Returns zero on success or error code otherwise.</returns>
|
||||||
|
///
|
||||||
|
[DllImport("avifil32.dll", CharSet = CharSet.Unicode)]
|
||||||
|
public static extern int AVIFileOpen(out IntPtr aviHandler, String fileName, OpenFileMode mode, IntPtr handler);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Release an open AVI stream.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <param name="aviHandler">Open AVI file interface.</param>
|
||||||
|
///
|
||||||
|
/// <returns>Returns the reference count of the file.</returns>
|
||||||
|
///
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVIFileRelease(IntPtr aviHandler);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get stream interface that is associated with a specified AVI file
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <param name="aviHandler">Handler to an open AVI file.</param>
|
||||||
|
/// <param name="streamHandler">Stream interface.</param>
|
||||||
|
/// <param name="streamType">Stream type to open.</param>
|
||||||
|
/// <param name="streamNumner">Count of the stream type. Identifies which occurrence of the specified stream type to access. </param>
|
||||||
|
///
|
||||||
|
/// <returns></returns>
|
||||||
|
///
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVIFileGetStream(IntPtr aviHandler, out IntPtr streamHandler, int streamType, int streamNumner);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new stream in an existing file and creates an interface to the new stream.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <param name="aviHandler">Handler to an open AVI file.</param>
|
||||||
|
/// <param name="streamHandler">Stream interface.</param>
|
||||||
|
/// <param name="streamInfo">Pointer to a structure containing information about the new stream.</param>
|
||||||
|
///
|
||||||
|
/// <returns>Returns zero if successful or an error otherwise.</returns>
|
||||||
|
///
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVIFileCreateStream(IntPtr aviHandler, out IntPtr streamHandler, ref AVISTREAMINFO streamInfo);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Release an open AVI stream.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <param name="streamHandler">Handle to an open stream.</param>
|
||||||
|
///
|
||||||
|
/// <returns>Returns the current reference count of the stream.</returns>
|
||||||
|
///
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVIStreamRelease(IntPtr streamHandler);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the format of a stream at the specified position.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <param name="streamHandler">Handle to an open stream.</param>
|
||||||
|
/// <param name="position">Position in the stream to receive the format.</param>
|
||||||
|
/// <param name="format">Pointer to a structure containing the new format.</param>
|
||||||
|
/// <param name="formatSize">Size, in bytes, of the block of memory referenced by <b>format</b>.</param>
|
||||||
|
///
|
||||||
|
/// <returns>Returns zero if successful or an error otherwise.</returns>
|
||||||
|
///
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVIStreamSetFormat(IntPtr streamHandler, int position, ref BitmapInfoHeader format, int formatSize);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the starting sample number for the stream.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <param name="streamHandler">Handle to an open stream.</param>
|
||||||
|
///
|
||||||
|
/// <returns>Returns the number if successful or – 1 otherwise.</returns>
|
||||||
|
///
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVIStreamStart(IntPtr streamHandler);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the length of the stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="streamHandler">Handle to an open stream.</param>
|
||||||
|
/// <returns>Returns the stream's length, in samples, if successful or -1 otherwise. </returns>
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVIStreamLength(IntPtr streamHandler);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obtain stream header information.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <param name="streamHandler">Handle to an open stream.</param>
|
||||||
|
/// <param name="streamInfo">Pointer to a structure to contain the stream information.</param>
|
||||||
|
/// <param name="infoSize">Size, in bytes, of the structure used for <b>streamInfo</b>.</param>
|
||||||
|
///
|
||||||
|
/// <returns>Returns zero if successful or an error otherwise.</returns>
|
||||||
|
///
|
||||||
|
[DllImport("avifil32.dll", CharSet = CharSet.Unicode)]
|
||||||
|
public static extern int AVIStreamInfo(IntPtr streamHandler, ref AVISTREAMINFO streamInfo, int infoSize);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prepare to decompress video frames from the specified video stream
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="streamHandler">Pointer to the video stream used as the video source.</param>
|
||||||
|
/// <param name="wantedFormat">Pointer to a structure that defines the desired video format. Specify NULL to use a default format.</param>
|
||||||
|
/// <returns>Returns an object that can be used with the <see cref="AVIStreamGetFrame"/> function.</returns>
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern IntPtr AVIStreamGetFrameOpen(IntPtr streamHandler, ref BitmapInfoHeader wantedFormat);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prepare to decompress video frames from the specified video stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="streamHandler">Pointer to the video stream used as the video source.</param>
|
||||||
|
/// <param name="wantedFormat">Pointer to a structure that defines the desired video format. Specify NULL to use a default format.</param>
|
||||||
|
/// <returns>Returns a <b>GetFrame</b> object that can be used with the <see cref="AVIStreamGetFrame"/> function.</returns>
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern IntPtr AVIStreamGetFrameOpen(IntPtr streamHandler, int wantedFormat);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Releases resources used to decompress video frames.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="getFrameObject">Handle returned from the <see cref="AVIStreamGetFrameOpen(IntPtr,int)"/> function.</param>
|
||||||
|
/// <returns>Returns zero if successful or an error otherwise.</returns>
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVIStreamGetFrameClose(IntPtr getFrameObject);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return the address of a decompressed video frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="getFrameObject">Pointer to a GetFrame object.</param>
|
||||||
|
/// <param name="position">Position, in samples, within the stream of the desired frame.</param>
|
||||||
|
/// <returns>Returns a pointer to the frame data if successful or NULL otherwise.</returns>
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern IntPtr AVIStreamGetFrame(IntPtr getFrameObject, int position);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write data to a stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="streamHandler">Handle to an open stream.</param>
|
||||||
|
/// <param name="start">First sample to write.</param>
|
||||||
|
/// <param name="samples">Number of samples to write.</param>
|
||||||
|
/// <param name="buffer">Pointer to a buffer containing the data to write. </param>
|
||||||
|
/// <param name="bufferSize">Size of the buffer referenced by <b>buffer</b>.</param>
|
||||||
|
/// <param name="flags">Flag associated with this data.</param>
|
||||||
|
/// <param name="samplesWritten">Pointer to a buffer that receives the number of samples written. This can be set to NULL.</param>
|
||||||
|
/// <param name="bytesWritten">Pointer to a buffer that receives the number of bytes written. This can be set to NULL.</param>
|
||||||
|
///
|
||||||
|
/// <returns>Returns zero if successful or an error otherwise.</returns>
|
||||||
|
///
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVIStreamWrite(IntPtr streamHandler, int start, int samples, IntPtr buffer, int bufferSize, int flags, IntPtr samplesWritten, IntPtr bytesWritten);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve the save options for a file and returns them in a buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="window">Handle to the parent window for the Compression Options dialog box.</param>
|
||||||
|
/// <param name="flags">Flags for displaying the Compression Options dialog box.</param>
|
||||||
|
/// <param name="streams">Number of streams that have their options set by the dialog box.</param>
|
||||||
|
/// <param name="streamInterfaces">Pointer to an array of stream interface pointers.</param>
|
||||||
|
/// <param name="options">Pointer to an array of pointers to AVICOMPRESSOPTIONS structures.</param>
|
||||||
|
/// <returns>Returns TRUE if the user pressed OK, FALSE for CANCEL, or an error otherwise.</returns>
|
||||||
|
[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);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Free the resources allocated by the AVISaveOptions function.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="streams">Count of the AVICOMPRESSOPTIONS structures referenced in <b>options</b>.</param>
|
||||||
|
/// <param name="options">Pointer to an array of pointers to AVICOMPRESSOPTIONS structures.</param>
|
||||||
|
/// <returns>Returns 0.</returns>
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVISaveOptionsFree(int streams, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] options);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a compressed stream from an uncompressed stream and a
|
||||||
|
/// compression filter, and returns the address of a pointer to
|
||||||
|
/// the compressed stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="compressedStream">Pointer to a buffer that receives the compressed stream pointer.</param>
|
||||||
|
/// <param name="sourceStream">Pointer to the stream to be compressed.</param>
|
||||||
|
/// <param name="options">Pointer to a structure that identifies the type of compression to use and the options to apply.</param>
|
||||||
|
/// <param name="clsidHandler">Pointer to a class identifier used to create the stream.</param>
|
||||||
|
/// <returns>Returns 0 if successful or an error otherwise.</returns>
|
||||||
|
[DllImport("avifil32.dll")]
|
||||||
|
public static extern int AVIMakeCompressedStream(out IntPtr compressedStream, IntPtr sourceStream, ref AVICOMPRESSOPTIONS options, IntPtr clsidHandler);
|
||||||
|
|
||||||
|
// --- structures
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Structure, which contains information for a single stream .
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
|
||||||
|
public struct AVISTREAMINFO
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Four-character code indicating the stream type.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Four-character code of the compressor handler that will compress this video stream when it is saved.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int handler;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applicable flags for the stream.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int flags;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Capability flags; currently unused.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int ñapabilities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Priority of the stream.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I2)]
|
||||||
|
public short priority;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Language of the stream.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I2)]
|
||||||
|
public short language;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Time scale applicable for the stream.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <remarks>Dividing <b>rate</b> by <b>scale</b> gives the playback rate in number of samples per second.</remarks>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int scale;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rate in an integer format.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int rate;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sample number of the first frame of the AVI file.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int start;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Length of this stream.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <remarks>The units are defined by <b>rate</b> and <b>scale</b>.</remarks>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int length;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Audio skew. This member specifies how much to skew the audio data ahead of the video frames in interleaved files.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int initialFrames;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recommended buffer size, in bytes, for the stream.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int suggestedBufferSize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Quality indicator of the video data in the stream.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <remarks>Quality is represented as a number between 0 and 10,000.</remarks>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int quality;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Size, in bytes, of a single data sample.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int sampleSize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dimensions of the video destination rectangle.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.Struct, SizeConst = 16)]
|
||||||
|
public RECT rectFrame;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of times the stream has been edited.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int editCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of times the stream format has changed.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int formatChangeCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Description of the stream.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
|
||||||
|
public string name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Structure, which contains information about a stream and how it is compressed and saved.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct AVICOMPRESSOPTIONS
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Four-character code indicating the stream type.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Four-character code for the compressor handler that will compress this video stream when it is saved.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int handler;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum period between video key frames.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int keyFrameEvery;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Quality value passed to a video compressor.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int quality;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Video compressor data rate.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int bytesPerSecond;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flags used for compression.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int flags;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pointer to a structure defining the data format.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int format;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Size, in bytes, of the data referenced by <b>format</b>.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int formatSize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Video-compressor-specific data; used internally.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int parameters;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Size, in bytes, of the data referenced by <b>parameters</b>.
|
||||||
|
/// </summary>
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int parametersSize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interleave factor for interspersing stream data with data from the first stream.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[MarshalAs(UnmanagedType.I4)]
|
||||||
|
public int interleaveEvery;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- enumerations
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// File access modes.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
[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
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// .NET replacement of mmioFOURCC macros. Converts four characters to code.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <param name="str">Four characters string.</param>
|
||||||
|
///
|
||||||
|
/// <returns>Returns the code created from provided characters.</returns>
|
||||||
|
///
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Inverse to <see cref="mmioFOURCC"/>. Converts code to fout characters string.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <param name="code">Code to convert.</param>
|
||||||
|
///
|
||||||
|
/// <returns>Returns four characters string.</returns>
|
||||||
|
///
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Version of <see cref="AVISaveOptions(IntPtr, int, int, IntPtr[], IntPtr[])"/> for one stream only.
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <param name="stream">Stream to configure.</param>
|
||||||
|
/// <param name="options">Stream options.</param>
|
||||||
|
///
|
||||||
|
/// <returns>Returns TRUE if the user pressed OK, FALSE for CANCEL, or an error otherwise.</returns>
|
||||||
|
///
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
725
Greenshot/Helpers/CaptureHelper.cs
Normal file
725
Greenshot/Helpers/CaptureHelper.cs
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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 {
|
||||||
|
/// <summary>
|
||||||
|
/// CaptureHelper contains all the capture logic
|
||||||
|
/// </summary>
|
||||||
|
public class CaptureHelper {
|
||||||
|
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(CaptureHelper));
|
||||||
|
private static CoreConfiguration conf = IniConfig.GetIniSection<CoreConfiguration>();
|
||||||
|
private static ScreenCaptureHelper screenCapture = null;
|
||||||
|
private List<WindowDetails> windows = new List<WindowDetails>();
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Make Capture with file name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filename">filename</param>
|
||||||
|
private void MakeCapture(string filename) {
|
||||||
|
capture.CaptureDetails.Filename = filename;
|
||||||
|
MakeCapture();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Make Capture with specified destinations
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pre-Initialization for CaptureWithFeedback, this will get all the windows before we change anything
|
||||||
|
/// </summary>
|
||||||
|
private void PrepareForCaptureWithFeedback() {
|
||||||
|
windows = new List<WindowDetails>();
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Select the window to capture, this has logic which takes care of certain special applications
|
||||||
|
/// like TOAD or Excel
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="windowToCapture">WindowDetails with the target Window</param>
|
||||||
|
/// <returns>WindowDetails with the target Window OR a replacement</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if Process uses PresentationFramework.dll -> meaning it uses WPF
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="process"></param>
|
||||||
|
/// <returns>true if the process uses WPF</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Capture the supplied Window
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="windowToCapture">Window to capture</param>
|
||||||
|
/// <param name="captureForWindow">The capture to store the details</param>
|
||||||
|
/// <param name="windowCaptureMode">What WindowCaptureMode to use</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
124
Greenshot/Helpers/DestinationHelper.cs
Normal file
124
Greenshot/Helpers/DestinationHelper.cs
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Greenshot.Plugin;
|
||||||
|
using GreenshotPlugin.Core;
|
||||||
|
|
||||||
|
namespace Greenshot.Helpers {
|
||||||
|
/// <summary>
|
||||||
|
/// Description of DestinationHelper.
|
||||||
|
/// </summary>
|
||||||
|
public static class DestinationHelper {
|
||||||
|
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(DestinationHelper));
|
||||||
|
private static Dictionary<string, IDestination> RegisteredDestinations = new Dictionary<string, IDestination>();
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register your destination here, if it doesn't come from a plugin and needs to be available
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="destination"></param>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a list of all destinations, registered or supplied by a plugin
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static List<IDestination> GetAllDestinations() {
|
||||||
|
List<IDestination> destinations = new List<IDestination>();
|
||||||
|
destinations.AddRange(RegisteredDestinations.Values);
|
||||||
|
foreach(IGreenshotPlugin plugin in PluginHelper.instance.Plugins.Values) {
|
||||||
|
destinations.AddRange(plugin.Destinations());
|
||||||
|
}
|
||||||
|
destinations.Sort();
|
||||||
|
return destinations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a destination by a designation
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="designation">Designation of the destination</param>
|
||||||
|
/// <returns>IDestination or null</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A simple helper method which will call ExportCapture for the destination with the specified designation
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="designation"></param>
|
||||||
|
/// <param name="surface"></param>
|
||||||
|
/// <param name="captureDetails"></param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
63
Greenshot/Helpers/GeometryHelper.cs
Normal file
63
Greenshot/Helpers/GeometryHelper.cs
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Greenshot.Helpers {
|
||||||
|
/// <summary>
|
||||||
|
/// Description of GeometryHelper.
|
||||||
|
/// </summary>
|
||||||
|
public static class GeometryHelper {
|
||||||
|
/// <summary>
|
||||||
|
/// Finds the distance between two points on a 2D surface.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x1">The point on the x-axis of the first point</param>
|
||||||
|
/// <param name="x2">The point on the x-axis of the second point</param>
|
||||||
|
/// <param name="y1">The point on the y-axis of the first point</param>
|
||||||
|
/// <param name="y2">The point on the y-axis of the second point</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the angle of a line defined by two points on a 2D surface.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x1">The point on the x-axis of the first point</param>
|
||||||
|
/// <param name="x2">The point on the x-axis of the second point</param>
|
||||||
|
/// <param name="y1">The point on the y-axis of the first point</param>
|
||||||
|
/// <param name="y2">The point on the y-axis of the second point</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static double Angle2D(int x1, int y1, int x2, int y2) {
|
||||||
|
return Math.Atan2(y2 - y1, x2 - x1) * 180 / Math.PI;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
121
Greenshot/Helpers/ProcessorHelper.cs
Normal file
121
Greenshot/Helpers/ProcessorHelper.cs
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Greenshot.Plugin;
|
||||||
|
using GreenshotPlugin.Core;
|
||||||
|
|
||||||
|
namespace Greenshot.Helpers {
|
||||||
|
/// <summary>
|
||||||
|
/// Description of ProcessorHelper.
|
||||||
|
/// </summary>
|
||||||
|
public static class ProcessorHelper {
|
||||||
|
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ProcessorHelper));
|
||||||
|
private static Dictionary<string, IProcessor> RegisteredProcessors = new Dictionary<string, IProcessor>();
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register your Processor here, if it doesn't come from a plugin and needs to be available
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Processor"></param>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a list of all Processors, registered or supplied by a plugin
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static List<IProcessor> GetAllProcessors() {
|
||||||
|
List<IProcessor> Processors = new List<IProcessor>();
|
||||||
|
Processors.AddRange(RegisteredProcessors.Values);
|
||||||
|
foreach(IGreenshotPlugin plugin in PluginHelper.instance.Plugins.Values) {
|
||||||
|
Processors.AddRange(plugin.Processors());
|
||||||
|
}
|
||||||
|
Processors.Sort();
|
||||||
|
return Processors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a Processor by a designation
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="designation">Designation of the Processor</param>
|
||||||
|
/// <returns>IProcessor or null</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A simple helper method which will call ProcessCapture for the Processor with the specified designation
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="designation"></param>
|
||||||
|
/// <param name="surface"></param>
|
||||||
|
/// <param name="captureDetails"></param>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
262
Greenshot/Helpers/ScreenCaptureHelper.cs
Normal file
262
Greenshot/Helpers/ScreenCaptureHelper.cs
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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 {
|
||||||
|
/// <summary>
|
||||||
|
/// Description of ScreenCaptureHelper.
|
||||||
|
/// </summary>
|
||||||
|
public class ScreenCaptureHelper {
|
||||||
|
private static log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ScreenCaptureHelper));
|
||||||
|
private static CoreConfiguration conf = IniConfig.GetIniSection<CoreConfiguration>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper method to create an exception that might explain what is wrong while capturing
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method">string with current method</param>
|
||||||
|
/// <param name="captureBounds">Rectangle of what we want to capture</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Free resources
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
Greenshot/Helpers/WindowWrapper.cs
Normal file
35
Greenshot/Helpers/WindowWrapper.cs
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue