I have a program using old data for a game I play to indicate which pixels correspond to the health bar in my game. (my program just loops until a button is clicked and presses a key if the health bar drops too low). Unfortunately the health bar has moved and the people that measured where the health bar was have stopped caring. I am wondering how do you find out exactly which pixel to watch in GetPixel based on a picture of the game. I tried opening it in GIMP and using the measure tool, but found that the top left pixel was rather fuzzy. Is there any easier way to do this? (Like maybe some program somebody could either write or explain to me how to write that would do exactly what I need)

Recommended Answers

All 5 Replies

Can you explain the nature of getpixel?
Are you able to read the memory of the executable in real time?

If the position remains constant after it is found, you could always image search the screen.

So pretend you have 3 surfaces (shapes).
1) the screen (rect)
2) some comparable, constant part of health bar image (rect)
3) a rectangle of equal dimension to (2), to fill in iteration

read (2) from .image memory
for each step size in dimension of (1),
sample spatial position as (3),
return true if equal to (2)
else next spatial iterator

It helps to have a simple expression of the shape in linear format in mind.
For example,
array[100] and array[10][10] are logically equivilant.
array[ y * width + x ] == array[x][y]

Once you find the image, save its position.
That way, you don't have to search for it unless the result is invalid.

True, that should work... theoretically. The big issue is that since they moved it, it uses dynamic positioning based on window size. So I would have to do that on every WM_RESIZE event. However I suppose that the user shouldn't be resizing their window all that frequently, so I guess your solution is sufficient.

I don't know if it's too late to reply since this thread is solved but I wrote something a while ago for a game and the image recognition is pretty flawless and fast too. Way faster than GetPixel. It was previously crossplatform but I thought that I might need bitmaps from a HWND or DC so now it's practically windows only :(
Other than that, it works like this. You create a new instance of "Client". You call Client.SetTarget(....); <-- Accepts a buffer, DC, Hwnd, etc..
That sets the target that will be searched. Whenever you call Client.Find... <-- FindBitmap, FindBitmapTolerance, FindColour, FindColours, etc.. It will search the target for whatever you're looking for. It automatically updates the internal target so you don't have to worry about calling SetTarget every time. Just use the finder functions as you like. So far it supports almost all colour formats. XYZ, RGBA, CIE, CIELAB, etc. 24 and 32 bit bmp's. The CTS Algorithms I got from: https://github.com/MerlijnWajer/Simba/blob/master/Units/MMLCore/bitmaps.pas written in pascal. Mine is different though but it all works the same. The code has tolerances so even if the colour change, it still finds it. Even if the image is partially skewed/deformed, it still finds it.

The idea is from a program called Simba which is created by MerlinWajer and is used for a game called Runescape and many others. It's one of the BEST screenscrapers (website is villavu.com). It is open source as well and they have a huge community for macroing/autoing/botting games. Anyway, this is a C++ community so I hope my code makes sense and you enjoy it. Might I ask, what game you're playing/doing this for?

IF you need PNG too, I have a version that does both TGA, PNG, BMP, etc.. but the source is larger than the below by quite a bit and you'd need libpng/zlib.

For now, you can just try the below and see if you like it.

http://www.mediafire.com/download/umi0sgtf21lxl4a/Image_Recognition.zip

That's the link to the code. I can Github it if you guys want.

If you don't want to download that link then you can just view the below:

Bitmap.h:

#ifndef BITMAP_H_INCLUDED
#define BITMAP_H_INCLUDED

/** By Brandon-T.. Brandon.T-@Live.com **/

#include <iostream>
#include <fstream>
#include <vector>
#include <stdexcept>
#include <sstream>
#include <cmath>
#include <tchar.h>
#include <windows.h>

typedef struct
{
    double X, Y, Z;
} XYZ, *PXYZ;

typedef struct
{
    double Hue, Sat, Lum;
} HSL, *PHSL;

typedef struct
{
    double L, A, B;
} LAB, *PLAB;

typedef union RGBA
{
    std::uint32_t Colour;
    struct
    {
        std::uint8_t B, G, R, A;
    };
} *PRGB;

typedef struct
{
    int X, Y;
} Point;

typedef struct
{
    int X1, Y1, X2, Y2;
    int Width() {return X2 - X1;}
    int Height() {return Y2 - Y1;}
} Box;

XYZ Xyz(double X, double Y, double Z);
HSL Hsl(double Hue, double Sat, double Lum);
LAB Lab(double L, double A, double B);
RGBA Rgba(std::uint32_t R, std::uint32_t G, std::uint32_t B);
RGBA Rgba(std::uint32_t Colour);
XYZ Xyz(RGBA Rgba);
RGBA Rgba(XYZ Xyz);
HSL Hsl(RGBA Rgba);
RGBA Rgba(HSL Hsl);
LAB Lab(XYZ Xyz);
XYZ Lab(LAB Lab);
HSL Hsl(std::uint32_t Color);
XYZ Xyz(std::uint32_t Color);
LAB Lab(std::uint32_t Color);

