diff --git a/DesertPaintLab.csproj b/DesertPaintLab.csproj --- a/DesertPaintLab.csproj +++ b/DesertPaintLab.csproj @@ -1,121 +1,165 @@ - - - - Debug - x86 - {1A885212-5FD2-4EBF-A98F-3EB1491A1CBB} - WinExe - DesertPaintLab - DesertPaintLab - v4.5 - - - true - full - false - bin\Debug - DEBUG - prompt - 4 - x86 - false - /unsafe - true - - - none - false - bin\Release - prompt - 4 - x86 - false - true - - - - - - - - - - - - - - gui.stetic - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - + + + + Debug + x86 + {1A885212-5FD2-4EBF-A98F-3EB1491A1CBB} + WinExe + DesertPaintLab + DesertPaintLab + v4.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + bin\Debug + DEBUG + prompt + 4 + x86 + false + /unsafe + true + + + none + false + bin\Release + prompt + 4 + x86 + false + true + + + true + bin\x64\Debug\ + DEBUG + true + full + x64 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x64\Release\ + true + x64 + prompt + MinimumRecommendedRules.ruleset + true + + + + + + + + + + + + + + gui.stetic + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + False + Microsoft .NET Framework 4.5 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + \ No newline at end of file diff --git a/DesertPaintLab.sln b/DesertPaintLab.sln --- a/DesertPaintLab.sln +++ b/DesertPaintLab.sln @@ -1,17 +1,28 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 +# 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 EndGlobal diff --git a/FileUtils.cs b/FileUtils.cs --- a/FileUtils.cs +++ b/FileUtils.cs @@ -115,6 +115,18 @@ namespace DesertPaintLab } return filePath; } + + public static string FindNumberedFile(string baseName, string extension, string folder) + { + string filename = ""; + int i = 0; + do + { + ++i; + filename = System.IO.Path.Combine(folder, String.Format("{0}_{1}.{2}", baseName, i, extension)); + } while (System.IO.File.Exists(filename)); + return filename; + } } } diff --git a/MainWindow.cs b/MainWindow.cs --- a/MainWindow.cs +++ b/MainWindow.cs @@ -139,7 +139,7 @@ public partial class MainWindow : Gtk.Wi screenBuffer = new Gdk.Pixbuf(Gdk.Colorspace.Rgb, false, 8, screenWidth, screenHeight); - ReactionRecorder.SetPixelMultiplier(pixelMultiplier); + ReactionRecorder.Instance.SetPixelMultiplier(pixelMultiplier); if (!OpenProfile()) { diff --git a/ReactionRecorder.cs b/ReactionRecorder.cs --- a/ReactionRecorder.cs +++ b/ReactionRecorder.cs @@ -21,6 +21,7 @@ */ using System; +using System.IO; namespace DesertPaintLab { @@ -38,14 +39,66 @@ namespace DesertPaintLab const int DEFAULT_SWATCH_TEST_WIDTH = 26; // width to test on ends of swatch (10% on either end) - static int swatchHeight = DEFAULT_SWATCH_HEIGHT; - static int swatchWidth = DEFAULT_SWATCH_WIDTH; - static int swatchTestWidth = DEFAULT_SWATCH_TEST_WIDTH; - static int colorBarWidth = DEFAULT_COLOR_BAR_WIDTH; - static int redBarSpacing = DEFAULT_RED_BAR_SPACING; - static int greenBarSpacing = DEFAULT_GREEN_BAR_SPACING; - static int blueBarSpacing = DEFAULT_BLUE_BAR_SPACING; - static int pixelMultiplier = 1; + int swatchHeight = DEFAULT_SWATCH_HEIGHT; + int swatchWidth = DEFAULT_SWATCH_WIDTH; + int swatchTestWidth = DEFAULT_SWATCH_TEST_WIDTH; + int colorBarWidth = DEFAULT_COLOR_BAR_WIDTH; + int redBarSpacing = DEFAULT_RED_BAR_SPACING; + int greenBarSpacing = DEFAULT_GREEN_BAR_SPACING; + int blueBarSpacing = DEFAULT_BLUE_BAR_SPACING; + int pixelMultiplier = 1; + + private static ReactionRecorder _instance; + public static ReactionRecorder Instance + { + get { + if (_instance == null) + { + _instance = new ReactionRecorder(); + } + return _instance; + } + } + + private StreamWriter _log; + public StreamWriter Log + { + set + { + _log = value; + } + } + private void WriteLog(string format, params object[] args) + { + if (_log != null) + { + _log.WriteLine(format, args); + } + } + + public ReactionRecorder() + { + this.pixelMultiplier = 1; + swatchHeight = DEFAULT_SWATCH_HEIGHT * pixelMultiplier; + swatchWidth = DEFAULT_SWATCH_WIDTH * pixelMultiplier; + colorBarWidth = DEFAULT_COLOR_BAR_WIDTH * pixelMultiplier; + redBarSpacing = DEFAULT_RED_BAR_SPACING * pixelMultiplier; + greenBarSpacing = DEFAULT_GREEN_BAR_SPACING * pixelMultiplier; + blueBarSpacing = DEFAULT_BLUE_BAR_SPACING * pixelMultiplier; + swatchTestWidth = DEFAULT_SWATCH_TEST_WIDTH * pixelMultiplier; + } + + public ReactionRecorder(int pixelMultiplier) + { + this.pixelMultiplier = pixelMultiplier; + swatchHeight = DEFAULT_SWATCH_HEIGHT * pixelMultiplier; + swatchWidth = DEFAULT_SWATCH_WIDTH * pixelMultiplier; + colorBarWidth = DEFAULT_COLOR_BAR_WIDTH * pixelMultiplier; + redBarSpacing = DEFAULT_RED_BAR_SPACING * pixelMultiplier; + greenBarSpacing = DEFAULT_GREEN_BAR_SPACING * pixelMultiplier; + blueBarSpacing = DEFAULT_BLUE_BAR_SPACING * pixelMultiplier; + swatchTestWidth = DEFAULT_SWATCH_TEST_WIDTH * pixelMultiplier; + } private static bool IsPapyTexture(byte r, byte g, byte b) { @@ -56,7 +109,7 @@ namespace DesertPaintLab ((r < 0xF4) && (g <= 0xED) && (b <= 0xCD)); } - public static void SetPixelMultiplier(int pixelMultiplier) + public void SetPixelMultiplier(int pixelMultiplier) { swatchHeight = DEFAULT_SWATCH_HEIGHT * pixelMultiplier; swatchWidth = DEFAULT_SWATCH_WIDTH * pixelMultiplier; @@ -65,10 +118,10 @@ namespace DesertPaintLab greenBarSpacing = DEFAULT_GREEN_BAR_SPACING * pixelMultiplier; blueBarSpacing = DEFAULT_BLUE_BAR_SPACING * pixelMultiplier; swatchTestWidth = DEFAULT_SWATCH_TEST_WIDTH * pixelMultiplier; - ReactionRecorder.pixelMultiplier = pixelMultiplier; + this.pixelMultiplier = pixelMultiplier; } - unsafe private static void ColorAt(byte *pixBytes, int x, int y, int stride, out byte r, out byte g, out byte b) + 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]; @@ -76,19 +129,19 @@ namespace DesertPaintLab b = pixBytes[pixelStart + 2]; } - private static bool IsDarkPixel(int r, int g, int b) + private bool IsDarkPixel(int r, int g, int b) { return ((r < 0x46) && (g < 0x46) && (b < 0x47)); } - private static bool IsColorMatch(byte test_r, byte test_g, byte test_b, byte target_r, byte target_g, byte target_b) + 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 static bool IsPossibleSwatchSlice(byte* pixBytes, int x, int y, int stride) + unsafe private bool IsPossibleSwatchSlice(byte* pixBytes, int x, int y, int stride) { int testPixelStart = (y * stride) + (x * 3); byte r = pixBytes[testPixelStart]; @@ -127,16 +180,22 @@ namespace DesertPaintLab 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 static bool IsPossibleSwatchUpperLeft(byte* pixBytes, int x, int y, int stride) + unsafe private bool IsPossibleSwatchUpperLeft(byte* pixBytes, int x, int y, int stride) { int testPixelStart = (y * stride) + (x * 3); bool result = true; // test the left edge for dark pixels - for (int i = 0; result && (i < swatchHeight-pixelMultiplier); ++i) + 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]); @@ -144,21 +203,26 @@ namespace DesertPaintLab // test the dark top border and for papyrus above and below the swatch int papyErrorCount = 0; - for (int i = 0; result && (i < swatchWidth); ++i) + for (i = 0; result && (i < swatchWidth); ++i) { int otherPixelStart = testPixelStart + (3 * i); result &= IsDarkPixel(pixBytes[otherPixelStart], pixBytes[otherPixelStart+1], pixBytes[otherPixelStart+2]); otherPixelStart = otherPixelStart - stride; - papyErrorCount += (IsPapyTexture(pixBytes[otherPixelStart], pixBytes[otherPixelStart+1], pixBytes[otherPixelStart+2]) ? 1 : 0); + papyErrorCount += (IsPapyTexture(pixBytes[otherPixelStart], pixBytes[otherPixelStart+1], pixBytes[otherPixelStart+2]) ? 0 : 1); otherPixelStart = testPixelStart + (stride * swatchHeight) + (3 * i); - papyErrorCount += (IsPapyTexture(pixBytes[otherPixelStart], pixBytes[otherPixelStart+1], pixBytes[otherPixelStart+2]) ? 1 : 0); - result &= (papyErrorCount < (swatchWidth / 20)); // allow up to 5% error rate checking for papy texture, because this seems to be inconsistent + 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)))) + { + 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 public static bool CaptureReaction(byte* pixBytes, int screenshotWidth, int screenshotHeight, int stride, ref PaintColor reactedColor, ref int redPixelStart) + unsafe public bool CaptureReaction(byte* pixBytes, int screenshotWidth, int screenshotHeight, int stride, ref PaintColor reactedColor, ref int redPixelStart) { byte pixel_r, pixel_g, pixel_b; int pixelStart, otherPixelStart; @@ -248,6 +312,7 @@ namespace DesertPaintLab } reactedColor.Blue = (byte)Math.Round((float)bluePixelCount * 255f / (float)colorBarWidth); + WriteLog("Found the color swatch at {0}, {1}. Color={2}", x, y, reactedColor); return true; } } @@ -256,7 +321,7 @@ namespace DesertPaintLab return false; } - public static bool RecordReaction(PlayerProfile profile, PaintColor expectedColor, PaintColor reactedColor, Reagent[] reagents) + public bool RecordReaction(PlayerProfile profile, PaintColor expectedColor, PaintColor reactedColor, Reagent[] reagents) { bool saved = false; int r, g, b; diff --git a/UI/CaptureView.cs b/UI/CaptureView.cs --- a/UI/CaptureView.cs +++ b/UI/CaptureView.cs @@ -1,5 +1,6 @@ using System; - +using System.IO; + namespace DesertPaintLab { [System.ComponentModel.ToolboxItem(true)] @@ -260,22 +261,29 @@ namespace DesertPaintLab // rootWindow.Colormap, 0, 0, 0, 0, screenWidth, screenHeight); int stride = screenBuffer.Rowstride; byte* pixBytes = (byte*)screenBuffer.Pixels; - int redPixelStart = -1; - - isCaptured = ReactionRecorder.CaptureReaction(pixBytes, screenWidth, screenHeight, stride, ref reactedColor, ref redPixelStart); + int redPixelStart = -1; + + StreamWriter log = null; + if (enableDebugMenu) + { + string logfile = FileUtils.FindNumberedFile("DesertPaintLab_Capture", "log", Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)); + log = new StreamWriter(logfile); + ReactionRecorder.Instance.Log = log; + } + isCaptured = ReactionRecorder.Instance.CaptureReaction(pixBytes, screenWidth, screenHeight, stride, ref reactedColor, ref redPixelStart); + if (log != null) + { + log.Flush(); + log.Close(); + log = null; + } if (enableDebugMenu && debugScreenshot) { if (!isCaptured) { // write out the whole screenshot on a failure to capture string screenshotDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); - string filename; - int i = 0; - do - { - ++i; - filename = System.IO.Path.Combine(screenshotDir, String.Format("DesertPaintLab_Colormatch{0}.png", i)); - } while (System.IO.File.Exists(filename)); + string filename = FileUtils.FindNumberedFile("DesertPaintLab_Colormatch", "png", screenshotDir); screenBuffer.Save(filename, "png"); } else @@ -286,16 +294,10 @@ namespace DesertPaintLab int redPixelStartY = (redPixelStart / stride); // write out the screenshot string screenshotDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); - string filename; - int i = 0; - do - { - ++i; - filename = System.IO.Path.Combine(screenshotDir, String.Format("DesertPaintLab_Colormatch{0}.png", i)); - } while (System.IO.File.Exists(filename)); + string filename = FileUtils.FindNumberedFile("DesertPaintLab_Colormatch", "png", screenshotDir); int captureAreaWidth = Math.Min(64, screenWidth - redPixelStartX + 64); int captureAreaHeight = Math.Min(64, screenHeight - redPixelStartY + 64); - Gdk.Pixbuf outPixBuf = new Gdk.Pixbuf(screenBuffer, Math.Max(0, redPixelStartX - 16), Math.Max(0, redPixelStartY - 16), captureAreaWidth, captureAreaHeight); + Gdk.Pixbuf outPixBuf = new Gdk.Pixbuf(screenBuffer, Math.Max(0, redPixelStartX - 32), Math.Max(0, redPixelStartY - 32), captureAreaWidth, captureAreaHeight); //screenBuffer.Save(filename, "png"); outPixBuf.Save(filename, "png"); } @@ -371,7 +373,7 @@ namespace DesertPaintLab protected virtual void OnRecord(object sender, System.EventArgs e) { - if (ReactionRecorder.RecordReaction(profile, expectedColor, reactedColor, reagents)) + if (ReactionRecorder.Instance.RecordReaction(profile, expectedColor, reactedColor, reagents)) { recordButton.Sensitive = false; }