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
33 changes: 33 additions & 0 deletions Content.Tests/DMProject/Tests/Icon/GetPixel.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/proc/RunTest()
var/icon/I = icon('hanoi.dmi')
ASSERT(I.GetPixel(5,5) == "#ff0000")
var/list/targetx_pixels = list("#e8083f", "#ff0000", "#6aee1d", "#f1b004", "#ffa308", "#ff34e2", "#6aeeff",\
"#f4f14d", "#fbed01", "#757121", "#c07760", "#cc7d66", "#c98164", "#5fed26", "#43fc13", "#43fc13", "#43fc13",\
"#43fc13", "#43fc13", "#48fa16", "#bf885e", "#625230", "#4d4b26", "#4c5234", "#09f5f3", "#00ffff", "#00ffff","#f44dfb",\
"#2c14ff", "#001bf9", "#00ef58", "#f74d04")
var/list/targety_pixels = list("#e8083f","#ff0000","#6bee1a","#1452f2","#e00bfc","#5be5fe","#00ffff","#f2ef4d",\
"#6b6622","#4b4b25","#c07760","#cb8065","#57f020","#43fc13","#43fc13","#43fc13","#43fc13","#43fc13",\
"#42fc12","#c68c63","#c87c64","#625230","#4c4a26","#f5e921","#2ffef1","#00f9ff","#ee54fc","#ffa04b",\
"#f8a225","#3530e4","#00ef58","#f74d04")
for(var/x in 1 to 32)
ASSERT(I.GetPixel(x,16,"target") == targetx_pixels[x])
for(var/y in 1 to 32)
ASSERT(I.GetPixel(16,y,"target") == targety_pixels[y])

//chose by fair dice roll, guaranteed to be random
var/list/random_pixels = list(\
list(9, 2, "#ff0000"),\
list(31, 8, "#00ef58"),\
list(32, 32, "#fe1100"),\
list(1, 1, "#d3105d"),\
list(2, 13, "#ff0000"),\
list(0, 0, null),\
list(31, 31, "#00fe28"),\
list(-1, 17, null),\
)
for(var/list/tuple in random_pixels)
ASSERT(I.GetPixel(tuple[1], tuple[2], "target") == tuple[3])

//byond bugs:
//- invalid iconstate causes error.log to be created but it's empty and no runtime triggers
//- invalid dir value (ie, 100) causes a hard crash
7 changes: 7 additions & 0 deletions Content.Tests/DMProject/Tests/Icon/GetPixelBadArgs.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/proc/RunTest()
var/icon/I = icon('hanoi.dmi')
ASSERT(I.GetPixel(1,1,"invalid iconstate") == null) //iconstate that doesn't exist
ASSERT(I.GetPixel(1,1,"0",dir=EAST) == null) //not a dir in this file
ASSERT(I.GetPixel(1,1,"0",dir=100) == null) //nonsense dir
ASSERT(I.GetPixel(1,1,"0",frame=-100) == "#ff0000") //frame is clipped at 1
ASSERT(I.GetPixel(1,1,"0",frame=100) == null) //there's only 1 frame in the file
Binary file added Content.Tests/DMProject/Tests/Icon/hanoi.dmi
Binary file not shown.
1 change: 0 additions & 1 deletion DMCompiler/DMStandard/Types/Icon.dm
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
set opendream_unimplemented = TRUE

proc/GetPixel(x, y, icon_state, dir = 0, frame = 0, moving = -1)
set opendream_unimplemented = TRUE

proc/Height()