std::uint32_t ColourSwap(std::uint32_t Colour);
RGBA ColourSwap(RGBA Rgba);
void ColourSwap(std::uint8_t* Buffer, int BufferSize, int BitsPerPixel);
std::ostream& operator << (std::ostream& Str, const RGBA &Rgba);
std::ostream& operator << (std::ostream& Str, const HSL &Hsl);
std::ostream& operator << (std::ostream& Str, const XYZ &Xyz);
bool SimilarColours(RGBA FirstCol, RGBA SecondCol, int Tol);


class Bitmap
{
    private:
        std::vector<RGBA> Pixels;
        std::uint32_t width, height;
        std::uint16_t BitsPerPixel;
        void ReadPixels(std::uint8_t *BuffPos);
        void WritePixels(std::uint8_t* &BuffPos);

    public:
        Bitmap(const char* FilePath);
        Bitmap(HWND Window, Box Area = {0}, int Bpp = 32);
        Bitmap(HDC DC, Box Area = {0}, int Bpp = 32);
        Bitmap(const void* Ptr, int Widht, int Height, int Bpp = 32);
        void Save(const char* FilePath);

        inline std::uint16_t Bits()
        {
            return BitsPerPixel;
        }

        inline int Width() const
        {
            return width;
        }

        inline int Height() const
        {
            return height;
        }

        inline RGBA* GetPixels()
        {
            return Pixels.data();
        }

        inline std::uint32_t GetPixel(int X, int Y) const
        {
            RGBA Pixel = Pixels[Y * width + X];
            return RGB(Pixel.R, Pixel.G, Pixel.B);
        }

        inline void SetPixel(int X, int Y, RGBA Colour)
        {
            Pixels[Y * width + X] = Colour;
        }

        inline void SetPixel(int X, int Y, std::uint32_t Color)
        {
            Pixels[Y * width + X] = Rgba(Color);
        }
};

#endif // BITMAP_H_INCLUDED

Bitmap.cpp:

/** By Brandon-T.. Brandon.T-@Live.com **/

#include "Bitmap.h"

std::ostream& operator << (std::ostream& Str, const RGBA &Rgba)
{
    return Str << _T("R: ") << (int)Rgba.R << _T(", G: ") << (int)Rgba.G << _T(", B: ") << (int)Rgba.B;
}

std::ostream& operator << (std::ostream& Str, const HSL &Hsl)
{
    return Str << _T("H: ") << Hsl.Hue << _T(", S: ") << Hsl.Sat << _T(", L: ") << Hsl.Lum;
}

std::ostream& operator << (std::ostream& Str, const XYZ &Xyz)
{
    return Str << _T("X: ") << Xyz.X << _T(", Y: ") << Xyz.Y << _T(", Z: ") << Xyz.Z;
}

XYZ Xyz(double X, double Y, double Z)
{
    return {X, Y, Z};
}

HSL Hsl(double Hue, double Sat, double Lum)
{
    return {Hue, Sat, Lum};
}

LAB Lab(double L, double A, double B)
{
    return {L, A, B};
}

RGBA Rgba(std::uint32_t R, std::uint32_t G, std::uint32_t B)
{
    return {((R << 16) & 0x00FF0000) | ((G << 8) & 0x0000FF00) | (B & 0x000000FF)};
}

RGBA Rgba(std::uint32_t Colour)
{
    return {((Colour & 0xFF000000) | ((Colour & 0xFF0000) >> 16) | (Colour & 0x00FF00) | ((Colour & 0x0000FF) << 16))};
}

XYZ Xyz(RGBA Rgba)
{
    XYZ Result;
    double Red = (Rgba.R / 255.0);
    double Green = (Rgba.G / 255.0);
    double Blue = (Rgba.B / 255.0);

    Red = (Red > 0.04045) ? std::pow(((Red + 0.055) / 1.055), 2.4) * 100 : Red / 12.92;
    Green = (Green > 0.04045) ? std::pow(((Green + 0.055) / 1.055), 2.4) * 100.0 : Green / 12.92;
    Blue = (Blue > 0.04045) ? std::pow(((Blue + 0.055) / 1.055), 2.4) * 100.0 : Blue / 12.92;
    Result.X = Red * 0.4124 + Green * 0.3576 + Blue * 0.1805;
    Result.Y = Red * 0.2126 + Green * 0.7152 + Blue * 0.0722;
    Result.Z = Red * 0.0193 + Green * 0.1192 + Blue * 0.9505;
    return Result;
}

RGBA Rgba(XYZ Xyz)
{
    RGBA Result;
    double X = Xyz.X / 100.0;
    double Y = Xyz.Y / 100.0;
    double Z = Xyz.Z / 100.0;

    double Red = X * 3.2406 + Y * -1.5372 + Z * -0.4986;
    double Green = X * -0.9689 + Y * 1.8758 + Z * 0.0415;
    double Blue = X * 0.0557 + Y * -0.2040 + Z * 1.0570;

    Red = (Red > 0.0031308) ? (1.055 * std::pow(Red, (1 / 2.4)) - 0.055) : 12.92 * Red;
    Green = (Green > 0.0031308) ? (1.055 * std::pow(Green, (1 / 2.4)) - 0.055) : 12.92 * Green;
    Blue = (Blue > 0.0031308) ? (1.055 * std::pow(Blue, (1 / 2.4)) - 0.055) : 12.92 * Blue;

    Result.R = static_cast<std::uint8_t>(std::round(Red * 255.0));
    Result.G = static_cast<std::uint8_t>(std::round(Green * 255.0));
    Result.B = static_cast<std::uint8_t>(std::round(Blue * 255.0));

    return Result;
}

