Changeset - cca1f914411a
[Not reviewed]
T9_V6 default
0 4 0
Jason Maltzen - 5 years ago 2019-11-04 19:08:16
jason@hiddenachievement.com
More fixes for finding the pigment lab interface. Add a function for loading a saved screenshot and scanning it instead of a screen capture.
4 files changed with 212 insertions and 69 deletions:
0 comments (0 inline, 0 general)
PaintColor.cs
Show inline comments
...
 
@@ -87,38 +87,38 @@ namespace DesertPaintLab
 
        public PaintColor(PaintColor other)
 
        {
 
            name = other.name;
 
            red = other.red;
 
            green = other.green;
 
            blue = other.blue;
 
        }
 
				
 
		public int GetDistanceSquared(PaintColor otherColor)
 
		{
 
			return (int)(Math.Pow(this.red - otherColor.red, 2) +
 
				Math.Pow(this.green - otherColor.green, 2) +
 
				Math.Pow(this.blue - otherColor.blue, 2));
 
		}
 
		
 
		public void Clear()
 
		{
 
			red = 0;
 
			green = 0;
 
			blue = 0;
 
		}
 

	
 
        public void Set(PaintColor other)
 
        {
 
            this.red = other.red;
 
            this.green = other.green;
 
            this.blue = other.blue;
 
            this.name = other.name;
 
        }
 
		
 
		public override string ToString()
 
		{
 
			return "[" + name + ", " + red + ", " + green + ", " + blue + "]";
 
			return "[" + name + ": " + red + ", " + green + ", " + blue + "]";
 
		}
 
		
 
	}
 
}
 

	
ReactionRecorder.cs
Show inline comments
...
 
@@ -25,436 +25,465 @@ using System.IO;
 

	
 
namespace DesertPaintLab
 
{
 
    // ReactionRecorder - business logic for recording paint reactions
 
    public class ReactionRecorder
 
    {
 
        public delegate void CaptureProgressHandler(int x, int y);
 
        public CaptureProgressHandler OnCaptureProgress;
 

	
 
        const int COLOR_TOLERANCE = 3;
 

	
 
        const InterfaceSize DEFAULT_INTERFACE_SIZE = InterfaceSize.Small;
 

	
 
        // Swatch is 301x20 solid color, 1px darker left/top, 1px black outer left/top, 1px light right/bottom, 1px bright right/bottom
 
        // top-right and bottom-left are bright instead of black
 

	
 
        // Color bars are 4x302 solid color, 2px border on all sides (darker left/top, lighter bottom/right
 

	
 
        public static readonly int[] SWATCH_HEIGHT = 
 
        {
 
            24, // tiny
 
            24, // small
 
            24, // medium
 
            24, // large
 
            24 // huge
 
        };
 
        public static readonly int[] SWATCH_WIDTH =
 
        {
 
            306, // tiny
 
            306, // small
 
            306, // medium
 
            320, // large
 
            320 // huge
 
            350 // huge
 
        };
 
        public static readonly int[] COLOR_BAR_WIDTH =
 
        {
 
            306, // tiny
 
            306, // small -- includes left and right borders
 
            306, // medium
 
            320, // large
 
            320 // huge
 
            350 // huge
 
        };
 
        public static readonly int[] RED_BAR_SPACING =
 
        {
 
            32, // tiny
 
            32, // small
 
            32, // medium
 
            32, // large
 
            32 // huge
 
        };
 
        public static readonly int[] GREEN_BAR_SPACING =
 
        {
 
            42, // tiny
 
            42, // small
 
            42, // medium
 
            42, // large
 
            42 // huge
 
        };
 
        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;
 
        public int PixelMultiplier
 
        {
 
            get
 
            {
 
                return pixelMultiplier;
 
            }
 
            set
 
            {
 
                pixelMultiplier = value;
 
                UpdateSwatchSizes();
 
            }
 
        }
 
        private InterfaceSize interfaceSize;
 
        public InterfaceSize InterfaceSize
 
        {
 
            get
 
            {
 
                return interfaceSize;
 
            }
 
            set
 
            {
 
                interfaceSize = value;
 
                UpdateSwatchSizes();
 
            }
 
        }
 

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

	
 
        private class PixelColor
 
        {
 
            public byte r;
 
            public byte g;
 
            public byte b;
 

	
 
            public bool IsMatch(PixelColor other)
 
            {
 
                return ((Math.Abs(r - other.r) <= COLOR_TOLERANCE) &&
 
                    (Math.Abs(g - other.g) <= COLOR_TOLERANCE) &&
 
                    (Math.Abs(b - other.b) <= COLOR_TOLERANCE));
 
            }
 

	
 
            public static bool IsDark(PixelColor color)
 
            {
 
                return (color.r < 0x47) && (color.g < 0x47) && (color.b < 0x47);
 
            }
 

	
 
            public static bool IsPapyrus(PixelColor color)
 
            {
 
                // red between 208 and 244
 
                // 240 and 255
 
                // green between 192 and 237
 
                // 223 and 248
 
                // 223 and 250
 
                // blue between 145 and 205
 
                // 178 and 232
 
                //return ((r > 0xD0) && (g >= 0xC0) && (b >= 0x91)) &&
 
                //       ((r < 0xF4) && (g <= 0xED) && (b <= 0xCD));
 
                return ((color.r >= 0xF0) && (color.r <= 0xFF) && (color.g >= 0xDF) && (color.g <= 0xF8) && (color.b >= 0xB2) && (color.b <= 0xE8));
 
                return ((color.r >= 0xF0) && (color.r <= 0xFF) && (color.g >= 0xDF) && (color.g <= 0xFA) && (color.b >= 0xB2) && (color.b <= 0xE8));
 
            }
 

	
 
            public static bool IsRed(PixelColor color)
 
            {
 
                return (color.r > 0x9F) && (color.g < 0x62) && (color.b < 0x62);
 
            }
 
            public static bool IsGreen(PixelColor color)
 
            {
 
                return (color.r < 0x62) && (color.g > 0x9F) && (color.b < 0x62);
 
            }
 
            public static bool IsBlue(PixelColor color)
 
            {
 
                return (color.r < 0x62) && (color.g < 0x62) && (color.b > 0x9F);
 
            }
 
        }
 

	
 
        unsafe private class Pixels
 
        {
 
            public byte* pixBytes;
 
            public int width;
 
            public int height;
 
            public int stride;
 
            public int pixelMultiplier;
 
            public int numChannels;
 

	
 
            private PixelColor _tempColor = new PixelColor();
 
            private PixelColor _tempColor2 = new PixelColor();
 

	
 
            public int ComputeOffset(int x, int y)
 
            {
 
                return (y * pixelMultiplier * stride) + (x * pixelMultiplier * 3);
 
                return (y * pixelMultiplier * stride) + (x * pixelMultiplier * numChannels);
 
            }
 

	
 
            public int UpdateOffset(int offset, int xChange, int yChange)
 
