From 01521661c261ce9b8fa9b54e6a5742be9bd0fbb1 Mon Sep 17 00:00:00 2001 From: Pink Serenity Date: Sat, 22 Nov 2025 12:35:57 +0100 Subject: [PATCH] Add PatchMode.Mask --- .../Framework/Content/AssetDataForImage.cs | 51 ++++++++++++++----- src/SMAPI/PatchMode.cs | 5 +- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/SMAPI/Framework/Content/AssetDataForImage.cs b/src/SMAPI/Framework/Content/AssetDataForImage.cs index f59e73bea..f6c1e46a6 100644 --- a/src/SMAPI/Framework/Content/AssetDataForImage.cs +++ b/src/SMAPI/Framework/Content/AssetDataForImage.cs @@ -213,22 +213,47 @@ private void PatchImageImpl(Color[] sourceData, int sourceWidth, int sourceHeigh // shortcut transparency if (above.A < AssetDataForImage.MinOpacity) continue; - if (below.A < AssetDataForImage.MinOpacity || above.A == byte.MaxValue) - mergedData[targetIndex] = above; - // merge pixels + if (patchMode == PatchMode.Overlay) + { + if (below.A < AssetDataForImage.MinOpacity || above.A == byte.MaxValue) + mergedData[targetIndex] = above; + + // merge pixels + else + { + // This performs a conventional alpha blend for the pixels, which are already + // premultiplied by the content pipeline. The formula is derived from + // https://blogs.msdn.microsoft.com/shawnhar/2009/11/06/premultiplied-alpha/. + float alphaBelow = 1 - (above.A / 255f); + mergedData[targetIndex] = new Color( + r: (int)(above.R + (below.R * alphaBelow)), + g: (int)(above.G + (below.G * alphaBelow)), + b: (int)(above.B + (below.B * alphaBelow)), + alpha: Math.Max(above.A, below.A) + ); + } + } else { - // This performs a conventional alpha blend for the pixels, which are already - // premultiplied by the content pipeline. The formula is derived from - // https://blogs.msdn.microsoft.com/shawnhar/2009/11/06/premultiplied-alpha/. - float alphaBelow = 1 - (above.A / 255f); - mergedData[targetIndex] = new Color( - r: (int)(above.R + (below.R * alphaBelow)), - g: (int)(above.G + (below.G * alphaBelow)), - b: (int)(above.B + (below.B * alphaBelow)), - alpha: Math.Max(above.A, below.A) - ); + // compute new alpha by subtracting mask alpha + int newAlphaInt = below.A - above.A; + + // fully masked out + if (newAlphaInt <= 0) + mergedData[targetIndex] = new Color((byte)0, (byte)0, (byte)0, (byte)0); + + else + { + // This blends the pixels by removing the alpha defined in the mask. + float scale = (float)newAlphaInt / below.A; + mergedData[targetIndex] = new Color( + r: (int)Math.Clamp(Math.Round(below.R * scale), 0, 255), + g: (int)Math.Clamp(Math.Round(below.G * scale), 0, 255), + b: (int)Math.Clamp(Math.Round(below.B * scale), 0, 255), + alpha: newAlphaInt + ); + } } } diff --git a/src/SMAPI/PatchMode.cs b/src/SMAPI/PatchMode.cs index 8575e5d0b..669a0f035 100644 --- a/src/SMAPI/PatchMode.cs +++ b/src/SMAPI/PatchMode.cs @@ -7,5 +7,8 @@ public enum PatchMode Replace, /// Draw the new content over the original content, so the original content shows through any transparent or semi-transparent pixels. - Overlay + Overlay, + + /// Masks the original content so that the alpha value of every pixel in the new content gets subtracted from the corresponding pixel in the original content. + Mask }