HSL Hsl(RGBA Rgba)
{
    HSL Result;
    double Red, Green, Blue;
    Red = Rgba.R / 255.0;
    Green = Rgba.G / 255.0;
    Blue = Rgba.B / 255.0;
    double Max = Red > Blue ? Red > Green ? Red : Green : Blue > Green ? Blue : Green;
    double Min = Red < Blue ? Red < Green ? Red : Green : Blue < Green ? Blue : Green;
    Result.Lum = (Max + Min) / 2.0;

    if(Max == Min)
        Result.Hue = Result.Sat = 0.0;
    else
    {
        double Delta = Max - Min;
        Result.Sat = Result.Lum > 0.5 ? Delta / (2.0 - Max - Min) : Delta / (Max + Min);
        if (Max == Red)
            Result.Hue = (Green - Blue) / Delta + (Green < Blue ? 6.0 : 0.0);
        else if(Max == Green)
            Result.Hue = (Blue - Red) / Delta + 2.0;
        else
            Result.Hue = (Red - Green) / Delta + 4.0;

        Result.Hue /= 6.0;
    }
    Result.Hue *= 100.0;
    Result.Sat *= 100.0;
    Result.Lum *= 100.0;
    return Result;
}

RGBA Rgba(HSL Hsl)
{
    RGBA Result;
    double H = Hsl.Hue / 100.0;
    double S = Hsl.Sat / 100.0;
    double L = Hsl.Lum / 100.0;

    auto HSLToRGBHelper = [](double I, double J, double Hue) -> double
    {
        if (Hue < 0.0) Hue += 1.0;
        if (Hue > 1.0) Hue -= 1.0;
        if (Hue < (1.0 / 6.0))
            return std::round((I + (J - I) * 6.0 * Hue) * 255.0);
        else if (Hue < (1.0 / 2.0))
            return std::round(J * 255.0);
        else if (Hue < (2.0 / 3.0))
            return std::round((I + (J - I) * 6.0 * ((2.0 / 3.0) - Hue)) * 255.0);
        else
            return std::round(I * 255.0);
    };

    if (S == 0.0)
    {
        Result.R = Result.G = Result.B = (std::uint8_t)(L * 255.0);
    }
    else
    {
        double J = (L < 0.5) ? (L * (1.0 + S)) : ((L + S) - (S * L));
        double I = 2.0 * L - J;

        Result.R = static_cast<std::uint8_t>(HSLToRGBHelper(I, J, H + (1.0 / 3.0)));
        Result.G = static_cast<std::uint8_t>(HSLToRGBHelper(I, J, H));
        Result.B = static_cast<std::uint8_t>(HSLToRGBHelper(I, J, H - (1.0 / 3.0)));
    }
    return Result;
}

LAB Lab(XYZ Xyz)
{
    double X = Xyz.X / 95.047;
    double Y = Xyz.Y / 100.000;
    double Z = Xyz.Z / 108.883;

    X = (X > 0.008856) ? std::pow(X, (1.0 / 3.0)) : (X * 7.787) + (16.0 / 116.0);
    Y = (Y > 0.008856) ? std::pow(Y, (1.0 / 3.0)) : (Y * 7.787) + (16.0 / 116.0);
    Z = (Z > 0.008856) ? std::pow(Z, (1.0 / 3.0)) : (Z * 7.787) + (16.0 / 116.0);

    return {((Y * 116.0) - 16.0), ((X - Y) * 500), ((Y - Z) * 200)};
}

XYZ Xyz(LAB Lab)
{
    double Y = (Lab.L + 16.0) / 116.0;
    double X = ((Lab.A / 500.0) + Y);
    double Z = (Y - (Lab.B / 200.0));

    Y = (std::pow(Y, 3) > 0.008856) ? std::pow(Y, 3) : ((Y - (16.0 / 116.0)) / 7.787);
    X = (std::pow(X, 3) > 0.008856) ? std::pow(X, 3) : ((X - (16.0 / 116.0)) / 7.787);
    Z = (std::pow(Z, 3) > 0.008856) ? std::pow(Z, 3) : ((Z - (16.0 / 116.0)) / 7.787);

    return {(X * 95.047), (Y * 100.000), (Z * 108.883)};
}

HSL Hsl(std::uint32_t Colour)
{
    return Hsl(Rgba(Colour));
}

XYZ Xyz(std::uint32_t Colour)
{
    return Xyz(Rgba(Colour));
}

LAB Lab(std::uint32_t Colour)
{
    return Lab(Xyz(Colour));
}


std::uint32_t ColourSwap(std::uint32_t Colour)
{
    return ((Colour & 0xFF000000) | ((Colour & 0xFF0000) >> 16) | (Colour & 0x00FF00) | ((Colour & 0x0000FF) << 16));
}

