Skip to content
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

[Breaking change]: System.Drawing.Common no longer supported on non-Windows OSs #25257

Closed
2 tasks done
safern opened this issue Jul 21, 2021 · 9 comments
Closed
2 tasks done
Assignees
Labels
binary incompatible Existing binaries may encounter a breaking change in behavior. breaking-change Indicates a .NET Core breaking change 🏁 Release: .NET 6 Issues and PRs for the .NET 6 release doc-idea Indicates issues that are suggestions for new topics [org][type][category] Pri1 High priority, do before Pri2 and Pri3 source incompatible Source code may encounter a breaking change in behavior when targeting the new version.

Comments

@safern
Copy link
Member

safern commented Jul 21, 2021

Description

As proposed on: dotnet/designs#234

System.Drawing.Common is now attributed as a Windows specific library which will cause the platform analyzer to emit warnings when compiled for non-windows OSs.

At runtime on non-windows OSs will now throw by default a TypeInitializerException with a PlatformNotSupportedException as the inner exception, unless a runtime config switch is set. The exception looks like this one:

System.TypeInitializationException : The type initializer for 'Gdip' threw an exception.
      ---- System.PlatformNotSupportedException : System.Drawing.Common is not supported on non-Windows platforms. See https://aka.ms/systemdrawingnonwindows for more information.
      Stack Trace:
           at System.Drawing.SafeNativeMethods.Gdip.GdipCreateBitmapFromFile(String filename, IntPtr& bitmap)
        /_/src/libraries/System.Drawing.Common/src/System/Drawing/Bitmap.cs(42,0): at System.Drawing.Bitmap..ctor(String filename, Boolean useIcm)
        /_/src/libraries/System.Drawing.Common/src/System/Drawing/Bitmap.cs(25,0): at System.Drawing.Bitmap..ctor(String filename)
        /_/src/libraries/System.Resources.ResourceManager/tests/ResourceManagerTests.cs(270,0): at System.Resources.Tests.ResourceManagerTests.EnglishImageResourceData()+MoveNext()
        /_/src/libraries/System.Linq/src/System/Linq/Select.cs(136,0): at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
        ----- Inner Stack Trace -----
        /_/src/libraries/System.Drawing.Common/src/System/Drawing/LibraryResolver.cs(31,0): at System.Drawing.LibraryResolver.EnsureRegistered()
        /_/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Unix.cs(65,0): at System.Drawing.SafeNativeMethods.Gdip.PlatformInitialize()
        /_/src/libraries/System.Drawing.Common/src/System/Drawing/Gdiplus.cs(27,0): at System.Drawing.SafeNativeMethods.Gdip..cctor()

To enable Unix support, the runtime config switch, System.Drawing.EnableUnixSupport, needs to be set to true.

Even if applications set the runtime config switch to true, this is just to give applications that heavily depend on this behavior cross plat, to migrate to more modern like libraries, and we will no longer give support for non-Windows bugs. This runtime config switch will be removed in .NET 7.

The libraries that we recommend libraries to search as an alternative are:

Version

.NET 6 Preview 7

Previous behavior

There was no compile warnings.

There was no runtime exception and no need to set a runtime config switch.

New behavior

  1. Compile time warnings emitted by the Platform analyzer as the assembly is now marked as windows specific.
  2. Runtime exception like the following unless a runtime config switch is set:
System.TypeInitializationException : The type initializer for 'Gdip' threw an exception.
      ---- System.PlatformNotSupportedException : System.Drawing.Common is not supported on non-Windows platforms. See https://aka.ms/systemdrawingnonwindows for more information.
      Stack Trace:
           at System.Drawing.SafeNativeMethods.Gdip.GdipCreateBitmapFromFile(String filename, IntPtr& bitmap)
        /_/src/libraries/System.Drawing.Common/src/System/Drawing/Bitmap.cs(42,0): at System.Drawing.Bitmap..ctor(String filename, Boolean useIcm)
        /_/src/libraries/System.Drawing.Common/src/System/Drawing/Bitmap.cs(25,0): at System.Drawing.Bitmap..ctor(String filename)
        /_/src/libraries/System.Resources.ResourceManager/tests/ResourceManagerTests.cs(270,0): at System.Resources.Tests.ResourceManagerTests.EnglishImageResourceData()+MoveNext()
        /_/src/libraries/System.Linq/src/System/Linq/Select.cs(136,0): at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
        ----- Inner Stack Trace -----
        /_/src/libraries/System.Drawing.Common/src/System/Drawing/LibraryResolver.cs(31,0): at System.Drawing.LibraryResolver.EnsureRegistered()
        /_/src/libraries/System.Drawing.Common/src/System/Drawing/GdiplusNative.Unix.cs(65,0): at System.Drawing.SafeNativeMethods.Gdip.PlatformInitialize()
        /_/src/libraries/System.Drawing.Common/src/System/Drawing/Gdiplus.cs(27,0): at System.Drawing.SafeNativeMethods.Gdip..cctor()

Type of breaking change

  • Binary incompatible (existing binaries may encounter a breaking change in behavior - for example, failure to load/execute or different run-time behavior)
  • Source incompatible (existing source code may encounter a breaking change in behavior when targeting the new runtime/component/SDK - for example, compile errors or different run-time behavior)

Reason for change

The cross-platform implementation of System.Drawing.Common is mainly provided
on the native side, via libgdiplus. It's effectively a re-implementation of
the parts of Windows that System.Drawing.Common is depending on. As you can
imagine, that makes libgdiplus a very non-trivial component. It's around 30k
lines of pretty old C code, virtually untested, and lacks a lot of
functionality. libgdiplus also has a lot of external dependencies for all the
image processing and text rendering, such as cairo, pango, and other native
libraries, which makes maintaining and shipping it even more challenging.

Since System.Drawing.Common was really designed to be a thin wrapper over
Windows technologies, its cross-platform implementation is a bit like a square
peg in a round hole; it's subpar because it was only designed with Windows in
mind.

