Changeset - de6e00728a10
[Not reviewed]
default
0 3 0
Jason Maltzen - 12 months ago 2023-12-07 04:39:45
jason@hiddenachievement.com
Fixed scanning of RGB bars to correctly account for the right edge highlight. Fixes problems detecting high-value clipping.
3 files changed with 13 insertions and 10 deletions:
0 comments (0 inline, 0 general)
Services/ReactionScannerService.cs
Show inline comments
...
 
@@ -322,103 +322,103 @@ namespace DesertPaintCodex.Services
 
            {
 
                if (!borderError && (bgErrorCount < _swatchWidth))
 
                {
 
                    WriteLog(LogVerbosity.Normal, "Found a potential swatch candidate of width {0} at {1},{2} that had {3} failures matching background color", i, x, y, bgErrorCount);
 
                }
 
            }
 

	
 
            return result;
 
        }
 

	
 
        private bool TestPosition(int x, int y, Pixels pixels, ref PaintColor reactedColor)
 
        {
 
            // Check 4 corners of solid area and left/right solid bar areas
 
            bool foundSwatch = IsPossibleSwatchUpperLeft(pixels, x, y);
 
            if (foundSwatch)
 
            {
 
                WriteLog(LogVerbosity.Normal, "Found probable swatch at {0},{1} - checking border slices", x, y);
 
                int borderXOffset = 0;
 
                for (borderXOffset = _swatchLeftBorder; foundSwatch && (borderXOffset < _swatchTestWidth); ++borderXOffset)
 
                {
 
                    foundSwatch &= IsPossibleSwatchSlice(pixels, x + borderXOffset, y);
 
                    if (!foundSwatch)
 
                    {
 
                        WriteLog(LogVerbosity.Normal, "Failed slice test at {0},{1}", x + borderXOffset, y);
 
                        break;
 
                    }
 
                    foundSwatch &= IsPossibleSwatchSlice(pixels, x + _swatchWidth - borderXOffset, y);
 
                    if (!foundSwatch)
 
                    {
 
                        WriteLog(LogVerbosity.Normal, "Failed slice test at {0},{1}", x + _swatchWidth - borderXOffset, y);
 
                        break;
 
                    }
 
                }
 
            }
 

	
 
            if (!foundSwatch)
 
            {
 
                if (!_lockedSwatchWidth)
 
                {
 
                    // Reset our guess at swatch width.
 
                    _swatchWidth = MinSwatchWidth;
 
                }
 
                return false;
 
            }
 
            
 
            // WE FOUND THE SWATCH!
 
            // Now we know where the color bars are.
 
            _lockedSwatchWidth = true;
 
            int redPixelCount = pixels.LengthOfColorAt(x, y + _redBarSpacing, PixelColor.IsRed);
 
            int redPixelCount = pixels.LengthOfColorAt(x, y + _redBarSpacing, _swatchWidth, PixelColor.IsRed);
 
            reactedColor.Red = (byte)Math.Round(redPixelCount * 255f / _swatchWidth);
 

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

	
 
            int bluePixelCount = pixels.LengthOfColorAt(x, y + _blueBarSpacing, PixelColor.IsBlue);
 
            int bluePixelCount = pixels.LengthOfColorAt(x, y + _blueBarSpacing, _swatchWidth, PixelColor.IsBlue);
 
            reactedColor.Blue = (byte)Math.Round(bluePixelCount * 255f / _swatchWidth);
 
            WriteLog(LogVerbosity.Low, "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;
 
        }
 

	
 
        public async Task<bool> CaptureReactionAsync(IProgress<float> progress)
 
        {
 
            _canceler = new CancellationTokenSource(); // You can't re-use these, sadly.
 
            return await Task.Run(() => CaptureReaction(progress, _canceler.Token));
 
        }
 

	
 
        private bool CaptureReaction(IProgress<float> progress, CancellationToken cancellToken)
 
        {
 
            PaintColor reactedColor = new();
 
            IsCaptured = false;
 
            _recordedColor.Clear();
 

	
 
            using (var g = Graphics.FromImage(_targetBitmap))
 
            {
 
                Debug.WriteLine("Scan starting at [" + _screenX + ", " + _screenY + "]");
 
                g.CopyFromScreen(_screenX, _screenY, 0, 0, _targetBitmap.Size, CopyPixelOperation.SourceCopy);
 
            }
 

	
 
            SettingsService.Get("Log.Verbosity", out var verbosityIdx, 1);
 
            _logVerbosity = (LogVerbosity)verbosityIdx;
 

	
 
            SettingsService.Get("Debug.Screenshot", out bool debugScreenshot, false);
 
            if (debugScreenshot)
 
            {
 
                _targetBitmap.Save(Path.Combine(ProfileManager.CurrentProfile?.Directory ?? "", "screenshot.png"), ImageFormat.Png);
 
            }
 

	
 
            Pixels pixels = new(_targetBitmap, _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))
 
                {
 
                    IsCaptured = true;
 
                    RecordedColor = reactedColor;
 
                    return true;
 
                }
 
                
 
                _firstRun = true;
 
            }
 

	
Util/PixelColor.cs
Show inline comments
 
using System;
 