RGBA ColourSwap(RGBA Rgba)
{
    //return (((Colour & 0xFF) << 16) + (((Colour & 0x00FF00) >> 8) << 8) + ((Colour & 0xFF0000) >> 16));
    return {((Rgba.Colour & 0xFF000000) | ((Rgba.Colour & 0xFF0000) >> 16) | (Rgba.Colour & 0x00FF00) | ((Rgba.Colour & 0x0000FF) << 16))};
}

void ColourSwap(std::uint8_t* Buffer, int BufferSize, int BitsPerPixel)
{
    for(int I = 0; I < BufferSize; I += (BitsPerPixel / 8))
    {
        Buffer[I] ^= Buffer[I + 2] ^= Buffer[I] ^= Buffer[I + 2];
    }
}

bool SimilarColours(RGBA FirstCol, RGBA SecondCol, int Tol)
{
    return ((FirstCol.R - SecondCol.R) * (FirstCol.R - SecondCol.R) + (FirstCol.G - SecondCol.G) * (FirstCol.G - SecondCol.G) + (FirstCol.B - SecondCol.B) * (FirstCol.B - SecondCol.B)) <= (Tol * Tol);
}

void Bitmap::ReadPixels(std::uint8_t *BuffPos)
{
    for (std::size_t I = 0; I < height; ++I)
    {
        for (std::size_t J = 0; J < width; ++J)
        {
            Pixels[(height - 1 - I) * width + J].B = *(BuffPos++);
            Pixels[(height - 1 - I) * width + J].G = *(BuffPos++);
            Pixels[(height - 1 - I) * width + J].R = *(BuffPos++);
            Pixels[(height - 1 - I) * width + J].A = (BitsPerPixel > 24 ? * (BuffPos++) : 0xFF);
        }
        if(BitsPerPixel == 24)
            BuffPos += (-width * 3) & 3;
    }
}

void Bitmap::WritePixels(std::uint8_t* &BuffPos)
{
    for (std::size_t I = 0; I < height; ++I)
    {
        for (std::size_t J = 0; J < width; ++J)
        {
            *(BuffPos++) = Pixels[(height - 1 - I) * width + J].B;
            *(BuffPos++) = Pixels[(height - 1 - I) * width + J].G;
            *(BuffPos++) = Pixels[(height - 1 - I) * width + J].R;

            if (BitsPerPixel > 24)
                *(BuffPos++) = Pixels[(height - 1 - I) * width + J].A;
        }
        if(BitsPerPixel == 24)
            BuffPos += (-width * 3) & 3;
    }
}

Bitmap::Bitmap(const char* FilePath) : Pixels(0), width(0), height(0), BitsPerPixel(0)
{
    std::fstream hFile(FilePath, std::ios::in | std::ios::binary);
    if (!hFile.is_open()) throw std::invalid_argument("Error: File Not Found.");

    hFile.seekg(0, std::ios::end);
    int Length = hFile.tellg();
    hFile.seekg(0, std::ios::beg);
    std::vector<std::uint8_t> FileInfo(Length);
    hFile.read(reinterpret_cast<char*>(FileInfo.data()), 54);

    if(FileInfo[0] != 'B' && FileInfo[1] != 'M')
    {
        hFile.close();
        throw std::invalid_argument("Error: Invalid File Format. Bitmap Required.");
    }

    if (FileInfo[28] != 24 && FileInfo[28] != 32)
    {
        hFile.close();
        throw std::invalid_argument("Error: Invalid File Format. 24 or 32 bit Image Required.");
    }

    BitsPerPixel = FileInfo[28];
    width = FileInfo[18] + (FileInfo[19] << 8);
    height = FileInfo[22] + (FileInfo[23] << 8);
    std::uint32_t PixelsOffset = FileInfo[10] + (FileInfo[11] << 8);
    std::vector<std::uint8_t> Data(((width * BitsPerPixel + 31) / 32) * 4 * height);
    hFile.seekg (PixelsOffset, std::ios::beg);
    hFile.read(reinterpret_cast<char*>(Data.data()), Data.size());
    hFile.close();

    Pixels.resize(width * height);
    ReadPixels(Data.data());
}

Bitmap::Bitmap(HWND Window, Box Area, int Bpp) : Pixels(0), width(0), height(0), BitsPerPixel(Bpp)
{
    if ((BitsPerPixel != 24) && (BitsPerPixel != 32))
    {
        throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
    }

    BITMAPINFO Info = {0};
    memset(&Info, 0, sizeof(BITMAPINFO));
    HDC DC = GetDC(Window);
    BITMAP Bmp;
    HBITMAP hBmp = (HBITMAP)GetCurrentObject(DC, OBJ_BITMAP);

    if (GetObject(hBmp, sizeof(BITMAP), &Bmp) == 0)
        std::cout << "ERROR: "<<GetLastError();

    RECT BMBox;
    GetClientRect(Window, &BMBox);
    width = (Area.Width() == 0) ? BMBox.right - BMBox.left : Area.Width();
    height = (Area.Height() == 0) ? BMBox.bottom - BMBox.top : Area.Height();

    HDC MemDC = GetDC(nullptr);
    HDC SDC = CreateCompatibleDC(MemDC);
    HBITMAP hSBmp = CreateCompatibleBitmap(MemDC, width, height);
    DeleteObject(SelectObject(SDC, hSBmp));

    BitBlt(SDC, 0, 0, width, height, DC, Area.X1, Area.Y1, SRCCOPY);
    std::size_t size = ((width * BitsPerPixel + 31) / 32) * 4 * height;

    Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    Info.bmiHeader.biWidth = width;
    Info.bmiHeader.biHeight = height;
    Info.bmiHeader.biPlanes = 1;
    Info.bmiHeader.biBitCount = BitsPerPixel;
    Info.bmiHeader.biCompression = BI_RGB;
    Info.bmiHeader.biSizeImage = size;
    std::vector<std::uint8_t>Data(size);
    GetDIBits(SDC, hSBmp, 0, height, Data.data(), &Info, DIB_RGB_COLORS);
    DeleteDC(SDC);
    DeleteObject(hSBmp);
    ReleaseDC(nullptr, MemDC);
    ReleaseDC(Window, DC);

    Pixels.resize(width * height);
    ReadPixels(Data.data());
}