            {
 
                return offset + (yChange * pixelMultiplier * stride) + (xChange * pixelMultiplier * 3);
 
                return offset + (yChange * pixelMultiplier * stride) + (xChange * pixelMultiplier * numChannels);
 
            }
 

	
 
            unsafe public void ColorAt(int offset, ref PixelColor pixel)
 
            {
 
                pixel.r = pixBytes[offset];
 
                pixel.g = pixBytes[offset + 1];
 
                pixel.b = pixBytes[offset + 2];
 
            }
 

	
 
            unsafe public void ColorAt(int x, int y, ref PixelColor pixel)
 
            {
 
                int offset = ComputeOffset(x, y);
 
                ColorAt(offset, ref pixel);
 
            }
 

	
 
            unsafe public bool DoesPixelMatch(int x, int y, Func<PixelColor, bool> matchFunc)
 
            {
 
                int offset = ComputeOffset(x, y);
 
                ColorAt(offset, ref _tempColor);
 
                return matchFunc(_tempColor);
 
            }
 

	
 
            unsafe public bool DoesPixelMatch(int offset, Func<PixelColor, bool> matchFunc)
 
            {
 
                ColorAt(offset, ref _tempColor);
 
                return matchFunc(_tempColor);
 
            }
 

	
 
            // Compute length of horizontal bar starting at x,y using matching function
 
            unsafe public int LengthOfColorAt(int x, int y, Func<PixelColor, bool> matchesColor)
 
            {
 
                int count = 0;
 
                for (int xVal = x; xVal < width; ++xVal)
 
                {
 
                    int offset = ComputeOffset(xVal, y);
 
                    if (!DoesPixelMatch(xVal, y, matchesColor)) break;
 
                    ++count;
 
                }
 
                return count;
 
            }
 

	
 
            unsafe public bool IsSolidPatchAt(int x, int y, int patchWidth, int patchHeight)
 
            {
 
                if ((x + patchWidth >= width) || (y + patchHeight >= height)) return false;
 
                ColorAt(x, y, ref _tempColor2);
 
                Console.WriteLine("color at {0},{1} = {2},{3},{4}", x, y, _tempColor2.r, _tempColor2.g, _tempColor2.b);
 
                bool ok = true;
 
                ok &= DoesPixelMatch(x + patchWidth - 1, y, _tempColor2.IsMatch);
 
                ok &= DoesPixelMatch(x, y + patchHeight - 1, _tempColor2.IsMatch);
 
                ok &= DoesPixelMatch(x + patchWidth - 1, y + patchHeight - 1, _tempColor2.IsMatch);
 
                for (int xOff = 1; ok && (xOff < patchWidth - 1); ++xOff)
 
                {
 
                    ok &= DoesPixelMatch(x + xOff, y, _tempColor2.IsMatch);
 
                    ok &= DoesPixelMatch(x + xOff, y + patchHeight - 1, _tempColor2.IsMatch);
 
                }
 
                for (int yOff = 1; ok && (yOff < patchHeight - 1); ++yOff)
 
                {
 
                    ok &= DoesPixelMatch(x, y + yOff, _tempColor2.IsMatch);
 
                    ok &= DoesPixelMatch(x + patchWidth - 1, y + yOff, _tempColor2.IsMatch);
 
                }
 
                for (int yOff = 1; ok && (yOff < patchHeight - 1); ++yOff)
 
                {
 
                    for (int xOff = 1; ok && (xOff < patchWidth - 1); ++xOff)
 
                    {
 
                        ok &= DoesPixelMatch(x + xOff, y + yOff, _tempColor2.IsMatch);
 
                    }
 
                }
 
                return ok;
 
            }
 
        }
 

	
 
        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;
 

	
 
            UpdateSwatchSizes();
 
        }
 

	
 
        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;
 

	
 