using System.Drawing;
 
using DesertPaintCodex.Services;
 

	
 
namespace DesertPaintCodex.Util
 
{
 
    public class PixelColor
 
    {
 
        private const int ColorTolerance = 2;
 
        
 
        public static bool IsMatch(Color colorA, Color colorB)
 
        {
 
            return ((Math.Abs(colorA.R - colorB.R) <= ColorTolerance) &&
 
                (Math.Abs(colorA.G - colorB.G) <= ColorTolerance) &&
 
                (Math.Abs(colorA.B - colorB.B) <= ColorTolerance));
 
        }
 

	
 
        public static bool IsDark(Color color)
 
        {
 
            return (color.R < 0x47) && (color.G < 0x47) && (color.B < 0x47);
 
        }
 

	
 
        public static bool IsUiBackground(Color color)
 
        {
 
            return (color.R is >= 0x27 and <= 0x2F) && (color.G is >= 0x3B and <= 0x42) && (color.B is >= 0x41 and <= 0x48);
 
        }
 

	
 
        public static bool IsRed(Color color)
 
        {
 
            return (color.R > 0x9D) && (color.G < 0x60) && (color.B < 0x60);
 
            return (color.R > 0x9D) && (color.G < 0x64) && (color.B < 0x64);
 
        }
 
        public static bool IsGreen(Color color)
 
        {
 
            return (color.R < 0x60) && (color.G > 0x9D) && (color.B < 0x60);
 
            return (color.R < 0x64) && (color.G > 0x9D) && (color.B < 0x64);
 
        }
 
        public static bool IsBlue(Color color)
 
        {
 
            return (color.R < 0x60) && (color.G < 0x60) && (color.B > 0x9D);
 
            return (color.R < 0x64) && (color.G < 0x64) && (color.B > 0x9D);
 
        }
 

	
 
    }
 
}
...
 
\ No newline at end of file
Util/Pixels.cs
Show inline comments
 
using System;
 
using System.Diagnostics;
 
using System.Drawing;
 

	
 
namespace DesertPaintCodex.Util
 
{
 
    public class Pixels
 
    {
 
        private readonly Bitmap _bitmap;
 
        private readonly int _pixelMultiplier;
 

	
 
        public int Height => _bitmap.Height / _pixelMultiplier;
 
        public int Width => _bitmap.Width / _pixelMultiplier;
 

	
 
        public Pixels(Bitmap bitmap, int pixellMutliplier)
 
        {
 
            _bitmap = bitmap;
 
            _pixelMultiplier = pixellMutliplier;
 
        }
 

	
 
        public Color ColorAt(int x, int y)
 
        {
 
            return _bitmap.GetPixel(x * _pixelMultiplier, y * _pixelMultiplier);
 
        }
 

	
 
        public bool DoesPixelMatch(int x, int y, Func<Color, bool> matchFunc)
 
        {
 
            int xMult = x * _pixelMultiplier;
 
            int yMult = y * _pixelMultiplier;
 

	
 
            if (xMult < 0 || xMult >= _bitmap.Width || yMult < 0 || yMult >= _bitmap.Height)
 
            {
 
                Debug.WriteLine("Problem at position [" + x + ", " + y + "]");
 
            }
 
            
 
            return matchFunc(_bitmap.GetPixel(x * _pixelMultiplier, y * _pixelMultiplier));
 
        }
 

	
 
        // Compute length of horizontal bar starting at x,y using matching function
 
        public int LengthOfColorAt(int x, int y, Func<Color, bool> matchFunc)
 
        public int LengthOfColorAt(int x, int y, int scanWidth, Func<Color, bool> matchFunc)
 
        {
 
            int count = 0;
 
            for (int xVal = x; xVal < Width; ++xVal)
 
            for (int xVal = x; xVal < scanWidth + x; ++xVal)
 
            {
 
                if (!matchFunc(_bitmap.GetPixel(xVal * _pixelMultiplier, y * _pixelMultiplier))) break;
 
                Color c = _bitmap.GetPixel(xVal * _pixelMultiplier, y * _pixelMultiplier);
 
                if (!matchFunc(c))
 
                {
 
                    break;
 
                }
 
                ++count;
 
            }
 
            return count;
 
        }
 

	
 
        public bool IsSolidPatchAt(int x, int y, int patchWidth, int patchHeight)
 
        {
 
            if ((x + patchWidth >= Width) || (y + patchHeight >= Height)) return false;
 
            Color color = _bitmap.GetPixel(x * _pixelMultiplier, y * _pixelMultiplier);
 
            
 
            // Debug.WriteLine("color at {0},{1} = {2},{3},{4}", x, y, color.R, color.G, color.B);
 

	
 
            for (int pX = 0; pX < patchWidth; pX++)
 
            {
 
                for (int pY = 0; pY < patchHeight; pY++)
 
                {
 
                    if (!PixelColor.IsMatch(color, _bitmap.GetPixel((x + pX) * _pixelMultiplier, (y + pY) * _pixelMultiplier)))
 
                        return false;
 
                }
 
            }
 

	
 
            return true;
 
        }
 
    }
 
}
...
 
\ No newline at end of file
0 comments (0 inline, 0 general)