Skip to content
Closed
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
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
uses: microsoft/setup-msbuild@v2

- name: Restore dependencies
run: dotnet restore "Simple Icon File Maker.sln"
run: msbuild "Simple Icon File Maker.sln" /t:Restore /p:Configuration=Release /p:Platform=x64

- name: Build solution
run: dotnet build "Simple Icon File Maker.sln" --configuration Release --no-restore
run: msbuild "Simple Icon File Maker.sln" /p:Configuration=Release /p:Platform=x64
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>

<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">
xmlns:systemai="http://schemas.microsoft.com/appx/manifest/systemai/windows10"
IgnorableNamespaces="uap rescap systemai">

<Identity
Name="40087JoeFinApps.SimpleIconFileMaker"
Publisher="CN=153F3B0F-BA3D-4964-8098-71AC78A1DF6A"
Version="1.14.0.0" />
Version="1.15.0.0" />


<Properties>
<DisplayName>Simple Icon File Maker</DisplayName>
<PublisherDisplayName>JoeFinApps</PublisherDisplayName>
<Logo>Images\StoreLogo.png</Logo>
</Properties>

<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
</Dependencies>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.26226.0" />
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.26226.0" />
</Dependencies>

<Resources>
<Resource Language="x-generate"/>
Expand Down Expand Up @@ -57,11 +59,24 @@
<uap:Logo>Images\Image128.png</uap:Logo>
</uap:FileTypeAssociation>
</uap:Extension>
<uap:Extension Category="windows.shareTarget">
<uap:ShareTarget>
<uap:SupportedFileTypes>
<uap:FileType>.png</uap:FileType>
<uap:FileType>.jpg</uap:FileType>
<uap:FileType>.jpeg</uap:FileType>
<uap:FileType>.bmp</uap:FileType>
<uap:FileType>.ico</uap:FileType>
</uap:SupportedFileTypes>
<uap:DataFormat>Bitmap</uap:DataFormat>
</uap:ShareTarget>
</uap:Extension>
</Extensions>
</Application>
</Applications>

<Capabilities>
<rescap:Capability Name="runFullTrust" />
<systemai:Capability Name="systemAIModels" />
</Capabilities>
</Package>
</Package>
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
<Import Project="$(WapProjPath)\Microsoft.DesktopBridge.props" />
<PropertyGroup>
<ProjectGuid>7887a19f-b1cd-4106-a9aa-abaacfe770a9</ProjectGuid>
<TargetPlatformVersion>10.0.22621.0</TargetPlatformVersion>
<TargetPlatformVersion>10.0.26100.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
<AssetTargetFallback>net9.0-windows$(TargetPlatformVersion);$(AssetTargetFallback)</AssetTargetFallback>
Expand All @@ -51,6 +51,8 @@
<GenerateTemporaryStoreCertificate>True</GenerateTemporaryStoreCertificate>
<HoursBetweenUpdateChecks>0</HoursBetweenUpdateChecks>
<AppxPackageSigningTimestampDigestAlgorithm>SHA256</AppxPackageSigningTimestampDigestAlgorithm>
<AppxOSMinVersionReplaceManifestVersion>false</AppxOSMinVersionReplaceManifestVersion>
<AppxOSMaxVersionTestedReplaceManifestVersion>false</AppxOSMaxVersionTestedReplaceManifestVersion>
<PublishAot>true</PublishAot>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'">
Expand Down Expand Up @@ -120,10 +122,10 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002">
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.260209005">
<IncludeAssets>build</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7175">
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7705">
<IncludeAssets>build</IncludeAssets>
</PackageReference>
<PackageReference Include="WinUIEx" Version="2.9.0" />
Expand Down
66 changes: 64 additions & 2 deletions Simple Icon File Maker/Simple Icon File Maker/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.UI.Xaml;
using Microsoft.Windows.AppLifecycle;
using Simple_Icon_File_Maker.Activation;
using Simple_Icon_File_Maker.Contracts.Services;
using Simple_Icon_File_Maker.Models;
using Simple_Icon_File_Maker.Services;
using Simple_Icon_File_Maker.ViewModels;
using Simple_Icon_File_Maker.Views;
using Windows.ApplicationModel.DataTransfer;
using Windows.ApplicationModel.DataTransfer.ShareTarget;
using Windows.Storage;
using Windows.Storage.Streams;