            UpdateSwatchSizes();
 
        }
 

	
 
        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;
 

	
 
            UpdateSwatchSizes();
 
        }
 

	
 
        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;
 
            UpdateSwatchSizes();
 
        }
 

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

	
 
            UpdateSwatchSizes();
 
        }
 

	
 
        private void UpdateSwatchSizes()
 
        {
 
            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 bool IsPossibleSwatchSlice(Pixels pixels, int x, int y)
 
        {
 
            int testPixelStart = pixels.ComputeOffset(x, y);
 
            PixelColor color = new PixelColor();
 
            pixels.ColorAt(x, y, ref color);
 

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

	
 
            // 1.) Check if the top pixel is a dark pixel.
 
            isDarkPixel = PixelColor.IsDark(color);
 
            
 
            //// 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
 
            // 2 down from top border
 
            pixels.ColorAt(x, y + 2, ref color);
 

	
 
            // scan the column from 2 below the top to 3 above the bottom to ensure the color matches
 
            for (int i = 2; result && (i < (swatchHeight-3)); ++i)
 
            {
 
                result &= pixels.DoesPixelMatch(x, y + i, color.IsMatch);
 
            }
 

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

	
 
            return result;
 
        }
 

	
 
        unsafe private bool IsPossibleSwatchUpperLeft(Pixels pixels, int x, int y)
 
        {
 
            int testPixelStart = pixels.ComputeOffset(x, y);
 

	
 
            if (testPixelStart < pixels.stride)
 
            {
 
                WriteLog("Can't test {0},{1} - is not at least 1 row in", x, y);
 
                return false;
 
            }
 

	
 
            bool result = true;
 

	
 
            int swatchSolidWidth = swatchWidth - 4;
 
            int swatchSolidHeight = swatchHeight - 5; // 2 top and 3 bottom pixels are slightly different colors
 
            int swatchSolidLeftX = x + 2;
 
            int swatchSolidTopY = y + 2;
 
            int swatchSolidRightX = swatchSolidLeftX + swatchSolidWidth - 1;
 
            int swatchSolidBottomY = swatchSolidTopY + swatchSolidHeight - 1;
 

	
 
            PixelColor swatchColor = new PixelColor();
 
            pixels.ColorAt(swatchSolidLeftX, swatchSolidTopY, ref swatchColor);
 

	
 
            // Check the other 3 corners of the swatch size for color match
 
            PixelColor testColor = new PixelColor();
 
            result &= pixels.DoesPixelMatch(swatchSolidRightX, swatchSolidTopY, swatchColor.IsMatch);
 
            result &= pixels.DoesPixelMatch(swatchSolidLeftX, swatchSolidBottomY, swatchColor.IsMatch);
 
            result &= pixels.DoesPixelMatch(swatchSolidRightX, swatchSolidBottomY, swatchColor.IsMatch);
 
            bool upperRightResult = pixels.DoesPixelMatch(swatchSolidRightX, swatchSolidTopY, swatchColor.IsMatch);
 
            //if (!upperRightResult)
 
            //{
 
            //    pixels.ColorAt(swatchSolidRightX, swatchSolidTopY, ref testColor);
 
            //    WriteLog("Upper-right mismatch for {8}, {9} - found {0},{1},{2} at {3}, {4} expected {5},{6},{7}", testColor.r, testColor.g, testColor.b, swatchSolidRightX, swatchSolidTopY, swatchColor.r, swatchColor.g, swatchColor.b, x, y);
 
            //}
 
            bool lowerLeftResult = pixels.DoesPixelMatch(swatchSolidLeftX, swatchSolidBottomY, swatchColor.IsMatch);
 
            //if (!lowerLeftResult)
 
            //{
 
            //    pixels.ColorAt(swatchSolidLeftX, swatchSolidBottomY, ref testColor);
 
            //    WriteLog("Lower-left mismatch for {8}, {9} - found {0},{1},{2} at {3}, {4} expected {5},{6},{7}", testColor.r, testColor.g, testColor.b, swatchSolidLeftX, swatchSolidBottomY, swatchColor.r, swatchColor.g, swatchColor.b, x, y);
 
            //}
 
            bool lowerRightResult = pixels.DoesPixelMatch(swatchSolidRightX, swatchSolidBottomY, swatchColor.IsMatch);
 
            //if (!lowerRightResult)
 
            //{
 
            //    pixels.ColorAt(swatchSolidRightX, swatchSolidBottomY, ref testColor);
 
            //    WriteLog("Lower-right mismatch for {8}, {9} - found {0},{1},{2} at {3}, {4} expected {5},{6},{7}", testColor.r, testColor.g, testColor.b, swatchSolidRightX, swatchSolidBottomY, swatchColor.r, swatchColor.g, swatchColor.b, x, y);
 
            //}
 

	
 
            result &= upperRightResult;
 
            result &= lowerLeftResult;
 
            result &= lowerRightResult;
 
            if (!result)
 
            {
 
                // Box corners test failed
 
                // 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;
 
            }
 

	
 
            // scan down the right and left sides
 
            for (int yOff = 1; yOff < (swatchSolidHeight - 1); ++yOff)
 
            {
 
                result &= pixels.DoesPixelMatch(swatchSolidLeftX, swatchSolidTopY + yOff, swatchColor.IsMatch);
 
                if (!result)
 
                {
 
                    pixels.ColorAt(swatchSolidLeftX, swatchSolidTopY + yOff, ref testColor);
 
                    break;
 
                }
 
                result &= pixels.DoesPixelMatch(swatchSolidRightX, swatchSolidTopY + yOff, swatchColor.IsMatch);
 
                if (!result)
 
                {
 
                    pixels.ColorAt(swatchSolidRightX, swatchSolidTopY + yOff, ref testColor);
 
                }
 
            }
 
            if (!result)
 
            {
 
                WriteLog("Failed to find left/right edges for potential swatch of color {2}, {3}, {4} at {0}, {1} [failed color = {5},{6},{7}]", x, y, swatchColor.r, swatchColor.g, swatchColor.b, testColor.r, testColor.g, testColor.b);
 
                return false;
 
            }
 
            for (int xOff = 1; xOff < (swatchSolidWidth - 1); ++xOff)
 
            {
 
                result &= pixels.DoesPixelMatch(swatchSolidLeftX + xOff, swatchSolidTopY, swatchColor.IsMatch);
 
                if (!result)
 
                {
...
 
@@ -462,206 +491,227 @@ namespace DesertPaintLab
 
                    break;
 
                }
 
                result &= pixels.DoesPixelMatch(swatchSolidLeftX + xOff, swatchSolidBottomY, swatchColor.IsMatch);
 
                if (!result)
 
                {
 
                    pixels.ColorAt(swatchSolidLeftX + xOff, swatchSolidBottomY, ref testColor);
 
                    break;
 
                }
 
            }
 
            if (!result)
 
            {
 
                WriteLog("Failed to match upper/lower edges for potential swatch of color {2}, {3}, {4} at {0}, {1} [failed color = {5},{6},{7}]", x, y, swatchColor.r, swatchColor.g, swatchColor.b, testColor.r, testColor.g, testColor.b);
 
                return false;
 
            }
 

	
 

	
 
            // test the left edge for dark pixels -- the bottom-most pixel is bright now
 
            int i = 0;
 
            for (i = 1; result && i < swatchHeight - 1; ++i)
 
            {
 
                result &= pixels.DoesPixelMatch(x, y + i, PixelColor.IsDark);
 
            }
 
            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, swatchColor.r, swatchColor.g, swatchColor.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)
 
            for (i = 0; result && (i < swatchWidth - 1); ++i)
 
            {
 
                bool isBorder = pixels.DoesPixelMatch(x + i, y, PixelColor.IsDark);
 
                result &= isBorder;
 
                if (!isBorder)
 
                {
 
                    WriteLog("Probably swatch at {0},{1} failed upper border test at {2},{3}", x, y, x + i, y);
 
                    borderError = true;
 
                }
 

	
 
                // Checking along the top of the swatch for papyrus
 
                // The row just above is shaded, so check 2 above
 
                if (y > 1)
 
                {
 
                    bool isPapyrus = pixels.DoesPixelMatch(x + i, y - 2, PixelColor.IsPapyrus);
 
                    papyErrorCount += isPapyrus ? 0 : 1;
 

	
 
                }
 
                else
 
                {
 
                    ++papyErrorCount;
 
                }
 

	
 
                // Checking along the bottom of the swatch for papyrus
 
                if (y < pixels.height)
 
                {
 
                    bool isPapyrus = pixels.DoesPixelMatch(x + i, y + swatchHeight, PixelColor.IsPapyrus);
 
                    papyErrorCount += isPapyrus ? 0 : 1;
 
                }
 
                else
 
                {
 
                    ++papyErrorCount;
 
                }
 
            }
 

	
 
            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, Pixels pixels, ref PaintColor reactedColor, ref int redPixelStart)
 
        {
 
            PixelColor pixelColor = new PixelColor();
 

	
 
            // Check 4 corners of solid area and left/right solid bar areas
 
            bool foundSwatch = IsPossibleSwatchUpperLeft(pixels, x, y); // ((pixel_r < 0x46) && (pixel_g < 0x46) && (pixel_b < 0x46));
 
            if (foundSwatch)
 
            {
 
                WriteLog("Found probable swatch at {0},{1} - checking border slices", x, y);
 
                int borderXOffset = 0;
 
                for (borderXOffset = 2; foundSwatch && (borderXOffset < swatchTestWidth); ++borderXOffset)
 
                {
 
                    foundSwatch &= IsPossibleSwatchSlice(pixels, x + borderXOffset, y);
 
                    if (!foundSwatch)
 
                    {
 
                        WriteLog("Failed slice test at {0},{1}", x + borderXOffset, y);
 
                        break;
 
                    }
 
                    foundSwatch &= IsPossibleSwatchSlice(pixels, x + swatchWidth - borderXOffset, y);
 
                    if (!foundSwatch)
 
                    {
 
                        WriteLog("Failed slice test at {0},{1}", x + swatchWidth - borderXOffset, y);
 
                        break;
 
                    }
 
                }
 
            }
 

	
 
            if (foundSwatch)
 
            {
 
                // WE FOUND THE SWATCH!
 
                // Now we know where the color bars are.
 
                int redPixelCount = pixels.LengthOfColorAt(x, y + redBarSpacing, PixelColor.IsRed);
 
                reactedColor.Red = (byte)Math.Round((float)redPixelCount * 255f / (float)colorBarWidth);
 

	
 
                int greenPixelCount = pixels.LengthOfColorAt(x, y + greenBarSpacing, PixelColor.IsGreen);
 
                reactedColor.Green = (byte)Math.Round((float)greenPixelCount * 255f / (float)colorBarWidth);
 

	
 
                int bluePixelCount = pixels.LengthOfColorAt(x, y + blueBarSpacing, PixelColor.IsBlue);
 
                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)
 
        unsafe public bool CaptureReaction(byte* pixBytes, int screenshotWidth, int screenshotHeight, int stride, int numChannels, int bitsPerSample)
 
        {
 
            PaintColor reactedColor = new PaintColor();
 
            int redPixelStart = 0;
 
            ScreenWidth = screenshotWidth;
 
            ScreenHeight = screenshotHeight;
 
            IsCaptured = false;
 
            _recordedColor.Clear();
 

	
 
            Pixels pixels = new Pixels
 
            {
 
                pixBytes = pixBytes,
 
                width = screenshotWidth,
 
                height = screenshotHeight,
 
                stride = stride,
 
                numChannels = numChannels,
 
                pixelMultiplier = pixelMultiplier
 
            };
 
            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, pixels, ref reactedColor, ref redPixelStart))
 
                {
 
                    IsCaptured = true;
 
                    RedBarX = (redPixelStart % stride) / 3;
 
                    RedBarX = (redPixelStart % stride) / numChannels;
 
                    RedBarY = redPixelStart / stride;
 
                    RecordedColor = reactedColor;
 
                    return true;
 
                }
 
                else
 
                {
 
                    firstRun = true;
 
                }
 
            }
 

	
 
            int startX;
 
            int endX;
 
            int startY;
 
            int endY;
 
            DesertPaintLab.AppSettings.Get("ScanOffsetX", out startX);
 
            DesertPaintLab.AppSettings.Get("ScanOffsetY", out startY);
 
            DesertPaintLab.AppSettings.Get("ScanLimitX", out endX);
 
            DesertPaintLab.AppSettings.Get("ScanLimitY", out endY);
 
            if (endX == 0) endX = screenshotWidth;
 
            if (endY == 0) endY = screenshotHeight;
 
            if (endX > screenshotWidth) endX = screenshotWidth;
 
            if (endY > screenshotHeight) endY = screenshotHeight;
 
            if (startX < 2) startX = 2;
 
            if (startY < 2) startY = 2;
 
            if (startX > screenshotWidth) startX = 2;
 
            if (startY > screenshotHeight) startY = 2;
 

	
 
            int patchTestSize = (swatchHeight / 2) - 1;
 
            int patchTestSize = ((swatchHeight - 5) / 2) - 1;
 
            PixelColor patchColor = new PixelColor();
 
            for (int roughX = startX; roughX < endX - colorBarWidth - patchTestSize; roughX += patchTestSize)
 
            for (int roughX = startX; roughX < endX - colorBarWidth + patchTestSize; roughX += patchTestSize)
 
            {
 
                for (int roughY = startY; roughY < (endY - (blueBarSpacing + 10) - patchTestSize  /*53*/); roughY += patchTestSize)
 
                for (int roughY = startY; roughY < (endY - (blueBarSpacing + 10) + patchTestSize  /*53*/); roughY += patchTestSize)
 
                {
 
                    OnCaptureProgress?.Invoke(roughX, roughY);
 
                    if (!pixels.IsSolidPatchAt(roughX, roughY, patchTestSize, patchTestSize)) continue;
 
                    pixels.ColorAt(roughX, roughY, ref patchColor);
 
                    for (X = roughX - patchTestSize; X < roughX; ++X)
 
                    //Console.WriteLine("Found a solid patch of {2},{3},{4} at {0}, {1}", roughX, roughY, patchColor.r, patchColor.g, patchColor.b);
 
                    for (X = Math.Max(0, roughX - patchTestSize); X < roughX; ++X)
 
                    {
 
                        for (Y = roughY - patchTestSize; Y < roughY; ++Y)
 
                        for (Y = Math.Max(0, roughY - patchTestSize); Y < roughY; ++Y)
 
                        {
 
                            //WriteLog("Checking for potential swatch at {0},{1} after found square at {2},{3}", X, Y, roughX, roughY);
 
                            if (TestPosition(X, Y, pixels, ref reactedColor, ref redPixelStart))
 
                            {
 
                                RedBarX = (redPixelStart % stride) / 3;
 
                                RedBarX = (redPixelStart % stride) / numChannels;
 
                                RedBarY = redPixelStart / stride;
 
                                RecordedColor = reactedColor;
 
                                lastSwatchX = X;
 
                                lastSwatchY = Y;
 
                                firstRun = false;
 
                                IsCaptured = true;
 
                                return true;
 
                            }
 
                        }
 
                    }
 
                    //System.Console.WriteLine("False-positive patch of color {0},{1},{2} at {3},{4}", patchColor.r, patchColor.g, patchColor.b, roughX, roughY);
 
                }
 
            }
 
            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)
