Skip to content
Open
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
90 changes: 90 additions & 0 deletions Content.Tests/DMProject/Tests/Icon/IconBlends.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/proc/RunTest()
// ICON_ADD
var/icon/A = icon('hanoi.dmi',"reddot")
var/icon/B = icon('hanoi.dmi',"bluedot")
A.Blend(B, ICON_ADD)
var/list/target_pixels = list(null,null,null,null,null,null,null,null,null,null,"#ff19ff","#ff19ff","#ff19ff","#ff19ff",null,null,null,null,"#ff19ff","#ff19ff","#ff19ff","#ff19ff",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"#ff19ff","#ff19ff","#ff19ff","#ff19ff",null,null,null,null,"#ff19ff","#ff19ff","#ff19ff","#ff19ff",null,null,null,null,null,null,null,null,null,null)
var/list/actual_pixels = list()
var/matching = TRUE
for(var/x in 1 to 32)
matching &= (A.GetPixel(x,x) == target_pixels[x])
actual_pixels += A.GetPixel(x,x)
if(!matching)
CRASH("ICON_ADD did not match, expected [json_encode(target_pixels)] but got [json_encode(actual_pixels)]")

// ICON_SUBTRACT
A = icon('hanoi.dmi',"reddot")
B = icon('hanoi.dmi',"bluedot")
A.Blend(B, ICON_SUBTRACT)
target_pixels = list(null,null,null,null,null,null,null,null,null,null,"#741900","#741900","#741900","#741900",null,null,null,null,"#ff1900","#ff1900","#ff1900","#ff1900",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"#741900","#741900","#741900","#741900",null,null,null,null,"#ff1900","#ff1900","#ff1900","#ff1900",null,null,null,null,null,null,null,null,null,null)
actual_pixels = list()
matching = TRUE
for(var/x in 1 to 32)
matching &= (A.GetPixel(x,x) == target_pixels[x])
actual_pixels += A.GetPixel(x,x)
if(!matching)
CRASH("ICON_SUBTRACT did not match, expected [json_encode(target_pixels)] but got [json_encode(actual_pixels)]")

// ICON_MULTIPLY
A = icon('hanoi.dmi',"reddot")
B = icon('hanoi.dmi',"bluedot")
A.Blend(B, ICON_MULTIPLY)
target_pixels = list(null,null,null,null,null,null,null,null,null,null,"#8b0035","#8b0035","#8b0035","#8b0035",null,null,null,null,"#000035","#000035","#000035","#000035",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"#8b0035","#8b0035","#8b0035","#8b0035",null,null,null,null,"#000035","#000035","#000035","#000035",null,null,null,null,null,null,null,null,null,null)
actual_pixels = list()
matching = TRUE
for(var/x in 1 to 32)
matching &= (A.GetPixel(x,x) == target_pixels[x])
actual_pixels += A.GetPixel(x,x)
if(!matching)
CRASH("ICON_MULTIPLY did not match, expected [json_encode(target_pixels)] but got [json_encode(actual_pixels)]")

// ICON_OVERLAY
A = icon('hanoi.dmi',"reddot")
B = icon('hanoi.dmi',"bluedot")
A.Blend(B, ICON_OVERLAY)
target_pixels = list(null,null,null,"#8b00ff","#8b00ff","#8b00ff","#8b00ff","#8b00ff","#8b00ff","#8b00ff","#8b00ff","#8b00ff","#8b00ff","#8b00ff","#ff1935","#ff1935","#ff1935","#ff1935","#0000ff","#0000ff","#0000ff","#0000ff","#0000ff","#0000ff","#0000ff","#0000ff","#0000ff","#0000ff","#0000ff",null,null,null)
actual_pixels = list()
for(var/x in 1 to 32)
matching &= (A.GetPixel(x,x) == target_pixels[x])
actual_pixels += A.GetPixel(x,x)
if(!matching)
CRASH("ICON_OVERLAY did not match, expected [json_encode(target_pixels)] but got [json_encode(actual_pixels)]")

// ICON_AND
A = icon('hanoi.dmi',"reddot")
B = icon('hanoi.dmi',"bluedot")
A.Blend(B, ICON_AND)
target_pixels = list(null,null,null,null,null,null,null,null,null,null,"#ff19ff","#ff19ff","#ff19ff","#ff19ff",null,null,null,null,"#ff19ff","#ff19ff","#ff19ff","#ff19ff",null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,"#ff19ff","#ff19ff","#ff19ff","#ff19ff",null,null,null,null,"#ff19ff","#ff19ff","#ff19ff","#ff19ff",null,null,null,null,null,null,null,null,null,null)
actual_pixels = list()
matching = TRUE
for(var/x in 1 to 32)
matching &= (A.GetPixel(x,x) == target_pixels[x])
actual_pixels += A.GetPixel(x,x)
if(!matching)
CRASH("ICON_AND did not match, expected [json_encode(target_pixels)] but got [json_encode(actual_pixels)]")