Since the inclusion of the Mono cross-platform implementation we spent lot of
time redirecting issues to libgdiplus that never got fixed, or helping people
install libgdiplus correctly (because it's not part of .NET Core proper). This
is very different from other external dependencies we have taken, for example,
icu or openssl, which are high-quality libraries, rather than poorly
maintained re-implementation of Windows components. Thus, we have come to
believe that it's not viable to get libgdiplus to the point where its feature
set and quality is on par with the rest of the .NET stack.

We have noticed (such as from analysis of NuGet packages) that
System.Drawing.Common is used on cross-platform mostly for image manipulation
like QR code generators and text rendering. We haven't noticed heavy graphics
usage as our cross-platform graphics support is very incomplete. For example,
right now some components crash on macOS because the native APIs we use have
been deprecated and are no longer included with recent macOS releases. We have
never gotten a report about that crash; we discovered it ourselves while making
System.Drawing.Common trim compatible.

The usages we see of System.Drawing.Common in non-Windows environments are
typically well supported with SkiaSharp and ImageSharp.

Thus, we believe it is not worth investing in making System.Drawing.Common
work on non-Windows platforms and instead propose we only keep evolving it in
the context of Windows Forms and GDI+.

Recommended action

Migrate code to one of the previously recommended libraries.

TODO: add examples.

Feature area

Other (please put exact area in description textbox)

Affected APIs

Namespace: System.Drawing

  • Bitmap
  • Image
  • Icon
  • Graphics
  • Brush
  • Brushes
  • Pen
  • Pens
  • BufferedGraphics
  • BufferedGraphicsContext
  • Font
  • FontFamily
  • FontConverter
  • IconConverter
  • ImageAnimator
  • Region
  • SolidBrush
  • StringFormat
  • SystemBrushes
  • SystemFonts
  • SystemIcons
  • SystemPens
  • TextureBrush

Namespace: System.Drawing.Drawing2D

  • AdjustableArrowCap
  • CustomLineCap
  • GraphicsPath
  • GraphicsPathIterator
  • GraphicsState
  • HatchBrush
  • LinearGradientBrush
  • Matrix
  • PathGradientBrush

Namespace: System.Drawing.Imaging

  • Encoder
  • EncoderParameter
  • EncoderParameters
  • ImageAttributes
  • ImageCodecInfo
  • ImageFormat
  • Metafile
  • MetafileHeader
  • PlayRecordCallback

Namespace: System.Drawing.Printing

  • PageSettings
  • PreviewPageInfo
  • PrintController
  • PrintDocument
  • PrinterSettings
  • PrintEventArgs
  • PrintPageEventArgs
  • PrintEventHandler
  • PrintPageEventHandler

Namespace: System.Drawing.Text

  • FontCollection
  • InstalledFontCollection
  • PrivateFontCollection

cc: @ericstj @terrajobst @danmoseley

@safern safern added the breaking-change Indicates a .NET Core breaking change label Jul 21, 2021
@dotnet-bot dotnet-bot added the ⌚ Not Triaged Not triaged label Jul 21, 2021
@safern
Copy link
Member Author

safern commented Jul 21, 2021

I will add source code examples on how to migrate from the most common scenarios tomorrow or the day after.

@gewarren gewarren added doc-idea Indicates issues that are suggestions for new topics [org][type][category] Pri1 High priority, do before Pri2 and Pri3 and removed ⌚ Not Triaged Not triaged Pri3 labels Jul 21, 2021
@gewarren gewarren added the 🏁 Release: .NET 6 Issues and PRs for the .NET 6 release label Jul 23, 2021
@gewarren
Copy link
Contributor

@safern Do you still plan to add some code examples to this issue?

@safern
Copy link
Member Author

safern commented Jul 26, 2021

@safern Do you still plan to add some code examples to this issue?

Yes. Will do it today or tomorrow. I'll ping you when ready.

@terrajobst
Copy link
Member

@gewarren the template should add a new category of breaking change:

[ ] Runtime breaking change (existing binaries will behave differently when running on new versions)

(which is the case for this one as it's neither a binary nor a source breaking change as defined above)

@terrajobst
Copy link
Member

@safern

Looks good to me! Thanks for getting this filed.

@gewarren
Copy link
Contributor

gewarren commented Jul 27, 2021

@gewarren the template should add a new category of breaking change:

[ ] Runtime breaking change (existing binaries will behave differently when running on new versions)

@terrajobst After discussing with Mark Miller and Priya, we decided to expand the definitions of source/binary compatibility instead of adding a new category. Does this PR work for you?

- Binary incompatible (breaks existing binaries when loaded into the new runtime/component - for example, failure to load/execute or different run-time behavior)
- Source incompatible (breaks source code when targeting the new runtime/component/SDK - for example, compile errors or different run-time behavior)

@terrajobst
Copy link
Member

Works and matches my intuition for binary compat

@gewarren gewarren added source incompatible Source code may encounter a breaking change in behavior when targeting the new version. binary incompatible Existing binaries may encounter a breaking change in behavior. labels Jul 28, 2021
@gewarren
Copy link
Contributor

gewarren commented Aug 4, 2021

@safern Is the namespace for the group that starts with AdjustableArrowCap incorrect? I could find those types under System.Drawing.Drawing2D but not System.Drawing.Design.

@safern
Copy link
Member Author

safern commented Aug 4, 2021

@gewarren, yes! Good catch. I updated the issue 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
binary incompatible Existing binaries may encounter a breaking change in behavior. breaking-change Indicates a .NET Core breaking change 🏁 Release: .NET 6 Issues and PRs for the .NET 6 release doc-idea Indicates issues that are suggestions for new topics [org][type][category] Pri1 High priority, do before Pri2 and Pri3 source incompatible Source code may encounter a breaking change in behavior when targeting the new version.
Projects
None yet
Development

No branches or pull requests

5 participants