UI/CaptureView.cs
Show inline comments
 
using System;
 
using System.IO;
 
using System.Threading;
 

	
 
namespace DesertPaintLab
 
{
 
    [System.ComponentModel.ToolboxItem(true)]
 
    public partial class CaptureView : Gtk.Bin
 
    {
 

	
 
        Reagent[] reagents = new Reagent[3];
 
        PaintRecipe recipe = new PaintRecipe();
 

	
 
        PaintColor expectedColor = new PaintColor();
 
        PaintColor reactedColor = new PaintColor();
 

	
 
        PlayerProfile profile = null;
 

	
 
        Gdk.Pixbuf screenBuffer = null;
 

	
 
        Gtk.ThreadNotify notifyCaptureProgress;
 
        Gtk.ThreadNotify notifyCaptureComplete;
 

	
 
        bool recordEnabled = true;
 
        bool isCaptured = false;
 

	
 
        public CaptureView(PlayerProfile profile, Gdk.Pixbuf screenBuffer) : base()
 
        public CaptureView(PlayerProfile profile, Gdk.Pixbuf screenBuffer)
 
        {
 
            this.profile = profile;
 
            this.screenBuffer = screenBuffer;
 

	
 
            this.Build();
 

	
 
            reagents[0] = null;
 
            reagents[1] = null;
 
            reagents[2] = null;
 

	
 
            if (unmodifiedSwatch != null)
 
            {
 
                unmodifiedSwatch.Clear();
 
            }
 
            if (reactionSwatch != null)
 
            {
 
                reactionSwatch.Clear();
 
            }
 

	
 
            PopulateDropDowns();
 
            recipe.Reactions = profile.Reactions;
 
        }
 

	
 