// ICON_OR
A = icon('hanoi.dmi',"reddot")
B = icon('hanoi.dmi',"bluedot")
A.Blend(B, ICON_OR)
target_pixels = list(null,null,null,"#8b00ff","#8b00ff","#8b00ff","#8b00ff","#8b00ff","#8b00ff","#8b00ff","#ff19ff","#ff19ff","#ff19ff","#ff19ff","#ff1935","#ff1935","#ff1935","#ff1935","#ff19ff","#ff19ff","#ff19ff","#ff19ff","#0000ff","#0000ff","#0000ff","#0000ff","#0000ff","#0000ff","#0000ff",null,null,null,null,null,null,"#8b00ff","#8b00ff","#8b00ff","#8b00ff","#8b00ff","#8b00ff","#8b00ff","#ff19ff","#ff19ff","#ff19ff","#ff19ff","#ff1935","#ff1935","#ff1935","#ff1935","#ff19ff","#ff19ff","#ff19ff","#ff19ff","#0000ff","#0000ff","#0000ff","#0000ff","#0000ff","#0000ff","#0000ff",null,null,null)
actual_pixels = list()
matching = TRUE
for(var/x in 1 to 32)
matching &= (A.GetPixel(x,x) == target_pixels[x])
actual_pixels += A.GetPixel(x,x)
if(!matching)
CRASH("ICON_OR did not match, expected [json_encode(target_pixels)] but got [json_encode(actual_pixels)]")

// ICON_UNDERLAY
A = icon('hanoi.dmi',"reddot")
B = icon('hanoi.dmi',"bluedot")
A.Blend(B, ICON_UNDERLAY)
target_pixels = list(null,null,null,"#8b00ff","#8b00ff","#8b00ff","#8b00ff","#8b00ff","#8b00ff","#8b00ff","#ff1935","#ff1935","#ff1935","#ff1935","#ff1935","#ff1935","#ff1935","#ff1935","#ff1935","#ff1935","#ff1935","#ff1935","#0000ff","#0000ff","#0000ff","#0000ff","#0000ff","#0000ff","#0000ff",null,null,null,null,null,null,"#8b00ff","#8b00ff","#8b00ff","#8b00ff","#8b00ff","#8b00ff","#8b00ff","#ff1935","#ff1935","#ff1935","#ff1935","#ff1935","#ff1935","#ff1935","#ff1935","#ff1935","#ff1935","#ff1935","#ff1935","#0000ff","#0000ff","#0000ff","#0000ff","#0000ff","#0000ff","#0000ff",null,null,null)
actual_pixels = list()
matching = TRUE
for(var/x in 1 to 32)
matching &= (A.GetPixel(x,x) == target_pixels[x])
actual_pixels += A.GetPixel(x,x)
if(!matching)
CRASH("ICON_UNDERLAY did not match, expected [json_encode(target_pixels)] but got [json_encode(actual_pixels)]")
83 changes: 83 additions & 0 deletions Content.Tests/DMProject/Tests/Icon/IconBlendsColor.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/proc/RunTest()
// ICON_ADD
var/icon/B = icon('hanoi.dmi',"gradient")
B.Blend(rgb(200, 10, 0, 170), ICON_ADD)
var/list/target_pixels = list("#c80affaa","#c80affaa","#c80affa9","#c80aff9f","#c80aff95","#c80aff8c","#c80aff83","#c80aff7b","#c80aff73","#c80aff6c","#c80aff65","#c80aff5f","#c80aff58","#c80aff52","#c80aff4d","#c80aff47","#c80aff42","#c80aff3d","#c80aff39","#c80aff34","#c80aff30","#c80aff2c","#c80aff28","#c80aff25","#c80aff21","#c80aff1f","#c80aff1b","#c80aff19","#c80aff16","#c80aff14","#c80aff11","#c80aff0f")
var/list/actual_pixels = list()
var/matching = TRUE
for(var/x in 1 to 32)
matching &= (B.GetPixel(x,x) == target_pixels[x])
actual_pixels += B.GetPixel(x,x)
if(!matching)
CRASH("ICON_ADD did not match, expected [json_encode(target_pixels)] but got [json_encode(actual_pixels)]")

