Skip to content

Commit 21a6bc1

Browse files
Merge pull request #235 from SixLabors/js/optimize-pixel-memory
Allow ImageSharp to determine the best pixel format on load.
2 parents ce43688 + 3a639ad commit 21a6bc1

39 files changed

Lines changed: 722 additions & 858 deletions

samples/ImageSharp.Web.Sample/wwwroot/imagesharp-logo.png

Lines changed: 0 additions & 3 deletions
This file was deleted.

samples/ImageSharp.Web.Sample/wwwroot/index.html

Lines changed: 194 additions & 194 deletions
Large diffs are not rendered by default.
Lines changed: 3 additions & 0 deletions
Loading

src/Directory.Build.targets

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,7 @@
1717
<Import Project="$(MSBuildThisFileDirectory)..\Directory.Build.targets" />
1818

1919
<ItemGroup>
20-
<PackageReference Update="AWSSDK.S3" Version="3.7.7.16" />
21-
<PackageReference Update="Azure.Storage.Blobs" Version="12.10.0" />
22-
<PackageReference Update="Microsoft.IO.RecyclableMemoryStream" Version="2.2.0" />
2320
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
24-
<PackageReference Update="SixLabors.ImageSharp" Version="2.0.0" />
2521
</ItemGroup>
2622

2723
<ItemGroup Condition="'$(TargetFramework)' != 'netcoreapp2.1'">

src/ImageSharp.Web.Providers.AWS/ImageSharp.Web.Providers.AWS.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
</ItemGroup>
3232

3333
<ItemGroup>
34-
<PackageReference Include="AWSSDK.S3" />
34+
<PackageReference Include="AWSSDK.S3" Version="3.7.8.8" />
3535
</ItemGroup>
3636

3737
<ItemGroup>

src/ImageSharp.Web.Providers.Azure/ImageSharp.Web.Providers.Azure.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
</ItemGroup>
3232

3333
<ItemGroup>
34-
<PackageReference Include="Azure.Storage.Blobs" />
34+
<!--TODO: Do not upgrade this. Last Version that supports .NET Core 2.1-->
35+
<PackageReference Include="Azure.Storage.Blobs" Version="12.10.0" />
3536
</ItemGroup>
3637

3738
<ItemGroup>