        public Gdk.Pixbuf ScreenBuffer
 
        {
 
            get { return screenBuffer; }
 
            set {
 
                if (screenBuffer != null)
 
                {
 
                    // TODO: free it up?
 
                }
 
                screenBuffer = value;
 
            }
 
        }
 

	
 
        public Gdk.Pixbuf RecordBuffer
 
        {
 
            get; set;
 
        }
 

	
 
        public PlayerProfile Profile
 
        {
 
            set {
 
                profile = value;
 
                PopulateDropDowns();
 
                recipe.Reactions = profile.Reactions;
 
                UpdateIngredients();
 
                // TODO: reset views
 
            }
 
        }
 

	
 
        public void DisableRecord()
 
        {
 
            recordEnabled = false;
 
            recordButton.Sensitive = false;
 
        }
 

	
 
        public void EnableRecord()
 
        {
 
            recordEnabled = true;
 
            if (isCaptured)
 
            {
 
                recordEnabled = true;
 
            }
 
            UpdateIngredients();
 
        }
 

	
 
        public void PopulateDropDowns()
 
        {
 
            ReagentManager.PopulateReagents(ref ingredient1ComboBox);
 
            ReagentManager.PopulateReagents(ref ingredient2ComboBox);
 
            ReagentManager.PopulateReagents(ref ingredient3ComboBox);
...
 
@@ -269,136 +274,143 @@ namespace DesertPaintLab
 
            clearReactionButton.Sensitive = false;
 

	
 
            progressBar.Show();
 
            recordButton.Hide();
 

	
 
            bool enableDebugMenu = false;
 
            DesertPaintLab.AppSettings.Get("EnableDebugMenu", out enableDebugMenu);
 

	
 
            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;
 
            }
 

	
 
            if (notifyCaptureProgress == null)
 
            {
 
                notifyCaptureProgress = new Gtk.ThreadNotify(new Gtk.ReadyEvent(NotifyCaptureProgress));
 
                notifyCaptureComplete = new Gtk.ThreadNotify(new Gtk.ReadyEvent(NotifyCaptureComplete));
 
            }
 

	
 
            this.reactionSwatch.Clear();
 

	
 
            Thread thr = new Thread(new ThreadStart(this.CaptureReactionThread));
 
            thr.Priority = ThreadPriority.AboveNormal;
 
            thr.Start();
 
            //OnCaptureComplete(isCaptured, reactedColor, screenWidth, screenHeight, stride, redPixelStart);
 
        }
 

	
 
        unsafe private void CaptureReactionThread()
 
        {
 
            byte* pixBytes = (byte*)screenBuffer.Pixels;
 
            byte* pixBytes = (byte*)RecordBuffer.Pixels;
 

	
 
            ReactionRecorder.Instance.OnCaptureProgress += OnCaptureProgress;
 
            ReactionRecorder.Instance.CaptureReaction(pixBytes, screenBuffer.Width, screenBuffer.Height, screenBuffer.Rowstride);
 
            ReactionRecorder.Instance.CaptureReaction(pixBytes, RecordBuffer.Width, RecordBuffer.Height, RecordBuffer.Rowstride, RecordBuffer.NChannels, RecordBuffer.BitsPerSample);
 
            ReactionRecorder.Instance.OnCaptureProgress -= OnCaptureProgress;
 

	
 
            notifyCaptureComplete.WakeupMain();
 
        }
 

	
 
        private void OnCaptureProgress(int x, int y)
 
        {
 
            notifyCaptureProgress.WakeupMain();
 
        }
 

	
 
        private void NotifyCaptureProgress()
 
        {
 
            // TODO: progress bar
 
            //ReactionRecorder.Instance.X;
 
            //ReactionRecorder.Instance.Y;
 
            progressBar.Fraction = ((double)(ReactionRecorder.Instance.X) / (double)(ReactionRecorder.Instance.ScreenWidth - ReactionRecorder.Instance.ColorBarWidth));
 
            double denom = (double)(ReactionRecorder.Instance.ScreenWidth - ReactionRecorder.Instance.ColorBarWidth);
 
            double numer = (double)(ReactionRecorder.Instance.X);
 
            double fraction = (denom > 0 && numer > 0) ? (numer / denom) : 0;
 
            if (numer > denom)
 
            {
 
                fraction = 1;
 
            }
 
            progressBar.Fraction = fraction;
 
        }
 

	
 
        void OnCaptureComplete(bool isCaptured, PaintColor reactedColor, int swatchX, int swatchY)
 
