Skip to content

Commit

Permalink
Added Image DownScaler
Browse files Browse the repository at this point in the history
image down scaling integration
  • Loading branch information
skrasekmichael committed Dec 22, 2023
1 parent cf7e200 commit c8d438e
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,23 @@ public sealed partial class CreatePlaceViewModel : BaseViewModel
private readonly IOptions<ServerOptions> _serverOptions;
private readonly ModelMapper _modelMapper;
private readonly IMediator _mediator;
private readonly ImageDownScaler _imgDownScaler;

[ObservableProperty]
private PlaceDetailViewModel _placeDetailViewModel;

public CreatePlaceViewModel(ApiClient apiClient, INavigationService navigationService, LocalDbService localDbService, IAlertService alertService, PlaceDetailViewModel placeDetailViewModel, IOptions<ServerOptions> serverOptions, ModelMapper modelMapper, IMediator mediator)
public CreatePlaceViewModel(ApiClient apiClient, INavigationService navigationService, LocalDbService localDbService, IAlertService alertService, PlaceDetailViewModel placeDetailViewModel, IOptions<ServerOptions> serverOptions, ModelMapper modelMapper, IMediator mediator, ImageDownScaler imgDownScaler)
{
_apiClient = apiClient;
_navigationService = navigationService;
_localDbService = localDbService;
_alertService = alertService;
_serverOptions = serverOptions;
_modelMapper = modelMapper;
_mediator = mediator;
_imgDownScaler = imgDownScaler;

PlaceDetailViewModel = placeDetailViewModel;
_mediator = mediator;
}

protected override async Task LoadAsync(CancellationToken ct)
Expand Down Expand Up @@ -74,8 +76,7 @@ protected override async Task LoadAsync(CancellationToken ct)
return null;

using var stream = await photo.OpenReadAsync();
var buffer = new byte[stream.Length];
await stream.ReadAsync(buffer.AsMemory(0, (int)stream.Length), ct);
var buffer = await _imgDownScaler.ScaleDownAsync(stream, ct);
return buffer;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public static IServiceCollection AddBL(this IServiceCollection serviceCollection

return serviceCollection
.AddSingleton<EntityMapper>()
.AddSingleton(new ImageDownScaler(400 * 1024, [1.0, 0.5, 0.25, 0.18]))
.AddScoped<LocalDbService>();
}
}
86 changes: 86 additions & 0 deletions src/Client/ShareLoc.Client.BL/Services/ImageDownScaler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using SkiaSharp;

namespace ShareLoc.Client.BL.Services;

public sealed class ImageDownScaler
{
private readonly int _maxSize;
private readonly double[] _compressionLevels;

public ImageDownScaler(int maxSize, int runCount, double initialCompressionLevel)
{
_maxSize = maxSize;

_compressionLevels = new double[runCount];
_compressionLevels[0] = initialCompressionLevel;
for (int i = 1; i < runCount; i++)
{
_compressionLevels[i] = _compressionLevels[i - 1] / 2;
}
}

public ImageDownScaler(int maxSize, double[] compressionLevels)
{
_maxSize = maxSize;
_compressionLevels = compressionLevels;
}

public Task<byte[]> ScaleDownAsync(Stream imageStream, CancellationToken ct)
{
return Task.Run(() =>
{
var bufferCount = imageStream.Length;
if (bufferCount < _maxSize)
{
using var newImageStream = new MemoryStream(_maxSize);
imageStream.CopyToAsync(newImageStream, ct);
return newImageStream.ToArray();
}
using var image = SKImage.FromEncodedData(imageStream);
using var original = SKBitmap.FromImage(image);
byte[] imageBuffer = [];
foreach (var compressionLevel in _compressionLevels)
{
//size could be > max size
imageBuffer = ScaleDown(original, bufferCount, compressionLevel);
if (imageBuffer.Length > 0)
return imageBuffer;
}
//100% size < max size
return ScaleDown(original, bufferCount, null);
}, ct);
}

private byte[] ScaleDown(SKBitmap original, long bufferCount, double? compressionLevel)
{
var bytesPerPixel = original.BytesPerPixel;

var rawByteCount = original.Width * original.Height * bytesPerPixel;
var compressionRatio = compressionLevel switch
{
null => 1,
_ => (double)rawByteCount / bufferCount * compressionLevel
};

var newWidth2 = (double)(_maxSize * original.Width * compressionRatio) / (original.Height * bytesPerPixel);
var newWidth = (int)Math.Sqrt(newWidth2);
if (newWidth > original.Width)
return [];

var newHeight = newWidth * original.Height / original.Width;

using var newImageStream = new MemoryStream(_maxSize);
using var newBitmap = original.Resize(new SKImageInfo(newWidth, newHeight, original.ColorType, original.AlphaType, original.ColorSpace), SKFilterQuality.High);
using var image = SKImage.FromBitmap(newBitmap);
using var data = image.Encode(SKEncodedImageFormat.Png, 100);
data.SaveTo(newImageStream);

if (newImageStream.Length > _maxSize)
return [];

return newImageStream.ToArray();
}
}
1 change: 1 addition & 0 deletions src/Client/ShareLoc.Client.BL/ShareLoc.Client.BL.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="OneOf" Version="3.0.263" />
<PackageReference Include="Riok.Mapperly" Version="3.2.0" />
<PackageReference Include="SkiaSharp" Version="2.88.6" />
</ItemGroup>

<ItemGroup>
Expand Down

0 comments on commit c8d438e

Please sign in to comment.