DisplayMagician/HeliosDisplayManagement.Shared/ProfileIcon.cs
temacdonald a9bb295d1f Renamed app to HeliosPlus namespace and more
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.
2020-04-23 20:16:16 +12:00

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);
}
}
}
}