-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Resizetizer: Allow custom backends to opt into image processing via ResizetizerPlatformType #34220
Description
Description
The Resizetizer's MSBuild targets hard-code a closed set of five platforms that are allowed to use its image processing pipeline. Custom community backends (macOS AppKit, GTK/Linux, etc.) are completely excluded with no supported way to hook in.
Two small changes — totaling ~25 lines — would enable custom backends to reuse Resizetizer's core SVG→PNG conversion and multi-density image generation, eliminating ~80% of the resource processing work they currently must re-implement from scratch.
Part of #34099
Problem 1: The _ResizetizerIsCompatibleApp Gate
In Microsoft.Maui.Resizetizer.After.targets, the entire processing pipeline is gated behind:
<PropertyGroup Condition="'$(_ResizetizerIsAndroidApp)' == 'True'
Or '$(_ResizetizerIsiOSApp)' == 'True'
Or '$(_ResizetizerIsWPFApp)' == 'True'
Or '$(_ResizetizerIsWindowsAppSdk)' == 'True'
Or '$(_ResizetizerIsTizenApp)' == 'True'">
<_ResizetizerIsCompatibleApp>True</_ResizetizerIsCompatibleApp>
</PropertyGroup>Custom backends cannot trigger ResizetizeImages, ProcessMauiFonts, ProcessMauiAssets, or any resource processing target because they don't match any of these five conditions. There is no public opt-in mechanism.
Note: The targets file already detects
macosandtvosplatform identifiers (_ResizetizerPlatformIsmacOS,_ResizetizerPlatformIstvOS) but never uses them to set_ResizetizerIsCompatibleApp.
Problem 2: DpiPath.GetDpis() Returns Null on Unknown Platforms
Even if a custom backend could get past the compatibility gate, the ResizetizeImages task would fail because DpiPath.GetDpis(platform) returns null for any platform not in its switch statement:
public static DpiPath[] GetDpis(string platform)
{
switch (platform.ToLowerInvariant())
{
case "ios": return Ios.Image;
case "android": return Android.Image;
case "uwp": return Windows.Image;
case "wpf": return Wpf.Image;
case "tizen": return Tizen.Image;
}
return null; // ← NullReferenceException downstream
}The same issue exists in GetAppIconDpis() and GetOriginal().
Proposed Solution
Change 1: Allow External Platform Opt-In (~5 lines in After.targets)
Add a property-based opt-in so that any backend setting ResizetizerPlatformType can trigger processing:
<!-- After the existing platform-specific PropertyGroup -->
<!-- Allow external backends to opt in by setting ResizetizerPlatformType -->
<PropertyGroup Condition="'$(_ResizetizerIsCompatibleApp)' != 'True'
And '$(ResizetizerPlatformType)' != ''">
<_ResizetizerIsCompatibleApp>True</_ResizetizerIsCompatibleApp>
</PropertyGroup>Custom backends would set this in their .targets file:
<PropertyGroup>
<ResizetizerPlatformType>macos-appkit</ResizetizerPlatformType>
</PropertyGroup>Change 2: Add Default DPI Fallback (~20 lines in DpiPath.cs)
Add a default case to the three methods in DpiPath.cs:
public static DpiPath[] GetDpis(string platform)
{
switch (platform.ToLowerInvariant())
{
case "ios": return Ios.Image;
case "android": return Android.Image;
case "uwp": return Windows.Image;
case "wpf": return Wpf.Image;
case "tizen": return Tizen.Image;
default:
// Generic desktop fallback: 1x and 2x
return new[]
{
new DpiPath("", 1.0m),
new DpiPath("", 2.0m, null, "@2x"),
};
}
}
public static DpiPath GetOriginal(string platform)
{
switch (platform.ToLowerInvariant())
{
// ... existing cases ...
default: return new DpiPath("", 1.0m);
}
}
public static DpiPath[] GetAppIconDpis(string platform, string appIconName)
{
// ... existing cases ...
default:
return new[]
{
new DpiPath("", 1.0m, null, null, new SKSize(16, 16)),
new DpiPath("", 1.0m, null, null, new SKSize(32, 32)),
new DpiPath("", 1.0m, null, null, new SKSize(48, 48)),
new DpiPath("", 1.0m, null, null, new SKSize(128, 128)),
new DpiPath("", 1.0m, null, null, new SKSize(256, 256)),
new DpiPath("", 1.0m, null, null, new SKSize(512, 512)),
new DpiPath("", 1.0m, null, null, new SKSize(1024, 1024)),
};
}The 1x + @2x default covers macOS Retina, GTK HiDPI, and any other desktop platform. Custom backends then handle the platform-specific "last mile" — placing files into the correct item groups (BundleResource for macOS, Content for GTK, etc.) via their own .targets files.
What This Enables
With just these two changes, a custom backend's .targets file can:
- Set
ResizetizerPlatformTypeto opt into image processing - Let
ResizetizeImagesrun — SVG→PNG conversion, tinting, multi-density generation all work - Consume outputs from
$(_MauiIntermediateImages)in their ownAfterTargetshook - Only implement the platform-specific output — which item groups to use, icon format generation, etc.
Example: macOS AppKit Backend Consuming Resizetizer Output
<!-- In Platform.Maui.MacOS.targets -->
<PropertyGroup>
<ResizetizerPlatformType>macos-appkit</ResizetizerPlatformType>
</PropertyGroup>
<!-- Resizetizer processes images into obj/.../resizetizer/r/ -->
<!-- We just need to bundle them: -->
<Target Name="_MacOSBundleProcessedImages"
AfterTargets="ResizetizeImages"
Condition="'@(_ResizetizerCollectedImages)' != ''">
<ItemGroup>
<BundleResource Include="@(_ResizetizerCollectedImages)">
<LogicalName>%(_ResizetizerCollectedImages.Filename)%(_ResizetizerCollectedImages.Extension)</LogicalName>
</BundleResource>
</ItemGroup>
</Target>Example: GTK Backend
<PropertyGroup>
<ResizetizerPlatformType>gtk</ResizetizerPlatformType>
</PropertyGroup>
<Target Name="_GtkBundleProcessedImages"
AfterTargets="ResizetizeImages"
Condition="'@(_ResizetizerCollectedImages)' != ''">
<ItemGroup>
<Content Include="@(_ResizetizerCollectedImages)">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>Resources\Images\%(_ResizetizerCollectedImages.Filename)%(_ResizetizerCollectedImages.Extension)</Link>
</Content>
</ItemGroup>
</Target>Impact
- Zero breaking changes — all existing platform behavior is unchanged
- ~25 lines of code total across 2 files
- Eliminates ~80% of custom backend resource processing work
- Consistent behavior — custom backends get the same SVG conversion, tinting, and density generation as official platforms
Files Changed
src/SingleProject/Resizetizer/src/nuget/buildTransitive/Microsoft.Maui.Resizetizer.After.targets— Add opt-in PropertyGroup (~5 lines)src/SingleProject/Resizetizer/src/DpiPath.cs— Adddefaultcases to 3 switch statements (~20 lines)