Skip to content

Commit

Permalink
Merge pull request #2019 from cwensley/curtis/mac-add-nsimage-templat…
Browse files Browse the repository at this point in the history
…e-drawing

Mac: Add ability to draw NSImage via Graphics when it is a template
  • Loading branch information
cwensley committed Aug 23, 2021
2 parents 4a67668 + 9849b29 commit a85aaf4
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 3 deletions.
2 changes: 1 addition & 1 deletion lib/monomac
Submodule monomac updated 1 files
+64 −13 src/appkit.cs
6 changes: 6 additions & 0 deletions src/Eto.Mac/Drawing/BitmapHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,12 @@ public void Save(Stream stream, ImageFormat format)

public override void DrawImage(GraphicsHandler graphics, RectangleF source, RectangleF destination)
{
if (Control.Template)
{
DrawTemplateImage(graphics, source, destination);
return;
}

var sourceRect = new CGRect(source.X, (float)Control.Size.Height - source.Y - source.Height, source.Width, source.Height);
var destRect = destination.ToNS();
if (alpha)
Expand Down
2 changes: 2 additions & 0 deletions src/Eto.Mac/Drawing/GraphicsHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ public class GraphicsHandler : GraphicsBase, Graphics.IHandler
public NSView DisplayView { get; private set; }

static readonly object PixelOffsetMode_Key = new object();

public NSGraphicsContext GraphicsContext => graphicsContext;

public PixelOffsetMode PixelOffsetMode
{
Expand Down
6 changes: 6 additions & 0 deletions src/Eto.Mac/Drawing/IconHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ public override NSImage GetImage()

public override void DrawImage(GraphicsHandler graphics, RectangleF source, RectangleF destination)
{
if (Control.Template)
{
DrawTemplateImage(graphics, source, destination);
return;
}

var sourceRect = new CGRect(source.X, (float)Control.Size.Height - source.Y - source.Height, source.Width, source.Height);
var destRect = destination.ToNS();
Control.Draw(destRect, sourceRect, NSCompositingOperation.SourceOver, 1, true, null);
Expand Down
49 changes: 47 additions & 2 deletions src/Eto.Mac/Drawing/ImageHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ interface IImageHandler : IImageSource
}

public abstract class ImageHandler<TControl, TWidget> : WidgetHandler<TControl, TWidget>, Image.IHandler, IImageHandler
where TControl: class
where TWidget: Image
where TControl : class
where TWidget : Image
{
public abstract Size Size { get; }

Expand All @@ -66,5 +66,50 @@ public virtual void DrawImage(GraphicsHandler graphics, float x, float y, float
}

public abstract void DrawImage(GraphicsHandler graphics, RectangleF source, RectangleF destination);


public virtual void DrawTemplateImage(GraphicsHandler graphics, RectangleF source, RectangleF destination)
{
var imageSize = Size;
// draw as a template image, and ignore color data
var ctx = graphics.Control;
ctx.SaveState();

RectangleF destMask;
if (destination.Size != source.Size)
{
// scale and position
var scale = destination.Size / source.Size;
destMask = new RectangleF(destination.Location - source.Location * scale, imageSize * scale);
}
else
{
// just position
destMask = new RectangleF(destination.Location - source.Location, imageSize);
}

var destRect = destination.ToNS();
var cgImage = GetImage().AsCGImage(ref destRect, graphics.GraphicsContext, null);

// clip to the image as a mask, using only alpha channel
ctx.ClipToMask(destMask.ToNS(), cgImage);

// set fill color based on current dark/light theme
// this is the best approximation I can find to get it to draw the same as NSImageView
// thus far..
NSColor color;
if (MacVersion.IsAtLeast(10, 14) && graphics.DisplayView.HasDarkTheme())
{
color = NSColor.FromWhite(1f, .55f);
}
else
{
color = NSColor.FromWhite(0, .5f);
}
color.SetFill();

ctx.FillRect(destRect);
ctx.RestoreState();
}
}
}
15 changes: 15 additions & 0 deletions src/Eto.Mac/Forms/MacControlExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,21 @@ public static void CenterInParent(this NSView view)
view.SetFrameOrigin(new CGPoint((nfloat)(superFrame.Width - size.Width) / 2, (nfloat)(superFrame.Height - size.Height) / 2));
}
}

public static bool HasDarkTheme(this NSView view)
{
if (!MacVersion.IsAtLeast(10, 14))
return false;

var appearance = view?.EffectiveAppearance ?? NSAppearance.CurrentAppearance;

var name = appearance.Name;

if (name == NSAppearance.NameDarkAqua || name == NSAppearance.NameAccessibilityHighContrastDarkAqua)
return true;

return false;
}
}
}

69 changes: 69 additions & 0 deletions test/Eto.Test.Mac/UnitTests/BitmapTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using Eto.Drawing;
using Eto.Forms;
using Eto.Test.UnitTests;
using NUnit.Framework;

#if XAMMAC2
using AppKit;
using CoreGraphics;
#else
using MonoMac.AppKit;
using MonoMac.CoreGraphics;
#endif

namespace Eto.Test.Mac.UnitTests
{
[TestFixture]
public class BitmapTests : TestBase
{
[TestCase(null)]
[TestCase("Blue")]
[TestCase("White")]
[TestCase("Black")]
[ManualTest]
public void TemplateImagesShouldDrawCorrectly(string color)
{
ManualForm("Both images should be the same in both light and dark mode",
form => {
Color? backgroundColor = color != null ? Color.Parse(color) : (Color?)null;
var bmp = new Bitmap(200, 200, PixelFormat.Format32bppRgba);
var img = bmp.ControlObject as NSImage;
img.Template = true;
using (var g = new Graphics(bmp))
{
g.PixelOffsetMode = PixelOffsetMode.Half;
g.FillRectangle(new Color(Colors.Black, .5f), 39, 39, 120, 120);
g.DrawRectangle(Colors.Red, 4.5f, 4.5f, 191, 191);
g.FillRectangle(Colors.Red, 49, 49, 100, 100);
g.DrawRectangle(Colors.Black, 0.5f, 0.5f, 199, 199);
g.FillRectangle(Colors.Black, 59, 59, 80, 80);
}
var drawable = new Drawable { Size = bmp.Size };
drawable.Paint += (sender, e) => {
if (backgroundColor != null)
e.Graphics.Clear(backgroundColor.Value);
e.Graphics.DrawImage(bmp, 0, 0);
// e.Graphics.DrawImage(bmp, 50, 50, 100, 100);
// e.Graphics.DrawImage(bmp, new RectangleF(99, 99, 100, 100), new Rectangle(0, 0, 200, 200));
};
var imageView = new ImageView();
imageView.Image = bmp;
if (backgroundColor != null)
imageView.BackgroundColor = backgroundColor.Value;
var content = new DynamicLayout();
content.AddRow(drawable, imageView, null);
content.AddSpace();
return content;
}
);
}
}
}

0 comments on commit a85aaf4

Please sign in to comment.