// ICON_SUBTRACT
B = icon('hanoi.dmi',"gradient")
B.Blend(rgb(200,10,0,170), ICON_SUBTRACT)
target_pixels = list("#0000ffaa","#0000ffaa","#0000ffa9","#0000ff9f","#0000ff95","#0000ff8c","#0000ff83","#0000ff7b","#0000ff73","#0000ff6c","#0000ff65","#0000ff5f","#0000ff58","#0000ff52","#0000ff4d","#0000ff47","#0000ff42","#0000ff3d","#0000ff39","#0000ff34","#0000ff30","#0000ff2c","#0000ff28","#0000ff25","#0000ff21","#0000ff1f","#0000ff1b","#0000ff19","#0000ff16","#0000ff14","#0000ff11","#0000ff0f")
actual_pixels = list()
matching = TRUE
for(var/x in 1 to 32)
matching &= (B.GetPixel(x,x) == target_pixels[x])
actual_pixels += B.GetPixel(x,x)
if(!matching)
CRASH("ICON_SUBTRACT did not match, expected [json_encode(target_pixels)] but got [json_encode(actual_pixels)]")

// ICON_MULTIPLY
B = icon('hanoi.dmi',"gradient")
B.Blend(rgb(200,10,0,170), ICON_MULTIPLY)
target_pixels = list("#000000aa","#000000aa","#000000a9","#0000009f","#00000095","#0000008c","#00000083","#0000007b","#00000073","#0000006c","#00000065","#0000005f","#00000058","#00000052","#0000004d","#00000047","#00000042","#0000003d","#00000039","#00000034","#00000030","#0000002c","#00000028","#00000025","#00000021","#0000001f","#0000001b","#00000019","#00000016","#00000014","#00000011","#0000000f")
actual_pixels = list()
matching = TRUE
for(var/x in 1 to 32)
matching &= (B.GetPixel(x,x) == target_pixels[x])
actual_pixels += B.GetPixel(x,x)
if(!matching)
CRASH("ICON_MULTIPLY did not match, expected [json_encode(target_pixels)] but got [json_encode(actual_pixels)]")

// ICON_OVERLAY
B = icon('hanoi.dmi',"gradient")
B.Blend(rgb(200,10,0,170), ICON_OVERLAY)
target_pixels = list("#850755","#850755","#850755","#850755f9","#850755f5","#850755f0","#850755ec","#850755e8","#850755e4","#850755e0","#850755dc","#850755d9","#850755d6","#850755d3","#850755d0","#850755cd","#850755cb","#850755c9","#850755c6","#850755c4","#850755c2","#850755c0","#850755be","#850755bc","#850755bb","#850755b9","#850755b8","#850755b6","#850755b5","#850755b4","#850755b3","#850755b2")
actual_pixels = list()
for(var/x in 1 to 32)
matching &= (B.GetPixel(x,x) == target_pixels[x])
actual_pixels += B.GetPixel(x,x)
if(!matching)
CRASH("ICON_OVERLAY did not match, expected [json_encode(target_pixels)] but got [json_encode(actual_pixels)]")

// ICON_AND
B = icon('hanoi.dmi',"gradient")
B.Blend(rgb(200,10,0,170), ICON_AND)
target_pixels = list("#c80affaa","#c80affaa","#c80affa9","#c80aff9f","#c80aff95","#c80aff8c","#c80aff83","#c80aff7b","#c80aff73","#c80aff6c","#c80aff65","#c80aff5f","#c80aff58","#c80aff52","#c80aff4d","#c80aff47","#c80aff42","#c80aff3d","#c80aff39","#c80aff34","#c80aff30","#c80aff2c","#c80aff28","#c80aff25","#c80aff21","#c80aff1f","#c80aff1b","#c80aff19","#c80aff16","#c80aff14","#c80aff11","#c80aff0f")
actual_pixels = list()
matching = TRUE
for(var/x in 1 to 32)
matching &= (B.GetPixel(x,x) == target_pixels[x])
actual_pixels += B.GetPixel(x,x)
if(!matching)
CRASH("ICON_AND did not match, expected [json_encode(target_pixels)] but got [json_encode(actual_pixels)]")

// ICON_OR
B = icon('hanoi.dmi',"gradient")
B.Blend(rgb(200,10,0,170), ICON_OR)
target_pixels = list("#c80aff","#c80aff","#c80aff","#c80afff9","#c80afff5","#c80afff0","#c80affec","#c80affe8","#c80affe4","#c80affe0","#c80affdc","#c80affd9","#c80affd6","#c80affd3","#c80affd0","#c80affcd","#c80affcb","#c80affc9","#c80affc6","#c80affc4","#c80affc2","#c80affc0","#c80affbe","#c80affbc","#c80affbb","#c80affb9","#c80affb8","#c80affb6","#c80affb5","#c80affb4","#c80affb3","#c80affb2")
actual_pixels = list()
matching = TRUE
for(var/x in 1 to 32)
matching &= (B.GetPixel(x,x) == target_pixels[x])
actual_pixels += B.GetPixel(x,x)
if(!matching)
CRASH("ICON_OR did not match, expected [json_encode(target_pixels)] but got [json_encode(actual_pixels)]")

