Giter VIP home page Giter VIP logo

microsoft.maui.graphics's Introduction

Microsoft.Maui.Graphics (Experiment)

NuGet

Microsoft.Maui.Graphics is an experimental cross-platform graphics library for iOS, Android, Windows, macOS, Tizen and Linux written completely in C#. A Microsoft supported portion of this library has been merged with dotnet/maui and is maintained separately. This project remains separate for developers to experiment further on additional scenarios such as WASM, WinForms, WPF, Xamarin, and Linux.

With this library you can use a common API to target multiple abstractions allowing you to share your drawing code between platforms, or mix and match graphics implementations within a singular application.

Goals

  • No dependencies on System.Drawing
  • Support all graphics operations within an abstraction that the underlying abstraction supports.

Status

This is an experimental library; however it's based on code that's been in use in production applications for over 10 years. Because it was refactored out of another code base, some things may have been broken in that process.

Disclaimer

There is no official support. Use at your own Risk.

Supported Platforms

Platform Supported Abstractions
Xamarin.iOS CoreGraphics & SkiaSharp
Xamarin.Android Android.Graphics & SkiaSharp
Xamarin.Mac CoreGraphics & SkiaSharp
WPF SharpDX, SkiaSharp, Xaml & GDI
UWP SharpDX, Win2D, Xaml, SkiaSharp
WinForms SharpDX, SkiaSharp & GDI
Tizen SkiaSharp
Linux SkiaSharp
Xamarin.Forms Dependent on native platform support (noted above)

Main Abstractions

  • Canvas - You can draw to a any of the supported abstractions with a common drawing canvas API and a support of common operations and primitives
    • Rectangle, Point and Color primitives
    • Shapes (Rectangles, Rounded Rectangles, Ellipses, Arcs)
    • Paths
    • Images
    • Fonts
    • Shadows
    • Image and pattern fills
    • Clipping
    • etc...
  • Fonts - You can access fonts with a common API
  • Attributed text - You can draw attributed text with a common API
  • Bitmaps - You can create and draw on bitmap images with a common API
  • PDF - You can create PDFs using a common API

Known Limitations

  • Attributed text is not currently supported with SkiaSharp
  • The included Blazor (Canvas) implementation no longer compiles, but is included as a reminder to get it working again

microsoft.maui.graphics's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

microsoft.maui.graphics's Issues

SkiaSharp Transplant

I have a SkiaSharp project. How can I transplant it to Microsoft.Maui.Graphics, is this necessary?

[Bug] No option to specify the font attribute on Canvas

Description

I was created the custom drawable text. I was using canvas for draw the text on custom control but there is no option to specify the font attribute in canvas.

Steps to Reproduce

  1. Create the custom view to draw the text
  2. Draw the text on canvas on draw method
  3. Canvas does not have a font attribute option

Expected Behavior

  • Canvas have a option to set font attribute

Actual Behavior

  • Canvas does not have a option to set font attribute

Color is missing `Default` and `Accent`

These two "colors" were nice to have so that there was a way to tell the renderers that either the default platform color or the platform accent color should be used.

Using this dummy color allows the actual to be determined at runtime on a particular OS. This is also good at hiding a concept from shared code that cannot be pre-determined. For example accent colors could change between OS and user prefs.

Having a real color would require some events to be raised and then when setting colors they will need to be requested by the user or somehow tracked.

