diff --git a/src/SMAPI/Framework/Content/AssetDataForImage.cs b/src/SMAPI/Framework/Content/AssetDataForImage.cs
index f59e73bea..e27dc1cab 100644
--- a/src/SMAPI/Framework/Content/AssetDataForImage.cs
+++ b/src/SMAPI/Framework/Content/AssetDataForImage.cs
@@ -205,30 +205,53 @@ private void PatchImageImpl(Color[] sourceData, int sourceWidth, int sourceHeigh
for (int i = startIndex; i <= endIndex; i++)
{
- int targetIndex = i - sourceOffset;
-
+ // get source pixel
Color above = sourceData[i];
- Color below = mergedData[targetIndex];
-
- // shortcut transparency
if (above.A < AssetDataForImage.MinOpacity)
continue;
- if (below.A < AssetDataForImage.MinOpacity || above.A == byte.MaxValue)
- mergedData[targetIndex] = above;
- // merge pixels
+ // get target pixel
+ int targetIndex = i - sourceOffset;
+ Color below = mergedData[targetIndex];
+
+ // apply
+ if (patchMode == PatchMode.Overlay)
+ {
+ // merge pixels
+ if (below.A < AssetDataForImage.MinOpacity || above.A == byte.MaxValue)
+ mergedData[targetIndex] = above;
+ 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)
- );
+ // subtract mask alpha
+ int newAlpha = below.A - above.A;
+ if (newAlpha <= 0)
+ mergedData[targetIndex] = Color.Transparent;
+ else
+ {
+ // Since the pixels are already premultiplied by the pipeline based on the
+ // alpha, rescale the RGB channels too to match the new alpha.
+ float scale = (float)newAlpha / 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: newAlpha
+ );
+ }
}
}
diff --git a/src/SMAPI/PatchMode.cs b/src/SMAPI/PatchMode.cs
index 8575e5d0b..352df15c8 100644
--- a/src/SMAPI/PatchMode.cs
+++ b/src/SMAPI/PatchMode.cs
@@ -7,5 +7,9 @@ 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,
+
+ /// Apply the new content over the original content as a transparency mask.
+ /// This subtracts the alpha value of each pixel in the new content from the corresponding pixel in the original content. Colors in the new content are ignored. For example, a fully opaque pixel in the new content will result in a fully transparent pixel in the final image.
+ Mask
}