Changeset - 2154d10fecad
[Not reviewed]
default
0 3 0
Jason Maltzen - 5 years ago 2019-11-02 13:25:48
jason@hiddenachievement.com
Fix another bug in scanning where it was scanning off the right side of the image.
3 files changed with 3 insertions and 3 deletions:
0 comments (0 inline, 0 general)
DesertPaintLab.csproj
Show inline comments
 
<?xml version="1.0" encoding="utf-8"?>
 
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
 
  <PropertyGroup>
 
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
 
    <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
 
    <ProjectGuid>{1A885212-5FD2-4EBF-A98F-3EB1491A1CBB}</ProjectGuid>
 
    <OutputType>WinExe</OutputType>
 
    <RootNamespace>DesertPaintLab</RootNamespace>
 
    <AssemblyName>DesertPaintLab</AssemblyName>
 
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
 
    <IsWebBootstrapper>false</IsWebBootstrapper>
 
    <PublishUrl>publish\</PublishUrl>
 
    <Install>true</Install>
 
    <InstallFrom>Disk</InstallFrom>
 
    <UpdateEnabled>false</UpdateEnabled>
 
    <UpdateMode>Foreground</UpdateMode>
 
    <UpdateInterval>7</UpdateInterval>
 
    <UpdateIntervalUnits>Days</UpdateIntervalUnits>
 
    <UpdatePeriodically>false</UpdatePeriodically>
 
    <UpdateRequired>false</UpdateRequired>
 
    <MapFileExtensions>true</MapFileExtensions>
 
    <AutorunEnabled>true</AutorunEnabled>
 
    <ApplicationRevision>0</ApplicationRevision>
 
    <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
 
    <UseApplicationTrust>false</UseApplicationTrust>
 
    <BootstrapperEnabled>true</BootstrapperEnabled>
 
    <ReleaseVersion>9.0.3</ReleaseVersion>
 
    <ReleaseVersion>9.0.4</ReleaseVersion>
 
  </PropertyGroup>
 
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
 
    <DebugSymbols>true</DebugSymbols>
 
    <DebugType>full</DebugType>
 
    <Optimize>false</Optimize>
 
    <OutputPath>bin\x86\Debug</OutputPath>
 
    <DefineConstants>DEBUG</DefineConstants>
 
    <ErrorReport>prompt</ErrorReport>
 
    <WarningLevel>4</WarningLevel>
 
    <PlatformTarget>x86</PlatformTarget>
 
    <ConsolePause>false</ConsolePause>
 
    <additionalargs>/unsafe</additionalargs>
 
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
 
  </PropertyGroup>
 
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
 
    <DebugType>none</DebugType>
 
    <Optimize>false</Optimize>
 
    <OutputPath>bin\x86\Release</OutputPath>
 
    <ErrorReport>prompt</ErrorReport>
 
    <WarningLevel>4</WarningLevel>
 
    <PlatformTarget>x86</PlatformTarget>
 
    <ConsolePause>false</ConsolePause>
 
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
 
  </PropertyGroup>
 
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
 
    <DebugSymbols>true</DebugSymbols>
 
    <OutputPath>bin\x64\Debug\</OutputPath>
 
    <DefineConstants>DEBUG</DefineConstants>
 
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
 
    <DebugType>full</DebugType>
 
    <PlatformTarget>x64</PlatformTarget>
 
    <ErrorReport>prompt</ErrorReport>
 
    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
 
    <Prefer32Bit>true</Prefer32Bit>
 
  </PropertyGroup>
 
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
 
    <OutputPath>bin\x64\Release\</OutputPath>
 
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
 
    <PlatformTarget>x64</PlatformTarget>
 
    <ErrorReport>prompt</ErrorReport>
 
    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
 
    <Prefer32Bit>true</Prefer32Bit>
 
  </PropertyGroup>
 
  <ItemGroup>
 
    <Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
 
    <Reference Include="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
 
    <Reference Include="System" />
 
    <Reference Include="glib-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
 
    <Reference Include="glade-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
 
    <Reference Include="pango-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
 
    <Reference Include="atk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
 
    <Reference Include="System.IO.Compression.FileSystem" />
 
  </ItemGroup>
 
  <ItemGroup>
 
    <Compile Include="InterfaceSize.cs" />
 
    <Compile Include="gtk-gui\generated.cs" />
 
    <Compile Include="MainWindow.cs" />
 
    <Compile Include="Main.cs" />
 
    <Compile Include="AssemblyInfo.cs" />
 
    <Compile Include="PlayerProfile.cs" />
 
    <Compile Include="FirstRunDialog.cs" />
 
    <Compile Include="SelectProfileDialog.cs" />
 
    <Compile Include="NewProfileDialog.cs" />
 
    <Compile Include="PaintSwatch.cs" />
 
    <Compile Include="Palette.cs" />
 
    <Compile Include="PaintColor.cs" />
 
    <Compile Include="Reaction.cs" />
 
    <Compile Include="ReagentManager.cs" />
 
    <Compile Include="Reagent.cs" />
 
    <Compile Include="gtk-gui\MainWindow.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.FirstRunDialog.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.SelectProfileDialog.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.NewProfileDialog.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.PaintSwatch.cs" />
 
    <Compile Include="ScreenCheckDialog.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.ScreenCheckDialog.cs" />
 
    <Compile Include="FileUtils.cs" />
 
    <Compile Include="PaintRecipe.cs" />
 
    <Compile Include="ReactionRecorder.cs" />
 
    <Compile Include="ReactionStatusWindow.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.ReactionStatusWindow.cs" />
 
    <Compile Include="ReactionSet.cs" />
 
    <Compile Include="RecipeGenerator.cs" />
 
    <Compile Include="ReagentWindow.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.ReagentWindow.cs" />
 
    <Compile Include="RecipeSearchNode.cs" />
 
    <Compile Include="Settings.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.CaptureView.cs" />
 
    <Compile Include="UI\CaptureView.cs" />
 
    <Compile Include="UI\RecipeGeneratorView.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.RecipeGeneratorView.cs" />
 
    <Compile Include="UI\SimulatorView.cs" />
 
    <Compile Include="gtk-gui\DesertPaintLab.SimulatorView.cs" />
 
    <Compile Include="StatusUpdateEventArgs.cs" />
 
    <Compile Include="AppSettings.cs" />
 
  </ItemGroup>
 
  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
 
  <ProjectExtensions>
 
    <MonoDevelop>
 
      <Properties>
 
        <Policies>
 
          <TextStylePolicy TabWidth="4" IndentWidth="4" RemoveTrailingWhitespace="True" NoTabsAfterNonTabs="False" EolMarker="Native" FileWidth="80" TabsToSpaces="True" scope="text/x-csharp" />
 
          <CSharpFormattingPolicy IndentBlock="True" IndentBraces="False" IndentSwitchCaseSection="True" LabelPositioning="OneLess" NewLinesForBracesInTypes="True" NewLinesForBracesInMethods="True" NewLinesForBracesInProperties="True" NewLinesForBracesInAccessors="True" NewLinesForBracesInControlBlocks="True" NewLinesForBracesInAnonymousTypes="True" NewLinesForBracesInObjectCollectionArrayInitializers="True" NewLineForElse="True" NewLineForCatch="True" NewLineForFinally="True" NewLineForMembersInObjectInit="True" NewLineForMembersInAnonymousTypes="True" NewLineForClausesInQuery="True" SpacingAfterMethodDeclarationName="False" SpaceWithinMethodDeclarationParenthesis="False" SpaceBetweenEmptyMethodDeclarationParentheses="False" SpaceAfterMethodCallName="False" SpaceWithinMethodCallParentheses="False" SpaceBetweenEmptyMethodCallParentheses="False" SpaceAfterControlFlowStatementKeyword="True" SpaceWithinExpressionParentheses="False" SpaceWithinCastParentheses="False" SpaceWithinOtherParentheses="False" SpaceAfterCast="False" SpacesIgnoreAroundVariableDeclaration="False" SpaceBeforeOpenSquareBracket="False" SpaceBetweenEmptySquareBrackets="False" SpaceWithinSquareBrackets="False" SpaceAfterColonInBaseTypeDeclaration="True" SpaceAfterComma="True" SpaceAfterDot="False" SpaceAfterSemicolonsInForStatement="True" SpaceBeforeColonInBaseTypeDeclaration="True" SpaceBeforeComma="False" SpaceBeforeDot="False" SpaceBeforeSemicolonsInForStatement="False" SpacingAroundBinaryOperator="Single" WrappingKeepStatementsOnSingleLine="True" IndentSwitchSection="False" NewLinesForBracesInAnonymousMethods="False" NewLinesForBracesInLambdaExpressionBody="False" WrappingPreserveSingleLine="False" scope="text/x-csharp" />
 
        </Policies>
 
        <GtkDesignInfo generateGettext="False" />
 
      </Properties>
 
    </MonoDevelop>
 
  </ProjectExtensions>
 
  <ItemGroup>
 
    <None Include="data\colors.txt">
 
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 
    </None>
 
    <None Include="data\ingredients.txt">
 
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 
    </None>
 
    <None Include="data\template\dp_reactions.txt">
 
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 
    </None>
 
    <None Include="data\template\ingredients.txt">
 
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 
    </None>
 
  </ItemGroup>
 
  <ItemGroup />
 
  <ItemGroup>
 
    <BootstrapperPackage Include=".NETFramework,Version=v4.5">
 
      <Visible>False</Visible>
 
      <ProductName>Microsoft .NET Framework 4.5 %28x86 and x64%29</ProductName>
 
      <Install>true</Install>
 
    </BootstrapperPackage>
 
    <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
 
      <Visible>False</Visible>
 
      <ProductName>.NET Framework 3.5 SP1</ProductName>
 
      <Install>false</Install>
 
    </BootstrapperPackage>
 
  </ItemGroup>
 