Switching to a class (#56) or using nullable, does sort of help with this, but also moves the issue. Assuming "null color" means "default color", we still have to resolve "accent color". But now we also need to determine what is a "no color" vs "default color"

These is some discussion in the Discord on this: https://discord.com/channels/732297728826277939/732297916680765551/830071063555080254

No color is null, transparent has an alpha of 0, default color and accent color are handled by the UI layer. They way I approach it in my apps is that default and accent color are provided by a style manager within my UI toolkit.

There is also a separate style manager/watcher/theme handling needed. Not sure what is best at this point:

  1. Magic colors that are resolved at the handler levels.
  2. Colors retrieved/events and explicitly set.

The Xamarin.Forms Color had a special enum field to flag a color as a default color or as a RGB/HSL color. WinForms has a state field. And this is also used by WPF in a weird way with a struct with sub-color structs... Not sure if this is the right way, but it is a way.

This can be a thing (not sure it is a good thing, but still a thing):

class/struct Color {
    ColorType _type;
    double/float _red, _green, _blue, _alpha;
}

enum ColorType {
    Default, 
    Accent,
    RGB,
    HSL,
}

Relates to parent issue: #58

How I can use the repo to overlay an image on a base image at specific location?

Hello:
As .NET 6.0 is rather new, I want to know if I can overlay an image on a base image at specific location.
For example, I have one base image called @"D:\Images\BaseImage.png", its width is 1280PX with height is 720PX, and I have another small image called: @"D:\Images\Overlay1.png", its width is 200PX with height 100PX.
I want to draw the small overlay image on top of the base image at the location: (800, 200), and show the merged image in a pictureBox1.
I can do this easily with Python, but I want to know if I can do the same in WinForms App in my Windows 10 PC.
Please advise! (I have both Visual Studio 2019 and 2022 for C#)
Thanks,

[Enhancement] clarify tranform relationship between CanvasState and ICanvas

From my previous PR, I've been digging into how the CanvasState and its derived classes behave, in relationship with ICanvas. It's possible that I got something wrong, and I would like to be corrected if that's the case, but so far, my understanding is this:

  • (so far) CanvasState represents the transform state of an ICanvas derived class.
  • CanvasState derived classes mirror the transform to the native transform implementation.

The issue I noticed is that several CanvasState implementations implement methods called NativeRotatate, NativeTranslate and so on, that are being called directly from the ICanvas Rotate, Translate and Scale methods. So when that happens, it bypasses the implict transform of the CanvasState base class.

Also, ICanvas has two paths to set a transform: via the ICanvas state CanvasState.Transform, where the CanvasState transform can be set directly (but right now would have no effect), and through the ICanvas transform methods, which are directed to the native transform, but don't change the CanvasState.Transform.

I think this is inconsistent and a potential source of conflicts, because there's two different ways of setting the transform, and it's not clear which is the right one, or if it even has an effect.

my proposal:

In my PR I've introduced a "TransformChanged" virtual method to the CanvasState base class, this method can be overridden by derived CanvasState classes, so whenever the base class Transform changes, they can mirror the transform change to the native transform. This would allow CanvasState.Transform to be the one and only transform API entry point.

Once CanvasState derived classes implement the TransformChanged, they would no longer need the Native* transform methods, and also, the ICanvas derived classes would reroute the rotate, translate and scale, directly to the base class

there's two advantages of this approach:

  • a more consistent API design.
  • CanvasState derived classes could be greatly simplified and streamlined.

[Question] Public NuGet

Is there a public NuGet for latest preview builds? Packages hosted here on GitHub seems to be outdated.

RetinaScale unused.

RetinaScale is defined in ICanvas, and it's implementing classes, but otherwise doesn't seem to be used.

Translate of ScalingCavnas

Here is implementation of Translate method on ScalingCanvas

public void Translate(float tx, float ty)
{
_canvas.Translate(tx, ty);
}

I am wondering it should be scaled on Translate method.

It is Android NativeCanvas implementation, it also scale on NativeTranslate

protected override void NativeTranslate(float tx, float ty)
{
_canvas.Translate(tx * CurrentState.ScaleX, ty * CurrentState.ScaleY);
}

In other words, there seems to be a problem with the Translate of ScalingCanvas

it shoud be scaled

		public void Translate(float tx, float ty)
		{
			_canvas.Translate(tx * _scaleX , ty * _scaleY);
		}

Is there any way to restrict the line count and overflow text(...) while draw the text/string in canvas

Description

I was created the custom drawable text. I was using canvas for draw the text on custom view but there is no option to specify the line count and overflow text(...) in canvas.

Steps to Reproduce

  1. Create the custom view to draw the text
  2. Draw the text on canvas on draw method
  3. Canvas does not have a options to specify the line count and overflow text

Expected Behavior

  • Canvas have a option to specify the line count and overflow text

Actual Behavior

  • Canvas does not have a options to specify the line count and overflow text

Breaking changes between Forms Color and Maui Color

Besides the Color/Colors changes which are mostly cosmetic and a bit rare in code... maybe... there are a few things that are quite different.

  1. #54 Going from R/G/B/A to Red/Green/Blue/Alpha is a bit new
  2. #55 The underlying components are now float and not double
  3. #57 Lack of Accent and Default (and Transparent, but that can be added to Colors)
  4. #56 The type is now a class and not a struct

This is just a list here to link to the real issues.

Here is a working gist of other changes: https://gist.github.com/hartez/593fc3fb87035a3aedc91657e9c15ab3

Update WinUI and Win2D to 0.8 stable

Seems there are new toys in the play pen:

  • WinUI 0.8
  • Win2D 0.8.0.21

Due to changes in WinUI, an update to Win2D is required. There is some underlying native changes that cause exceptions.

[Enhancement] ICanvas.Clear

I can see a need for an ICanvas.Clear method. It would then be possible to provide basic animation of graphics object.

Contributing docs

As correctly pointed out in #87 the documentation for contributing is currently absent.

A guide is needed for development environment setup and to list out required prerequisites so that the project is easier to contribute to.

Color property name changes `R` to `Red`

The Color.R -> Color.Red is not super different as I am not sure how many people actually use the components individually. However, this may be different enough.

But, it is worth pointing out that it is different and it is also different to most other Microsoft color types out there (WPF, UWP, WinUI, WinForms, Xamarin.Forms).

iOS does not have this and they have a GetRGBA(out r, out g, out b, out a)

Relates to parent issue: #58

Three state `CheckBox`

Could you please add third (Indeterminate) state to a checkbox?
API-wise this can be done by changing type of IsChecked from bool to bool?, similar to how it's done in Syncfusion's checkbox.

I'm forced to use their checkbox just because of this feature.

[Enhancement] Some extensions classes might be internal.

I've noticed there's some extension classes that are too generic to be defined as public, I think these classes should be marked as internal, because they may conflict with user's extensions:

  • NumericExtensions
    • Clamp is probably extended in thousands of libraries, so by including Maui.Graphics into a large project, which already has Clamp extensions, the compiler may fail due to ambiguity.
  • InvariantExtensions

If these are used in derived libraries, it's possible to make these internal classes visible to them with InternalsVisibleTo attribute.

In general, a library should extend only the types that define.

[Feature request] Set different stroke Caps at each end

I cannot find a straight forward approach (framework built-in, not workarounds) even in SkiaSharp, to draw an arc or a line with different stroke Caps at each end, something like attached picture (don't mind the red marks picture taken from internet).

It would be nice to be able to specify the stroke cap at each end.

Skia package description has incorrect URLs

The nuspec file for the Skia package has invalid URLs for the package and repository. This error is currently causing broken links on the Microsoft.Maui.Graphics.Skia NuGet page. All other packages seem ok though.

The problem is here:

<projectUrl>https://github.com/dotnet/Microsoft.Maui.Graphics.Skia</projectUrl>
<repository type="git" url="https://github.com/dotnet/Microsoft.Maui.Graphics.Skia"/>

[Enhancement] AbstractCanvas Dispose uses incorrect pattern

I've noticed AbstractCanvas Dispose is implemented like this:

public virtual void Dispose()
{
	if (_currentState != null)
	{
		_currentState.Dispose();
		_currentState = null;
	}
}

for classes intended to be used in derived classes, I think the pattern recomended by Microsoft is this one:

public void Dispose()
{
    Dispose(true);
    GC.SupressFinalize(this);  // if Dispose() is called correctly, prevents calling the finalizer.
}

~AbstractCanvas()   // fallback finalizer
{
    Dispose(false);
}

protected virtual void Dispose(bool disposing)   // method to be overrided by derived classes
{
        if (disposing)
        {
   	    if (_currentState != null)
	    {
		_currentState.Dispose();
		_currentState = null;
	    }
        }
}

This would allow a derived class which absolutely requires some hardware resources to be released in case the developer "forgets" to call Dispose, to be released by the finalizer.

remove native* out of Microsoft.Maui.Graphics.csproj and provide native projects for each platform

currently, all the native implementations are mangled into one project

it is impossible to compile on environments there not all workloads are available

i tried to split in a first (common) native project here:

https://github.com/lytico/Microsoft.Maui.Graphics/tree/lytico/natives_splitted

in a further step, Microsoft.Maui.Graphics.Native could be splitted

Purpose

make it easier to implement more platforms

eg. Microsoft.Maui.Graphics.Native.Gtk

without harming / having all dependencies to resolve

Color type is now a `class` instead of `struct`

I have been back and forth on this one, but it is still a large breaking change that will affect users. Maybe. I am not sure exactly how this affects all the code when recompiled, but it is different.

This will matter though when there are new null ref exceptions popping up, but hopefully tooling can aid with that.

Most frameworks are structs - except for iOS - and this may be unexpected.

@jonlipsky did mention that he noticed some perf improvements with TouchDraw, but this might need a check on say Android with the GC. It may be that it is fine and the GC is OK and that copying structs is more expensive.

@Clancey mentions a nicer reading:

var foo = color?.ToNative() ?? defaultColor
// vs
var foo = color == Color.Default ? defaultColor : color.ToNative()

This is true, but it is also the same with Color? as a nullable.

This also relates to #57 where there is no longer a Default "color" and null is used.

Relates to parent issue: #58

How does this code interact with WinForms High-DPI?

What the title says. I want to be able to write my drawing code while reasoning about values (borders, corner radii, etc) to look correct at 100% DPI scaling, then have the system translate the values (making border lines wider, increasing the corner radius diameter, etc) so the end result is visually identical under DPI scaling values greater than 100%.

Direct2D can do this, but using it is a pain in the butt. WPF does this intrinsically, but I have decided to not use it for my projects due to how disorganized the code is (making it impossible to refactor and hard to contribute useful improvements). WinUI 3 can also do it, but then again WinUI 3 is useful for just about nothing right now considering how pre-alpha the codebase still is, and its glacial release cycle. I want to stick with Windows Forms for my app, but at the same time want more sophisticated drawing primitives to build upon. Can you tell me how to use System.Graphics to accomplish this? Thanks!

[Bug] ImageFormat not always respected

The ImageFormat enumeration specifies the image format for load/save operations (PNG, JPEG etc.). However, it's not always respected by methods that it's passed to. In some cases it's not even used, which makes passing it pointless.

I'd suggest a thorough audit of the methods that use the ImageFormat members, and either removing it in places or ensure it's fully supported.

Add methods to create rounded rectangles with different corner radius values for each corner.

void DrawRoundedRectangle (float x, float y, float width, float height, float topLeftCornerRadius, float topRightCornerRadius, float bottomLeftCornerRadius, float bottomRightCornerRadius);

void FillRoundedRectangle (float x, float y, float width, float height, float topLeftCornerRadius, float topRightCornerRadius, float bottomLeftCornerRadius, float bottomRightCornerRadius);

[Enhancement] AffineTransform should be a readonly struct, if not System.Numerics.Matrix3x2

Before anything else, I don't understand why reinventing the wheel, because AffineTransform has the exact same functionality and purpose than System.Numerics.Matrix3x2.

Also, System.Numerics.Matrix3x2 has a number of advantages over AffineTransform:

  • It's a BCL type.
  • It's a struct, which is more GC friendly.
  • It's SIMD accelerated in the platforms that support it, so transform operations will be much faster.
  • It's a type developers know well, so no new stuff to learn.
  • it's been thoughtfully tested (#133)

So I would suggest to remove AffineTransform altogether and use System.Numerics.Matrix3x2 for all transform operations.

If that's not possible anymore, or not acceptable, I would propose these changes to AffineTransform:

  • Make it a readonly struct, not a class. Intensive drawing on complex scenes might require lots of transformations, so if AffineTransform remains a class, it will require lots of tiny GC allocations.
  • Add an implicit converter operator: static implicit operator AffineTransform(System.Numerics.Matrix3x2 transform);

Basic premise of the library is based upon a fallacy and harms existing projects.

Update: see this comment for resolution.


Within the dotnet ecosystem there are multiple graphics libraries available depending on your target platforms; however, if you are doing cross-platform development there is not a unified graphics abstraction. Some legacy API's (System.Drawing, I'm looking at you) only have limited support/usefulness on non-Windows platforms. SkiaSharp runs almost everywhere these days, but for many use cases the native graphics abstractions are needed.

This is fundamentally untrue. There exists cross platform graphics libraries in the .NET ecosystem that provide a unified API and experience.

ImageSharp
ImageSharp.Drawing

By ignoring existing projects Microsoft is actively harming the .NET open source community. Time would be better spent actually contributing to them to fill any feature gaps rather than reinventing something with the Microsoft label.

Nested ScalingCanvas scaling issue

When ScalingCanvas was nested, scale was not proper working.

for example,

			
canvas.SaveState();

canvas.StrokeColor = Colors.Black;
canvas.DrawRectangle(new Rectangle(10, 10, 20, 20));

var normalScale = new ScalingCanvas(canvas);

var scaledCanvas = new ScalingCanvas(normalScale);
scaledCanvas.Scale(2, 2);

scaledCanvas.StrokeColor = Colors.Black;
scaledCanvas.DrawRectangle(new Rectangle(10, 10, 20, 20));

scaledCanvas.Translate(10, 10);

scaledCanvas.StrokeColor = Colors.Red;
scaledCanvas.DrawRectangle(new Rectangle(10, 10, 20, 20));

canvas.RestoreState();

scaledCanvas(x2) wrapped a normalScale(x1), I expect, a result should be 2x scaled.
but actually it 4x scaled.

image

public void Scale(float sx, float sy)
{
_scaleX *= Math.Abs(sx);
_scaleY *= Math.Abs(sy);
_canvas.Scale(sx, sy);

I think, it should not call Scale of original canvas, because ScalingCanvas modify a size on it's method like this var scaledPath = path.AsScaledPath(_scaleX);, so, if we call Scale of original canvas, it doubled scaled.

[Bug] IImage.Resize ObjectDisposedException on Android

I tried resizing an image in Android (via a MAUI GraphicsView). It resulted in an ObjectDisposedException: Cannot access a disposed object (object name: Android.Graphics.Bitmap).

Code that causes the exception:

IImage image;
var assembly = GetType().GetTypeInfo().Assembly;
using (var stream = assembly.GetManifestResourceStream("MyMauiApp.Resources.Images.dotnet_bot.png"))
{
    image = GraphicsPlatform.CurrentService.LoadImageFromStream(stream);
}

if (image != null)
{
    IImage newImage = image.Resize(100, 100, ResizeMode.Fit, false);
    canvas.DrawImage(newImage, 50, 50, newImage.Width, newImage.Height);
}

Note that I'm explicitly telling Resize not to dispose the source image. Also note that this problem doesn't occur with Downsize, which fully works (which also tells me that my image is being retrieved correctly from the assembly).

remove SizeF, PointF etc.

Color component type from `double` to `float`

One issue I see with the underlying component moving from double to float is more the default implicit things that will require updates:

new Color(0.5, 0.5, 0.5, 0.5) // <- error

This will now fail to compile saying that there is no overload that will match and all the usages will need to have the "f" suffix added:

new Color(0.5f, 0.5f, 0.5f, 0.5f)

This is not a "big" change per se, but will be annoying. However, maybe not so bad as I am sure most people use the parsing from string instead...

However this also means that any implicit usage of the components will now break and require a cast.

var red = color.Red;
red = 0.5; // <- error

Relates to parent issue: #58

Create a "paint" object to hold all the drawing properties

Currently all the drawing properties are on the canvas object, and that is mostly fine.

However, this makes swapping to a new set of styles a bit laborious. For example, assume I have to populate some list of blocks. One set is has a red border, a blue fill and dashed lines. The other has a single green border. I would end up with code like this:

while (true) {
  canvas.Color = Red;
  canvas.Dash = Dotted;
  canvas.DrawRect();

  canvas.Color = Blue;
  canvas.Dash = None;
  canvas.FillRect();

  canvas.Color = Green;
  canvas.Dash = None;
  canvas.DrawRect();
}

If we had a paint object, we could do this:

var redBorder = new Paint { Color = Red, Dash = Dotted };
var blueBox = new Paint { Color = Blue };
var greenBorder = new Paint { Color = Green };

while (true) {
  canvas.DrawRect(redBorder);
  canvas.FillRect(blueBox);
  canvas.DrawRect(greenBorder);
}

Not only is it less code for me, but I don't have to remember to unset everything each time I draw - especially if I am making some methods that could set any properties.

I am even a fan of having a Style property on the Paint so that we don't need a DrawRect and a FillRect. We can just do DrawRect and the Paint has the border or fill option.

https://github.com/dotnet/System.Graphics/blob/main/src/System.Graphics/ICanvas.cs#L10-L22

[Bug] Concatenating an AffineTransform has no effect on Android

I've been using a MAUI GraphicsView to experiment with the AffineTransform class on Android. Long story short is whenever you concatenate your AffineTransform onto the canvas, then draw an object, you get a blank canvas.

Looking at the source code, it looks like the implementation is unfinished.

Take the following code that draws an 11-pointed star centred at (0,0):

            PathF path = new PathF();
            for (int i = 0; i < 11; i++)
            {
                double angle = 5 * i * 2 * Math.PI / 11;
                PointF point = new PointF(100 * (float)Math.Sin(angle), -100 * (float)Math.Cos(angle));
 
                if (i == 0)
                    path.MoveTo(point);
                else
                    path.LineTo(point);
            }
 
            canvas.FillColor = Colors.Red;
            canvas.Translate(150, 150);
            canvas.FillPath(path);

It has to be translated to fully appear on the screen. Trying the same with an AffineTransform yields a blank canvas:

            PathF path = new PathF();
            for (int i = 0; i < 11; i++)
            {
                double angle = 5 * i * 2 * Math.PI / 11;
                PointF point = new PointF(100 * (float)Math.Sin(angle), -100 * (float)Math.Cos(angle));
 
                if (i == 0)
                    path.MoveTo(point);
                else
                    path.LineTo(point);
            }
 
            canvas.FillColor = Colors.Red;
            AffineTransform transform = new AffineTransform();
            transform.SetToTranslation(150, 150);           
            canvas.ConcatenateTransform(transform);
            canvas.FillPath(path);

New projects throws Unable to find Microsoft.Maui.Graphics with version (>= 6.0.100-preview.5.213)

Upon close inspection on Nuget Package manager on visual studio "Microsoft.Maui.Graphics" show version "6.0.100-preview.4.158+sha.bfceaf9-azdo.4797686" maybe that's the problem same goes for "Microsoft.Maui.Graphics.Win2D.WinUI.Desktop"

Edit: wait version "6.0.100-preview.5.213" is not available on nuget package how to create new MAUI project then? should I downgrade all other packages to "6.0.100-preview.4.158"?

Edit 2: There is no previous version for MAUI how do I get started?

[SkiaCanvas] SolidPaint was not handled in SetFillPaint

public override void SetFillPaint(Paint paint, RectangleF rectangle)

If we call SetFillPaint with SolidPaint, fill color was not setted, because it was not handled in SetFillPaint.

it is android canvas implementation,

public override void SetFillPaint(Paint paint, RectangleF rectangle)
{
if (paint == null)
paint = Colors.White.AsPaint();
if (_shader != null)
{
CurrentState.SetFillPaintShader(null);
_shader.Dispose();
_shader = null;
}
if (paint is SolidPaint solidPaint)
{
FillColor = solidPaint.Color;
}

We can see 274 line SolidPaint.Color is used for FilledColor

SkiaCanvas implementation is missed handling of SolidPaint

[Enhancement] Cross-platform image load/save APIs

While it's possible to use GraphicsService.LoadImageFromStream to load images from a variety of sources (local files, embedded files etc.), it seems to involve writing platform code if the image isn't an embedded resource. Ditto for IImage.Save.

For MAUI usage, a fully cross-platform image load/save API would be beneficial so that you could load/save an image from/to a known location, without having to resort to platform code.

[Bug] AffineTransform ScaleX and ScaleY return invalid values.

I think the root of the problem is that AffineTransform's public properties for matrix members seem to be defined after SKMatrix, which uses some funky names for the matrix members, to say the least.

So:

  • ScaleX is M00
  • SkewX is M10
  • SkewY is M01
  • ScaleY is M11
  • TransX is M02
  • TransY is M12

The problem with this naming convention is that it is misleading, to say the least. And misleading names use to be the root of many bugs.

To someone that is used to work with transforms, ScaleX is usually interpreted as the Scale in the X axis of the matrix, not the scale along the horizon.

This is specially horrible on smartphones with display rotation: at 0 degrees, ScaleX and ScaleY will return (1,1) , and at 90 degrees will return (0,0)

So the problem is renaming M00 and M11 as ScaleX and ScaleY is that to the end developer, you don't know what these properties really return, it can be one of these:

  • M00 and M11
  • Scale along the horizon and the vertical
  • Scale along the X and Y axes.

In fact, the misinterpretation of what ScaleX means might have already propagated here which is assuming the Scale will remain the same regardless of the rotation angle.

Furthermore: Given that AffineTransform has methods like GetScaleInstance, one can believe that you can use ScaleX and ScaleY as arguments of GetScaleInstance in a general way and this is not the case for rotated transforms. Notice that GetScaleInstance expects values to be Scale along the X and Y axes.

So something like this will give incorrect results:

var at1 = AffineTransform.Multiply( AffineTransform.GetScaleInstance(1,2) , AffineTransform.GetRotateInstance(1) );

var sx = at1.ScaleX; // a developer would expect this to be 1, but it is not.
var sy = at1.ScaleY; // a developer would expect this to be 2, but it is not.

var at2 = AffineTransform.Multiply( AffineTransform.GetScaleInstance(sx, sy) , AffineTransform.GetRotateInstance(1) );

// at this point, at1 and at2 should be the same, but they're not.

So I strongly request: at the very least, the public properties of AffineTransform must be renamed to follow the field names (M00, M01 and so on)

And after that, to have, correct, mathematical definitions of ScaleX and ScaleY:

public float ScaleX => _m10 == 0 ? _m00 : new Vector2(_m00,_m10).Length();
public float ScaleY => _m01 == 0 ? _m11 : new Vector2(_m01,_m11).Length();

or just:

public float ScaleX => new Vector2(_m00,_m10).Length();
public float ScaleY => new Vector2(_m01,_m11).Length();

A generalization of deconstructing an AffineTransform as long as it's orthogonal:

public void Deconstruct(out Vector2 scale, out float rotation, out Vector2 translation)
        {         
            var sx = new Vector2(_m00,_m10).Length();
            var sy = new Vector2(_m01,_m11).Length();

            scale = new Vector2(sx, sy);

            rotation = (float)Math.Atan2(_m10, _m00);
            translation = new Vector2(_m02, _m12);
        }

My general impression is that AffineTransform is designed in a way that it doesn't really support rotations. All the math inside seem to work only as long as M01 and M10 are zero, that is: no rotation at all. The moment you add arbitrary concatenations with rotations, most of the methods and properties become misleading.

I predict this is going to be a source of headaches in the long run.

[Enhancement] Changes in Rectangle types

The issue

Using Rectangle from XAML creates a Shape by default. However, to set position and size we sometimes need to use Rectangle struct from Microsoft.Maui.Graphics.

Although it is possible to do it from C#, not from XAML.

<ContentPage.Resources>
   <ResourceDictionary>
      
      <!-- This is a Shape -->
      <Rectangle  x:Key="Rectangle" />
      
   </ResourceDictionary>
</ContentPage.Resources>

<!-- Here we need a  Microsoft.Maui.Graphics.Rectangle type -->
<ScrollView AbsoluteLayout.LayoutBounds="{StaticResource Rectangle}" />

Because we have more than one Rectangle type. See issue dotnet/maui#3022 for more information

Proposal

Rename Rectangle and RectangleF to Rect and RectF. In this way we would have two different types with different names avoiding the problem. We could look for other alternative options but they would require the use of aliases or similar.

Named colors

Some of the wrapped graphics APIs have concept of named colors (eg. NSColor on macOS). These could be system colors (eg. window background, border color) or custom catalog colors. Converting them to the internal Color type however loses their lazy resolution to actual RGB value (eg. the named color can have different color based on system wide dark mode / light mode). Is there any plan to expose this functionality? Even System.Drawing had it in some limited form.

[Enhancement] ICanvas.Shear

The AffineTransform class makes it possible to apply a shear transform, in addition to rotate, scale, translate. However, a Shear/Skew operation isn't exposed on ICanvas. Doing so would make it easier for users to shear/skew objects.

Better interoperability with System.Numerics.Vectors

Along with my previous issue #135 , There's some other types that could be mapped to System.Numerics.Vectors in some nice ways:

  • Color: Is exactly the same as System.Numerics.Vector4
  • PointF: Is exactly the same as System.Numerics.Vector2
  • SizeF: Is exactly the same as System.Numerics.Vector2
  • RectangleF: could have constructors using two System.Numerics.Vector2

I thought Microsoft learned the lesson: Since the dawn of graphics APIs, every time Microsoft rolled a new API, it has reimplemented again and again the same primitive types.

So now we're going to have:
PointF, SizeF, RectangleF and so on in System.Common.Drawing (GDI).
Point, Vector, Rectangle and so on in System.Windows (WPF).
PointF, SizeF, RectangleF and so on in Microsoft.MAUI.

So, this might come at a surprise, but people has been using System.Numerics Vector2 and Matrix3x2 as the de-facto standard for Point, Size, and AffineTransforms. You just need to take a look at ImageSharp library to realize that.

So please, don't force everybody to have to do conversion extensions between System.Numerics and MAUI types. Please implement implicit converters, or, at the very least, constructors accepting Vectors.

SkiaTextLayout LayoutCenterAligned issue

I have a issue with DrawString on SkiaCanvas.
I draw a center aligned text as vertically,but it a little bit biased to bottom.

Here is I used code

                canvas.SaveState();
                canvas.FontSize = 20;
                canvas.FillColor = Colors.Black;
                canvas.DrawRectangle(10, 10, dirtyRect.Width - 20, dirtyRect.Height - 20);

                canvas.DrawLine(dirtyRect.Left, dirtyRect.Bottom / 2.0f, dirtyRect.Right, dirtyRect.Bottom / 2.0f);

                canvas.DrawString("Hello", 0, 0, dirtyRect.Width, dirtyRect.Height,
                    System.Graphics.HorizontalAlignment.Center,
                    System.Graphics.VerticalAlignment.Center, TextFlow.OverflowBounds);
                canvas.RestoreState();

It is a result
image

I think, this line have issue

// Figure out the center index of the list, and the center point to start drawing from.
var startIndex = (lines.Count / 2);
if (linesToDraw % 2 == 0)
y -= _lineHeight / 2;

You fix a center line if linesToDraw was even. but it should be fixed when linesToDraw was odd.

			if (linesToDraw % 2 != 0)
				y -= _lineHeight / 2;

[Bug] Scale() displays nothing on Android when using negative numbers

I've been experimenting with the different transform methods, in a MAUI app on Android. Scale seems to work fine when the arguments are positive. When I switch to using negative arguments, nothing is displayed.

I was really hoping that the standard scaling rules would be applied:

  • Values between 0 and 1 decrease the width and height of the scaled object.
  • Values greater than 1 increase the width and height of the scaled object.
  • Values of 1 indicate that the object is not scaled.
  • Negative values flip the scaled object horizontally and vertically.
  • Values between 0 and -1 flip the scaled object and decrease its width and height.
  • Values less than -1 flip the object and increase its width and height.
  • Values of -1 flip the scaled object but do not change its horizontal or vertical size.
canvas.StrokeColor = Colors.Red;
canvas.StrokeSize = 4;
canvas.StrokeDashPattern = new float[] { 2, 2 };
canvas.FontColor = Colors.Blue;
canvas.FontSize = 18;

canvas.DrawRoundedRectangle(50, 50, 80, 20, 5);
canvas.DrawString(".NET MAUI", 50, 50, 80, 20, HorizontalAlignment.Left, VerticalAlignment.Top);

canvas.Scale(2, 2); // Try changing me to (2, -0.5f)
canvas.DrawRoundedRectangle(50, 100, 80, 20, 5);
canvas.DrawString(".NET MAUI", 50, 100, 80, 20, HorizontalAlignment.Left, VerticalAlignment.Top);

Is there going to be a release for .Net SDK 6.0.100?

I see there's a list of 6.0.1011 releases and a bunch of releases for 6.0.100 with "-xyz" prerelease tags, but no release for 6.0.100. Is this one still coming? (We're migrating a project here from .Net 5 to 6 and we need to move away from System.Drawing since it is no longer supported on Linux)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.