Минималистичный скриншотер на .NET

Источник: habrahabr
habrahabr

Минималистичный скриншотер на .NET

Лучше один раз увидеть, чем 100 раз услышать.
Уверен, не я один часто делаю скриншоты.
Проиллюстрировать баг, отправить смешную картинку знакомому,
вставить в комментарий на каком-нибудь ресурсе, etc.
А началось всё с разочарования в некогда любимом Gyazo,
а именно сам скриншот уже трудно найти под кучей рекламных блоков.
Когда я пересмотрел аналоги, они мне показались чересчур громоздкими.
Пройти через несколько окон, чтобы просто сделать скриншот? Нет, спасибо.

И вот, желая облегчить себе жизнь, а заодно ответить на вопрос
было решено сделать всё самому.

Суть

А хочется то предельно простого.
Один клик - запустить приложение, второй клик - выделить область на экране.
Всё - после этого видим скриншот уже в браузере (в PNG) и имеем ссылку на него в буфере обмена.

Примерный алгоритм работы видится мне таким:

  • При запуске программа делает скриншот.
  • Устанавливает его фоном для окна, развёрнутого на весь экран.
  • Мышкой выделяем нужную область на экране.
  • Готово, картинка улетает на сервер.


Как и ожидалось, возникла аналогичная проблема с мерцанием.
Для устранения установим форме свойство DoubleBuffered.


DoubleBuffered = true;

К сожалению помогло только частично. На просторах интернета был найден совет
отправить функции CreateWindowEx сообщение WS_CLIPCHILDREN в параметре dwExStyle.
Сделать это можно переопределив CreateParams.


protected override CreateParams CreateParams
{
   get
   {
      CreateParams cp = base.CreateParams;
      cp.ExStyle /= 0x02000000;
      return cp;
   }
}

Загружаются скриншоты на imgur, присутсвует простой апи.
Для использования нужен api key, регистрируем приложение - получаем тут же ключ.
Отправляем картинку:


class Uploader
{
   private static string imgur_key = null;

   public static string IMGUR_KEY
   {
      set { imgur_key = value; }
   }
   public static string UploadToImgur(Bitmap bitmap, ImageFormat imgFormat)
   {
      MemoryStream memStream = new MemoryStream();
      bitmap.Save(memStream, imgFormat);
      using (WebClient wc = new WebClient())
      {
         NameValueCollection values = new NameValueCollection
         {
            { "key", imgur_key },
            { "image", Convert.ToBase64String(memStream.ToArray()) }
         };
         byte[] response = wc.UploadValues("http://api.imgur.com/2/upload.xml", values);
         XDocument xDocument = XDocument.Load(new MemoryStream(response));
         return (string)xDocument.Root.Element("links").Element("original");
      }
   }
}

И собственно код самой программы:


public partial class ScreenForm : Form
   {
      Rectangle rect;
      Bitmap bmpScreenshot;
      //формат скриншотов
      ImageFormat imgFormat = ImageFormat.Png;

      public ScreenForm()
      {
         InitializeComponent()
         this.Paint += new System.Windows.Forms.PaintEventHandler(this.ScreenForms_Paint);
         this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.ScreenForm_KeyDown);
         this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.ScreenForm_MouseDown);
         this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ScreenForm_MouseMove);
         this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.ScreenForm_MouseUp);
         this.FormBorderStyle = FormBorderStyle.None;
         this.ShowInTaskbar = false;
         this.TopMost = true;
         this.DoubleBuffered = true;
         this.Height = Screen.PrimaryScreen.Bounds.Height;
         this.Width = Screen.PrimaryScreen.Bounds.Width;
         //Указываем полученный ключ
         Uploader.IMGUR_KEY = "83dd0aacde6c6fc74d16638d5724bf96";
         MakeScreenShot();            
         this.BackgroundImage = (Image)bmpScreenshot;
         this.Cursor = Cursors.Cross;
      }
      protected override CreateParams CreateParams
      {
         get
         {
            CreateParams cp = base.CreateParams;
            cp.ExStyle /= 0x02000000;
            return cp;
         }
      }
      private void MakeScreenShot()
      {
         Graphics gfxScreenshot;
         bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, 
PixelFormat.Format32bppArgb);
         gfxScreenshot = Graphics.FromImage(bmpScreenshot);
         gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, 
Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
         gfxScreenshot.Dispose();
      }
      public Bitmap CropImage(Bitmap source, Rectangle section)
      {
         Bitmap bmp = new Bitmap(section.Width, section.Height);
         Graphics g = Graphics.FromImage(bmp);
         g.DrawImage(source, 0, 0, section, GraphicsUnit.Pixel);
         return bmp;
      }
      private void ScreenForms_Paint(object sender, PaintEventArgs e)
      {
         //цвет заливаемой области
         using (SolidBrush b = new SolidBrush(Color.FromArgb(75, 100, 100, 100)))
         {
            e.Graphics.FillRectangle(b, rect);
         }
      }
      private void ScreenForm_KeyDown(object sender, KeyEventArgs e)
      {
         if (e.KeyCode == Keys.Escape)
            base.Close();
      }
      private void ScreenForm_MouseDown(object sender, MouseEventArgs e)
      {
         if (e.Button == MouseButtons.Left)
         {
            rect = new Rectangle(e.X, e.Y, 0, 0);
         }
         else if (e.Button == MouseButtons.Right)
         {
            base.Close();
         }
      }
      private void ScreenForm_MouseMove(object sender, MouseEventArgs e)
      {
         if (e.Button == MouseButtons.Left)
         {
            rect = new Rectangle(rect.Left, rect.Top, e.X - rect.Left, e.Y - rect.Top);
         }
         this.Invalidate();
      }
      private void ScreenForm_MouseUp(object sender, MouseEventArgs e)
      {
         this.Hide();
         //вырезаем выделенную область
         Bitmap croppedBmp = CropImage(bmpScreenshot, rect);
         //мне понадобилось указывать в углу  время скришнота, что и делаем
         StringFormat strFormat = new StringFormat();
         strFormat.Alignment = StringAlignment.Far;
         strFormat.LineAlignment = StringAlignment.Far;
         Graphics g = Graphics.FromImage(croppedBmp);
         g.DrawString(DateTime.Now.ToString(), new Font("Tahoma", 8), Brushes.Purple, new RectangleF
(0, 0, croppedBmp.Width,croppedBmp.Height), strFormat);
         //загрузка на сервер, копирование ссылки в буфер и открытие url картинки в браузере
         string link = Uploader.UploadToImgur(croppedBmp, imgFormat);
         Clipboard.SetText(link);
         Process.Start(link);
         Application.Exit();
      }
}

Что же получилось?

В результате имеем то, что хотели.
Простая быстрая программа, не делающая ничего лишнего.
image
Никаких мусорных временных файлов не создаётся.
Минимальная базовая функциональность, как готовое законченное решение
рассматривать пока рано.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=28089