        {
 
            int screenWidth = screenBuffer.Width;
 
            int screenHeight = screenBuffer.Height;
 
            int stride = screenBuffer.Rowstride;
 
            int screenHeight = RecordBuffer.Height;
 
            int stride = RecordBuffer.Rowstride;
 

	
 
            StreamWriter log = ReactionRecorder.Instance.Log;
 
            if (log != null)
 
            {
 
                log.Flush();
 
                log.Close();
 
                log = null;
 
                ReactionRecorder.Instance.Log = null;
 
            }
 

	
 
            bool enableDebugMenu = false;
 
            DesertPaintLab.AppSettings.Get("EnableDebugMenu", out enableDebugMenu);
 
            bool debugScreenshot = false;
 
            DesertPaintLab.AppSettings.Get("DebugScreenshot", out debugScreenshot);
 
            if (enableDebugMenu && debugScreenshot)
 
            {
 
                if (!isCaptured)
 
                {
 
                    // write out the whole screenshot on a failure to capture
 
                    string screenshotDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
 
                    string filename = FileUtils.FindNumberedFile("DesertPaintLab_Colormatch", "png", screenshotDir);
 
                    screenBuffer.Save(filename, "png");
 
                    RecordBuffer.Save(filename, "png");
 
                }
 
                else
 
                {
 
                    // record the swatch that was captured
 
                    // write out the screenshot
 
                    string screenshotDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
 
                    string filename = FileUtils.FindNumberedFile("DesertPaintLab_Colormatch", "png", screenshotDir);
 
                    int x = swatchX - 16;
 
                    int captureAreaWidth = ReactionRecorder.Instance.ColorBarWidth + 32;
 
                    captureAreaWidth = Math.Min(screenWidth - x, captureAreaWidth);
 
                    int y = swatchY - 16;
 
                    int captureAreaHeight = ReactionRecorder.Instance.BlueBarSpacing + 42;
 
                    captureAreaHeight = Math.Min(screenHeight - y, captureAreaHeight);
 

	
 
                    Gdk.Pixbuf outPixBuf = new Gdk.Pixbuf(screenBuffer, Math.Max(0, x), Math.Max(0, y), 
 
                    Gdk.Pixbuf outPixBuf = new Gdk.Pixbuf(RecordBuffer, Math.Max(0, x), Math.Max(0, y), 
 
                        captureAreaWidth, captureAreaHeight);
 
                    //Gdk.Pixbuf outPixBuf = new Gdk.Pixbuf(screenBuffer, Math.Max(0, redPixelStartX), Math.Max(0, redPixelStartY),
 
                    //Gdk.Pixbuf outPixBuf = new Gdk.Pixbuf(RecordBuffer, Math.Max(0, redPixelStartX), Math.Max(0, redPixelStartY),
 
                    //    ReactionRecorder.Instance.ColorBarWidth, ReactionRecorder.Instance.GreenBarSpacing - ReactionRecorder.Instance.RedBarSpacing);
 
                    //screenBuffer.Save(filename, "png");
 
                    //RecordBuffer.Save(filename, "png");
 
                    outPixBuf.Save(filename, "png");
 
                }
 
            }
 
            //screenBuffer.Save("screenshot.png", "png");
 
            //RecordBuffer.Save("screenshot.png", "png");
 

	
 
            if (isCaptured)
 
            {
 
                string warning = "";
 
                if (reactedColor.Red == 0)
 
                {
 
                    warning = warning + "\nRed is too low.";
 
                }
 
                if (reactedColor.Green == 0)
 
                {
 
                    warning = warning + "\nGreen is too low.";
 
                }
 
                if (reactedColor.Blue == 0)
 
                {
 
                    warning = warning + "\nBlue is too low.";
 
                }
 
                if (reactedColor.Red == 255)
 
                {
 
                    warning = warning + "\nRed is too high.";
 
                }
 
                if (reactedColor.Green == 255)
 
                {
 
                    warning = warning + "\nGreen is too high.";
 
                }
 
                if (reactedColor.Blue == 255)
 
                {
 
                    warning = warning + "\nBlue is too high.";
 
                }
 

	
 
                // Always update the reacted swatch
 
                this.reactionSwatch.Color = reactedColor;
 
                this.reactedColor.Set(reactedColor);
...
 
@@ -410,98 +422,164 @@ namespace DesertPaintLab
 
                    Gtk.MessageType.Error, Gtk.ButtonsType.Ok,
 
                    "Reaction clipped.  You will need to do a " +
 
                    "3-way reaction to test this pair.  Details: " +
 
                    warning);
 

	
 
                    md.Run();
 
                    md.Destroy();
 
                    // reaction clipped - don't let them record
 
                    recordButton.Sensitive = false;
 
                }
 
                else
 
                {
 
                    recordButton.Sensitive = recordEnabled;
 
                }
 
            }
 
            else
 
            {
 
                Gtk.MessageDialog md = new Gtk.MessageDialog((Gtk.Window)Toplevel,
 
                    Gtk.DialogFlags.DestroyWithParent,
 
                    Gtk.MessageType.Error, Gtk.ButtonsType.Ok,
 
                    "Pigment Lab dialog box NOT FOUND.  Please ensure " +
 
                    "that there is an unobstructed view of the dialog " +
 
                    "and that your interface size is set to 'small' " +
 
                    "when you press the Capture button.");
 

	
 
                md.Run();
 
                md.Destroy();
 
            }
 

	
 
            progressBar.Hide();
 
            recordButton.Show();
 

	
 
            // release
 
            RecordBuffer = null;
 

	
 
            ingredient1ComboBox.Sensitive = true;
 
            ingredient2ComboBox.Sensitive = true;
 
