Exemple #1
0
        /// <summary>
        /// Counts pixels of given color. Search in Y coordinate given and X range given
        /// </summary>
        /// <param name="locked">bitmap to search in</param>
        /// <param name="colorFunc">function that checks if the color should be counted or not</param>
        /// <param name="xRange">horizontal range in which to search for pixels</param>
        /// <param name="y">Y coordinate</param>
        /// <returns>number of pixels of given color found</returns> 
        public static int CountPixelsHorizontal(LockBitmap locked, Func<Color, bool> colorFunc, IEnumerable<int> xRange, int y)
        {
            int count = 0;

            foreach (var x in xRange)
            {
                var pixel = locked.GetPixel(x, y);
                if (colorFunc(pixel))
                {
                    count++;
                }
                else
                {
                    break;
                }
            }

            return count;
        }
Exemple #2
0
        private List<Line> GetTooltipBlackLines(LockBitmap locked, Rectangle searchArea, int projectedTooltipWidth, Func<double, int> v, Graphics g)
        {
            var lines = new List<Line>();

            Func<Color, bool> blackFunc = c => c.R < 10 && c.G < 10 && c.B < 10;
            var blackLinesThreshold = (int)Math.Floor(0.6 / 100 * locked.Height); // 0,6% of Height

            // It doesn't make sense to start search at the beginning of searchArea as it always covers the upper-left corner,
            // so we can skip this first vertical scanline to save some time (wouldn't find black lines there anyway).
            // In most cases tooltip will be found in first iteration as long as it was on the left side of the mouse cursor,
            // otherwise it should take no more than 2 iterations of the outer loop
            for (int x = searchArea.Left + projectedTooltipWidth * 3 / 4; x < searchArea.Right; x += projectedTooltipWidth * 3 / 4)
            {
                int? firstLineX = null;

                g.DrawLine(Pens.Red, x, searchArea.Top, x, searchArea.Bottom);

                for (int y = searchArea.Top; y < searchArea.Bottom; y++)
                {
                    var pixel = locked.GetPixel(x, y);

                    if (blackFunc(pixel))
                    {
                        var leftRange = Enumerable.Range(searchArea.Left, x - searchArea.Left).Reverse(); // search until the searchArea.Left as it covers the upper-left corner
                        var rightRange = Enumerable.Range(x + 1, (int)(projectedTooltipWidth * 1.2));

                        int left = CountPixelsHorizontal(locked, blackFunc, leftRange, y); // count black pixels to the left from current pos
                        int right = CountPixelsHorizontal(locked, blackFunc, rightRange, y); // count black pixels to the right from current pos

                        var line_width = left + right + 1;

                        if (line_width > v(39))
                        {
                            // make bigger steps when longer lines are found, they waste a lot of pixels and give no results (step is almost like tooltip border width)
                            y += v(0.5);
                            continue;
                        }
                        if (line_width > v(37) && line_width < v(39)) // potential tooltip width, can't calculate it to the pixel's accuracy
                        {
                            if (!firstLineX.HasValue && lines.Count > 0 && lines.Last().P1.X != x - left) // group only lines with the same x-pos
                            {
                                lines.Clear();
                            }

                            if (!firstLineX.HasValue || lines[0].P1.X == x - left)
                            {
                                lines.Add(new Line(new Point(x - left, y), new Point(x + right, y)));
                                g.DrawLine(Pens.Lime, lines.Last().P1, lines.Last().P2);
                            }

                            if (!firstLineX.HasValue && lines.Count > blackLinesThreshold) // just found the beginning of the tooltip
                            {
                                firstLineX = lines[0].P1.X;

                                // already found a potential tooltip, so need to adjust X a bit, so that it's more efficiently used (as less pixels checked as possible)
                                // leaving it as it is would cause the algorithm to find a lot of black areas inside the tooltip (and a lot of pixels read for no reason),
                                // but if we move X just next to the left border then the other inner border of the tooltip will stop search very soon for each line, reducing the number of checked pixels massively
                                // Note: it's safe to modify a loop iterator as the code won't iterate more over the outer loop anyway (see below: if (firstLineX.HasValue))
                                x = firstLineX.Value;

                                searchArea.Height = locked.Height - searchArea.Top; // extend searchArea to the bottom (almost.. see comment below)
                            }
                        }
                    }
                    else if (firstLineX.HasValue) // no need to search until the very bottom of the screenshot, last "black" pixel is for sure last "black" line in the tooltip
                    {
                        break;
                    }
                }

                if (firstLineX.HasValue)
                {
                    return lines;
                }
            }

            return null;
        }