src/ImageSharp.Web/Caching/PhysicalFileSystemCache.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ internal static string GetCacheRoot(PhysicalFileSystemCacheOptions cacheOptions,
8484
/// <inheritdoc/>
8585
public Task<IImageCacheResolver> GetAsync(string key)
8686
{
87-
string path = ToFilePath(key, this.cacheFolderDepth);
87+
string path = Path.Combine(this.cacheRootPath, ToFilePath(key, this.cacheFolderDepth));
8888

8989
var metaFileInfo = new FileInfo(this.ToMetaDataFilePath(path));
9090
if (!metaFileInfo.Exists)

src/ImageSharp.Web/FormattedImage.cs

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,17 @@ public sealed class FormattedImage : IDisposable
2727
/// </summary>
2828
/// <param name="image">The image.</param>
2929
/// <param name="format">The format.</param>
30-
internal FormattedImage(Image<Rgba32> image, IImageFormat format)
30+
internal FormattedImage(Image image, IImageFormat format)
3131
{
3232
this.Image = image;
3333
this.imageFormatsManager = image.GetConfiguration().ImageFormatsManager;
3434
this.Format = format;
3535
}
3636

3737
/// <summary>
38-
/// Gets the image.
38+
/// Gets the decoded image.
3939
/// </summary>
40-
public Image<Rgba32> Image { get; private set; }
40+
public Image Image { get; private set; }
4141

4242
/// <summary>
4343
/// Gets or sets the format.
@@ -82,41 +82,36 @@ public IImageEncoder Encoder
8282
}
8383

8484
/// <summary>
85-
/// Loads the specified source.
85+
/// Create a new instance of the <see cref="FormattedImage"/> class from the given stream.
8686
/// </summary>
87+
/// <typeparam name="TPixel">The pixel format.</typeparam>
8788
/// <param name="configuration">The configuration.</param>
8889
/// <param name="source">The source.</param>
89-
/// <returns>The <see cref="FormattedImage"/>.</returns>
90-
public static FormattedImage Load(Configuration configuration, Stream source)
90+
/// <returns>A <see cref="Task{FormattedImage}"/> representing the asynchronous operation.</returns>
91+
internal static async Task<FormattedImage> LoadAsync<TPixel>(Configuration configuration, Stream source)
92+
where TPixel : unmanaged, IPixel<TPixel>
9193
{
92-
var image = ImageSharp.Image.Load<Rgba32>(configuration, source, out IImageFormat format);
94+
(Image<TPixel> image, IImageFormat format) = await Image.LoadWithFormatAsync<TPixel>(configuration, source);
9395
return new FormattedImage(image, format);
9496
}
9597

9698
/// <summary>
97-
/// Loads the specified source.
99+
/// Create a new instance of the <see cref="FormattedImage"/> class from the given stream.
98100
/// </summary>
99101
/// <param name="configuration">The configuration.</param>
100102
/// <param name="source">The source.</param>
101103
/// <returns>A <see cref="Task{FormattedImage}"/> representing the asynchronous operation.</returns>
102-
public static async Task<FormattedImage> LoadAsync(Configuration configuration, Stream source)
104+
internal static async Task<FormattedImage> LoadAsync(Configuration configuration, Stream source)
103105
{
104-
(Image<Rgba32> image, IImageFormat format) = await ImageSharp.Image.LoadWithFormatAsync<Rgba32>(configuration, source);
106+
(Image image, IImageFormat format) = await Image.LoadWithFormatAsync(configuration, source);
105107
return new FormattedImage(image, format);
106108
}
107109

108110
/// <summary>
109-
/// Saves image to the specified destination stream.
110-
/// </summary>
111-
/// <param name="destination">The destination stream.</param>
112-
public void Save(Stream destination) => this.Image.Save(destination, this.encoder);
113-
114-
/// <summary>
115-
/// Saves image to the specified destination stream.
111+
/// Saves the <see cref="FormattedImage"/> to the specified destination stream.
116112
/// </summary>
117113
/// <param name="destination">The destination stream.</param>
118-
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
119-
public Task SaveAsync(Stream destination) => this.Image.SaveAsync(destination, this.encoder);
114+
internal void Save(Stream destination) => this.Image.Save(destination, this.encoder);
120115

121116
/// <summary>
122117
/// Gets the EXIF orientation metata for the <see cref="FormattedImage"/>.

src/ImageSharp.Web/ImageSharp.Web.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<AssemblyTitle>SixLabors.ImageSharp.Web</AssemblyTitle>
@@ -44,8 +44,8 @@
4444
</ItemGroup>
4545

4646
<ItemGroup>
47-
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" />
48-
<PackageReference Include="SixLabors.ImageSharp" />
47+
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.2.0" />
48+
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.0" />
4949
</ItemGroup>
5050

5151
<Import Project="..\..\shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems" Label="Shared" />

src/ImageSharp.Web/Middleware/ImageSharpMiddleware.cs

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using Microsoft.Extensions.Options;
1414
using Microsoft.IO;
1515
using SixLabors.ImageSharp.Formats;
16+
using SixLabors.ImageSharp.PixelFormats;
1617
using SixLabors.ImageSharp.Web.Caching;
1718
using SixLabors.ImageSharp.Web.Commands;
1819
using SixLabors.ImageSharp.Web.Processors;
@@ -334,19 +335,43 @@ private async Task ProcessRequestAsync(
334335
}
335336
else
336337
{
337-
using FormattedImage image = await FormattedImage.LoadAsync(this.options.Configuration, inStream);
338-
339-
image.Process(
340-
this.logger,
341-
this.processors,
342-
commands,
343-
this.commandParser,
344-
this.parserCulture);
345-
346-
await this.options.OnBeforeSaveAsync.Invoke(image);
347-
348-
image.Save(outStream);
349-
format = image.Format;
338+
FormattedImage image = null;
339+
try
340+
{
341+
// Now we can finally process the image.
342+
// We first sort the processor collection by command order then use that collection to determine whether the decoded image pixel format
343+
// explicitly requires an alpha component in order to allow correct processing.
344+
//
345+
// The non-generic variant will decode to the correct pixel format based upon the encoded image metadata which can yield
346+
// massive memory savings.
347+
IReadOnlyList<(int Index, IImageWebProcessor Processor)> sortedProcessors = this.processors.OrderBySupportedCommands(commands);
348+
bool requiresAlpha = sortedProcessors.RequiresTrueColorPixelFormat(commands, this.commandParser, this.parserCulture);
349+
350+
if (requiresAlpha)
351+
{
352+
image = await FormattedImage.LoadAsync<Rgba32>(this.options.Configuration, inStream);
353+
}
354+
else
355+
{
356+
image = await FormattedImage.LoadAsync(this.options.Configuration, inStream);
357+
}
358+
359+
image.Process(
360+
this.logger,
361+
sortedProcessors,
362+
commands,
363+
this.commandParser,
364+
this.parserCulture);
365+
366+
await this.options.OnBeforeSaveAsync.Invoke(image);
367+
368+
image.Save(outStream);
369+
format = image.Format;
370+
}
371+
finally
372+
{
373+
image?.Dispose();
374+
}
350375
}
351376
}
352377

0 commit comments

Comments
 (0)