// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using MouseJumpUI.Models.Drawing;
using MouseJumpUI.NativeMethods;
using static MouseJumpUI.NativeMethods.Core;
namespace MouseJumpUI.Helpers;
internal static class DrawingHelper
{
///
/// Draw the gradient-filled preview background.
///
public static void DrawPreviewBackground(
Graphics previewGraphics, RectangleInfo previewBounds, IEnumerable screenBounds)
{
using var backgroundBrush = new LinearGradientBrush(
previewBounds.Location.ToPoint(),
previewBounds.Size.ToPoint(),
Color.FromArgb(13, 87, 210), // light blue
Color.FromArgb(3, 68, 192)); // darker blue
// it's faster to build a region with the screen areas excluded
// and fill that than it is to fill the entire bounding rectangle
var backgroundRegion = new Region(previewBounds.ToRectangle());
foreach (var screen in screenBounds)
{
backgroundRegion.Exclude(screen.ToRectangle());
}
previewGraphics.FillRegion(backgroundBrush, backgroundRegion);
}
public static void EnsureDesktopDeviceContext(ref HWND desktopHwnd, ref HDC desktopHdc)
{
if (desktopHwnd.IsNull)
{
desktopHwnd = User32.GetDesktopWindow();
}
if (desktopHdc.IsNull)
{
desktopHdc = User32.GetWindowDC(desktopHwnd);
if (desktopHdc.IsNull)
{
throw new InvalidOperationException(
$"{nameof(User32.GetWindowDC)} returned null");
}
}
}
public static void FreeDesktopDeviceContext(ref HWND desktopHwnd, ref HDC desktopHdc)
{
if (!desktopHwnd.IsNull && !desktopHdc.IsNull)
{
var result = User32.ReleaseDC(desktopHwnd, desktopHdc);
if (result == 0)
{
throw new InvalidOperationException(
$"{nameof(User32.ReleaseDC)} returned {result}");
}
}
desktopHwnd = HWND.Null;
desktopHdc = HDC.Null;
}
///
/// Checks if the device context handle exists, and creates a new one from the
/// specified Graphics object if not.
///
public static void EnsurePreviewDeviceContext(Graphics previewGraphics, ref HDC previewHdc)
{
if (previewHdc.IsNull)
{
previewHdc = new HDC(previewGraphics.GetHdc());
var result = Gdi32.SetStretchBltMode(previewHdc, Gdi32.STRETCH_BLT_MODE.STRETCH_HALFTONE);
if (result == 0)
{
throw new InvalidOperationException(
$"{nameof(Gdi32.SetStretchBltMode)} returned {result}");
}
}
}
///
/// Free the specified device context handle if it exists.
///
public static void FreePreviewDeviceContext(Graphics previewGraphics, ref HDC previewHdc)
{
if ((previewGraphics is not null) && !previewHdc.IsNull)
{
previewGraphics.ReleaseHdc(previewHdc.Value);
previewHdc = HDC.Null;
}
}
///
/// Draw placeholder images for any non-activated screens on the preview.
/// Will release the specified device context handle if it needs to draw anything.
///
public static void DrawPreviewScreenPlaceholders(
Graphics previewGraphics, IEnumerable screenBounds)
{
// we can exclude the activated screen because we've already draw
// the screen capture image for that one on the preview
if (screenBounds.Any())
{
var brush = Brushes.Black;
previewGraphics.FillRectangles(brush, screenBounds.Select(screen => screen.ToRectangle()).ToArray());
}
}
///
/// Draws a screen capture from the specified desktop handle onto the target device context.
///
public static void DrawPreviewScreen(
HDC sourceHdc,
HDC targetHdc,
RectangleInfo sourceBounds,
RectangleInfo targetBounds)
{
var source = sourceBounds.ToRectangle();
var target = targetBounds.ToRectangle();
var result = Gdi32.StretchBlt(
targetHdc,
target.X,
target.Y,
target.Width,
target.Height,
sourceHdc,
source.X,
source.Y,
source.Width,
source.Height,
Gdi32.ROP_CODE.SRCCOPY);
if (!result)
{
throw new InvalidOperationException(
$"{nameof(Gdi32.StretchBlt)} returned {result.Value}");
}
}
///
/// Draws screen captures from the specified desktop handle onto the target device context.
///
public static void DrawPreviewScreens(
HDC sourceHdc,
HDC targetHdc,
IList sourceBounds,
IList targetBounds)
{
for (var i = 0; i < sourceBounds.Count; i++)
{
var source = sourceBounds[i].ToRectangle();
var target = targetBounds[i].ToRectangle();
var result = Gdi32.StretchBlt(
targetHdc,
target.X,
target.Y,
target.Width,
target.Height,
sourceHdc,
source.X,
source.Y,
source.Width,
source.Height,
Gdi32.ROP_CODE.SRCCOPY);
if (!result)
{
throw new InvalidOperationException(
$"{nameof(Gdi32.StretchBlt)} returned {result.Value}");
}
}
}
}