            ingredient3ComboBox.Sensitive = (GetSelectedReagentName(1) != null) && (GetSelectedReagentName(2) != null);
 
        }
 

	
 
        private void NotifyCaptureComplete()
 
        {
 
            OnCaptureComplete(
 
                ReactionRecorder.Instance.IsCaptured,
 
                ReactionRecorder.Instance.RecordedColor,
 
                ReactionRecorder.Instance.SwatchX,
 
                ReactionRecorder.Instance.SwatchY);
 
        }
 

	
 
        unsafe void CaptureReactionColor()
 
        {
 
            // Take a screenshot.
 
            int screenWidth, screenHeight;
 
            Gdk.Window rootWindow = Gdk.Global.DefaultRootWindow;
 
            DesertPaintLab.AppSettings.Get("ScreenWidth", out screenWidth);
 
            DesertPaintLab.AppSettings.Get("ScreenHeight", out screenHeight);
 
            Gdk.Image rootImage = rootWindow.GetImage(0, 0, screenWidth, screenHeight);
 
            screenBuffer.GetFromImage(rootImage, rootImage.Colormap, 0, 0, 0, 0, screenWidth, screenHeight);
 
            RecordBuffer = screenBuffer;
 
            RecordBuffer.GetFromImage(rootImage, rootImage.Colormap, 0, 0, 0, 0, screenWidth, screenHeight);
 
            rootImage.Unref();
 
            System.GC.Collect(); // really, clean up now
 

	
 
            int pixelMultiplier;
 
            AppSettings.Get("PixelMultiplier", out pixelMultiplier);
 
            if (pixelMultiplier == 0) pixelMultiplier = 1;
 
            int interfaceSizeIndex = 1;
 
            AppSettings.Get("InterfaceSize", out interfaceSizeIndex);
 
            InterfaceSize interfaceSize = (InterfaceSize)interfaceSizeIndex;
 
            ReactionRecorder.Instance.PixelMultiplier = pixelMultiplier;
 
            ReactionRecorder.Instance.InterfaceSize = interfaceSize;
 

	
 
            BeginCapture();
 
        }
 

	
 
        unsafe void CaptureReactionColorFromLoadedImage(string filePath)
 
        {
 
            // read the screenshot from a file
 
            string screenshotDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
 
            FileStream pixStream = new FileStream(filePath, FileMode.Open);
 
            RecordBuffer = new Gdk.Pixbuf(pixStream);
 
            ReactionRecorder.Instance.SetPixelMultiplier(1);
 
            // TODO: allow for other size
 
            InterfaceSize interfaceSize = InterfaceSize.Small;
 
            if (filePath.Contains("Large"))
 
            {
 
                interfaceSize = InterfaceSize.Large;
 
            }
 
            else if (filePath.Contains("Huge"))
 
            {
 
                interfaceSize = InterfaceSize.Huge;
 
            }
 
            ReactionRecorder.Instance.InterfaceSize = interfaceSize;
 

	
 
            System.Console.WriteLine("Loaded {0} size {1} x {2} stride {3} bits {4} colorspace {5} channels {6} hasAlpha {7}", filePath, RecordBuffer.Width, RecordBuffer.Height, RecordBuffer.Rowstride, RecordBuffer.BitsPerSample, RecordBuffer.Colorspace, RecordBuffer.NChannels, RecordBuffer.HasAlpha);
 

	
 
            System.GC.Collect(); // really, clean up now
 

	
 
            BeginCapture();
 
        }
 

	
 
        protected virtual void OnCapture(object sender, System.EventArgs e)
 
        {
 
            CaptureReactionColor();
 
        }
 
        
 

	
 
        protected void OnCaptureFromFile(object sender, EventArgs e)
 
        {
 
            Gtk.FileChooserDialog fileDialog =
 
                new Gtk.FileChooserDialog("Select screenshot file.",
 
                        (Gtk.Window)Toplevel, Gtk.FileChooserAction.Open,
 
                        Gtk.Stock.Cancel, Gtk.ResponseType.Cancel,
 
                        Gtk.Stock.Open, Gtk.ResponseType.Accept);
 
            Gtk.FileFilter filter = new Gtk.FileFilter();
 
            filter.AddPattern("*.png");
 
            fileDialog.AddFilter(filter);
 
            Gtk.ResponseType resp = (Gtk.ResponseType)fileDialog.Run();
 
            if (resp == Gtk.ResponseType.Accept)
 
            {
 
                string fileName = fileDialog.Filename;
 
                string directory = fileDialog.CurrentFolder;
 
                if (directory != null)
 
                {
 
                    CaptureReactionColorFromLoadedImage(System.IO.Path.Combine(directory, fileName));
 
                }
 
                else
 
                {
 
                    CaptureReactionColorFromLoadedImage(fileName);
 
                }
 
            }
 
            fileDialog.Destroy();
 
        }
 

	
 
        protected virtual void OnRecord(object sender, System.EventArgs e)
 
        {
 
            if (ReactionRecorder.Instance.RecordReaction(profile, expectedColor, reactedColor, reagents))
 
            {
 
                recordButton.Sensitive = false;
 
            }
 
        }
 
        
 
        protected virtual void OnChangedIngredient1(object sender, System.EventArgs e)
 
        {
 
            UpdateIngredients();
 
        }
 
        
 
        protected virtual void OnChangedIngredient2(object sender, System.EventArgs e)
 
        {
 
            UpdateIngredients();
 
        }
 
        
 
        protected virtual void OnChangedIngredient3(object sender, System.EventArgs e)
 
        {
 
            UpdateIngredients();
 
        }
 
        
 
        protected void OnClearReaction(object sender, EventArgs e)
 
        {
 
            string reagentName1 = GetSelectedReagentName(1);
 
            string reagentName2 = GetSelectedReagentName(2);
 
            string reagentName3 = GetSelectedReagentName(3);
 

	
 
            if ((reagentName1 != null) && (reagentName2 != null) && (reagentName3 == null))
 
            {
 
                Reagent reagent1 = ReagentManager.GetReagent(reagentName1);
gtk-gui/DesertPaintLab.CaptureView.cs
Show inline comments
...
 
@@ -14,66 +14,68 @@ namespace DesertPaintLab
 
		
 
		private global::Gtk.HBox hbox2;
 
		
 
		private global::Gtk.Label ingredient1Label;
 
		
 
		private global::Gtk.ComboBox ingredient1ComboBox;
 
		
 
		private global::Gtk.HBox hbox3;
 
		
 
		private global::Gtk.Label ingredient2Label;
 
		
 
		private global::Gtk.ComboBox ingredient2ComboBox;
 
		
 
		private global::Gtk.HBox hbox4;
 
		
 
		private global::Gtk.Label ingredient3Label;
 
		
 
		private global::Gtk.ComboBox ingredient3ComboBox;
 
		
 
		private global::Gtk.Button clearReactionButton;
 
		
 
		private global::Gtk.Label selectIngredientsLabel;
 
		
 
		private global::Gtk.Frame unmodifiedColorFrame;
 
		
 
		private global::Gtk.Alignment GtkAlignment1;
 
		
 
		private global::Gtk.VBox vbox3;
 
		
 
		private global::DesertPaintLab.PaintSwatch unmodifiedSwatch;
 
		
 
		private global::Gtk.Button captureButton;
 
		
 
		private global::Gtk.Label unmodifiedLabel;
 

	
 
        private global::Gtk.Button captureFromFileButton;
 

	
 
        private global::Gtk.Label unmodifiedLabel;
 
		
 
		private global::Gtk.Frame reactedColorFrame;
 
		
 
		private global::Gtk.Alignment GtkAlignment2;
 
		
 
		private global::Gtk.VBox vbox4;
 
		
 
		private global::DesertPaintLab.PaintSwatch reactionSwatch;
 
		
 
		private global::Gtk.Button recordButton;
 
		
 
		private global::Gtk.Label reactionLabel;
 

	
 
        private global::Gtk.ProgressBar progressBar;
 

	
 
        protected virtual void Build ()
 
		{
 
			global::Stetic.Gui.Initialize (this);
 
			// Widget DesertPaintLab.CaptureView
 
			global::Stetic.BinContainer.Attach (this);
 
			this.Name = "DesertPaintLab.CaptureView";
 
			// Container child DesertPaintLab.CaptureView.Gtk.Container+ContainerChild
 
			this.hbox1 = new global::Gtk.HBox ();
 
			this.hbox1.Name = "hbox1";
 
			this.hbox1.Spacing = 6;
 
			// Container child hbox1.Gtk.Box+BoxChild
 
			this.selectIngredientsFrame = new global::Gtk.Frame ();
 
			this.selectIngredientsFrame.Name = "selectIngredientsFrame";
 
			this.selectIngredientsFrame.BorderWidth = ((uint)(4));
 
			// Container child selectIngredientsFrame.Gtk.Container+ContainerChild
 
			this.GtkAlignment = new global::Gtk.Alignment (0F, 0F, 1F, 1F);
 
			this.GtkAlignment.Name = "GtkAlignment";
...
 
@@ -182,69 +184,81 @@ namespace DesertPaintLab
 
			// Container child hbox1.Gtk.Box+BoxChild
 
			this.unmodifiedColorFrame = new global::Gtk.Frame ();
 
			this.unmodifiedColorFrame.Name = "unmodifiedColorFrame";
 
			this.unmodifiedColorFrame.BorderWidth = ((uint)(4));
 
			// Container child unmodifiedColorFrame.Gtk.Container+ContainerChild
 
			this.GtkAlignment1 = new global::Gtk.Alignment (0F, 0F, 1F, 1F);
 
			this.GtkAlignment1.Name = "GtkAlignment1";
 
			this.GtkAlignment1.LeftPadding = ((uint)(5));
 
			this.GtkAlignment1.TopPadding = ((uint)(5));
 
			this.GtkAlignment1.RightPadding = ((uint)(5));
 
			this.GtkAlignment1.BottomPadding = ((uint)(6));
 
			// Container child GtkAlignment1.Gtk.Container+ContainerChild
 
			this.vbox3 = new global::Gtk.VBox ();
 
			this.vbox3.WidthRequest = 120;
 
			this.vbox3.Name = "vbox3";
 
			this.vbox3.Spacing = 6;
 
			// Container child vbox3.Gtk.Box+BoxChild
 
			this.unmodifiedSwatch = new global::DesertPaintLab.PaintSwatch ();
 
			this.unmodifiedSwatch.WidthRequest = 120;
 
			this.unmodifiedSwatch.HeightRequest = 120;
 
			this.unmodifiedSwatch.Events = ((global::Gdk.EventMask)(256));
 
			this.unmodifiedSwatch.Name = "unmodifiedSwatch";
 
			this.vbox3.Add (this.unmodifiedSwatch);
 
			global::Gtk.Box.BoxChild w14 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.unmodifiedSwatch]));
 
			w14.Position = 0;
 
			// Container child vbox3.Gtk.Box+BoxChild
 
			this.captureButton = new global::Gtk.Button ();
 
			this.captureButton.CanFocus = true;
 
			this.captureButton.Name = "captureButton";
 
			this.captureButton.UseUnderline = true;
 
			this.captureButton.Label = "Capture";
 
			this.vbox3.Add (this.captureButton);
 
			global::Gtk.Box.BoxChild w15 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.captureButton]));
 
			w15.Position = 1;
 
			w15.Expand = false;
 
			w15.Fill = false;
 
			this.GtkAlignment1.Add (this.vbox3);
 
            // Container child vbox3.Gtk.Box+BoxChild
 
            global::Gtk.Box.BoxChild w15 = ((global::Gtk.Box.BoxChild)(this.vbox3[this.captureButton]));
 
            w15.Position = 1;
 
            w15.Expand = false;
 
            w15.Fill = false;
 
            this.captureFromFileButton = new global::Gtk.Button();
 
            this.captureFromFileButton.CanFocus = true;
 
            this.captureFromFileButton.Name = "captureButton";
 
            this.captureFromFileButton.UseUnderline = true;
 
            this.captureFromFileButton.Label = "Capture from File";
 
            this.vbox3.Add(this.captureFromFileButton);
 
            // Container child vbox3.Gtk.Box+BoxChild
 
            global::Gtk.Box.BoxChild w16 = ((global::Gtk.Box.BoxChild)(this.vbox3[this.captureFromFileButton]));
 
            w16.Position = 2;
 
            w16.Expand = false;
 
            w16.Fill = false;
 
            this.GtkAlignment1.Add (this.vbox3);
 
			this.unmodifiedColorFrame.Add (this.GtkAlignment1);
 
			this.unmodifiedLabel = new global::Gtk.Label ();
 
			this.unmodifiedLabel.Name = "unmodifiedLabel";
 
			this.unmodifiedLabel.LabelProp = "<b>Unmodified</b>";
 
			this.unmodifiedLabel.UseMarkup = true;
 
			this.unmodifiedLabel.Justify = ((global::Gtk.Justification)(2));
 
			this.unmodifiedColorFrame.LabelWidget = this.unmodifiedLabel;
 
			this.hbox1.Add (this.unmodifiedColorFrame);
 
			global::Gtk.Box.BoxChild w18 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.unmodifiedColorFrame]));
 
			w18.Position = 1;
 
			w18.Expand = false;
 
			w18.Fill = false;
 
			// Container child hbox1.Gtk.Box+BoxChild
 
			this.reactedColorFrame = new global::Gtk.Frame ();
 
			this.reactedColorFrame.Name = "reactedColorFrame";
 
			this.reactedColorFrame.BorderWidth = ((uint)(4));
 
			// Container child reactedColorFrame.Gtk.Container+ContainerChild
 
			this.GtkAlignment2 = new global::Gtk.Alignment (0F, 0F, 1F, 1F);
 
			this.GtkAlignment2.Name = "GtkAlignment2";
 
			this.GtkAlignment2.LeftPadding = ((uint)(5));
 
			this.GtkAlignment2.TopPadding = ((uint)(5));
 
			this.GtkAlignment2.RightPadding = ((uint)(5));
 
			this.GtkAlignment2.BottomPadding = ((uint)(6));
 
			// Container child GtkAlignment2.Gtk.Container+ContainerChild
 
			this.vbox4 = new global::Gtk.VBox ();
 
			this.vbox4.WidthRequest = 120;
 
			this.vbox4.Name = "vbox4";
 
			this.vbox4.Spacing = 6;
 
			// Container child vbox4.Gtk.Box+BoxChild
 
			this.reactionSwatch = new global::DesertPaintLab.PaintSwatch ();
 
			this.reactionSwatch.WidthRequest = 120;
 
			this.reactionSwatch.HeightRequest = 120;
