ShareX/ShareX.ScreenCaptureLib/Forms/ScrollingCaptureForm.cs

551 lines
18 KiB
C#
Raw Normal View History

#region License Information (GPL v3)
/*
ShareX - A program that allows you to take screenshots and share any file type
Copyright (c) 2007-2015 ShareX Team
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 2
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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Optionally you can also view the license at <http://www.gnu.org/licenses/>.
*/
#endregion License Information (GPL v3)
using ShareX.HelpersLib;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
2015-09-25 20:12:03 +12:00
using System.Threading;
using System.Windows.Forms;
namespace ShareX.ScreenCaptureLib
{
public partial class ScrollingCaptureForm : BaseForm
{
public event Action<Image> ProcessRequested;
2015-09-25 20:12:03 +12:00
public ScrollingCaptureOptions Options { get; set; }
2015-09-25 23:36:13 +12:00
public Image Result { get; set; }
2015-09-25 20:12:03 +12:00
private WindowInfo selectedWindow;
private List<Image> images = new List<Image>();
private int currentScrollCount;
private bool isBusy;
2015-09-25 20:12:03 +12:00
public ScrollingCaptureForm(ScrollingCaptureOptions options)
{
2015-09-25 20:12:03 +12:00
Options = options;
InitializeComponent();
cbScrollMethod.Items.AddRange(Enum.GetNames(typeof(ScrollingCaptureScrollMethod)));
cbScrollMethod.SelectedIndex = (int)Options.ScrollMethod;
2015-09-25 20:12:03 +12:00
nudScrollDelay.Value = Options.ScrollDelay;
nudMaximumScrollCount.Value = Options.MaximumScrollCount;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
Clean();
}
base.Dispose(disposing);
}
protected void OnProcessRequested(Image image)
{
if (ProcessRequested != null)
{
ProcessRequested(image);
}
}
2015-09-25 20:12:03 +12:00
private void btnSelectHandle_Click(object sender, EventArgs e)
{
Hide();
SimpleWindowInfo simpleWindowInfo;
try
{
Thread.Sleep(250);
simpleWindowInfo = GetWindowInfo();
}
finally
{
Show();
}
if (simpleWindowInfo != null)
{
selectedWindow = new WindowInfo(simpleWindowInfo.Handle);
2015-09-25 22:32:53 +12:00
lblControlText.Text = selectedWindow.ClassName ?? String.Empty;
2015-09-25 20:12:03 +12:00
btnCapture.Enabled = true;
}
else
{
btnCapture.Enabled = false;
}
}
private void btnCapture_Click(object sender, EventArgs e)
{
StartCapture();
}
private SimpleWindowInfo GetWindowInfo()
{
using (RectangleRegion surface = new RectangleRegion())
{
surface.OneClickMode = true;
surface.Config.ForceWindowCapture = true;
surface.Config.IncludeControls = true;
surface.Config.UseDimming = false;
surface.Config.ShowInfo = true;
surface.Config.ShowMagnifier = false;
surface.Config.ShowTips = false;
surface.Prepare();
surface.ShowDialog();
if (surface.Result == SurfaceResult.Region)
{
return surface.SelectedWindow;
}
}
return null;
}
private void StartCapture()
{
btnCapture.Enabled = false;
Clean();
2015-09-25 20:12:03 +12:00
selectedWindow.Activate();
captureTimer.Interval = Options.ScrollDelay;
captureTimer.Start();
}
private void StopCapture()
{
captureTimer.Stop();
btnCapture.Enabled = true;
this.ShowActivate();
tcScrollingCapture.SelectedTab = tpOutput;
btnGuessEdges.Enabled = btnGuessCombineAdjustments.Enabled = btnCombine.Enabled = images.Count > 1;
ResetCombine();
}
private void Clean()
{
currentScrollCount = 0;
if (images != null)
{
foreach (Image image in images)
{
if (image != null)
{
image.Dispose();
}
}
images.Clear();
}
2015-09-25 20:12:03 +12:00
}
private void captureTimer_Tick(object sender, EventArgs e)
{
2015-09-25 22:32:53 +12:00
Screenshot.CaptureCursor = false;
Image image = Screenshot.CaptureRectangle(selectedWindow.Rectangle);
if (image != null)
{
images.Add(image);
}
2015-09-25 20:12:03 +12:00
currentScrollCount++;
2015-09-25 22:32:53 +12:00
if (currentScrollCount == Options.MaximumScrollCount || IsScrollReachedBottom(selectedWindow.Handle))
2015-09-25 20:12:03 +12:00
{
StopCapture();
}
switch (Options.ScrollMethod)
{
case ScrollingCaptureScrollMethod.SendMessageScroll:
NativeMethods.SendMessage(selectedWindow.Handle, (int)WindowsMessages.VSCROLL, (int)ScrollBarCommands.SB_PAGEDOWN, 0);
break;
case ScrollingCaptureScrollMethod.KeyPressPageDown:
InputHelpers.SendKeyPress(VirtualKeyCode.NEXT);
//NativeMethods.SendMessage(selectedWindow.Handle, (int)WindowsMessages.KEYDOWN, (int)VirtualKeyCode.NEXT, 0);
break;
case ScrollingCaptureScrollMethod.MouseWheel:
InputHelpers.SendMouseWheel(120);
break;
}
2015-09-25 20:12:03 +12:00
}
private void nudScrollDelay_ValueChanged(object sender, EventArgs e)
{
Options.ScrollDelay = (int)nudScrollDelay.Value;
}
private void nudMaximumScrollCount_ValueChanged(object sender, EventArgs e)
{
Options.MaximumScrollCount = (int)nudMaximumScrollCount.Value;
}
2015-09-25 22:32:53 +12:00
private bool IsScrollReachedBottom(IntPtr handle)
{
SCROLLINFO scrollInfo = new SCROLLINFO();
scrollInfo.cbSize = (uint)Marshal.SizeOf(scrollInfo);
scrollInfo.fMask = (uint)(ScrollInfoMask.SIF_RANGE | ScrollInfoMask.SIF_PAGE | ScrollInfoMask.SIF_TRACKPOS);
if (NativeMethods.GetScrollInfo(handle, (int)SBOrientation.SB_VERT, ref scrollInfo))
{
return scrollInfo.nMax == scrollInfo.nTrackPos + scrollInfo.nPage - 1;
}
if (images.Count > 1)
{
bool result = ImageHelpers.IsImagesEqual((Bitmap)images[images.Count - 1], (Bitmap)images[images.Count - 2]);
if (result)
{
Image last = images[images.Count - 1];
images.Remove(last);
last.Dispose();
}
return result;
}
return false;
}
2015-09-25 23:14:51 +12:00
private void nudTrimLeft_ValueChanged(object sender, EventArgs e)
{
Options.TrimLeftEdge = (int)nudTrimLeft.Value;
2015-09-25 23:36:13 +12:00
CombineAndPreviewImages();
2015-09-25 23:14:51 +12:00
}
private void nudTrimTop_ValueChanged(object sender, EventArgs e)
{
Options.TrimTopEdge = (int)nudTrimTop.Value;
2015-09-25 23:36:13 +12:00
CombineAndPreviewImages();
2015-09-25 23:14:51 +12:00
}
private void nudTrimRight_ValueChanged(object sender, EventArgs e)
{
Options.TrimRightEdge = (int)nudTrimRight.Value;
2015-09-25 23:36:13 +12:00
CombineAndPreviewImages();
2015-09-25 23:14:51 +12:00
}
private void nudTrimBottom_ValueChanged(object sender, EventArgs e)
{
Options.TrimBottomEdge = (int)nudTrimBottom.Value;
2015-09-25 23:36:13 +12:00
CombineAndPreviewImages();
2015-09-25 23:14:51 +12:00
}
private void nudCombineVertical_ValueChanged(object sender, EventArgs e)
{
Options.CombineAdjustmentVertical = (int)nudCombineVertical.Value;
2015-09-25 23:36:13 +12:00
CombineAndPreviewImages();
2015-09-25 23:14:51 +12:00
}
private void nudCombineLastVertical_ValueChanged(object sender, EventArgs e)
{
Options.CombineAdjustmentLastVertical = (int)nudCombineLastVertical.Value;
2015-09-25 23:36:13 +12:00
CombineAndPreviewImages();
2015-09-25 23:14:51 +12:00
}
private void btnGuessEdges_Click(object sender, EventArgs e)
{
GuessEdges();
}
private void btnGuessCombineAdjustments_Click(object sender, EventArgs e)
2015-09-25 23:14:51 +12:00
{
GuessCombineAdjustments();
2015-09-25 23:14:51 +12:00
}
private void btnCombine_Click(object sender, EventArgs e)
{
2015-09-25 23:36:13 +12:00
CombineAndPreviewImages();
}
private void btnProcess_Click(object sender, EventArgs e)
{
OnProcessRequested((Image)Result.Clone());
2015-09-25 23:36:13 +12:00
}
private void btnResetCombine_Click(object sender, EventArgs e)
{
ResetCombine();
}
private void ResetCombine()
{
nudTrimLeft.Value = nudTrimTop.Value = nudTrimRight.Value = nudTrimBottom.Value = nudCombineVertical.Value = nudCombineLastVertical.Value = 0;
CombineAndPreviewImages();
}
2015-09-25 23:36:13 +12:00
private void CombineAndPreviewImages()
{
if (!isBusy)
{
if (pbOutput.Image != null) pbOutput.Image.Dispose();
Result = CombineImages();
pbOutput.Image = Result;
}
2015-09-25 23:14:51 +12:00
}
private Image CombineImages()
{
if (images == null || images.Count == 0)
{
return null;
}
if (images.Count == 1)
{
return (Image)images[0].Clone();
2015-09-25 23:14:51 +12:00
}
List<Image> output = new List<Image>();
for (int i = 0; i < images.Count; i++)
{
Image newImage = null;
2015-09-25 23:14:51 +12:00
Image image = images[i];
if (Options.TrimLeftEdge > 0 || Options.TrimTopEdge > 0 || Options.TrimTopEdge > 0 || Options.TrimBottomEdge > 0 ||
Options.CombineAdjustmentVertical > 0 || Options.CombineAdjustmentLastVertical > 0)
{
Rectangle rect = new Rectangle(Options.TrimLeftEdge, Options.TrimTopEdge, image.Width - Options.TrimLeftEdge - Options.TrimRightEdge,
image.Height - Options.TrimTopEdge - Options.TrimBottomEdge);
2015-09-25 23:14:51 +12:00
if (i == images.Count - 1)
{
rect.Y += Options.CombineAdjustmentLastVertical;
rect.Height -= Options.CombineAdjustmentLastVertical;
}
else if (i > 0)
{
rect.Y += Options.CombineAdjustmentVertical;
rect.Height -= Options.CombineAdjustmentVertical;
}
newImage = ImageHelpers.CropImage(image, rect);
}
if (newImage == null)
2015-09-25 23:14:51 +12:00
{
newImage = (Image)image.Clone();
2015-09-25 23:14:51 +12:00
}
output.Add(newImage);
}
Image result = ImageHelpers.CombineImages(output);
foreach (Image image in output)
{
if (image != null)
2015-09-25 23:14:51 +12:00
{
image.Dispose();
2015-09-25 23:14:51 +12:00
}
}
2015-09-25 23:14:51 +12:00
output.Clear();
2015-09-25 23:14:51 +12:00
return result;
}
private void GuessEdges()
{
if (images.Count < 2) return;
isBusy = true;
nudTrimLeft.Value = nudTrimTop.Value = nudTrimRight.Value = nudTrimBottom.Value = 0;
Rectangle rect = new Rectangle(0, 0, images[0].Width, images[0].Height);
using (UnsafeBitmap bmp1 = new UnsafeBitmap((Bitmap)images[0], true, ImageLockMode.ReadOnly))
using (UnsafeBitmap bmp2 = new UnsafeBitmap((Bitmap)images[1], true, ImageLockMode.ReadOnly))
{
bool valueFound = false;
// Left edge
for (int x = rect.X; !valueFound && x < rect.Width; x++)
2015-09-25 23:14:51 +12:00
{
for (int y = rect.Y; y < rect.Height; y++)
{
if (bmp1.GetPixel(x, y) != bmp2.GetPixel(x, y))
{
valueFound = true;
nudTrimLeft.Value = x;
rect.X = x;
break;
}
}
2015-09-25 23:14:51 +12:00
}
valueFound = false;
// Top edge
for (int y = rect.Y; !valueFound && y < rect.Height; y++)
{
for (int x = rect.X; x < rect.Width; x++)
{
if (bmp1.GetPixel(x, y) != bmp2.GetPixel(x, y))
{
valueFound = true;
nudTrimTop.Value = y;
rect.Y = y;
break;
}
}
}
valueFound = false;
// Right edge
for (int x = rect.Width - 1; !valueFound && x >= rect.X; x--)
{
for (int y = rect.Y; y < rect.Height; y++)
{
if (bmp1.GetPixel(x, y) != bmp2.GetPixel(x, y))
{
valueFound = true;
nudTrimRight.Value = rect.Width - x - 1;
rect.Width = x + 1;
break;
}
}
}
valueFound = false;
// Bottom edge
for (int y = rect.Height - 1; !valueFound && y >= rect.X; y--)
{
for (int x = rect.X; x < rect.Width; x++)
{
if (bmp1.GetPixel(x, y) != bmp2.GetPixel(x, y))
{
valueFound = true;
nudTrimBottom.Value = rect.Height - y - 1;
rect.Height = y + 1;
break;
}
}
}
2015-09-25 23:14:51 +12:00
}
isBusy = false;
2015-09-25 23:14:51 +12:00
}
private void GuessCombineAdjustments()
{
if (images.Count > 1)
{
isBusy = true;
nudCombineVertical.Value = CalculateVerticalOffset(images[0], images[1]);
if (images.Count > 2)
{
nudCombineLastVertical.Value = CalculateVerticalOffset(images[images.Count - 2], images[images.Count - 1]);
}
isBusy = false;
}
}
private int CalculateVerticalOffset(Image img1, Image img2, int ignoreRightOffset = 50, int matchCount = 5)
{
Rectangle rect = new Rectangle(Options.TrimLeftEdge, Options.TrimTopEdge,
img1.Width - Options.TrimLeftEdge - Options.TrimRightEdge - (img1.Width > ignoreRightOffset ? ignoreRightOffset : 0),
img1.Height - Options.TrimTopEdge - Options.TrimBottomEdge);
using (UnsafeBitmap bmp1 = new UnsafeBitmap((Bitmap)img1, true, ImageLockMode.ReadOnly))
using (UnsafeBitmap bmp2 = new UnsafeBitmap((Bitmap)img2, true, ImageLockMode.ReadOnly))
{
for (int y = rect.Y; y < rect.Height; y++)
{
bool isLineMatches = true;
for (int x = rect.X; x < rect.Width; x++)
{
if (bmp2.GetPixel(x, y) != bmp1.GetPixel(x, rect.Height - 1))
{
isLineMatches = false;
break;
}
}
if (isLineMatches)
{
int lineMatchesCount = 0;
int y3 = 2;
for (int y2 = y - 1; y2 >= rect.Y; y2--)
{
bool isLineMatches2 = true;
for (int x2 = rect.X; x2 < rect.Width; x2++)
{
if (bmp2.GetPixel(x2, y2) != bmp1.GetPixel(x2, rect.Height - y3))
{
isLineMatches2 = false;
break;
}
}
if (isLineMatches2)
{
lineMatchesCount++;
y3++;
}
else
{
break;
}
if (lineMatchesCount == matchCount || y2 == rect.Y)
{
return y;
}
}
}
}
}
return 0;
}
}
}