Skip to content

Commit

Permalink
Image effects refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
Ruben2776 committed Jan 21, 2025
1 parent ebfd015 commit be8ef1b
Show file tree
Hide file tree
Showing 2 changed files with 199 additions and 218 deletions.
168 changes: 87 additions & 81 deletions src/PicView.Avalonia/ImageEffects/ImageEffectsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,107 +12,37 @@ public static async Task<WriteableBitmap> GetRadialBlur(string file)
{
using var magick = new MagickImage();
await magick.ReadAsync(file).ConfigureAwait(false);
// Apply radial blur
magick.AdaptiveBlur(10);
ApplyRadialBlur(magick);
return magick.ToWriteableBitmap();
}

// Create a new morphology settings
private static void ApplyRadialBlur(MagickImage magick)
{
magick.AdaptiveBlur(10);
var morphology = new MorphologySettings
{
Kernel = Kernel.DoG,
Method = MorphologyMethod.Convolve
};
magick.Morphology(morphology);
return magick.ToWriteableBitmap();
}

public static async Task ApplyEffects(MainViewModel vm, ImageEffectConfig config,
CancellationToken cancellationToken)
public static async Task ApplyEffects(MainViewModel vm, ImageEffectConfig config, CancellationToken cancellationToken)
{
vm.IsLoading = true;
try
{
await Task.Run(async () =>
{
var fileInfo = vm.FileInfo;
await using var filestream = FileHelper.GetOptimizedFileStream(fileInfo);
using var magick = new MagickImage();
if (fileInfo.Length >= 2147483648)
{
// Fixes "The file is too long. This operation is currently limited to supporting files less than 2 gigabytes in size."
// ReSharper disable once MethodHasAsyncOverloadWithCancellation
magick.Read(filestream);
}
else
{
await magick.ReadAsync(filestream, cancellationToken).ConfigureAwait(false);
}

cancellationToken.ThrowIfCancellationRequested();

magick.BrightnessContrast(config.Brightness, config.Contrast);
magick.BackgroundColor = MagickColors.Transparent;
magick.Settings.BackgroundColor = MagickColors.Transparent;
magick.Settings.FillColor = MagickColors.Transparent;

if (config.Negative)
{
magick.Negate();
}

if (config.BlackAndWhite)
{
magick.Grayscale();
}

if (config.OldMovie)
{
// 1. Apply sepia tone
magick.SepiaTone(new Percentage(80));

// 2. Add noise
magick.AddNoise(NoiseType.MultiplicativeGaussian);

var random = new Random();

// 3. Add vertical bands (simulate scratches)
for (var i = 0; i < magick.Width; i += random.Next(1, 50))
{
using var band = new MagickImage(new MagickColor("#3E382A"), (uint)random.Next(1, 3),
magick.Height);
band.Evaluate(Channels.Alpha, EvaluateOperator.Set, 0.2); // semi-transparent
magick.Composite(band, i, 0, CompositeOperator.Over);
}
}

if (config.SketchStrokeWidth is not 0)
{
magick.Charcoal(config.SketchStrokeWidth, 3);
}

if (config.PosterizeLevel is not 0)
{
magick.Posterize(config.PosterizeLevel);
}

if (config.BlurLevel is not 0)
{
magick.Blur(0, config.BlurLevel);
}

if (config.Solarize.ToUInt32() is not 0)
{
magick.Solarize(config.Solarize);
}

cancellationToken.ThrowIfCancellationRequested();

using var magick = await LoadImage(vm.FileInfo, cancellationToken);
ApplyImageEffects(magick, config, cancellationToken);
var bitmap = magick.ToWriteableBitmap();
vm.ImageSource = bitmap;
}, cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
//
// Operation was canceled
}
catch (Exception e)
{
Expand All @@ -125,4 +55,80 @@ await Task.Run(async () =>
vm.IsLoading = false;
}
}
}

private static async Task<MagickImage> LoadImage(FileInfo fileInfo, CancellationToken cancellationToken)
{
await using var filestream = FileHelper.GetOptimizedFileStream(fileInfo);
var magick = new MagickImage();
if (fileInfo.Length >= 2147483648)
{
// ReSharper disable once MethodHasAsyncOverloadWithCancellation
magick.Read(filestream);
}
else
{
await magick.ReadAsync(filestream, cancellationToken).ConfigureAwait(false);
}
return magick;
}

private static void ApplyImageEffects(MagickImage magick, ImageEffectConfig config, CancellationToken cancellationToken)
{
magick.BrightnessContrast(config.Brightness, config.Contrast);
magick.BackgroundColor = MagickColors.Transparent;
magick.Settings.BackgroundColor = MagickColors.Transparent;
magick.Settings.FillColor = MagickColors.Transparent;

if (config.Negative) ApplyNegative(magick);
if (config.BlackAndWhite) ApplyBlackAndWhite(magick);
if (config.OldMovie) ApplyOldMovieEffect(magick);
if (config.SketchStrokeWidth != 0) ApplyPencilSketch(magick, config.SketchStrokeWidth);
if (config.PosterizeLevel != 0) ApplyPosterize(magick, config.PosterizeLevel);
if (config.BlurLevel != 0) ApplyBlur(magick, config.BlurLevel);
if (config.Solarize.ToUInt32() != 0) ApplySolarize(magick, config.Solarize);

cancellationToken.ThrowIfCancellationRequested();
}

private static void ApplyNegative(MagickImage magick) => magick.Negate();

private static void ApplyBlackAndWhite(MagickImage magick) => magick.Grayscale();

private static void ApplyOldMovieEffect(MagickImage magick)
{
magick.SepiaTone(new Percentage(80));
magick.AddNoise(NoiseType.MultiplicativeGaussian);
AddVerticalBands(magick);
}

private static void AddVerticalBands(MagickImage magick)
{
var random = new Random();
for (var i = 0; i < magick.Width; i += random.Next(1, 50))
{
using var band = new MagickImage(new MagickColor("#3E382A"), (uint)random.Next(1, 3), magick.Height);
band.Evaluate(Channels.Alpha, EvaluateOperator.Set, 0.2);
magick.Composite(band, i, 0, CompositeOperator.Over);
}
}

private static void ApplyPencilSketch(MagickImage magick, double sketchStrokeWidth)
{
magick.Charcoal(sketchStrokeWidth, 3);
}

private static void ApplyPosterize(MagickImage magick, int posterizeLevel)
{
magick.Posterize(posterizeLevel);
}

private static void ApplyBlur(MagickImage magick, double blurLevel)
{
magick.Blur(0, blurLevel);
}

private static void ApplySolarize(MagickImage magick, Percentage solarize)
{
magick.Solarize(solarize);
}
}
Loading

0 comments on commit be8ef1b

Please sign in to comment.