Bitmap::Bitmap(HDC DC, Box Area, int Bpp) : Pixels(0), width(0), height(0), BitsPerPixel(Bpp)
{
    if ((BitsPerPixel != 24) && (BitsPerPixel != 32))
    {
        throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
    }

    BITMAPINFO Info = {0};
    memset(&Info, 0, sizeof(BITMAPINFO));
    BITMAP Bmp;
    HBITMAP hBmp = (HBITMAP)GetCurrentObject(DC, OBJ_BITMAP);

    if (GetObject(hBmp, sizeof(BITMAP), &Bmp) == 0)
        std::cout << "ERROR: "<<GetLastError();

    Box BMBox;
    HWND Window = WindowFromDC(DC);
    GetClientRect(Window, reinterpret_cast<LPRECT>(&BMBox));
    width = (Area.Width() == 0) ? BMBox.Width() : Area.Width();
    height = (Area.Height() == 0) ? BMBox.Height() : Area.Height();

    HDC MemDC = GetDC(nullptr);
    HDC SDC = CreateCompatibleDC(MemDC);
    HBITMAP hSBmp = CreateCompatibleBitmap(MemDC, width, height);
    DeleteObject(SelectObject(SDC, hSBmp));

    BitBlt(SDC, 0, 0, width, height, DC, Area.X1, Area.Y1, SRCCOPY);
    std::size_t size = ((width * BitsPerPixel + 31) / 32) * 4 * height;

    Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    Info.bmiHeader.biWidth = width;
    Info.bmiHeader.biHeight = height;
    Info.bmiHeader.biPlanes = 1;
    Info.bmiHeader.biBitCount = BitsPerPixel;
    Info.bmiHeader.biCompression = BI_RGB;
    Info.bmiHeader.biSizeImage = size;
    std::vector<std::uint8_t> Data(size);
    GetDIBits(SDC, hSBmp, 0, height, Data.data(), &Info, DIB_RGB_COLORS);
    DeleteDC(SDC);
    DeleteObject(hSBmp);
    ReleaseDC(nullptr, MemDC);

    Pixels.resize(width * height);
    ReadPixels(Data.data());
}

Bitmap::Bitmap(const void* Ptr, int Width, int Height, int Bpp) : Pixels(0), width(Width), height(Height), BitsPerPixel(Bpp)
{
    Pixels.resize(((width * BitsPerPixel + 31) / 32) * 4 * height);
    ReadPixels((std::uint8_t*)Ptr);
}

void Bitmap::Save(const char* FilePath)
{
    std::fstream hFile(FilePath, std::ios::out | std::ios::binary);
    if (!hFile.is_open()) throw std::invalid_argument("Error: File not found.");

    std::uint32_t Trash = 0;
    std::uint16_t Planes = 1;
    std::uint32_t biSize = 40;
    std::uint16_t Type = 0x4D42;
    std::uint32_t compression = 0;
    std::uint32_t PixelsOffsetBits = 54;
    std::uint32_t size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
    std::uint32_t bfSize = 54 + size;
    std::vector<std::uint8_t> Data(size);
    std::uint8_t* DataPointer = Data.data();
    WritePixels(DataPointer);

    hFile.write(reinterpret_cast<char*>(&Type), sizeof(Type));
    hFile.write(reinterpret_cast<char*>(&bfSize), sizeof(bfSize));
    hFile.write(reinterpret_cast<char*>(&Trash), sizeof(std::uint32_t));
    hFile.write(reinterpret_cast<char*>(&PixelsOffsetBits), sizeof(PixelsOffsetBits));
    hFile.write(reinterpret_cast<char*>(&biSize), sizeof(biSize));
    hFile.write(reinterpret_cast<char*>(&width), sizeof(width));
    hFile.write(reinterpret_cast<char*>(&height), sizeof(height));
    hFile.write(reinterpret_cast<char*>(&Planes), sizeof(Planes));
    hFile.write(reinterpret_cast<char*>(&BitsPerPixel), sizeof(BitsPerPixel));
    hFile.write(reinterpret_cast<char*>(&compression), sizeof(compression));
    hFile.write(reinterpret_cast<char*>(&size), sizeof(size));
    hFile.write(reinterpret_cast<char*>(&Trash), sizeof(std::uint32_t) * 4);
    hFile.write(reinterpret_cast<char*>(Data.data()), size);
    hFile.close();
}

