Repository: SixLabors/ImageSharp

Issue:
context.BackgroundColor throws Object reference not set to an instance of an object.
### Prerequisites

- [x] I have written a descriptive issue title
- [x] I have verified that I am running the latest version of ImageSharp
- [x] I have verified if the problem exist in both `DEBUG` and `RELEASE` mode
- [x] I have searched [open](https://github.com/SixLabors/ImageSharp/issues) and [closed](https://github.com/SixLabors/ImageSharp/issues?q=is%3Aissue+is%3Aclosed) issues to ensure it has not already been reported

### Description

```
Object reference not set to an instance of an object.

   at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
   at System.Collections.Generic.Dictionary`2.set_Item(TKey key, TValue value)
   at SixLabors.ImageSharp.GraphicOptionsDefaultsExtensions.GetGraphicsOptions(Configuration configuration)
   at SixLabors.ImageSharp.GraphicOptionsDefaultsExtensions.GetGraphicsOptions(IImageProcessingContext context)
   at SixLabors.ImageSharp.Processing.BackgroundColorExtensions.BackgroundColor(IImageProcessingContext source, Color color)
   at Dropmaker.Program.<RunOnce>b__12_0(IImageProcessingContext ctx) in C:\Users\misir\source\repos\Dropmarker\src\Program.cs:line 280
   at SixLabors.ImageSharp.Processing.ProcessingExtensions.ProcessingVisitor.Visit[TPixel](Image`1 image)
   at SixLabors.ImageSharp.Image`1.Accept(IImageVisitor visitor)
   at SixLabors.ImageSharp.Advanced.AdvancedImageExtensions.AcceptVisitor(Image source, IImageVisitor visitor)
   at SixLabors.ImageSharp.Processing.ProcessingExtensions.Mutate(Image source, Configuration configuration, Action`1 operation)
   at SixLabors.ImageSharp.Processing.ProcessingExtensions.Mutate(Image source, Action`1 operation)
   at Dropmaker.Program.RunOnce(State state) in C:\Users\misir\source\repos\Dropmarker\src\Program.cs:line 278
```

### Steps to Reproduce

It happens sometimes when I compile & run this code.

```csharp
var image = Image.Load("image.jpg");
image.Mutate(ctx =>
{
    ctx.BackgroundColor(Color.White);
});
```

### System Configuration
<!-- Tell us about the environment where you are experiencing the bug -->

- ImageSharp version: SixLabors.ImageSharp@1.0.0-rc0002
- Environment (Operating system, version and so on): Windows 10 x64
- .NET Framework version: .NET Core 3.1

<!-- Thanks for reporting the issue to ImageSharp! -->



Hints:
> It happens sometimes when I compile & run this code.

What do you mean by this, Is the issue intermittent? What environmental factors trigger a difference in result.

Are you running this in multiple threads?

Patch:
diff --git a/src/ImageSharp/Configuration.cs b/src/ImageSharp/Configuration.cs
index 03e59d34b2..067257132f 100644
--- a/src/ImageSharp/Configuration.cs
+++ b/src/ImageSharp/Configuration.cs
@@ -2,6 +2,7 @@
 // Licensed under the Apache License, Version 2.0.
 
 using System;
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Net.Http;
 using SixLabors.ImageSharp.Formats;
@@ -78,7 +79,7 @@ public int MaxDegreeOfParallelism
         /// Gets a set of properties for the Congiguration.
         /// </summary>
         /// <remarks>This can be used for storing global settings and defaults to be accessable to processors.</remarks>
-        public IDictionary<object, object> Properties { get; } = new Dictionary<object, object>();
+        public IDictionary<object, object> Properties { get; } = new ConcurrentDictionary<object, object>();
 
         /// <summary>
         /// Gets the currently registered <see cref="IImageFormat"/>s.
diff --git a/src/ImageSharp/GraphicOptionsDefaultsExtensions.cs b/src/ImageSharp/GraphicOptionsDefaultsExtensions.cs
index d1581a00eb..80cdfd153e 100644
--- a/src/ImageSharp/GraphicOptionsDefaultsExtensions.cs
+++ b/src/ImageSharp/GraphicOptionsDefaultsExtensions.cs
@@ -71,11 +71,9 @@ public static GraphicsOptions GetGraphicsOptions(this IImageProcessingContext co
                 return go;
             }
 
-            var configOptions = context.Configuration.GetGraphicsOptions();
-
             // do not cache the fall back to config into the the processing context
             // in case someone want to change the value on the config and expects it re trflow thru
-            return configOptions;
+            return context.Configuration.GetGraphicsOptions();
         }
 
         /// <summary>
diff --git a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs
index 4f47545fa3..7ed73d5176 100644
--- a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs
+++ b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs
@@ -1,6 +1,7 @@
 // Copyright (c) Six Labors.
 // Licensed under the Apache License, Version 2.0.
 
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 using SixLabors.ImageSharp.PixelFormats;
 using SixLabors.ImageSharp.Processing.Processors;
@@ -41,7 +42,7 @@ public DefaultImageProcessorContext(Configuration configuration, Image<TPixel> s
         public Configuration Configuration { get; }
 
         /// <inheritdoc/>
-        public IDictionary<object, object> Properties { get; } = new Dictionary<object, object>();
+        public IDictionary<object, object> Properties { get; } = new ConcurrentDictionary<object, object>();
 
         /// <inheritdoc/>
         public Image<TPixel> GetResultImage()


Test patch:
diff --git a/tests/ImageSharp.Tests/GraphicOptionsDefaultsExtensionsTests.cs b/tests/ImageSharp.Tests/GraphicOptionsDefaultsExtensionsTests.cs
index 72ec355385..21f26cd01c 100644
--- a/tests/ImageSharp.Tests/GraphicOptionsDefaultsExtensionsTests.cs
+++ b/tests/ImageSharp.Tests/GraphicOptionsDefaultsExtensionsTests.cs
@@ -1,7 +1,9 @@
 // Copyright (c) Six Labors.
 // Licensed under the Apache License, Version 2.0.
 
+using System.Threading.Tasks;
 using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing;
 using SixLabors.ImageSharp.Tests.Processing;
 using SixLabors.ImageSharp.Tests.TestUtilities;
 using Xunit;
@@ -168,5 +170,18 @@ public void GetDefaultOptionsFromProcessingContext_IgnoreIncorectlyTypesDictionE
             Assert.NotNull(options);
             Assert.IsType<GraphicsOptions>(options);
         }
+
+        [Theory]
+        [WithBlankImages(100, 100, PixelTypes.Rgba32)]
+        public void CanGetGraphicsOptionsMultiThreaded<TPixel>(TestImageProvider<TPixel> provider)
+            where TPixel : unmanaged, IPixel<TPixel>
+        {
+            // Could not get fake operations to trigger #1230 so using a real image.
+            Parallel.For(0, 10, _ =>
+            {
+                using Image<TPixel> image = provider.GetImage();
+                image.Mutate(x => x.BackgroundColor(Color.White));
+            });
+        }
     }
 }
diff --git a/tests/ImageSharp.Tests/Processing/FakeImageOperationsProvider.cs b/tests/ImageSharp.Tests/Processing/FakeImageOperationsProvider.cs
index 5f673b9d29..03fa6dfc2e 100644
--- a/tests/ImageSharp.Tests/Processing/FakeImageOperationsProvider.cs
+++ b/tests/ImageSharp.Tests/Processing/FakeImageOperationsProvider.cs
@@ -1,6 +1,7 @@
 // Copyright (c) Six Labors.
 // Licensed under the Apache License, Version 2.0.
 
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Linq;
 using SixLabors.ImageSharp.PixelFormats;
@@ -56,7 +57,7 @@ public FakeImageOperations(Configuration configuration, Image<TPixel> source, bo
 
             public Configuration Configuration { get; }
 
-            public IDictionary<object, object> Properties { get; } = new Dictionary<object, object>();
+            public IDictionary<object, object> Properties { get; } = new ConcurrentDictionary<object, object>();
 
             public Image<TPixel> GetResultImage()
             {