public async Task CreateThumbnailAsync(Stream source, string mimeType, Stream destination, ResizeOptions options, CancellationToken ct = default) { Guard.NotNull(source, nameof(source)); Guard.NotNull(destination, nameof(destination)); Guard.NotNull(options, nameof(options)); if (!options.IsValid) { await source.CopyToAsync(destination, ct); return; } var w = options.TargetWidth ?? 0; var h = options.TargetHeight ?? 0; using (var collection = new MagickImageCollection()) { await collection.ReadAsync(source, GetFormat(mimeType), ct); collection.Coalesce(); foreach (var image in collection) { var clone = image.Clone(); var color = options.ParseColor(); if (w > 0 || h > 0) { var isCropUpsize = options.Mode == ResizeMode.CropUpsize; var resizeMode = options.Mode; if (isCropUpsize) { resizeMode = ResizeMode.Crop; } if (w >= image.Width && h >= image.Height && resizeMode == ResizeMode.Crop && !isCropUpsize) { resizeMode = ResizeMode.BoxPad; } PointF?centerCoordinates = null; if (options.FocusX.HasValue && options.FocusY.HasValue) { centerCoordinates = new PointF( +(options.FocusX.Value / 2f) + 0.5f, -(options.FocusY.Value / 2f) + 0.5f ); } var(size, pad) = ResizeHelper.CalculateTargetLocationAndBounds(resizeMode, new Size(image.Width, image.Height), w, h, centerCoordinates); var sourceRectangle = new MagickGeometry(pad.Width, pad.Height) { IgnoreAspectRatio = true }; clone.Resize(sourceRectangle); image.Extent(size.Width, size.Height); image.CompositeClear(color); image.Composite(clone, pad.X, pad.Y, CompositeOperator.Over); } else { image.CompositeClear(color); image.Composite(clone, 0, 0, CompositeOperator.Over); } image.AutoOrient(); if (options.Quality.HasValue) { image.Quality = options.Quality.Value; } } var firstImage = collection[0]; var firstFormat = firstImage.Format; var targetFormat = options.GetFormat(firstFormat); await collection.WriteAsync(destination, targetFormat, ct); } }