Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions samples/ImageSharp.Web.Sample/Pages/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,29 @@
<img src="sixlabors.imagesharp.web.png" imagesharp-width="300" imagesharp-rsampler="Resampler.NearestNeighbor" />
</p>
</div>

</section>
<hr />
<section>
<h2>ICC Profiles</h2>
<div>
<p>
<code>issue_2723.jpg?width=2000</code>
</p>
<p>
<img src="issue_2723.jpg" imagesharp-width="2000" />
</p>
</div>
<div>
<p>
<code>issue_2723.jpg?width=2000&format=webp</code>
</p>
<p>
<img src="issue_2723.jpg" imagesharp-width="2000" imagesharp-format="Format.WebP" />
</p>
</div>
</section>

<hr />
<section>
<h2>Format</h2>
Expand Down
3 changes: 3 additions & 0 deletions samples/ImageSharp.Web.Sample/wwwroot/issue_2723.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 16 additions & 2 deletions src/ImageSharp.Web/Middleware/ImageSharpMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -344,8 +344,22 @@ private async Task ProcessRequestAsync(

await using (Stream inStream = await sourceImageResolver.OpenReadAsync())
{
DecoderOptions decoderOptions = await this.options.OnBeforeLoadAsync.Invoke(imageCommandContext, this.options.Configuration)
?? new() { Configuration = this.options.Configuration };
DecoderOptions decoderOptions =
await this.options.OnBeforeLoadAsync.Invoke(imageCommandContext, this.options.Configuration)

// If custom decoder options have not been provided and we know our options are the
// default options it is safe to configure the decoder options to convert color profiles
// since we know tha the JPEG encoder will always use YCbCr encoding instead of preserving the
// original color encoding (potentially CMYK, Ycck) which can cause color loss.
// Compaction is always safe as this simply removes sRGB color profile data from the image
// metadata which is not required for correct processing.
?? new()
{
Configuration = this.options.Configuration,
ColorProfileHandling = this.options.HasDefaultConfiguration
? ColorProfileHandling.Convert
: ColorProfileHandling.Compact
};

FormattedImage? image = null;
try
Expand Down
41 changes: 40 additions & 1 deletion src/ImageSharp.Web/Middleware/ImageSharpMiddlewareOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
using Microsoft.AspNetCore.Http;
using Microsoft.IO;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Web.Commands;
using SixLabors.ImageSharp.Web.Providers;

Expand All @@ -15,6 +18,8 @@ namespace SixLabors.ImageSharp.Web.Middleware;
/// </summary>
public class ImageSharpMiddlewareOptions
{
private static readonly Configuration DefaultConfiguration = CreateDefaultConfiguration();

private Func<ImageCommandContext, byte[], string> onComputeHMAC = (context, secret) =>
{
string uri = CaseHandlingUriBuilder.BuildRelative(
Expand All @@ -35,7 +40,12 @@ public class ImageSharpMiddlewareOptions
/// <summary>
/// Gets or sets the base library configuration.
/// </summary>
public Configuration Configuration { get; set; } = Configuration.Default;
public Configuration Configuration { get; set; } = DefaultConfiguration;

/// <summary>
/// Gets a value indicating whether the current configuration is the default configuration.
/// </summary>
internal bool HasDefaultConfiguration => ReferenceEquals(this.Configuration, DefaultConfiguration);

/// <summary>
/// Gets or sets the recyclable memorystream manager used for managing pooled stream
Expand Down Expand Up @@ -176,4 +186,33 @@ public Func<HttpContext, Task> OnPrepareResponseAsync
this.onPrepareResponseAsync = value;
}
}

private static Configuration CreateDefaultConfiguration()
{
// Build a Configuration for the requests that replaces the default JPEG, PNG, and WebP encoders
// with ones with compression options that are more suitable for web use.
// We do not skip metadata as that can affect things like orientation.
Configuration configuration = Configuration.Default.Clone();
configuration.ImageFormatsManager.SetEncoder(JpegFormat.Instance, new JpegEncoder()
{
Quality = 75,
Progressive = true,
Interleaved = true,
ColorType = JpegColorType.YCbCrRatio420,
});

configuration.ImageFormatsManager.SetEncoder(PngFormat.Instance, new PngEncoder()
{
CompressionLevel = PngCompressionLevel.BestCompression,
FilterMethod = PngFilterMethod.Adaptive,
});

configuration.ImageFormatsManager.SetEncoder(WebpFormat.Instance, new WebpEncoder()
{
Quality = 75,
Method = WebpEncodingMethod.BestQuality,
});

return configuration;
}
}
Loading