...
 
@@ -270,36 +284,37 @@ namespace DesertPaintLab
 
			this.recordButton.UseUnderline = true;
 
			this.recordButton.Label = "Record";
 
			this.vbox4.Add (this.recordButton);
 
			global::Gtk.Box.BoxChild w20 = ((global::Gtk.Box.BoxChild)(this.vbox4 [this.recordButton]));
 
			w20.Position = 2;
 
			w20.Expand = false;
 
			w20.Fill = false;
 
			this.GtkAlignment2.Add (this.vbox4);
 
			this.reactedColorFrame.Add (this.GtkAlignment2);
 
			this.reactionLabel = new global::Gtk.Label ();
 
			this.reactionLabel.Name = "reactionLabel";
 
			this.reactionLabel.LabelProp = "<b>Reacted</b>";
 
			this.reactionLabel.UseMarkup = true;
 
			this.reactionLabel.Justify = ((global::Gtk.Justification)(2));
 
			this.reactedColorFrame.LabelWidget = this.reactionLabel;
 
			this.hbox1.Add (this.reactedColorFrame);
 
			global::Gtk.Box.BoxChild w23 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.reactedColorFrame]));
 
			w23.Position = 3;
 
			w23.Expand = false;
 
			w23.Fill = false;
 
			this.Add (this.hbox1);
 
			if ((this.Child != null)) {
 
				this.Child.ShowAll ();
 
			}
 
			this.clearReactionButton.Hide ();
 
            this.progressBar.Hide();
 
            this.Hide ();
 
			this.ingredient1ComboBox.Changed += new global::System.EventHandler (this.OnChangedIngredient1);
 
			this.ingredient2ComboBox.Changed += new global::System.EventHandler (this.OnChangedIngredient2);
 
			this.ingredient3ComboBox.Changed += new global::System.EventHandler (this.OnChangedIngredient3);
 
			this.clearReactionButton.Clicked += new global::System.EventHandler (this.OnClearReaction);
 
			this.captureButton.Clicked += new global::System.EventHandler (this.OnCapture);
 
			this.recordButton.Clicked += new global::System.EventHandler (this.OnRecord);
 
            this.captureFromFileButton.Clicked += new global::System.EventHandler(this.OnCaptureFromFile);
 
            this.recordButton.Clicked += new global::System.EventHandler (this.OnRecord);
 
		}
 
	}
 
}
0 comments (0 inline, 0 general)