mirror of
https://github.com/terrymacdonald/DisplayMagician.git
synced 2024-08-30 18:32:20 +00:00
a9bb295d1f
Renamed app to HeliosPlus namespace so that the updated changes don't interfere with HeliosDisplayManagement if that is also installed. The fact that I've changed so much of the app means that my changes would be unlikely to be accepted by Soroush, so I'm best to release this work in a similar way to other projects like Notepad++, by keeping the same root name, and adding a plus. I've also changed the Shortcut form to put all the games in a single list to reduce the number of clicks a user has to do in order for them to create a shortcut. I have begun to prepare the form so that it will support multiple game libraries, but now I am at a point that I need to fix the Steam and Uplay game detection mechanisms so that they report the correct information for the lv_games list view.
347 lines
12 KiB
C#
347 lines
12 KiB
C#
using System;
|
|
using System.Drawing;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Drawing.IconLib;
|
|
using System.Drawing.Imaging;
|
|
using System.Linq;
|
|
using HeliosPlus.Shared.Topology;
|
|
|
|
namespace HeliosPlus.Shared
|
|
{
|
|
public class ProfileIcon
|
|
{
|
|
public ProfileIcon(Profile profile, int paddingX = 100, int paddingY = 100)
|
|
{
|
|
Profile = profile;
|
|
PaddingX = paddingX;
|
|
PaddingY = paddingY;
|
|
}
|
|
|
|
public int PaddingX { get; }
|
|
public int PaddingY { get; }
|
|
|
|
public Profile Profile { get; }
|
|
|
|
// ReSharper disable once TooManyArguments
|
|
public static RectangleF CalculateViewSize(
|
|
Path[] paths,
|
|
bool withPadding = false,
|
|
int paddingX = 0,
|
|
int paddingY = 0)
|
|
{
|
|
var minX = 0;
|
|
var maxX = 0;
|
|
var minY = 0;
|
|
var maxY = 0;
|
|
|
|
foreach (var path in paths)
|
|
{
|
|
var res = NormalizeResolution(path);
|
|
minX = Math.Min(minX, path.Position.X);
|
|
maxX = Math.Max(maxX, res.Width + path.Position.X);
|
|
minY = Math.Min(minY, path.Position.Y);
|
|
maxY = Math.Max(maxY, res.Height + path.Position.Y);
|
|
}
|
|
|
|
if (withPadding)
|
|
{
|
|
minX -= paddingX;
|
|
maxX += paddingX;
|
|
minY -= paddingY;
|
|
maxY += paddingY;
|
|
}
|
|
|
|
var size = new SizeF(Math.Abs(minX) + maxX, Math.Abs(minY) + maxY);
|
|
var rect = new RectangleF(new PointF(minX, minY), size);
|
|
|
|
return rect;
|
|
}
|
|
|
|
public static Size NormalizeResolution(Size resolution, Rotation rotation)
|
|
{
|
|
if (rotation == Rotation.Rotate90 || rotation == Rotation.Rotate270)
|
|
{
|
|
return new Size(resolution.Height, resolution.Width);
|
|
}
|
|
|
|
return resolution;
|
|
}
|
|
|
|
public static Size NormalizeResolution(Path path)
|
|
{
|
|
var bigest = Size.Empty;
|
|
|
|
foreach (var target in path.Targets)
|
|
{
|
|
var res = NormalizeResolution(path.Resolution, target.Rotation);
|
|
|
|
if ((ulong) res.Width * (ulong) res.Height > (ulong) bigest.Width * (ulong) bigest.Height)
|
|
{
|
|
bigest = res;
|
|
}
|
|
}
|
|
|
|
return bigest.IsEmpty ? path.Resolution : bigest;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Creates a rounded rectangle path
|
|
/// By @taffer
|
|
/// https://stackoverflow.com/questions/33853434/how-to-draw-a-rounded-rectangle-in-c-sharp
|
|
/// </summary>
|
|
public static GraphicsPath RoundedRect(RectangleF bounds, float radius)
|
|
{
|
|
var diameter = radius * 2;
|
|
var size = new SizeF(diameter, diameter);
|
|
var arc = new RectangleF(bounds.Location, size);
|
|
var path = new GraphicsPath();
|
|
|
|
if (radius < 0.01)
|
|
{
|
|
path.AddRectangle(bounds);
|
|
|
|
return path;
|
|
}
|
|
|
|
// top left arc
|
|
path.AddArc(arc, 180, 90);
|
|
|
|
// top right arc
|
|
arc.X = bounds.Right - diameter;
|
|
path.AddArc(arc, 270, 90);
|
|
|
|
// bottom right arc
|
|
arc.Y = bounds.Bottom - diameter;
|
|
path.AddArc(arc, 0, 90);
|
|
|
|
// bottom left arc
|
|
arc.X = bounds.Left;
|
|
path.AddArc(arc, 90, 90);
|
|
|
|
path.CloseFigure();
|
|
|
|
return path;
|
|
}
|
|
|
|
public Bitmap ToBitmap(int width, int height, PixelFormat format = PixelFormat.Format32bppArgb)
|
|
{
|
|
var bitmap = new Bitmap(width, height, format);
|
|
bitmap.MakeTransparent();
|
|
|
|
using (var g = Graphics.FromImage(bitmap))
|
|
{
|
|
g.SmoothingMode = SmoothingMode.HighQuality;
|
|
DrawView(g, width, height);
|
|
}
|
|
|
|
return bitmap;
|
|
}
|
|
|
|
public Bitmap ToBitmapOverly(Bitmap bitmap)
|
|
{
|
|
var viewSize = CalculateViewSize(Profile.Paths, true, PaddingX, PaddingY);
|
|
var width = bitmap.Width * 0.7f;
|
|
var height = width / viewSize.Width * viewSize.Height;
|
|
|
|
using (var g = Graphics.FromImage(bitmap))
|
|
{
|
|
g.SmoothingMode = SmoothingMode.HighQuality;
|
|
g.TranslateTransform(bitmap.Width - width, bitmap.Height - height * 1.1f);
|
|
DrawView(g, width, height);
|
|
}
|
|
|
|
return bitmap;
|
|
}
|
|
|
|
public MultiIcon ToIcon()
|
|
{
|
|
var iconSizes = new[]
|
|
{
|
|
new Size(256, 256),
|
|
new Size(64, 64),
|
|
new Size(48, 48),
|
|
new Size(32, 32),
|
|
new Size(24, 24),
|
|
new Size(16, 16)
|
|
};
|
|
var multiIcon = new MultiIcon();
|
|
var icon = multiIcon.Add("Icon1");
|
|
|
|
foreach (var size in iconSizes)
|
|
{
|
|
icon.Add(ToBitmap(size.Width, size.Height));
|
|
|
|
if (size.Width >= 256 && size.Height >= 256)
|
|
{
|
|
icon[icon.Count - 1].IconImageFormat = IconImageFormat.PNG;
|
|
}
|
|
}
|
|
|
|
multiIcon.SelectedIndex = 0;
|
|
|
|
return multiIcon;
|
|
}
|
|
|
|
public MultiIcon ToIconOverly(string iconAddress)
|
|
{
|
|
var multiIcon = new MultiIcon();
|
|
var icon = multiIcon.Add("Icon1");
|
|
var mainIcon = new MultiIcon();
|
|
mainIcon.Load(iconAddress);
|
|
|
|
foreach (var singleIcon in mainIcon[0].Where(image =>
|
|
image.PixelFormat == PixelFormat.Format16bppRgb565 ||
|
|
image.PixelFormat == PixelFormat.Format24bppRgb ||
|
|
image.PixelFormat == PixelFormat.Format32bppArgb)
|
|
.OrderByDescending(
|
|
image =>
|
|
image.PixelFormat == PixelFormat.Format16bppRgb565
|
|
? 1
|
|
: image.PixelFormat == PixelFormat.Format24bppRgb
|
|
? 2
|
|
: 3)
|
|
.ThenByDescending(image => image.Size.Width * image.Size.Height))
|
|
{
|
|
if (!icon.All(i => singleIcon.Size != i.Size || singleIcon.PixelFormat != i.PixelFormat))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var bitmap = singleIcon.Icon.ToBitmap();
|
|
|
|
if (bitmap.PixelFormat != singleIcon.PixelFormat)
|
|
{
|
|
var clone = new Bitmap(bitmap.Width, bitmap.Height, singleIcon.PixelFormat);
|
|
|
|
using (var gr = Graphics.FromImage(clone))
|
|
{
|
|
gr.DrawImage(bitmap, new Rectangle(0, 0, clone.Width, clone.Height));
|
|
}
|
|
|
|
bitmap.Dispose();
|
|
bitmap = clone;
|
|
}
|
|
|
|
icon.Add(singleIcon.Size.Height * singleIcon.Size.Width < 24 * 24 ? bitmap : ToBitmapOverly(bitmap));
|
|
|
|
if (singleIcon.Size.Width >= 256 && singleIcon.Size.Height >= 256)
|
|
{
|
|
icon[icon.Count - 1].IconImageFormat = IconImageFormat.PNG;
|
|
}
|
|
|
|
bitmap.Dispose();
|
|
}
|
|
|
|
if (icon.Count == 0)
|
|
{
|
|
throw new ArgumentException();
|
|
}
|
|
|
|
multiIcon.SelectedIndex = 0;
|
|
|
|
return multiIcon;
|
|
}
|
|
|
|
private void DrawPath(Graphics g, Path path)
|
|
{
|
|
var res = NormalizeResolution(path);
|
|
var rect = new Rectangle(path.Position, res);
|
|
var rows = rect.Width < rect.Height ? path.Targets.Length : 1;
|
|
var cols = rect.Width >= rect.Height ? path.Targets.Length : 1;
|
|
|
|
for (var i = 0; i < path.Targets.Length; i++)
|
|
{
|
|
DrawTarget(g, path, path.Targets[i],
|
|
new Rectangle(
|
|
rect.X + PaddingX,
|
|
rect.Y + PaddingY,
|
|
rect.Width - 2 * PaddingX,
|
|
rect.Height - 2 * PaddingY),
|
|
rows > 1 ? i : 0, cols > 1 ? i : 0, rows, cols);
|
|
}
|
|
}
|
|
|
|
// ReSharper disable once TooManyArguments
|
|
private void DrawTarget(
|
|
Graphics g,
|
|
Path path,
|
|
PathTarget target,
|
|
Rectangle rect,
|
|
int row,
|
|
int col,
|
|
int rows,
|
|
int cols)
|
|
{
|
|
var targetSize = new Size(rect.Width / cols, rect.Height / rows);
|
|
var targetPosition = new Point(targetSize.Width * col + rect.X, targetSize.Height * row + rect.Y);
|
|
var targetRect = new Rectangle(targetPosition, targetSize);
|
|
|
|
if (target.SurroundTopology != null)
|
|
{
|
|
g.FillRectangle(new SolidBrush(Color.FromArgb(255, 106, 185, 0)), targetRect);
|
|
}
|
|
//else if (target.EyefinityTopology != null)
|
|
// g.FillRectangle(new SolidBrush(Color.FromArgb(255, 99, 0, 0)), targetRect);
|
|
else if (path.Targets.Length > 1)
|
|
{
|
|
g.FillRectangle(new SolidBrush(Color.FromArgb(255, 255, 97, 27)), targetRect);
|
|
}
|
|
else if (path.Position == Point.Empty)
|
|
{
|
|
g.FillRectangle(new SolidBrush(Color.FromArgb(255, 0, 174, 241)), targetRect);
|
|
}
|
|
else
|
|
{
|
|
g.FillRectangle(new SolidBrush(Color.FromArgb(255, 155, 155, 155)), targetRect);
|
|
}
|
|
|
|
g.DrawRectangle(new Pen(Color.FromArgb(125, 50, 50, 50), 2f), targetRect);
|
|
}
|
|
|
|
private void DrawView(Graphics g, float width, float height)
|
|
{
|
|
var viewSize = CalculateViewSize(Profile.Paths, true, PaddingX, PaddingY);
|
|
var standPadding = height * 0.005f;
|
|
height -= standPadding * 8;
|
|
var factor = Math.Min((width - 2 * standPadding - 1) / viewSize.Width,
|
|
(height - 2 * standPadding - 1) / viewSize.Height);
|
|
g.ScaleTransform(factor, factor);
|
|
|
|
var xOffset = ((width - 1) / factor - viewSize.Width) / 2f;
|
|
var yOffset = ((height - 1) / factor - viewSize.Height) / 2f;
|
|
g.TranslateTransform(-viewSize.X + xOffset, -viewSize.Y + yOffset);
|
|
|
|
if (standPadding * 6 >= 1)
|
|
{
|
|
using (var boundRect = RoundedRect(viewSize, 2 * standPadding / factor))
|
|
{
|
|
g.FillPath(new SolidBrush(Color.FromArgb(200, 255, 255, 255)), boundRect);
|
|
g.DrawPath(new Pen(Color.FromArgb(170, 50, 50, 50), standPadding / factor), boundRect);
|
|
}
|
|
|
|
using (
|
|
var boundRect =
|
|
RoundedRect(
|
|
new RectangleF(viewSize.Width * 0.375f + viewSize.X,
|
|
viewSize.Height + standPadding / factor,
|
|
viewSize.Width / 4, standPadding * 7 / factor), 2 * standPadding / factor))
|
|
{
|
|
g.FillPath(new SolidBrush(Color.FromArgb(250, 50, 50, 50)), boundRect);
|
|
g.DrawPath(new Pen(Color.FromArgb(50, 255, 255, 255), 2 / factor), boundRect);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g.FillRectangle(new SolidBrush(Color.FromArgb(200, 255, 255, 255)), viewSize);
|
|
g.DrawRectangle(new Pen(Color.FromArgb(170, 50, 50, 50), standPadding / factor), viewSize.X, viewSize.Y,
|
|
viewSize.Width, viewSize.Height);
|
|
}
|
|
|
|
foreach (var path in Profile.Paths)
|
|
{
|
|
DrawPath(g, path);
|
|
}
|
|
}
|
|
}
|
|
} |