Client.hpp:

#ifndef CLIENT_HPP_INCLUDED
#define CLIENT_HPP_INCLUDED

/** By Brandon-T.. Brandon.T-@Live.com **/

#include <windows.h>
#include <iostream>
#include "Bitmap.h"


typedef std::vector<Point> PointArray;
typedef std::vector<int> IntegerArray;
typedef std::vector<IntegerArray> Integer2DArray;
typedef std::vector<PointArray> Point2DArray;

class Client
{
    private:
        HDC DC;
        HWND Window;
        std::uint8_t* Buffer;
        Bitmap* Target;
        int CTSValue, Width, Height;
        double TMod, HueMod, SatMod;

    protected:
            bool (Client::*CTS) (RGBA FirstCol, RGBA SecondCol, int Tol);
            bool CTSN(RGBA FirstCol, RGBA SecondCol, int Tol);
            bool CTS0(RGBA FirstCol, RGBA SecondCol, int Tol);
            bool CTS1(RGBA FirstCol, RGBA SecondCol, int Tol);
            bool CTS2(RGBA FirstCol, RGBA SecondCol, int Tol);
            bool CTS3(RGBA FirstCol, RGBA SecondCol, int Tol);
            void ValidateRange(int X1, int Y1, int X2, int Y2);
            void UpdateTarget();

    public:
        Client();
        ~Client();

        void SetTarget(HDC DC);
        void SetTarget(HWND Window);
        void SetTarget(const Bitmap &Img);
        void SetTarget(std::uint8_t* Buffer, int Width, int Height);

        void Reset();
        int GetColourToleranceSpeed();
        void SetColourToleranceSpeed(int Value);
        void GetSpeed2Modifiers(double &HueMod, double &SatMod);
        void SetSpeed2Modifiers(double HueMod, double SatMod);
        double GetSpeed3Modifier();
        void SetSpeed3Modifier(double TMod);
        bool SimilarColours(std::uint32_t FirstColour, std::uint32_t SecondColour, int Tolerance);
        std::uint32_t GetColour(int X, int Y);
        std::uint32_t CountColour(std::uint32_t Colour, int X1, int Y1, int X2, int Y2);
        std::uint32_t CountColourTolerance(std::uint32_t Colour, int X1, int Y1, int X2, int Y2, int Tolerance);
        bool FindColour(int &X, int &Y, std::uint32_t Colour, int X1, int Y1, int X2, int Y2);
        bool FindColours(PointArray &Points, std::uint32_t Colour, int X1, int Y1, int X2, int Y2);
        bool FindColourTolerance(int &X, int &Y, std::uint32_t Colour, int X1, int Y1, int X2, int Y2, int Tolerance);
        bool FindColoursTolerance(PointArray &Points, std::uint32_t Colour, int X1, int Y1, int X2, int Y2, int Tolerance);
        bool FindBitmap(const Bitmap &BitmapToFind, int &X, int &Y);
        bool FindBitmapIn(const Bitmap &BitmapToFind, int &X, int &Y, int X1, int Y1, int X2, int Y2);
        bool FindBitmapToleranceIn(const Bitmap &BitmapToFind, int &X, int &Y, int X1, int Y1, int X2, int Y2, int Tolerance);
};

#endif // CLIENT_HPP_INCLUDED

Client.cpp:

/** By Brandon-T.. Brandon.T-@Live.com **/


#include "Client.hpp"

Client::Client() : DC(nullptr), Window(nullptr), Buffer(nullptr), Target(nullptr), CTSValue(1), Width(0), Height(0), TMod(4.0), HueMod(0.2), SatMod(0.2), CTS(&Client::CTS1) {}
Client::~Client()
{
    delete Target;
    Target = nullptr;
}

void Client::SetTarget(HDC DC)
{
    this->DC = DC;
    this->Window = nullptr;
    this->Buffer = nullptr;
    delete this->Target;
    this->Target = nullptr;
}

void Client::SetTarget(HWND Window)
{
    this->Window = Window;
    this->DC = nullptr;
    this->Buffer = nullptr;
    delete this->Target;
    this->Target = nullptr;
}

void Client::SetTarget(const Bitmap &Img)
{
    this->Window = nullptr;
    this->DC = nullptr;
    this->Buffer = nullptr;
    delete this->Target;
    this->Target = new Bitmap(Img);
}

void Client::SetTarget(std::uint8_t* Buffer, int Width, int Height)
{
    this->Window = nullptr;
    this->DC = nullptr;
    this->Buffer = Buffer;
    delete this->Target;
    this->Target = nullptr;
    this->Width = Width;
    this->Height = Height;
}

void Client::Reset()
{
    CTSValue = 1;
    CTS = &Client::CTS1;
    TMod = 4.0;
    HueMod = 0.2;
    SatMod = 0.2;
}

int Client::GetColourToleranceSpeed()
{
    return this->CTSValue;
}