</Project>
DesertPaintLab.sln
Show inline comments
 

 
Microsoft Visual Studio Solution File, Format Version 12.00
 
# Visual Studio 14
 
VisualStudioVersion = 14.0.23107.0
 
MinimumVisualStudioVersion = 10.0.40219.1
 
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DesertPaintLab", "DesertPaintLab.csproj", "{1A885212-5FD2-4EBF-A98F-3EB1491A1CBB}"
 
EndProject
 
Global
 
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 
		Debug|x64 = Debug|x64
 
		Debug|x86 = Debug|x86
 
		Release|x64 = Release|x64
 
		Release|x86 = Release|x86
 
	EndGlobalSection
 
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
 
		{1A885212-5FD2-4EBF-A98F-3EB1491A1CBB}.Debug|x64.ActiveCfg = Debug|x64
 
		{1A885212-5FD2-4EBF-A98F-3EB1491A1CBB}.Debug|x64.Build.0 = Debug|x64
 
		{1A885212-5FD2-4EBF-A98F-3EB1491A1CBB}.Debug|x86.ActiveCfg = Debug|x86
 
		{1A885212-5FD2-4EBF-A98F-3EB1491A1CBB}.Debug|x86.Build.0 = Debug|x86
 
		{1A885212-5FD2-4EBF-A98F-3EB1491A1CBB}.Release|x64.ActiveCfg = Release|x64
 
		{1A885212-5FD2-4EBF-A98F-3EB1491A1CBB}.Release|x64.Build.0 = Release|x64
 
		{1A885212-5FD2-4EBF-A98F-3EB1491A1CBB}.Release|x86.ActiveCfg = Release|x86
 
		{1A885212-5FD2-4EBF-A98F-3EB1491A1CBB}.Release|x86.Build.0 = Release|x86
 
	EndGlobalSection
 
	GlobalSection(SolutionProperties) = preSolution
 
		HideSolutionNode = FALSE
 
	EndGlobalSection
 
	GlobalSection(MonoDevelopProperties) = preSolution
 
		Policies = $0
 
		$0.DotNetNamingPolicy = $1
 
		$1.DirectoryNamespaceAssociation = PrefixedHierarchical
 
		$0.TextStylePolicy = $2
 
		$2.inheritsSet = null
 
		$2.scope = text/x-csharp
 
		$0.CSharpFormattingPolicy = $3
 
		$3.scope = text/x-csharp
 
		$0.TextStylePolicy = $4
 
		$4.FileWidth = 80
 
		$4.TabsToSpaces = True
 
		$4.scope = text/plain
 
		$0.StandardHeader = $5
 
		$0.VersionControlPolicy = $6
 
		description = Desert Paint Lab utility for A Tale in the Desert
 
		version = 9.0.3
 
		version = 9.0.4
 
	EndGlobalSection
 
