Forum Libraries, Components, and Plugins
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

Find a pixel of given color in a bitmap

Hello,

I'm making a color picker using SkiaSharp, and so far so good. I can load a bitmap with a color gradient and get the selected color using the following (where _sampleBitmapImageInfo is a 1x1 imageinfo in the format of my gradient bitmap):

    private SKColor getPixelAtPoint(SKSurface surface, int x, int y)
    {
        var bitmap = new SKBitmap(_sampleBitmapImageInfo);

        // get the pixel buffer for the bitmap
        var pixels = bitmap.GetPixels();

        // read the surface into the bitmap
        surface.ReadPixels(_sampleBitmapImageInfo, pixels, _sampleBitmapImageInfo.RowBytes, x, y);

        // access the color
        var color = bitmap.GetPixel(0, 0);

        return color;
    }

You'll see from the comments that this was taken from another post on this forum, and it works great.

Additionally, as the user drags their finger around the color picker, it draws a little circle indicator to show what color they have selected. Again, works great.

My problem is that when I'm loading the color picker back, I want to draw that circle indicator at the location of a specified color. So if the user picked Red (255, 0, 0), I'd like to be able to find the first Red (255, 0, 0) pixel in the bitmap.

I've tried using the above getPixelAtPoint method while iterating over the entire bitmap's width / height and it never finds the color, even though it was a color that was just selected using the same bitmap. Additionally, it runs painfully slow (which makes sense). I just have a feeling that there has to be better way.

Thanks!

Answers

  • WilliamWattersonWilliamWatterson USUniversity ✭✭

    @jhalliday - did you ever find a solution for this?

  • jhallidayjhalliday Member ✭✭

    Sort of. In my use case it wasn't important that the indicator be pixel-perfect. It just had to be close. This is what I ended up implementing:

    private SKPoint? FindPixelInSurface(SKImageInfo info, SKSurface surface)
    {
        var bitmap = new SKBitmap(info);
    
        // get the pixel buffer for the bitmap
        var pixels = bitmap.GetPixels();
    
        // read the surface into the bitmap
        if (surface.ReadPixels(info, pixels, info.RowBytes, 0, 0))
        {
            // Search entire bitmap for a matching color
            // IMPORTANT: Only scan 1/4th of the pixels - it's too slow otherwise
            // Combined with "IsInRange", a close match is made - it doesn't have to 
            // be perfect
            for (int x = 0; x < info.Width; x += 4)
            {
                for (int y = 0; y < info.Height; y += 4)
                {
                    var pixelColor = bitmap.GetPixel(x, y);
    
                    // Colors don't match without a fudge factor for some reason
                    if (pixelColor.IsInRange(_setColor.Value, SetColorRange))
                    {
                        return new SKPoint(x, y);
                    }
                    // Try once more at 2x the range
                    // Once the scene import feature was implemented, some colors weren't
                    // within range. Most are at 2x, and forget the indicator if not...
                    else if(pixelColor.IsInRange(_setColor.Value, SetColorRange * 2.0f))
                    {
                        return new SKPoint(x, y);
                    }
                }
            }
        }
    
        return null;
    }
    

    IsInRange is implemented as an extention method to SKColor:

    public static bool IsInRange(this SKColor color, SKColor otherColor, float range)
    {
        if(color.Red < otherColor.Red + otherColor.Red * range
            && color.Red > otherColor.Red - otherColor.Red * range
            && color.Green < otherColor.Green + otherColor.Green * range
            && color.Green > otherColor.Green - otherColor.Green * range
            && color.Blue < otherColor.Blue + otherColor.Blue * range
            && color.Blue > otherColor.Blue - otherColor.Blue * range
            && color.Alpha < otherColor.Alpha + otherColor.Alpha * range
            && color.Alpha > otherColor.Alpha - otherColor.Alpha * range)
        {
            return true;
        }
    
        return false;
    }
    

    Hope that helps

Sign In or Register to comment.