Expand Down
1 change: 1 addition & 0 deletions OpenDreamRuntime/Procs/Native/DreamProcNative.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ public static void SetupNativeProcs(DreamObjectTree objectTree) {
objectTree.SetNativeProc(objectTree.Icon, DreamProcNativeIcon.NativeProc_Blend);
objectTree.SetNativeProc(objectTree.Icon, DreamProcNativeIcon.NativeProc_Scale);
objectTree.SetNativeProc(objectTree.Icon, DreamProcNativeIcon.NativeProc_Turn);
objectTree.SetNativeProc(objectTree.Icon, DreamProcNativeIcon.NativeProc_GetPixel);

objectTree.SetNativeProc(objectTree.Savefile, DreamProcNativeSavefile.NativeProc_ExportText);
objectTree.SetNativeProc(objectTree.Savefile, DreamProcNativeSavefile.NativeProc_Flush);
Expand Down
51 changes: 51 additions & 0 deletions OpenDreamRuntime/Procs/Native/DreamProcNativeIcon.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Linq;
using OpenDreamRuntime.Objects;
using OpenDreamRuntime.Objects.Types;
using OpenDreamRuntime.Resources;
Expand Down Expand Up @@ -76,6 +77,56 @@
return DreamValue.Null;
}

//x, y, icon_state, dir = 0, frame = 0, moving = -1
[DreamProc("GetPixel")]
[DreamProcParameter("x", Type = DreamValueTypeFlag.Float)]
[DreamProcParameter("y", Type = DreamValueTypeFlag.Float)]
[DreamProcParameter("icon_state", Type = DreamValueTypeFlag.String, DefaultValue = "")]
[DreamProcParameter("dir", Type = DreamValueTypeFlag.Float, DefaultValue = 0)]
[DreamProcParameter("frame", Type = DreamValueTypeFlag.Float, DefaultValue = 0)]
[DreamProcParameter("moving", Type = DreamValueTypeFlag.Float, DefaultValue = -1)]
public static DreamValue NativeProc_GetPixel(NativeProc.Bundle bundle, DreamObject? src, DreamObject? usr) {
var srcDreamIcon = (DreamObjectIcon)src!;

//arg validation
int x = bundle.GetArgument(0, "x").MustGetValueAsInteger();
int y = bundle.GetArgument(1, "y").MustGetValueAsInteger();

//outside valid bounds returns null
if(x < 1 || x > srcDreamIcon.Icon.Width || y < 1 || y > srcDreamIcon.Icon.Height)
return DreamValue.Null;

string iconState = bundle.GetArgument(2, "icon_state").MustGetValueAsString();
if(!srcDreamIcon.Icon.GenerateDMI().DMI.States.TryGetValue(iconState, out var iconStateObject)){
if(iconState == string.Empty)
iconStateObject = srcDreamIcon.Icon.GenerateDMI().DMI.States.First().Value;
else //invalid icon state causes BYOND to create error.log but it's empty
return DreamValue.Null; //throw new ArgumentException($"Invalid icon_state {iconState} passed to /icon.GetPixel()");
}

AtomDirection dir = (AtomDirection)bundle.GetArgument(3, "dir").MustGetValueAsInteger();
if(dir == AtomDirection.None)
dir = iconStateObject.Directions.Keys.First();
else if(!iconStateObject.Directions.ContainsKey(dir))
return DreamValue.Null;

int frame = Math.Max(1, bundle.GetArgument(4, "frame").MustGetValueAsInteger())-1;

if (iconStateObject.FrameCount < frame)
return DreamValue.Null;

DreamValue moving = bundle.GetArgument(5, "moving"); // TODO: implement movement states

var stateDirFrame = iconStateObject.Directions[dir][frame];

Check warning

Code scanning / InspectCode

Unused local variable Warning

Local variable 'moving' is never used

var pixel = srcDreamIcon.Icon.GenerateDMI().Texture[stateDirFrame.X+x-1,stateDirFrame.Y+(srcDreamIcon.Icon.Height-y)];
return pixel.A switch {
0 => DreamValue.Null,
255 => new DreamValue($"#{pixel.R:x2}{pixel.G:x2}{pixel.B:x2}"),
_ => new DreamValue($"#{pixel.R:x2}{pixel.G:x2}{pixel.B:x2}{pixel.A:x2}")
};
}

[DreamProc("Scale")]
[DreamProcParameter("width", Type = DreamValueTypeFlag.Float)]
[DreamProcParameter("height", Type = DreamValueTypeFlag.Float)]
Expand Down
Loading