namespace Simple_Icon_File_Maker;

Expand Down Expand Up @@ -37,6 +42,8 @@ public static T GetService<T>()

public static UIElement? AppTitlebar { get; set; }

public static string? SharedImagePath { get; set; }

public App()
{
InitializeComponent();
Expand Down Expand Up @@ -93,8 +100,63 @@ private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledEx
protected override async void OnLaunched(LaunchActivatedEventArgs args)
{
base.OnLaunched(args);

var activatedEventArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
if (activatedEventArgs.Kind == ExtendedActivationKind.ShareTarget)
{
await HandleShareTargetActivationAsync(activatedEventArgs);
}

await App.GetService<IActivationService>().ActivateAsync(args);
}

private static async Task HandleShareTargetActivationAsync(AppActivationArguments activatedEventArgs)
{
if (activatedEventArgs.Data is Windows.ApplicationModel.Activation.IShareTargetActivatedEventArgs shareArgs)
{
ShareOperation shareOperation = shareArgs.ShareOperation;
shareOperation.ReportStarted();

try
{
if (shareOperation.Data.Contains(StandardDataFormats.StorageItems))
{
IReadOnlyList<IStorageItem> items = await shareOperation.Data.GetStorageItemsAsync();
foreach (IStorageItem item in items)
{
if (item is StorageFile file && Constants.FileTypes.SupportedImageFormats.Contains(file.FileType, StringComparer.OrdinalIgnoreCase))
{
StorageFolder tempFolder = ApplicationData.Current.TemporaryFolder;
StorageFile copiedFile = await file.CopyAsync(tempFolder, file.Name, NameCollisionOption.GenerateUniqueName);
SharedImagePath = copiedFile.Path;
break;
}
}
}
else if (shareOperation.Data.Contains(StandardDataFormats.Bitmap))
{
var bitmapRef = await shareOperation.Data.GetBitmapAsync();
var stream = await bitmapRef.OpenReadAsync();

StorageFolder tempFolder = ApplicationData.Current.TemporaryFolder;
StorageFile tempFile = await tempFolder.CreateFileAsync("shared_image.png", CreationCollisionOption.GenerateUniqueName);

using (var outputStream = await tempFile.OpenAsync(FileAccessMode.ReadWrite))
{
await RandomAccessStream.CopyAsync(stream, outputStream);
}

SharedImagePath = tempFile.Path;
}
}
catch (Exception)
{
// If share handling fails, continue with normal launch
}

shareOperation.ReportCompleted();
}
}

public static string[]? cliArgs { get; } = Environment.GetCommandLineArgs();
}
}
3 changes: 0 additions & 3 deletions Simple Icon File Maker/Simple Icon File Maker/FodyWeavers.xml

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using Microsoft.Windows.AI;
using Microsoft.Windows.AI.Imaging;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Graphics;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Streams;

namespace Simple_Icon_File_Maker.Helpers;

public static class BackgroundRemoverHelper
{
public static async Task<bool> IsAvailableAsync()
{
try
{
AIFeatureReadyState readyState = ImageObjectExtractor.GetReadyState();

if (readyState == AIFeatureReadyState.Ready)
return true;

if (readyState == AIFeatureReadyState.NotReady)
{
var result = await ImageObjectExtractor.EnsureReadyAsync();
return result.Status == AIFeatureReadyResultState.Success;
}

// NotSupportedOnCurrentSystem or DisabledByUser
return false;
}
catch
{
return false;
}
}

public static async Task<string> RemoveBackgroundAsync(string imagePath)
{
StorageFile inputFile = await StorageFile.GetFileFromPathAsync(imagePath);
SoftwareBitmap sourceBitmap;

using (IRandomAccessStream stream = await inputFile.OpenAsync(FileAccessMode.Read))
{
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
sourceBitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
}

using ImageObjectExtractor extractor = await ImageObjectExtractor.CreateWithSoftwareBitmapAsync(sourceBitmap);

// Hint with the entire image rect as the region of interest
ImageObjectExtractorHint hint = new(
includeRects: [new RectInt32(0, 0, sourceBitmap.PixelWidth, sourceBitmap.PixelHeight)],
includePoints: [],
excludePoints: []);

SoftwareBitmap mask = extractor.GetSoftwareBitmapObjectMask(hint);

SoftwareBitmap resultBitmap = ApplyMaskAsAlpha(sourceBitmap, mask);

StorageFolder cacheFolder = ApplicationData.Current.LocalCacheFolder;
string fileName = Path.GetFileNameWithoutExtension(imagePath);
string outputFileName = $"{fileName}_nobg.png";
StorageFile outputFile = await cacheFolder.CreateFileAsync(outputFileName, CreationCollisionOption.ReplaceExisting);

using (IRandomAccessStream outputStream = await outputFile.OpenAsync(FileAccessMode.ReadWrite))
{
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, outputStream);
encoder.SetSoftwareBitmap(resultBitmap);
await encoder.FlushAsync();
}

return outputFile.Path;
}

