Extracted the refresh/animation logic to its own class, so it could be reused elsewhere. It would be even possible to extract more, but for now this is a good basis.

git-svn-id: http://svn.code.sf.net/p/greenshot/code/trunk@2366 7dccd23d-a4a3-4e1f-8c07-b4c1b4018ab4
This commit is contained in:
RKrom 2012-12-07 15:53:45 +00:00
commit b7cfe23605
3 changed files with 129 additions and 110 deletions

View file

@ -34,12 +34,13 @@ using Greenshot.Plugin;
using GreenshotPlugin.UnmanagedHelpers; using GreenshotPlugin.UnmanagedHelpers;
using GreenshotPlugin.Core; using GreenshotPlugin.Core;
using Greenshot.IniFile; using Greenshot.IniFile;
using GreenshotPlugin.Controls;
namespace Greenshot.Forms { namespace Greenshot.Forms {
/// <summary> /// <summary>
/// The capture form is used to select a part of the capture /// The capture form is used to select a part of the capture
/// </summary> /// </summary>
public partial class CaptureForm : Form { public partial class CaptureForm : AnimatingForm {
private enum FixMode {None, Initiated, Horizontal, Vertical}; private enum FixMode {None, Initiated, Horizontal, Vertical};
private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(CaptureForm)); private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(CaptureForm));
@ -49,8 +50,10 @@ namespace Greenshot.Forms {
private static Pen OverlayPen = new Pen(Color.FromArgb(50, Color.Black)); private static Pen OverlayPen = new Pen(Color.FromArgb(50, Color.Black));
private static CaptureForm currentForm = null; private static CaptureForm currentForm = null;
private static Brush backgroundBrush = null; private static Brush backgroundBrush = null;
private static int vRefresh = 0;
/// <summary>
/// Initialize the background brush
/// </summary>
static CaptureForm() { static CaptureForm() {
Image backgroundForTransparency = GreenshotPlugin.Core.GreenshotResources.getImage("Checkerboard.Image"); Image backgroundForTransparency = GreenshotPlugin.Core.GreenshotResources.getImage("Checkerboard.Image");
backgroundBrush = new TextureBrush(backgroundForTransparency, WrapMode.Tile); backgroundBrush = new TextureBrush(backgroundForTransparency, WrapMode.Tile);
@ -67,49 +70,11 @@ namespace Greenshot.Forms {
private Rectangle captureRect = Rectangle.Empty; private Rectangle captureRect = Rectangle.Empty;
private ICapture capture = null; private ICapture capture = null;
private Image capturedImage = null; private Image capturedImage = null;
private Timer timer = null;
private Point previousMousePos = Point.Empty; private Point previousMousePos = Point.Empty;
private FixMode fixMode = FixMode.None; private FixMode fixMode = FixMode.None;
private RectangleAnimator windowAnimator = null; private RectangleAnimator windowAnimator = null;
private RectangleAnimator zoomAnimator = null; private RectangleAnimator zoomAnimator = null;
/// <summary>
/// Vertical Refresh Rate
/// </summary>
private static int VRefresh {
get {
if (vRefresh == 0) {
// get te hDC of the desktop to get the VREFRESH
IntPtr hDCDesktop = User32.GetWindowDC(User32.GetDesktopWindow());
vRefresh = GDI32.GetDeviceCaps(hDCDesktop, DeviceCaps.VREFRESH);
User32.ReleaseDC(hDCDesktop);
}
return vRefresh;
}
}
/// <summary>
/// Check if we need to optimize for RDP / Terminal Server sessions
/// </summary>
private static bool optimizeForTerminalServer {
get {
return conf.OptimizeForRDP || SystemInformation.TerminalServerSession;
}
}
/// <summary>
/// Calculate the amount of frames that an animation takes
/// </summary>
/// <param name="milliseconds"></param>
/// <returns>Number of frames, 1 if in Terminal Server Session</returns>
private static int calculateFrames(int milliseconds) {
// If we are in a Terminal Server Session we return 1
if (optimizeForTerminalServer) {
return 1;
}
return milliseconds / VRefresh;
}
/// <summary> /// <summary>
/// Property to access the selected capture rectangle /// Property to access the selected capture rectangle
/// </summary> /// </summary>
@ -153,7 +118,7 @@ namespace Greenshot.Forms {
/// </summary> /// </summary>
/// <param name="capture"></param> /// <param name="capture"></param>
/// <param name="windows"></param> /// <param name="windows"></param>
public CaptureForm(ICapture capture, List<WindowDetails> windows) { public CaptureForm(ICapture capture, List<WindowDetails> windows) : base() {
if (currentForm != null) { if (currentForm != null) {
LOG.Debug("Found currentForm, Closing already opened CaptureForm"); LOG.Debug("Found currentForm, Closing already opened CaptureForm");
currentForm.Close(); currentForm.Close();
@ -162,9 +127,6 @@ namespace Greenshot.Forms {
} }
currentForm = this; currentForm = this;
// comment this out if the timer should not be used
timer = new Timer();
// Using 32bppPArgb speeds up the drawing. // Using 32bppPArgb speeds up the drawing.
//capturedImage = ImageHelper.Clone(capture.Image, PixelFormat.Format32bppPArgb); //capturedImage = ImageHelper.Clone(capture.Image, PixelFormat.Format32bppPArgb);
// comment the clone, uncomment the assignment and the original bitmap is used. // comment the clone, uncomment the assignment and the original bitmap is used.
@ -185,16 +147,13 @@ namespace Greenshot.Forms {
// //
InitializeComponent(); InitializeComponent();
// Only double-buffer when we are not in a TerminalServerSession // Only double-buffer when we are not in a TerminalServerSession
this.DoubleBuffered = !optimizeForTerminalServer; this.DoubleBuffered = !OptimizeForTerminalServer;
this.Text = "Greenshot capture form"; this.Text = "Greenshot capture form";
// Make sure we never capture the captureform // Make sure we never capture the captureform
WindowDetails.RegisterIgnoreHandle(this.Handle); WindowDetails.RegisterIgnoreHandle(this.Handle);
// Unregister at close // Unregister at close
this.FormClosing += delegate { this.FormClosing += delegate {
if (timer != null) {
timer.Stop();
}
// remove the buffer if it was created inside this form // remove the buffer if it was created inside this form
if (capturedImage != capture.Image) { if (capturedImage != capture.Image) {
capturedImage.Dispose(); capturedImage.Dispose();
@ -208,7 +167,7 @@ namespace Greenshot.Forms {
// Initialize the animations, the window capture zooms out from the cursor to the window under the cursor // Initialize the animations, the window capture zooms out from the cursor to the window under the cursor
if (captureMode == CaptureMode.Window) { if (captureMode == CaptureMode.Window) {
windowAnimator = new RectangleAnimator(new Rectangle(cursorPos, Size.Empty), captureRect, calculateFrames(700), EasingType.Quintic, EasingMode.EaseOut); windowAnimator = new RectangleAnimator(new Rectangle(cursorPos, Size.Empty), captureRect, CalculateFrames(700), EasingType.Quintic, EasingMode.EaseOut);
} }
// Set the zoomer animation // Set the zoomer animation
@ -221,12 +180,6 @@ namespace Greenshot.Forms {
// Fix missing focus // Fix missing focus
WindowDetails.ToForeground(this.Handle); WindowDetails.ToForeground(this.Handle);
this.TopMost = true; this.TopMost = true;
if (timer != null) {
timer.Interval = 1000/VRefresh;
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
} }
/// <summary> /// <summary>
@ -235,10 +188,10 @@ namespace Greenshot.Forms {
void InitializeZoomer(bool isOn) { void InitializeZoomer(bool isOn) {
if (isOn) { if (isOn) {
// Initialize the zoom with a invalid position // Initialize the zoom with a invalid position
zoomAnimator = new RectangleAnimator(Rectangle.Empty, new Rectangle(int.MaxValue, int.MaxValue, 0, 0), calculateFrames(1000), EasingType.Quintic, EasingMode.EaseOut); zoomAnimator = new RectangleAnimator(Rectangle.Empty, new Rectangle(int.MaxValue, int.MaxValue, 0, 0), CalculateFrames(1000), EasingType.Quintic, EasingMode.EaseOut);
VerifyZoomAnimation(cursorPos, false); VerifyZoomAnimation(cursorPos, false);
} else if (zoomAnimator != null) { } else if (zoomAnimator != null) {
zoomAnimator.ChangeDestination(new Rectangle(Point.Empty, Size.Empty), calculateFrames(1000)); zoomAnimator.ChangeDestination(new Rectangle(Point.Empty, Size.Empty), CalculateFrames(1000));
} }
} }
@ -310,7 +263,7 @@ namespace Greenshot.Forms {
// "Fade out" Zoom // "Fade out" Zoom
InitializeZoomer(false); InitializeZoomer(false);
// "Fade in" window // "Fade in" window
windowAnimator = new RectangleAnimator(new Rectangle(cursorPos, Size.Empty), captureRect, calculateFrames(700), EasingType.Quintic, EasingMode.EaseOut); windowAnimator = new RectangleAnimator(new Rectangle(cursorPos, Size.Empty), captureRect, CalculateFrames(700), EasingType.Quintic, EasingMode.EaseOut);
captureRect = Rectangle.Empty; captureRect = Rectangle.Empty;
Invalidate(); Invalidate();
break; break;
@ -318,7 +271,7 @@ namespace Greenshot.Forms {
// Set the region capture mode // Set the region capture mode
captureMode = CaptureMode.Region; captureMode = CaptureMode.Region;
// "Fade out" window // "Fade out" window
windowAnimator.ChangeDestination(new Rectangle(cursorPos, Size.Empty), calculateFrames(700)); windowAnimator.ChangeDestination(new Rectangle(cursorPos, Size.Empty), CalculateFrames(700));
// Fade in zoom // Fade in zoom
InitializeZoomer(conf.ZoomerEnabled); InitializeZoomer(conf.ZoomerEnabled);
captureRect = Rectangle.Empty; captureRect = Rectangle.Empty;
@ -412,20 +365,6 @@ namespace Greenshot.Forms {
// Make sure the mouse coordinates are fixed, when pressing shift // Make sure the mouse coordinates are fixed, when pressing shift
mouseMovePos = FixMouseCoordinates(WindowCapture.GetCursorLocation()); mouseMovePos = FixMouseCoordinates(WindowCapture.GetCursorLocation());
mouseMovePos = WindowCapture.GetLocationRelativeToScreenBounds(mouseMovePos); mouseMovePos = WindowCapture.GetLocationRelativeToScreenBounds(mouseMovePos);
// If the timer is used, the timer_Tick does the following.
// If the timer is not used, we need to call the update ourselves
if (timer == null) {
updateFrame();
}
}
/// <summary>
/// The tick handler of the capture form, this initiates the frame drawing.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void timer_Tick(object sender, EventArgs e) {
updateFrame();
} }
/// <summary> /// <summary>
@ -443,7 +382,7 @@ namespace Greenshot.Forms {
/// <summary> /// <summary>
/// update the frame, this only invalidates /// update the frame, this only invalidates
/// </summary> /// </summary>
void updateFrame() { protected override void Animate() {
Point lastPos = cursorPos.Clone(); Point lastPos = cursorPos.Clone();
cursorPos = mouseMovePos.Clone(); cursorPos = mouseMovePos.Clone();
@ -526,7 +465,7 @@ namespace Greenshot.Forms {
invalidateRectangle = new Rectangle(x1,y1, x2-x1, y2-y1); invalidateRectangle = new Rectangle(x1,y1, x2-x1, y2-y1);
Invalidate(invalidateRectangle); Invalidate(invalidateRectangle);
} else if (captureMode != CaptureMode.Window) { } else if (captureMode != CaptureMode.Window) {
if (!optimizeForTerminalServer) { if (!OptimizeForTerminalServer) {
Rectangle allScreenBounds = WindowCapture.GetScreenBounds(); Rectangle allScreenBounds = WindowCapture.GetScreenBounds();
allScreenBounds.Location = WindowCapture.GetLocationRelativeToScreenBounds(allScreenBounds.Location); allScreenBounds.Location = WindowCapture.GetLocationRelativeToScreenBounds(allScreenBounds.Location);
if (verticalMove) { if (verticalMove) {
@ -549,7 +488,7 @@ namespace Greenshot.Forms {
} else { } else {
if (selectedCaptureWindow != null && !selectedCaptureWindow.Equals(lastWindow)) { if (selectedCaptureWindow != null && !selectedCaptureWindow.Equals(lastWindow)) {
// Window changes, make new animation from current to target // Window changes, make new animation from current to target
windowAnimator.ChangeDestination(captureRect, calculateFrames(700)); windowAnimator.ChangeDestination(captureRect, CalculateFrames(700));
} }
} }
// always animate the Window area through to the last frame, so we see the fade-in/out untill the end // always animate the Window area through to the last frame, so we see the fade-in/out untill the end
@ -657,11 +596,9 @@ namespace Greenshot.Forms {
using (GraphicsPath path = new GraphicsPath()) { using (GraphicsPath path = new GraphicsPath()) {
path.AddEllipse(destinationRectangle); path.AddEllipse(destinationRectangle);
using (Region clipRegion = new Region(path)) { graphics.SetClip(path);
graphics.Clip = clipRegion; graphics.FillRectangle(backgroundBrush, destinationRectangle);
graphics.FillRectangle(backgroundBrush, destinationRectangle); graphics.DrawImage(capturedImage, destinationRectangle, sourceRectangle, GraphicsUnit.Pixel);
graphics.DrawImage(capturedImage, destinationRectangle, sourceRectangle, GraphicsUnit.Pixel);
}
} }
// Draw the circle around the zoomer // Draw the circle around the zoomer
@ -842,7 +779,7 @@ namespace Greenshot.Forms {
} }
} }
} else { } else {
if (!optimizeForTerminalServer) { if (!OptimizeForTerminalServer) {
using (Pen pen = new Pen(Color.LightSeaGreen)) { using (Pen pen = new Pen(Color.LightSeaGreen)) {
pen.DashStyle = DashStyle.Dot; pen.DashStyle = DashStyle.Dot;
Rectangle screenBounds = capture.ScreenBounds; Rectangle screenBounds = capture.ScreenBounds;

View file

@ -0,0 +1,107 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2012 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.Windows.Forms;
using GreenshotPlugin.Core;
using GreenshotPlugin.UnmanagedHelpers;
using Greenshot.IniFile;
namespace GreenshotPlugin.Controls {
/// <summary>
/// Extend this Form to have the possibility for animations on your form
/// </summary>
public abstract class AnimatingForm : Form {
protected static CoreConfiguration coreConfiguration = IniConfig.GetIniSection<CoreConfiguration>();
private int vRefresh = 0;
private Timer timer = null;
/// <summary>
/// Vertical Refresh Rate
/// </summary>
protected int VRefresh {
get {
if (vRefresh == 0) {
// get te hDC of the desktop to get the VREFRESH
IntPtr hDCDesktop = User32.GetWindowDC(User32.GetDesktopWindow());
vRefresh = GDI32.GetDeviceCaps(hDCDesktop, DeviceCaps.VREFRESH);
User32.ReleaseDC(hDCDesktop);
}
return vRefresh;
}
}
/// <summary>
/// Check if we need to optimize for RDP / Terminal Server sessions
/// </summary>
protected bool OptimizeForTerminalServer {
get {
return coreConfiguration.OptimizeForRDP || SystemInformation.TerminalServerSession;
}
}
/// <summary>
/// Calculate the amount of frames that an animation takes
/// </summary>
/// <param name="milliseconds"></param>
/// <returns>Number of frames, 1 if in Terminal Server Session</returns>
protected int CalculateFrames(int milliseconds) {
// If we are in a Terminal Server Session we return 1
if (OptimizeForTerminalServer) {
return 1;
}
return milliseconds / VRefresh;
}
/// <summary>
/// Initialize the animation
/// </summary>
protected AnimatingForm() {
timer = new Timer();
timer.Interval = 1000 / VRefresh;
timer.Tick += new EventHandler(timer_Tick);
this.Load += delegate {
timer.Start();
};
// Unregister at close
this.FormClosing += delegate {
if (timer != null) {
timer.Stop();
}
};
}
/// <summary>
/// The tick handler initiates the animation.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void timer_Tick(object sender, EventArgs e) {
Animate();
}
/// <summary>
/// This method will be called every frame, so implement your animation/redraw logic here.
/// </summary>
protected abstract void Animate();
}
}

View file

@ -28,6 +28,9 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Controls\AnimatingForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="IEInterop\IHTMLBodyElement.cs" /> <Compile Include="IEInterop\IHTMLBodyElement.cs" />
<Compile Include="IEInterop\IHTMLCurrentStyle.cs" /> <Compile Include="IEInterop\IHTMLCurrentStyle.cs" />
<Compile Include="IEInterop\IHTMLDocument.cs" /> <Compile Include="IEInterop\IHTMLDocument.cs" />
@ -201,32 +204,4 @@
<PreBuildEvent>rmdir /S /Q "$(SolutionDir)bin\$(Configuration)\Plugins" <PreBuildEvent>rmdir /S /Q "$(SolutionDir)bin\$(Configuration)\Plugins"
"$(SolutionDir)tools\TortoiseSVN\SubWCRev.exe" "$(ProjectDir)." "$(ProjectDir)Properties\AssemblyInfo.cs.template" "$(ProjectDir)Properties\AssemblyInfo.cs"</PreBuildEvent> "$(SolutionDir)tools\TortoiseSVN\SubWCRev.exe" "$(ProjectDir)." "$(ProjectDir)Properties\AssemblyInfo.cs.template" "$(ProjectDir)Properties\AssemblyInfo.cs"</PreBuildEvent>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'x86' ">
<RegisterForComInterop>False</RegisterForComInterop>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<BaseAddress>4194304</BaseAddress>
<PlatformTarget>x86</PlatformTarget>
<FileAlignment>4096</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'AnyCPU' ">
<RegisterForComInterop>False</RegisterForComInterop>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<BaseAddress>4194304</BaseAddress>
<PlatformTarget>AnyCPU</PlatformTarget>
<FileAlignment>4096</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<Optimize>True</Optimize>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
<DebugType>None</DebugType>
<DebugSymbols>false</DebugSymbols>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<Optimize>False</Optimize>
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
<DebugType>Full</DebugType>
<DebugSymbols>true</DebugSymbols>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
</Project> </Project>