// ICON_UNDERLAY
B = icon('hanoi.dmi',"gradient")
B.Blend(rgb(200,10,0,170), ICON_UNDERLAY)
target_pixels = list("#0000ff","#0000ff","#0100fe","#0d01eef9","#1801e0f5","#2302d2f0","#2d02c5ec","#3703b9e8","#4003ade4","#4904a2e0","#520497dc","#59048ed9","#600584d6","#68057bd3","#6e0573d0","#75066acd","#7a0663cb","#80065cc9","#850755c6","#8b074ec4","#900748c2","#940742c0","#99083cbe","#9d0837bc","#a10832bb","#a4082eb9","#a80829b8","#ab0925b6","#ae0921b5","#b0091eb4","#b4091ab3","#b60917b2")
actual_pixels = list()
matching = TRUE
for(var/x in 1 to 32)
matching &= (B.GetPixel(x,x) == target_pixels[x])
actual_pixels += B.GetPixel(x,x)
if(!matching)
CRASH("ICON_UNDERLAY did not match, expected [json_encode(target_pixels)] but got [json_encode(actual_pixels)]")
Binary file modified Content.Tests/DMProject/Tests/Icon/hanoi.dmi
Binary file not shown.
32 changes: 14 additions & 18 deletions OpenDreamRuntime/Objects/DreamIcon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -327,17 +327,15 @@ protected void BlendPixel(Rgba32[] pixels, int dstPixelPosition, Rgba32 src) {
pixels[dstPixelPosition].G = (byte)Math.Min(dst.G + src.G, byte.MaxValue);
pixels[dstPixelPosition].B = (byte)Math.Min(dst.B + src.B, byte.MaxValue);

// BYOND uses the smaller of the two alphas
pixels[dstPixelPosition].A = Math.Min(dst.A, src.A);
pixels[dstPixelPosition].A = (byte)Math.Round((dst.A * src.A)/255.0);
break;
}
case BlendType.Subtract: {
pixels[dstPixelPosition].R = (byte)Math.Max(dst.R - src.R, byte.MinValue);
pixels[dstPixelPosition].G = (byte)Math.Max(dst.G - src.G, byte.MinValue);
pixels[dstPixelPosition].B = (byte)Math.Max(dst.B - src.B, byte.MinValue);

// BYOND uses the smaller of the two alphas
pixels[dstPixelPosition].A = Math.Min(dst.A, src.A);
pixels[dstPixelPosition].A = (byte)Math.Round((dst.A * src.A)/255.0);
break;
}

Expand All @@ -360,30 +358,28 @@ protected void BlendPixel(Rgba32[] pixels, int dstPixelPosition, Rgba32 src) {
break;
}

pixels[dstPixelPosition].R = (byte) (dst.R + (src.R - dst.R) * src.A / 255);
pixels[dstPixelPosition].G = (byte) (dst.G + (src.G - dst.G) * src.A / 255);
pixels[dstPixelPosition].B = (byte) (dst.B + (src.B - dst.B) * src.A / 255);
pixels[dstPixelPosition].R = (byte) Math.Round(dst.R + (src.R - dst.R) * src.A / 255.0);
pixels[dstPixelPosition].G = (byte) Math.Round(dst.G + (src.G - dst.G) * src.A / 255.0);
pixels[dstPixelPosition].B = (byte) Math.Round(dst.B + (src.B - dst.B) * src.A / 255.0);

byte highAlpha = Math.Max(dst.A, src.A);
byte lowAlpha = Math.Min(dst.A, src.A);
pixels[dstPixelPosition].A = (byte) (highAlpha + (highAlpha * lowAlpha / 255));
pixels[dstPixelPosition].A = (byte) Math.Round(dst.A + src.A - (dst.A * src.A)/255.0);
break;
}

case BlendType.Underlay: {
// Opposite of overlay
(dst, src) = (src, dst);
goto case BlendType.Overlay;
}
case BlendType.Or: {
pixels[dstPixelPosition].R = (byte)(dst.R | src.R);
pixels[dstPixelPosition].G = (byte)(dst.G | src.G);
pixels[dstPixelPosition].B = (byte)(dst.B | src.B);

pixels[dstPixelPosition].A = (byte)(dst.A | src.A);
pixels[dstPixelPosition].A = (byte) Math.Round(dst.A + src.A - (dst.A * src.A)/255.0);
break;
}

case BlendType.Underlay: {
// Opposite of overlay
(dst, src) = (src, dst);
goto case BlendType.Overlay;
}
default:
throw new NotImplementedException("Blend type not implemented");
}
}
}
Expand Down
Loading