EndGlobal
ReactionRecorder.cs
Show inline comments
...
 
@@ -83,513 +83,513 @@ namespace DesertPaintLab
 
        public static readonly int[] BLUE_BAR_SPACING =
 
        {
 
            52, // tiny
 
            52, // small
 
            52, // medium
 
            52, // large
 
            52 // huge
 
        };
 
        // width to test on ends of swatch (10% on either end)
 
        public static readonly int[] SWATCH_TEST_WIDTH =
 
        {
 
            26, // tiny
 
            26, // small
 
            26, // medium
 
            28, // large
 
            31  // huge
 
        };
 

	
 
        int swatchHeight = SWATCH_HEIGHT[(int)DEFAULT_INTERFACE_SIZE];
 
        int swatchWidth = SWATCH_WIDTH[(int)DEFAULT_INTERFACE_SIZE];
 
        int swatchTestWidth = SWATCH_TEST_WIDTH[(int)DEFAULT_INTERFACE_SIZE];
 
        int colorBarWidth = COLOR_BAR_WIDTH[(int)DEFAULT_INTERFACE_SIZE];
 
        int redBarSpacing = RED_BAR_SPACING[(int)DEFAULT_INTERFACE_SIZE];
 
        int greenBarSpacing = GREEN_BAR_SPACING[(int)DEFAULT_INTERFACE_SIZE];
 
        int blueBarSpacing = BLUE_BAR_SPACING[(int)DEFAULT_INTERFACE_SIZE];
 

	
 
        public int SwatchHeight {  get { return swatchHeight; } }
 
        public int SwatchWidth {  get { return swatchWidth; } }
 
        public int ColorBarWidth { get { return colorBarWidth;  } }
 
        public int RedBarSpacing {  get { return redBarSpacing; } }
 
        public int GreenBarSpacing { get { return greenBarSpacing; } }
 
        public int BlueBarSpacing {  get { return blueBarSpacing; } }
 

	
 
        // Current Status
 
        public bool IsCaptured { get; private set; }
 
        public int X { get; private set; }
 
        public int Y { get; private set; }
 
        public int ScreenWidth { get; private set; }
 
        public int ScreenHeight { get; private set; }
 
        private PaintColor _recordedColor = new PaintColor();
 
        public PaintColor RecordedColor { get { return _recordedColor; } private set { _recordedColor.Set(value); } }
 
        public int RedBarX { get; private set; }
 
        public int RedBarY { get; private set; }
 

	
 
        int pixelMultiplier = 1;
 
        InterfaceSize interfaceSize;
 

	
 
        bool firstRun = true;
 
        int lastSwatchX = -1;
 
        int lastSwatchY = -1;
 
        public int SwatchX { get { return lastSwatchX; } }
 
        public int SwatchY { get { return lastSwatchY; } }
 

	
 
        private static ReactionRecorder _instance;
 
        public static ReactionRecorder Instance
 
        {
 
            get {
 
                if (_instance == null)
 
                {
 
                    _instance = new ReactionRecorder();
 
                }
 
                return _instance;
 
            }
 
        }
 

	
 
        public StreamWriter Log { get; set; }
 
        private void WriteLog(string format, params object[] args)
 
        {
 
            if (Log != null)
 
            {
 
                Log.WriteLine(format, args);
 
            }
 
        }
 

	
 
        public delegate void ReactionRecordedHandler(Reagent reagent1, Reagent reagent2, Reaction reaction);
 
        public ReactionRecordedHandler OnReactionRecorded;
 

	
 
        public ReactionRecorder()
 
        {
 
            pixelMultiplier = 1;
 
            interfaceSize = DEFAULT_INTERFACE_SIZE;
 
            swatchHeight = SWATCH_HEIGHT[(int)interfaceSize] * pixelMultiplier;
 
            swatchWidth = SWATCH_WIDTH[(int)interfaceSize] * pixelMultiplier;
 
            colorBarWidth = COLOR_BAR_WIDTH[(int)interfaceSize] * pixelMultiplier;
 
            redBarSpacing = RED_BAR_SPACING[(int)interfaceSize] * pixelMultiplier;
 
            greenBarSpacing = GREEN_BAR_SPACING[(int)interfaceSize] * pixelMultiplier;
 
            blueBarSpacing = BLUE_BAR_SPACING[(int)interfaceSize] * pixelMultiplier;
 
            swatchTestWidth = SWATCH_TEST_WIDTH[(int)interfaceSize] * pixelMultiplier;
 
        }
 

	
 
        public ReactionRecorder(int pixelMultiplier)
 
        {
 
            this.pixelMultiplier = pixelMultiplier;
 
            this.interfaceSize = DEFAULT_INTERFACE_SIZE;
 
            swatchHeight = SWATCH_HEIGHT[(int)interfaceSize] * pixelMultiplier;
 
            swatchWidth = SWATCH_WIDTH[(int)interfaceSize] * pixelMultiplier;
 
            colorBarWidth = COLOR_BAR_WIDTH[(int)interfaceSize] * pixelMultiplier;
 
            redBarSpacing = RED_BAR_SPACING[(int)interfaceSize] * pixelMultiplier;
 
            greenBarSpacing = GREEN_BAR_SPACING[(int)interfaceSize] * pixelMultiplier;
 
            blueBarSpacing = BLUE_BAR_SPACING[(int)interfaceSize] * pixelMultiplier;
 
            swatchTestWidth = SWATCH_TEST_WIDTH[(int)interfaceSize] * pixelMultiplier;
 
        }
 

	
 
        public ReactionRecorder(int pixelMultiplier, InterfaceSize interfaceSize)
 
        {
 
            this.pixelMultiplier = pixelMultiplier;
 
            this.interfaceSize = interfaceSize;
 
            swatchHeight = SWATCH_HEIGHT[(int)interfaceSize] * pixelMultiplier;
 
            swatchWidth = SWATCH_WIDTH[(int)interfaceSize] * pixelMultiplier;
 
            colorBarWidth = COLOR_BAR_WIDTH[(int)interfaceSize] * pixelMultiplier;
 
            redBarSpacing = RED_BAR_SPACING[(int)interfaceSize] * pixelMultiplier;
 
            greenBarSpacing = GREEN_BAR_SPACING[(int)interfaceSize] * pixelMultiplier;
 
            blueBarSpacing = BLUE_BAR_SPACING[(int)interfaceSize] * pixelMultiplier;
 
            swatchTestWidth = SWATCH_TEST_WIDTH[(int)interfaceSize] * pixelMultiplier;
 
        }
 

	
 
        private static bool IsPapyTexture(byte r, byte g, byte b)
 
        {
 
            // red between 208 and 244
 
            // 240 and 255
 
            // green between 192 and 237
 
            // 223 and 248
 
            // blue between 145 and 205
 
            // 178 and 228
 
            //return ((r > 0xD0) && (g >= 0xC0) && (b >= 0x91)) &&
 
            //       ((r < 0xF4) && (g <= 0xED) && (b <= 0xCD));
 
            return ((r >= 0xF0) && (r <= 0xFF) && (g >= 0xDF) && (g <= 0xF8) && (b >= 0xB2) && (b <= 0xE4));
 
        }
 

	
 
        public void SetPixelMultiplier(int pixelMultiplier)
 
        {
 
            this.pixelMultiplier = pixelMultiplier;
 

	
 
            swatchHeight = SWATCH_HEIGHT[(int)interfaceSize] * pixelMultiplier;
 
            swatchWidth      = SWATCH_WIDTH[(int)interfaceSize] * pixelMultiplier;
 
            colorBarWidth    = COLOR_BAR_WIDTH[(int)interfaceSize] * pixelMultiplier;
 
            redBarSpacing    = RED_BAR_SPACING[(int)interfaceSize] * pixelMultiplier;
 
            greenBarSpacing  = GREEN_BAR_SPACING[(int)interfaceSize] * pixelMultiplier;
 
            blueBarSpacing   = BLUE_BAR_SPACING[(int)interfaceSize] * pixelMultiplier;
 
            swatchTestWidth  = SWATCH_TEST_WIDTH[(int)interfaceSize] * pixelMultiplier;
 
        }
 

	
 
        public void SetInterfaceSize(InterfaceSize interfaceSize)
 
        {
 
            this.interfaceSize = interfaceSize;
 

	
 
            swatchHeight = SWATCH_HEIGHT[(int)interfaceSize] * pixelMultiplier;
 
            swatchWidth = SWATCH_WIDTH[(int)interfaceSize] * pixelMultiplier;
 
            colorBarWidth = COLOR_BAR_WIDTH[(int)interfaceSize] * pixelMultiplier;
 
            redBarSpacing = RED_BAR_SPACING[(int)interfaceSize] * pixelMultiplier;
 
            greenBarSpacing = GREEN_BAR_SPACING[(int)interfaceSize] * pixelMultiplier;
 
            blueBarSpacing = BLUE_BAR_SPACING[(int)interfaceSize] * pixelMultiplier;
 
            swatchTestWidth = SWATCH_TEST_WIDTH[(int)interfaceSize] * pixelMultiplier;
 
        }
 

	
 
        unsafe private void ColorAt(byte *pixBytes, int x, int y, int stride, out byte r, out byte g, out byte b)
 
        {
 
            int pixelStart = (y * stride) + (x * 3);
 
                    r = pixBytes[pixelStart];
 
                    g = pixBytes[pixelStart + 1];
 
                    b = pixBytes[pixelStart + 2];
 
        }
 

	
 
        private bool IsDarkPixel(int r, int g, int b)
 
        {
 
            return ((r < 0x46) && (g < 0x46) && (b < 0x47));
 
        }
 

	
 
        private bool IsColorMatch(byte test_r, byte test_g, byte test_b, byte target_r, byte target_g, byte target_b)
 
        {
 
            return ((Math.Abs(test_r - target_r) <= COLOR_TOLERANCE) &&
 
                (Math.Abs(test_g - target_g) <= COLOR_TOLERANCE) &&
 
                (Math.Abs(test_b - target_b) <= COLOR_TOLERANCE));
 
        }
 

	
 
        unsafe private bool IsPossibleSwatchSlice(byte* pixBytes, int x, int y, int stride)
 
        {
 
            int testPixelStart = (y * stride) + (x * 3);
 
            byte r = pixBytes[testPixelStart];
 
            byte g = pixBytes[testPixelStart+1];
 
            byte b = pixBytes[testPixelStart+2];
 

	
 
            bool isDarkPixel = false;
 
            //bool isPapyAbove = false;
 
            //bool isPapyBelow = false;
 

	
 
            // 1.) Check if the top pixel is a dark pixel.
 
            isDarkPixel = IsDarkPixel(r, g, b); // ((r < 0x46) && (g < 0x46) && (b < 0x46));
 
            
 
            //// 2.) Check the pixel above it to see if it's from the papy texture.
 
            //int otherPixelStart = testPixelStart - stride;
 
            //isPapyAbove = ((otherPixelStart >= 0) &&
 
            //    IsPapyTexture(pixBytes[otherPixelStart++], pixBytes[otherPixelStart++], pixBytes[otherPixelStart]));
 

	
 
            //// 3.) Check the pixel below where the swatch should be to see if it's also from the papy texture.
 
            //otherPixelStart = testPixelStart + (stride * swatchHeight);
 
            //isPapyBelow = (IsPapyTexture(pixBytes[otherPixelStart++], pixBytes[otherPixelStart++], pixBytes[otherPixelStart]));
 

	
 
            //bool result = isDarkPixel && isPapyAbove && isPapyBelow;
 
            bool result = isDarkPixel;
 

	
 
            // grab the swatch color
 
            int swatchColorStart = testPixelStart + (2 * pixelMultiplier * stride); // 2 rows below the test pixel, skipping the faded color
 
            r = pixBytes[swatchColorStart];
 
            g = pixBytes[swatchColorStart+1];
 
            b = pixBytes[swatchColorStart+2];
 

	
 
            // scan the column from 2 below the top to 2 above the bottom to ensure the color matches
 
            for (int i = (2*pixelMultiplier); result && (i < swatchHeight-(2*pixelMultiplier)); ++i)
 
            {
 
                int otherPixelStart = testPixelStart + (stride * i);
 
                result &= IsColorMatch(pixBytes[otherPixelStart], pixBytes[otherPixelStart+1], pixBytes[otherPixelStart+2], r, g, b);
 
            }
 

	
 
            if (!result)
 
            {
 
                WriteLog("Swatch slice at {0}, {1} failed to match", x, y);
 
            }
 

	
 
            return result;
 
        }
 

	
 
        unsafe private bool IsPossibleSwatchUpperLeft(byte* pixBytes, int x, int y, int stride)
 
        {
 
            int testPixelStart = (y * stride) + (x * 3);
 

	
 
            if (testPixelStart < stride)
 
            {
 
                return false;
 
            }
 

	
 
            bool result = true;
 

	
 
            int solidUpperLeft = testPixelStart + (2 * stride) + (2 * 3);
 
            int solidLowerLeft = solidUpperLeft + ((swatchHeight - 4) * stride);
 
            int solidUpperRight = solidUpperLeft + ((swatchWidth - 4) * 3);
 
            int solidLowerRight = solidLowerLeft + ((swatchWidth - 4) * 3);
 
            byte swatch_r = pixBytes[solidUpperLeft];
 
            byte swatch_g = pixBytes[solidUpperLeft + 1];
 
            byte swatch_b = pixBytes[solidUpperLeft + 2];
 

	
 
            // Check the other 3 corners of the swatch size for color match
 
            result &= IsColorMatch(swatch_r, swatch_r, swatch_r, pixBytes[solidUpperRight], pixBytes[solidUpperRight + 1], pixBytes[solidUpperRight + 2]);
 
            result &= IsColorMatch(swatch_r, swatch_r, swatch_r, pixBytes[solidLowerLeft], pixBytes[solidLowerLeft + 1], pixBytes[solidLowerLeft + 2]);
 
            result &= IsColorMatch(swatch_r, swatch_r, swatch_r, pixBytes[solidLowerRight], pixBytes[solidLowerRight + 1], pixBytes[solidLowerRight + 2]);
 

	
 
            if (!result)
 
            {
 
                return false;
 
            }
 

	
 
            // scan down the right and left sides
 
            for (int yOff = 1; yOff < (swatchHeight - 5); ++yOff)
 
            {
 
                int testPixel = solidUpperLeft + (yOff * stride);
 
                result &= IsColorMatch(swatch_r, swatch_r, swatch_r, pixBytes[testPixel], pixBytes[testPixel + 1], pixBytes[testPixel + 2]);
 
                testPixel += ((swatchWidth - 1) * (x + 3));
 
                testPixel += ((swatchWidth - 1) * 3);
 
                result &= IsColorMatch(swatch_r, swatch_r, swatch_r, pixBytes[testPixel], pixBytes[testPixel + 1], pixBytes[testPixel + 2]);
 
            }
 

	
 
            if (!result)
 
            {
 
                WriteLog("Failed to find left edge for potential swatch of color {2}, {3}, {4} at {0}, {1}", x, y, swatch_r, swatch_g, swatch_b);
 
                return false;
 
            }
 

	
 
            // test the left edge for dark pixels
 
            int i = 0;
 
            for (i = 0; result && (i < swatchHeight-pixelMultiplier); ++i)
 
            {
 
                int otherPixelStart = testPixelStart + (stride * i);
 
                result &= IsDarkPixel(pixBytes[otherPixelStart], pixBytes[otherPixelStart+1], pixBytes[otherPixelStart+2]);
 
            }
 
            if (!result)
 
            {
 
                // No dark border on the left side
 
                WriteLog("Failed to find left border for potential swatch of color {2}, {3}, {4} at {0}, {1}", x, y, swatch_r, swatch_g, swatch_b);
 
                return false;
 
            }
 

	
 
            // test the dark top border and for papyrus above and below the swatch
 
            bool borderError = false;
 
            int papyErrorCount = 0;
 
            for (i = 0; result && (i < swatchWidth); ++i)
 
            {
 
                int otherPixelStart = testPixelStart + (3 * i);
 
                bool isBorder = IsDarkPixel(pixBytes[otherPixelStart], pixBytes[otherPixelStart+1], pixBytes[otherPixelStart+2]);
 
                result &= isBorder;
 
                if (!isBorder)
 
                {
 
                    borderError = true;
 
                }
 
                if (otherPixelStart >= stride)
 
                {
 
                    otherPixelStart = otherPixelStart - stride;
 
                    papyErrorCount += (IsPapyTexture(pixBytes[otherPixelStart], pixBytes[otherPixelStart + 1], pixBytes[otherPixelStart + 2]) ? 0 : 1);
 
                }
 
                else
 
                {
 
                    papyErrorCount++;
 
                }
 

	
 
                // Checking along the bottom of the swatch - 
 
                otherPixelStart = testPixelStart + (stride * swatchHeight) + (3 * i);
 
                papyErrorCount += (IsPapyTexture(pixBytes[otherPixelStart], pixBytes[otherPixelStart+1], pixBytes[otherPixelStart+2]) ? 0 : 1);
 
            }
 

	
 
            result &= (papyErrorCount < (swatchWidth / 20)); // allow up to 5% error rate checking for papy texture, because this seems to be inconsistent
 
            if (!result && ((i > (swatchWidth*0.8)) || (papyErrorCount >= (swatchWidth/20))))
 
            {
 
                if (!borderError && (papyErrorCount < swatchWidth))
 
                {
 
                    WriteLog("Found a potential swatch candidate of width {0} at {1},{2} that had {3} failures matching papyrus texture", i, x, y, papyErrorCount);
 
                }
 
            }
 

	
 
            return result;
 
        }
 

	
 
        unsafe private bool TestPosition(int x, int y, byte* pixBytes, int screenshotWidth, int screenshotHeight, int stride, ref PaintColor reactedColor, ref int redPixelStart)
 
        {
 
            byte pixel_r, pixel_g, pixel_b;
 
            int pixelStart, otherPixelStart;
 
            bool colorMatch = true;
 

	
 
            // Look for the color swatch.
 
            pixelStart = (y * stride) + (x * 3);
 
            pixel_r = pixBytes[pixelStart];
 
            pixel_g = pixBytes[pixelStart + 1];
 
            pixel_b = pixBytes[pixelStart + 2];
 

	
 
            // Check 4 corners of solid area and left/right solid bar areas
 
            bool foundSwatch = IsPossibleSwatchUpperLeft(pixBytes, x, y, stride); // ((pixel_r < 0x46) && (pixel_g < 0x46) && (pixel_b < 0x46));
 
            if (foundSwatch)
 
            {
 
                int borderXOffset = 0;
 
                for (borderXOffset = (2 * pixelMultiplier); foundSwatch && (borderXOffset < swatchTestWidth); ++borderXOffset)
 
                {
 
                    foundSwatch &= IsPossibleSwatchSlice(pixBytes, x + borderXOffset, y, stride);
 
                    foundSwatch &= IsPossibleSwatchSlice(pixBytes, x + swatchWidth - borderXOffset, y, stride);
 
                }
 
            }
 

	
 
            if (foundSwatch)
 
            {
 
                // found a swatch with an appropriate dark top border with papyrus texture above it and papyrus a jump below it
 
                // 4.) Scan the left border of the potential swatch
 
                // location.
 
                colorMatch = true;
 
                for (int i = 2; i < swatchHeight - (2 * pixelMultiplier); ++i)
 
                {
 
                    otherPixelStart = pixelStart + (stride * i);
 
                    if (!IsColorMatch(pixel_r, pixel_g, pixel_b, pixBytes[otherPixelStart], pixBytes[otherPixelStart + 1], pixBytes[otherPixelStart + 2]))
 
                    {
 
                        colorMatch = false;
 
                        break;
 
                    }
 
                }
 

	
 
                if (colorMatch)
 
                {
 
                    // WE FOUND THE SWATCH!
 
                    // Now we know where the color bars are.
 
                    redPixelStart = pixelStart + (redBarSpacing * stride);
 
                    int redPixel = redPixelStart;
 
                    int redPixelCount = 0;
 
                    while ((pixBytes[redPixel] > 0x9F) &&
 
                           (pixBytes[redPixel + 1] < 0x62) &&
 
                           (pixBytes[redPixel + 2] < 0x62))
 
                    {
 
                        redPixelCount++;
 
                        // pixBytes[redPixel] = 0x00;
 
                        // pixBytes[redPixel + 1] = 0xFF;
 
                        // pixBytes[redPixel + 2] = 0xFF;
 
                        redPixel += 3;
 
                    }
 
                    WriteLog("Color {0}, {1}, {2} is no longer red at {3},{4}", pixBytes[redPixel], pixBytes[redPixel + 1], pixBytes[redPixel + 2], (redPixel % stride)/3, redPixel / stride);
 

	
 
                    reactedColor.Red = (byte)Math.Round((float)redPixelCount * 255f / (float)colorBarWidth);
 
                    int greenPixelStart = pixelStart + (greenBarSpacing * stride);
 

	
 
                    int greenPixelCount = 0;
 
                    while ((pixBytes[greenPixelStart] < 0x62) &&
 
                           (pixBytes[greenPixelStart + 1] > 0x9F) &&
 
                           (pixBytes[greenPixelStart + 2] < 0x62))
 
                    {
 
                        greenPixelCount++;
 
                        // pixBytes[greenPixelStart] = 0x00;
 
                        // pixBytes[greenPixelStart + 1] = 0xFF;
 
                        // pixBytes[greenPixelStart + 2] = 0xFF;
 
                        greenPixelStart += 3;
 
                    }
 
                    WriteLog("Color {0}, {1}, {2} is no longer green at pixel offset {3},{4}", pixBytes[greenPixelStart], pixBytes[greenPixelStart + 1], pixBytes[greenPixelStart + 2], (greenPixelStart % stride)/3, greenPixelStart / stride);
 

	
 
                    reactedColor.Green = (byte)Math.Round((float)greenPixelCount * 255f / (float)colorBarWidth);
 
                    int bluePixelStart = pixelStart + (blueBarSpacing * stride);
 

	
 
                    int bluePixelCount = 0;
 
                    while ((pixBytes[bluePixelStart] < 0x62) &&
 
                        (pixBytes[bluePixelStart + 1] < 0x62) &&
 
                        (pixBytes[bluePixelStart + 2] > 0x9F))
 
                    {
 
                        bluePixelCount++;
 
                        // pixBytes[bluePixelStart] = 0x00;
 
                        // pixBytes[bluePixelStart + 1] = 0xFF;
 
                        // pixBytes[bluePixelStart + 2] = 0xFF;
 
                        bluePixelStart += 3;
 
                    }
 
                    WriteLog("Color {0}, {1}, {2} is no longer blue at pixel offset {3},{4}", pixBytes[bluePixelStart], pixBytes[bluePixelStart + 1], pixBytes[bluePixelStart + 2], (bluePixelStart % stride)/3, bluePixelStart / stride);
 

	
 
                    reactedColor.Blue = (byte)Math.Round((float)bluePixelCount * 255f / (float)colorBarWidth);
 
                    WriteLog("Found the color swatch at {0}, {1}. Color={2} Red={3}px Green={4}px Blue={5}px", x, y, reactedColor, redPixelCount, greenPixelCount, bluePixelCount);
 
                    return true;
 
                }
 
            }
 
            return false;
 
        }
 

	
 
        unsafe public bool CaptureReaction(byte* pixBytes, int screenshotWidth, int screenshotHeight, int stride)
 
        {
 
            PaintColor reactedColor = new PaintColor();
 
            int redPixelStart = 0;
 
            ScreenWidth = screenshotWidth;
 
            ScreenHeight = screenshotHeight;
 
            IsCaptured = false;
 
            _recordedColor.Clear();
 

	
 
            IsCaptured = false;
 
            if (!firstRun)
 
            {
 
                // If this is not the first run, let's check the last location, to see if the UI is still there.
 
                if (TestPosition(lastSwatchX, lastSwatchY, pixBytes, screenshotWidth, screenshotHeight, stride, ref reactedColor, ref redPixelStart))
 
                {
 
                    IsCaptured = true;
 
                    RedBarX = (redPixelStart % stride) / 3;
 
                    RedBarY = redPixelStart / stride;
 
                    RecordedColor = reactedColor;
 
                    return true;
 
                }
 
                else
 
                {
 
                    firstRun = true;
 
                }
 
            }
 

	
 
            for (X = 0; X < screenshotWidth - colorBarWidth; ++X)
 
            {
 
                for (int Y = 0; Y < (screenshotHeight - (blueBarSpacing + 10)  /*53*/); ++Y)
 
                {
 
                    if (TestPosition(X, Y, pixBytes, screenshotWidth, screenshotHeight, stride, ref reactedColor, ref redPixelStart))
 
                    {
 
                        RedBarX = (redPixelStart % stride) / 3;
 
                        RedBarY = redPixelStart / stride;
 
                        RecordedColor = reactedColor;
 
                        lastSwatchX = X;
 
                        lastSwatchY = Y;
 
                        firstRun = false;
 
                        IsCaptured = true;
 
                        return true;
 
                    }
 
                    OnCaptureProgress?.Invoke(X, Y);
 
                }
 
            }
 
            return false;
 
        }
 

	
 
        public bool RecordReaction(PlayerProfile profile, PaintColor expectedColor, PaintColor reactedColor, Reagent[] reagents)
 
        {
 
            bool saved = false;
 
            int r, g, b;
 
            if (reagents[2] != null)
 
            {
 
                // A 3-reagent reaction.
 
                Reaction reaction1 = profile.FindReaction(reagents[0], reagents[1]);
 
                Reaction reaction2 = profile.FindReaction(reagents[0], reagents[2]);
 
                Reaction reaction3 = profile.FindReaction(reagents[1], reagents[2]);
 
                
 
                r = reactedColor.Red - expectedColor.Red;
 
                g = reactedColor.Green - expectedColor.Green;
 
                b = reactedColor.Blue - expectedColor.Blue;
 
                
 
                if (reaction2 == null)
 
                {
 
                    r = r - reaction1.Red - reaction3.Red;
 
                    g = g - reaction1.Green - reaction3.Green;
 
                    b = b - reaction1.Blue - reaction3.Blue;
 
                    Reaction reaction = new Reaction(r, g, b);
 
                    profile.SetReaction(reagents[0], reagents[2], reaction);
 
                    profile.Save();
 
                    saved = true;
 
                    OnReactionRecorded?.Invoke(reagents[0], reagents[2], reaction);
 
                }
 
                else if (reaction3 == null)
 
                {
 
                    r = r - reaction1.Red - reaction2.Red;
 
                    g = g - reaction1.Green - reaction2.Green;
 
                    b = b - reaction1.Blue - reaction2.Blue;
 
                    Reaction reaction = new Reaction(r, g, b);
 
                    profile.SetReaction(reagents[1], reagents[2], reaction);
 
                    profile.Save();
 
                    saved = true;
 
                    OnReactionRecorded?.Invoke(reagents[1], reagents[2], reaction);
 
                }
 
            }
 
            else if ((reagents[0] != null) && (reagents[1] != null))
 
            {
 
                // A 2-reagent reaction.
 
                r = reactedColor.Red - expectedColor.Red;
 
                g = reactedColor.Green - expectedColor.Green;
 
                b = reactedColor.Blue - expectedColor.Blue;
 
                Reaction reaction = new Reaction(r, g, b);
 
                profile.SetReaction(reagents[0], reagents[1], reaction);
 
                profile.Save();
0 comments (0 inline, 0 general)