Exemple #3
0
        private Line FindVerticalBorder(LockBitmap locked, IEnumerable<int> range, int y, Func<double, int> v, Func<Color, bool> borderFunc, bool isLeft = true)
        {
            foreach (var x in range)
            {
                var pixel = locked.GetPixel(x, y);
                if (borderFunc(pixel))
                {
                    var upStart = Math.Max(y - v(6), 0);
                    var upRange = Enumerable.Range(upStart, y - upStart).Reverse();
                    var downRange = Enumerable.Range(y + 1, locked.Height - y - 1);

                    var rng = isLeft ? Enumerable.Range(x + 1, 5) : Enumerable.Range(x - 5, 5).Reverse();
                    var up = CountPixelsVertical(locked, borderFunc, upRange, x, rng);
                    var down = CountPixelsVertical(locked, borderFunc, downRange, x);

                    return new Line(new Point(x, y - up), new Point(x, y + down));
                }
            }

            return null;
        }
Exemple #4
0
        private int CountPixelsVertical(LockBitmap locked, Func<Color, bool> colorFunc, IEnumerable<int> range, int x, IEnumerable<int> additionalRange = null)
        {
            int count = 0;

            foreach (var y in range)
            {
                var pixel = locked.GetPixel(x, y);
                if (colorFunc(pixel))
                {
                    count++;

                    if (additionalRange != null && additionalRange.All(ax => colorFunc(locked.GetPixel(ax, y))))
                    {
                        break;
                    }
                }
                else
                {
                    break;
                }
            }

            return count;
        }
Exemple #5
0
        private static int? GetVerticalTextBounds(LockBitmap bmp, int top, int bottom, int left, int right, Func<Color, bool> colorFunc, bool bottomToTop = false)
        {
            var range = Enumerable.Range(left, right - left + 1);
            var vRange = Enumerable.Range(top, bottom - top + 1);

            if (bottomToTop)
            {
                vRange = vRange.Reverse();
            }

            foreach (var y in vRange)
            {
                var r = range.Where(x => colorFunc(bmp.GetPixel(x, y)));
                if (r.Count() > 0)
                {
                    return y;
                }
            }

            return null;
        }
Exemple #6
0
        private static int? GetHorizontalTextBounds(LockBitmap bmp, int top, int bottom, IEnumerable<int> widths, Func<Color, bool> colorFunc, bool rightToLeft = false)
        {
            var w = widths.ToArray();
            var count = widths.Count();
            var range = Enumerable.Range(top, bottom - top);

            for (int i = 0; i < count; i++)
            {
                var r = range.Where(y => colorFunc(bmp.GetPixel(w[i], y)));
                if (r.Count() > 0)
                {
                    if (i > 0) // need to search previous "sectors" line by line to get exact results
                    {
                        var newStart = w[i - 1];
                        if (i > 2)
                        {
                            newStart = w[i - 3]; // ...or even 3 (sometimes a letter 'I' might get skipped by the scanlines)
                        }
                        else if (i > 1)
                        {
                            newStart = w[i - 2]; // search 2 previous sectors if possible
                        }

                        if (rightToLeft)
                        {
                            widths = Enumerable.Range(w[i], newStart - w[i]).Reverse();
                        }
                        else
                        {
                            widths = Enumerable.Range(newStart, w[i] - newStart);
                        }

                        foreach (var x in widths)
                        {
                            r = range.Where(y => colorFunc(bmp.GetPixel(x, y)));
                            if (r.Count() > 0)
                            {
                                return x;
                            }
                        }
                    }

                    return w[i]; // that's first found vertical scanline
                }
            }

            return null;
        }
Exemple #7
0
        public static Rectangle GetTextBounding(LockBitmap bitmap, Rectangle outerBound, Func<Color, bool> colorFunc)
        {
            int sampleSize = 3;

            int l = outerBound.Left,
                r = outerBound.Right,
                t = outerBound.Top,
                b = outerBound.Bottom;

            var left = GetHorizontalTextBounds(bitmap, t, b, Enumerable.Range(l, outerBound.Width * 3 / 4).Sample(sampleSize), colorFunc);
            if (left.HasValue)
            {
                l = left.Value;
            }

            var right = GetHorizontalTextBounds(bitmap, t, b, Enumerable.Range(outerBound.Right - outerBound.Width * 9 / 10, outerBound.Width * 9 / 10).Reverse().Sample(sampleSize), colorFunc, true);
            if (right.HasValue)
            {
                r = right.Value;
            }

            var top = GetVerticalTextBounds(bitmap, t, b, l, r, colorFunc);
            if (top.HasValue)
            {
                t = top.Value;
            }

            var bottom = GetVerticalTextBounds(bitmap, t, b, l, r, colorFunc, true);
            if (bottom.HasValue)
            {
                b = bottom.Value;
            }

            return Rectangle.FromLTRB(l, t, r, b);
        }