-
Notifications
You must be signed in to change notification settings - Fork 585
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove image sharp #2069
Remove image sharp #2069
Conversation
[Traige]: FYI @raffaeler and @krwq, we are going to do a design review next week to look into this one in more detail. @pgrawehr will go over the problem and design, so we can try to get this in for 3.0. |
[Triage] With Jose's words, this PR tries to solve these problems:
|
@@ -22,6 +22,7 @@ | |||
<!-- Excluding samples and test projects when getting source files --> | |||
<_ExcludeProjectReferences Include="$(DeviceRoot)**/samples/**/*.csproj" /> | |||
<_ExcludeProjectReferences Include="$(DeviceRoot)**/tests/**/*.csproj" /> | |||
<_ExcludeProjectReferences Include="$(DeviceRoot)SkiaSharpConnector/*.csproj" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain why do we need to exclude this? This would make it so that the connector won't be part of the iot.device.bindings library.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see you created an extra package for that. Can you explain what is the reasoning for separating that piece?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's by design. The intent is that the connector is in an extra nuget, so that it can be exchanged for another implementation without touching Iot.device.bindings or its API. That means that we can provide different connectors without affecting the API (or the user could write their own).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not following why keeping it as part of IoT.Device.Bindings would not allow for an external implementation without touching Iot.device.bindings. The part that seems wrong to me, is that we are adding an abstract class with no default implementation on the main library, which seems wrong, as ideally we would want IoT.Device.bindings to not require additional packages on top for some bindings to work. Model of having an included implementation on the library, plus the ability to use a different one via a NuGet package seems to satisfy both goals.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to avoid that the iot.device.bindings again references large dependencies. SkiaSharp's native implementation file is ~10MB per platform, and one might not want to carry this dependency around if one doesn't need it. There are many bindings that don't need any imaging features, so if you only use those bindings, you would not need to have a large dependency lying around for nothing.
So either we have some bindings that require an additional package to work, or we have many bindings that pull a dependency they don't need. I prefer the first solution.
@@ -35,60 +35,58 @@ public override int PinCount | |||
get; | |||
} | |||
|
|||
public override int ConvertPinNumberToLogicalNumberingScheme(int pinNumber) | |||
/// <inheritdoc /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm failing to understand how all of the changes in the Arduino device are related to this PR. Are those just there because when working on the sample you made some adjustments? Does it make sense to separate these changes into a separate PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, most of these changes are improvements for the example. I'm considering to split that into a separate PR, but I'm waiting until the design has been roughly finalized, to avoid diverging branches.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, yeah, we'd probably want those in a separate PR. If anything, it'll make reviewing this PR easier 😃
This is now mostly ready for review. A few points still:
|
fa2a1b5
to
9facad0
Compare
This is an adapter that uses SkiaSharp as Bitmap handling backend
3391eda
to
5fb2e5c
Compare
I have now rebased the PR and separated the changes into logical units. Even though they're not fully independent, it should be easier to review now. I suggest to use Rebase or real merge instead of squash for this PR, to keep the stuff separated in history. |
@@ -6,7 +6,7 @@ | |||
<PreReleaseVersionLabel>prerelease</PreReleaseVersionLabel> | |||
<MicrosoftDotNetGenAPIPackageVersion>7.0.0-beta.23211.2</MicrosoftDotNetGenAPIPackageVersion> | |||
<!-- dotnet/corefx dependencies --> | |||
<SystemDrawingCommonPackageVersion>5.0.3</SystemDrawingCommonPackageVersion> | |||
<SystemDrawingCommonPackageVersion>6.0.0</SystemDrawingCommonPackageVersion> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason why we are picking 6.0 as opposed to latest?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just took the version matching the TargetRuntime version we currently use, but I suppose we could use the 7.0 version as well.
@@ -1,6 +1,20 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids --> | |||
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> | |||
<Suppression> | |||
<DiagnosticId>CP0001</DiagnosticId> | |||
<Target>T:BuildHat.Models.BuildHatInformation</Target> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's really hard to go through all of these, but I'm assuming they are pretty much all around removing APIs that exposed ImageSharp types, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are mainly three kinds of changes:
- Apis that were changed from ImageSharp to System.Drawing (e.g.
ImageSharp.Color
toSystem.Drawing.Color
) - Apis that were using ImageSharp bitmap types now take
Iot.Device.Common.BitmapImage
- Individual types that were in incorrect namespaces
For the user, the changes typically only require adapting their using definitions.
src/Iot.Device.Bindings.SkiaSharpAdapter/Iot.Device.Bindings.SkiaSharpAdapter.csproj
Show resolved
Hide resolved
src/Iot.Device.Bindings.SkiaSharpAdapter/Iot.Device.Bindings.SkiaSharpAdapter.csproj
Show resolved
Hide resolved
BitmapImage is the new abstract bitmap type. Changed most bindings to use the new type, and also changed bindings to use System.Drawing again for those types that will still be supported on all platforms (Color, Size, Rectangle, Point)
5fb2e5c
to
cbe2b79
Compare
Isolated the relevant changes now. The Ili9341 binding is temporarily broken now, though. The fix will be provided in a separate PR. |
<Project> | ||
<!-- Packaging related properties --> | ||
<PropertyGroup> | ||
<MajorVersion>2</MajorVersion> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we actually care about those versions and settings? Presumably adapter is not getting shipped directly as nuget?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wait a sec.. looking at code later seems like this will be a new package, do we actually need it? could this be constructed similarly to other common projects for Iot.Device.Bindings? I might have missed the triage meeting where you discussed this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we want this as a separate package (to reduce dependencies for those who do not need any bitmap functionality or want to use another imaging library).
I'm not sure how the version for the release is actually computed in our setup though (question for @joperezr )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think for version we either match Iot.Device.Bindings and System.Device.Gpio or we start from 1 and only update when needed (I'm personally not super opinionated on that - matching versions seems easier)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should match, which is the case now (IIRC). But we should (in a separate PR) maybe improve the way the version number is calculated or defined, so we're sure we get the same number everywhere. That is burried somewhere in the arcade repo I guess, and I don't really understand that stuff.
@@ -57,8 +55,7 @@ public void DoTest() | |||
float blueCorrection = 0.95f; | |||
while (!Console.KeyAvailable) | |||
{ | |||
var hsv = new Hsv(angle, 1.0f, 1.0f); | |||
var rgb = converter.ToRgb(hsv); | |||
Color rgb = Color.Blue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't this sample just displaying blue rather than color changing over time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this will be fixed with the next PR.
@@ -1,7 +1,7 @@ | |||
// Licensed to the .NET Foundation under one or more agreements. | |||
// The .NET Foundation licenses this file to you under the MIT license. | |||
|
|||
namespace BuildHat.Models | |||
namespace Iot.Device.BuildHat.Models |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this need sample update?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the moment, it appears to build fine.
/// A test method | ||
/// </summary> | ||
/// <param name="lcd">The lcd driver</param> | ||
public static void ValueTest(ICharacterLcd lcd) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where are you calling this new method?
{ | ||
if (s_currentFactory == null) | ||
{ | ||
throw new InvalidOperationException("No image factory registered. Call BitmapImage.RegisterImageFactory() with a suitable implementation first. Consult the documentation for further information"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if there are some steps needed it might be good to possibly display a link where they can find more info. We should discuss if we want to do that during our triage meeting and if so what's the location we want those at
return s_currentFactory!.CreateBitmap(width, height, pixelFormat); | ||
} | ||
|
||
private static void VerifyFactoryAvailable() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you put [MemberNotNull(nameof(s_currentFactory))]
on this method then you won't need !
like: s_currentFactory!
everywhere
/// Return the data pointer as a raw span of integers. For 32bit images, this equals to one pixel per entry. | ||
/// </summary> | ||
/// <returns>A span of integers</returns> | ||
public virtual Span<int> AsIntSpan() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this need to be virtual? could it be just regular method? do we actually need it? Would be better if callsite did conversion
/// </summary> | ||
/// <param name="stream">The stream to save the data to</param> | ||
/// <param name="format">The image format</param> | ||
public abstract void SaveToStream(Stream stream, ImageFileType format); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider WriteToStream
or just WriteTo
since Stream is an argument it's obvious we're writing to stream
|
||
if (Environment.OSVersion.Platform != PlatformID.Win32NT) | ||
{ | ||
throw new PlatformNotSupportedException("This operation is only supported on Windows"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wasn't system.drawing available on Linux as well (IIRC it needed libgdi+)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No longer. System.Drawing V6.0+ is only supported on Windows. Without this test, I get a warning about an unsupported platform-specific call.
/// This declarative interface provides the basis for drawing functions using image specific APIs. | ||
/// This interface is empty. The implementation is provided via extension methods. | ||
/// </summary> | ||
public interface IGraphics : IDisposable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not just regular class in that case? what's the value of this being interface and having extension methods elsewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't really matter whether it's an interface or an empty class type. In either case, it's intended to be a handle only. The reason I'm using extension methods is because it is up to the implementation to provide the set of drawing functions. With this approach, it is possible to add new drawing functions without changing the interface.
/// <summary> | ||
/// Undefined | ||
/// </summary> | ||
Undefined, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO we should stick with either Undefined
or Unspecified
everywhere
@@ -6,6 +6,7 @@ | |||
using System.Drawing.Imaging; | |||
using System.Runtime.InteropServices; | |||
|
|||
#pragma warning disable CA1416 // Temporarily, will be removed after binding is updated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please remove the comment and replace with link to new issue (unless it's already in the other PR)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comment everywhere where applies
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's part of the other PR.
Color.FromRgba(0, 0, 0, byte.MaxValue), | ||
Color.FromRgba(0, 0, 0, byte.MaxValue), | ||
Color.FromRgba(0, 0, 0, byte.MaxValue) | ||
Color.FromArgb(255, 0, 0, 0), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can just not pass the first value now
var myBitmap = BitmapImage.CreateFromStream(stream); | ||
var g = myBitmap.GetDrawingApi(); | ||
g.DrawText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), "Tahoma", 20, Color.White, new Point(0, 0)); | ||
using (var ms = new MemoryStream()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any particular reason for not using non-nesting using
?
@@ -7,6 +7,7 @@ | |||
|
|||
<ItemGroup> | |||
<ProjectReference Include="../../../Media.csproj" /> | |||
<ProjectReference Include="..\..\..\..\SkiaSharpAdapter\SkiaSharpAdapter.csproj" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fix/me\please
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(fixing line above would be more consistent though)
/// <inheritdoc /> | ||
public BitmapImage CreateFromStream(Stream file) | ||
{ | ||
using var image = SKImage.FromEncodedData(file); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
once this gets disposed presumably bitmap won't?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Jup, it wouldn't work otherwise...
/// <summary> | ||
/// An abstract class that acts as a data container for device-dependent pixel arrays | ||
/// </summary> | ||
public abstract class RawPixelContainer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could this potentially use your bitmap image with correct image format?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided against it, because the data format used by this class (or rather, it's descendants) is hardware-specific. If possible, I want to keep the BitmapImage to only support 32-Bit RGB images, to keep things simple and fast.
@@ -0,0 +1,13 @@ | |||
<Project> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overall do we need to update samples to mention that package should be used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's done (or will be done) for all projects requiring the imaging library.
|
||
<PropertyGroup> | ||
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks> | ||
<LangVersion>10</LangVersion> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use Latest instead? That would make sure that this is not pinning down in the future.
@@ -10,7 +10,7 @@ | |||
using System.Threading.Tasks; | |||
using Iot.Device.Ft4222; | |||
using Iot.Device.Ili9341; | |||
|
|||
#pragma warning disable CA1416 // Temporarily, will be removed after binding is updated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you either explain what is this for or add a link to an issue to track us actually doing this work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be fixed with #2084, which shall be reviewed and merged just after.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amazing work! Ship it :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please address as you see fit since there is nothing particularly blocking. It would be good to cleanup the library from helper methods.
Fixes #1767 (and probably a few others)
System.Drawing.Primitives.dll
(these are supported on all platforms and even part of the core runtime)BitmapImage
class, which is an abstract image typeImageFactoryRegistry
static class to create instances of imagesSkiaSharpConnector
as (one possible) adapter that provides the actual implementation.I suggest to create a new package for the SkiaSharpConnector (maybe better name it SkiaSharpAdapter?), because other adapters are possible (e.g. plain old System.Drawing.Imaging would do if only used on Windows) and because SkiaSharp is a quite heavy dependency (~100MB with the native dependencies)
Rough testing done, but a quick test of all the heavier affected bindings would be good:
Microsoft Reviewers: Open in CodeFlow