void Client::SetColourToleranceSpeed(int Value)
{
    switch (Value)
    {
        case -1:
            CTSValue = -1;
            CTS = &Client::CTSN;
            break;
        case 0:
            CTSValue = 0;
            CTS = &Client::CTS0;
            break;
        case 1:
            CTSValue = 1;
            CTS = &Client::CTS1;
            break;
        case 2:
            CTSValue = 2;
            CTS = &Client::CTS2;
            break;
        case 3:
            CTSValue = 3;
            CTS = &Client::CTS3;
            break;
        default:
            CTSValue = 1;
            CTS = &Client::CTS1;
            break;
    }
}

void Client::GetSpeed2Modifiers(double &HueMod, double &SatMod)
{
    HueMod = this->HueMod;
    SatMod = this->SatMod;
}

void Client::SetSpeed2Modifiers(double HueMod, double SatMod)
{
    this->HueMod = HueMod;
    this->SatMod = SatMod;
}

double Client::GetSpeed3Modifier()
{
    return TMod;
}

void Client::SetSpeed3Modifier(double TMod)
{
    this->TMod = TMod;
}

bool Client::SimilarColours(std::uint32_t FirstColour, std::uint32_t SecondColour, int Tolerance)
{
    return (this->*Client::CTS)(Rgba(FirstColour), Rgba(SecondColour), Tolerance);
}

bool Client::CTSN(RGBA FirstCol, RGBA SecondCol, int Tol)
{
    return (FirstCol.Colour == SecondCol.Colour);
}

bool Client::CTS0(RGBA FirstCol, RGBA SecondCol, int Tol)
{
    return std::abs(FirstCol.R - SecondCol.R) <= Tol && std::abs(FirstCol.G - SecondCol.G) <= Tol && std::abs(FirstCol.B - SecondCol.B) <= Tol;
}

bool Client::CTS1(RGBA FirstCol, RGBA SecondCol, int Tol)
{
    return ((FirstCol.R - SecondCol.R) * (FirstCol.R - SecondCol.R) + (FirstCol.G - SecondCol.G) * (FirstCol.G - SecondCol.G) + (FirstCol.B - SecondCol.B) * (FirstCol.B - SecondCol.B)) <= (Tol * Tol);
}

bool Client::CTS2(RGBA FirstCol, RGBA SecondCol, int Tol)
{
    static HSL HFirstCol = Hsl(0), HSecondCol = Hsl(0);
    if (FirstCol.Colour != 0)
    {
        HFirstCol = Hsl(FirstCol);
    }

    if (SecondCol.Colour != 0)
    {
        HSecondCol = Hsl(SecondCol);
    }

    return (std::fabs(HSecondCol.Hue - HFirstCol.Hue) <= (Tol * HueMod)) && (std::fabs(HSecondCol.Sat - HFirstCol.Sat) <= (Tol * SatMod));
}

bool Client::CTS3(RGBA FirstCol, RGBA SecondCol, int Tol)
{
    static LAB LabFirstCol = Lab(0), LabSecondCol = Lab(0);
    if (FirstCol.Colour != 0)
    {
        LabFirstCol = Lab(Xyz(FirstCol));
    }

    if (SecondCol.Colour != 0)
    {
        LabSecondCol = Lab(Xyz(SecondCol));
    }

    double L = (LabSecondCol.L - LabFirstCol.L);
    double A = (LabSecondCol.A - LabFirstCol.A);
    double B = (LabSecondCol.B - LabFirstCol.B);

    return ((L * L) + (A * A) + (B * B)) <= std::ceil(std::sqrt(Tol * TMod));
}

void Client::ValidateRange(int X1, int Y1, int X2, int Y2)
{
    if (X1 < 0 || Y1 < 0)
    {
        throw std::invalid_argument("Error: X1 and Y1 must be greater than Zero.");
    }

    if (X2 < X1)
    {
        throw std::invalid_argument("Error: X2 < X1. X2 must be greater than X1.");
    }

    if (Y2 < Y1)
    {
        throw std::invalid_argument("Error: Y2 < Y1. Y2 must be greater than Y1.");
    }

    if (X2 > Target->Width())
    {
        throw std::out_of_range("Error: X2 out of bounds. X2 must be within the search area.");
    }

    if (Y2 > Target->Height())
    {
        throw std::out_of_range("Error: Y2 out of bounds. Y2 must be within the search area.");
    }
}

void Client::UpdateTarget()
{
    if (this->DC != nullptr)
    {
        delete this->Target;
        this->Target = new Bitmap(this->DC);
    }
    else if (this->Window != nullptr)
    {
        delete this->Target;
        this->Target = new Bitmap(this->Window);
    }
    else if (this->Buffer != nullptr)
    {
        delete this->Target;
        std::vector<std::uint8_t> Buffer(765 * 553 * 4, 0);
        //FlipBitmapBytes(this->Buffer, Buffer.data(), 765, 553);
        this->Target = new Bitmap(Buffer.data(), this->Width, this->Height);
    }
}

std::uint32_t Client::GetColour(int X, int Y)
{
    this->UpdateTarget();
    if (X < 0 || Y < 0 || X > this->Target->Width() || Y > this->Target->Height())
    {
        throw std::invalid_argument("Error: X and Y must lie within: (0, 0, " + std::to_string(this->Target->Width()) + ", " + std::to_string(this->Target->Height()) + ").");
    }
    return this->Target->GetPixel(X, Y);
}