private static SoftwareBitmap ApplyMaskAsAlpha(SoftwareBitmap original, SoftwareBitmap mask)
{
int width = original.PixelWidth;
int height = original.PixelHeight;

SoftwareBitmap gray = mask.BitmapPixelFormat == BitmapPixelFormat.Gray8
? mask
: SoftwareBitmap.Convert(mask, BitmapPixelFormat.Gray8);

byte[] originalPixels = new byte[4 * width * height];
byte[] maskPixels = new byte[width * height];
original.CopyToBuffer(originalPixels.AsBuffer());
gray.CopyToBuffer(maskPixels.AsBuffer());

byte[] resultPixels = new byte[4 * width * height];
for (int i = 0; i < maskPixels.Length; i++)
{
int px = i * 4;
int m = 255 - maskPixels[i];
resultPixels[px + 0] = (byte)(originalPixels[px + 0] * m / 255);
resultPixels[px + 1] = (byte)(originalPixels[px + 1] * m / 255);
resultPixels[px + 2] = (byte)(originalPixels[px + 2] * m / 255);
resultPixels[px + 3] = (byte)(originalPixels[px + 3] * m / 255);
}

SoftwareBitmap result = new(BitmapPixelFormat.Bgra8, width, height, BitmapAlphaMode.Premultiplied);
result.CopyFromBuffer(resultPixels.AsBuffer());
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public static async Task TrySetSuggestedFolderFromSourceImage(FileSavePicker sav
if (File.Exists(imagePath))
{
StorageFile sourceFile = await StorageFile.GetFileFromPathAsync(imagePath);
string name = Path.GetFileNameWithoutExtension(imagePath);
await sourceFile.RenameAsync(name);
savePicker.SuggestedSaveFile = sourceFile;
}
}
Expand All @@ -25,4 +27,4 @@ public static async Task TrySetSuggestedFolderFromSourceImage(FileSavePicker sav
// If file access fails, fall back to default picker behavior
}
}
}
}
26 changes: 14 additions & 12 deletions Simple Icon File Maker/Simple Icon File Maker/Models/IconSize.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
using System.ComponentModel;
using CommunityToolkit.Mvvm.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace Simple_Icon_File_Maker.Models;

[DebuggerDisplay("SideLength = {SideLength}, IsSelected = {IsSelected}")]
public partial class IconSize : INotifyPropertyChanged, IEquatable<IconSize>
public partial class IconSize : ObservableObject, IEquatable<IconSize>
{
public int SideLength { get; set; }
public bool IsSelected { get; set; } = true;
[ObservableProperty]
public partial int SideLength { get; set; }

public bool IsEnabled { get; set; } = true;
[ObservableProperty]
public partial bool IsSelected { get; set; } = true;

public bool IsHidden { get; set; } = false;
[ObservableProperty]
public partial bool IsEnabled { get; set; } = true;

public int Order { get; set; } = 0;
[ObservableProperty]
public partial bool IsHidden { get; set; } = false;

[ObservableProperty]
public partial int Order { get; set; } = 0;

public string Tooltip => $"{SideLength} x {SideLength}";

Expand Down Expand Up @@ -53,10 +59,6 @@ public IconSize(IconSize iconSize)
Order = iconSize.Order;
}

#pragma warning disable CS0067 // The event 'IconSize.PropertyChanged' is never used
public event PropertyChangedEventHandler? PropertyChanged;
#pragma warning restore CS0067 // The event 'IconSize.PropertyChanged' is never used

public static IconSize[] GetAllSizes()
{
return
Expand Down Expand Up @@ -165,4 +167,4 @@ public int GetHashCode([DisallowNull] IconSize obj)
{
return obj.GetHashCode();
}
}
}
Loading