std::uint32_t Client::CountColour(std::uint32_t Colour, int X1, int Y1, int X2, int Y2)
{
    int TemporaryCTS = this->CTSValue;
    SetColourToleranceSpeed(-1);
    std::uint32_t Result = CountColourTolerance(Colour, X1, Y1, X2, Y2, 0);
    SetColourToleranceSpeed(TemporaryCTS);
    return Result;
}

std::uint32_t Client::CountColourTolerance(std::uint32_t Colour, int X1, int Y1, int X2, int Y2, int Tolerance)
{
    int Result = 0;
    this->UpdateTarget();
    ValidateRange(X1, Y1, X2, Y2);

    for (int I = Y1; I < Y2; ++I)
    {
        for (int J = X1; J < X2; ++J)
        {
            if (SimilarColours(Colour, Target->GetPixel(J, I), Tolerance))
            {
                ++Result;
            }
        }
    }

    return Result;
}

bool Client::FindColour(int &X, int &Y, std::uint32_t Colour, int X1, int Y1, int X2, int Y2)
{
    int TemporaryCTS = CTSValue;
    SetColourToleranceSpeed(-1);
    bool Result = FindColourTolerance(X, Y, Colour, X1, Y1, X2, Y2, 0);
    SetColourToleranceSpeed(TemporaryCTS);
    return Result;
}

bool Client::FindColours(PointArray &Points, std::uint32_t Colour, int X1, int Y1, int X2, int Y2)
{
    int TemporaryCTS = CTSValue;
    SetColourToleranceSpeed(-1);
    bool Result = FindColoursTolerance(Points, Colour, X1, Y1, X2, Y2, 0);
    SetColourToleranceSpeed(TemporaryCTS);
    return Result;
}

bool Client::FindColourTolerance(int &X, int &Y, std::uint32_t Colour, int X1, int Y1, int X2, int Y2, int Tolerance)
{
    this->UpdateTarget();
    ValidateRange(X1, Y1, X2, Y2);
    for (int I = Y1; I < Y2; ++I)
    {
        for (int J = X1; J < X2; ++J)
        {
            if (SimilarColours(Target->GetPixel(J, I), Colour, Tolerance))
            {
                X = J;
                Y = I;
                return true;
            }
        }
    }
    return false;
}

bool Client::FindColoursTolerance(PointArray &Points, std::uint32_t Colour, int X1, int Y1, int X2, int Y2, int Tolerance)
{
    this->UpdateTarget();
    ValidateRange(X1, Y1, X2, Y2);
    for (int I = Y1; I < Y2; ++I)
    {
        for (int J = X1; J < X2; ++J)
        {
            if (SimilarColours(Target->GetPixel(J, I), Colour, Tolerance))
                Points.push_back({J, I});
        }
    }
    return (!Points.empty());
}

bool Client::FindBitmap(const Bitmap &BitmapToFind, int &X, int &Y)
{
    return FindBitmapIn(BitmapToFind, X, Y, 0, 0, Target->Width(), Target->Height());
}

bool Client::FindBitmapIn(const Bitmap &BitmapToFind, int &X, int &Y, int X1, int Y1, int X2, int Y2)
{
    int TemporaryCTS = CTSValue;
    bool Result = FindBitmapToleranceIn(BitmapToFind, X, Y, X1, Y1, X2, Y2, 0);
    SetColourToleranceSpeed(TemporaryCTS);
    return Result;
}

bool Client::FindBitmapToleranceIn(const Bitmap &BitmapToFind, int &X, int &Y, int X1, int Y1, int X2, int Y2, int Tolerance)
{
    this->UpdateTarget();
    ValidateRange(X1, Y1, X2, Y2);

    int dX = (X2 - X1) - (BitmapToFind.Width() - 1);
    int dY = (Y2 - Y1) - (BitmapToFind.Height() - 1);

    for (int I = 0; I < dY; ++I)
    {
        for (int J = 0; J < dX; ++J)
        {
            for (int YY = 0; YY < BitmapToFind.Height(); ++YY)
            {
                for (int XX = 0; XX < BitmapToFind.Width(); ++XX)
                {
                    if (BitmapToFind.GetPixel(XX, YY) != 0)
                    {
                        if (!SimilarColours(BitmapToFind.GetPixel(XX, YY), Target->GetPixel(XX + J, YY + I), Tolerance))
                        {
                            goto Skip;
                        }
                    }
                }
            }

            X = J + X1;
            Y = I + Y1;
            return true;
Skip:
            continue;
        }
    }

    X = -1;
    Y = -1;
    return false;
}

main.cpp (Sample Usage):

/** By Brandon-T.. Brandon.T-@Live.com **/


#include <iostream>
#include "Client.hpp"

using namespace std;

int main()
{
    Client C;
    C.SetTarget((HWND)6490202);
    int X, Y;
    try
    {
        RECT Rect;
        GetClientRect((HWND)6490202, &Rect);
        if (C.FindColour(X, Y, 13711872, 0, 0, Rect.right - Rect.left, Rect.bottom - Rect.top))
        {
            std::cout<<X<<", "<<Y<<"\n";
        }
    }
    catch (std::exception &e)
    {
        std::cout<<e.what();
    }
    return 0;
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.