SRL Archive - S.M.A.R.T. Questions and Answers.

SMART -> C How do I use GetImageArray


DrChill

2013-07-22 02:53:26

I've been playing with getting SMART working in c under Linux, and I have spawning+mouse+keyboard functions done(pretty simple, actually), but now I wish to read from the screen, GetImageArray returns a void*, but how do I use this? Is it an array of pixels or something? After I port this to C, porting to other languages will be simple (for some reason the scripting language I am using has trouble reading the DLL created by SMART).

It's been pretty easy to get everything up and running so far, thanks to the examples in the git repo, but there are no examples with regards to reading the screen pixels.

Brandon

2013-07-22 03:05:36

Ok keep and eye on this post.. I'm about to edit it in roughly 3 minutes.. You will see a shit ton of code so brace yourself. I STRONGLY suggest sticking to the bitmap format shown below.. The PNG and other formats are just there as other options so you can see how things are done.


First you need a way of reading the Image Smart gives you. SmartImageArray is a pointer to a 32bit structure of pixels that has the following info (It's C++ but can convert it to C easily.. If you don't understand the C++ code, I can help you convert it to C or I can do it I guess.. Would be a good idea to try and figure it out first though):


typedef struct
{
unsigned char B, G, R, A;
} RGBA;

Each RGBA represents one pixel and each pixel has those 4 components. They can be read into a PNG or a Bitmap or whatever image type you choose.
For the sake of completeness, you can read a bitmap using the following "Cross Platform code":

Bitmap.hpp:

#ifndef BITMAP_H_INCLUDED
#define BITMAP_H_INCLUDED

#include <iostream>
#include <fstream>
#include <vector>
#include <stdexcept>
#include <sstream>
#include <cmath>

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;

RGBA Rgba(std::uint32_t R, std::uint32_t G, std::uint32_t B);
RGBA Rgba(std::uint32_t Colour);


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(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 Rgba(Pixel.R, Pixel.G, Pixel.B).Colour;
}

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:

#include "Bitmap.hpp"

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

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

Then you can use it like so:


int main()
{
Bitmap Img(SmartImageArray, 765, 553); //Whatever dimensions it is..
Img.Save("C:/Users/Brandon/Desktop/SmartImage.bmp"); //Saves as 32bit by default. You can just set the bit and it'll save as 24.
}




EXTRAS:

What I did when writing Simba++ was I created something like:

SmartParams.hpp:

#ifndef SMARTPARAMS_HPP_INCLUDED
#define SMARTPARAMS_HPP_INCLUDED

#include "Internals.hpp"
#include "Platform.hpp"

class Smart
{
private:
Library* Remote;
std::string Plugins;

public:
Smart(const std::string &Path);
~Smart();

void SetPlugins(const std::string &plugins);
std::vector<std::string> GetParameters();
std::vector<int> GetClientIDs();
void KillClients();
bool Pair(int ID);
int Init(bool ForceNew);


void (*SmartShowConsole) (bool show);
int (*SmartClientID) (int idx);
int (*SmartGetClients) (bool only_unpaired);
int (*SmartSpawnClient) (char* remote_path, char *root, char *params, int width, int height, char *initseq, char *useragent, char* javaargs, char* Plugins);
bool (*SmartPairClient) (int pid);
int (*SmartGetCurrent) ();
bool (*SmartKillClient) (int pid);
void* (*SmartGetImageArray) ();
void* (*SmartGetDebugArray) ();
void (*SmartSetTransparentColor) (int color);
void (*SmartSetDebug) (bool enabled);
void (*SmartSetGraphics) (bool enabled);
void (*SmartSetKeyInput) (bool enabled);
void (*SmartSetMouseInput) (bool Enabled);
bool (*SmartIsActive) ();
bool (*SmartIsKeysEnabled) ();
bool (*SmartIsMouseEnabled) ();
void (*SmartGetMousePos) (int &x, int &y);
void (*SmartHoldMouse) (int x, int y, bool left);
void (*SmartReleaseMouse) (int x, int y, bool left);
void (*SmartHoldMousePlus) (int x, int y, int button);
void (*SmartReleaseMousePlus) (int x, int y, int button);
void (*SmartMoveMouse) (int x, int y);
void (*SmartWindMouse) (int x, int y);
void (*SmartClickMouse) (int x, int y, bool left);
void (*SmartClickMousePlus) (int x, int y, int button);
bool (*SmartIsMouseButtonHeld) (int button);
void (*SmartSendKeys) (char *text, int keywait, int keymodwait);
void (*SmartHoldKey) (int code);
void (*SmartReleaseKey) (int code);
bool (*SmartIsKeyDown) (int code);
void (*SmartSetMode) (int mode);
};

#endif // SMARTPARAMS_HPP_INCLUDED

SmartParams.cpp:

#include "SmartParams.hpp"

Smart::~Smart()
{
delete this->Remote;
this->Remote = nullptr;
}

Smart::Smart(const std::string &Path)
{
if ((this->Remote = new Library(Path)) != nullptr)
{
this->Remote->FunctionAddress(this->SmartShowConsole, "exp_smartShowConsole");
this->Remote->FunctionAddress(this->SmartClientID, "exp_clientID");
this->Remote->FunctionAddress(this->SmartGetClients, "exp_getClients");
this->Remote->FunctionAddress(this->SmartSpawnClient, "exp_spawnClient");
this->Remote->FunctionAddress(this->SmartPairClient, "exp_pairClient");
this->Remote->FunctionAddress(this->SmartGetCurrent, "exp_getCurrent");
this->Remote->FunctionAddress(this->SmartKillClient, "exp_killClient");
this->Remote->FunctionAddress(this->SmartGetImageArray, "exp_getImageArray");
this->Remote->FunctionAddress(this->SmartGetDebugArray, "exp_getDebugArray");
this->Remote->FunctionAddress(this->SmartSetTransparentColor, "exp_setTransparentColor");
this->Remote->FunctionAddress(this->SmartSetDebug, "exp_setDebug");
this->Remote->FunctionAddress(this->SmartSetGraphics, "exp_setGraphics");
this->Remote->FunctionAddress(this->SmartSetKeyInput, "exp_setKeyInput");
this->Remote->FunctionAddress(this->SmartSetMouseInput, "exp_setMouseInput");
this->Remote->FunctionAddress(this->SmartIsActive, "exp_isActive");
this->Remote->FunctionAddress(this->SmartIsKeysEnabled, "exp_isKeysEnabled");
this->Remote->FunctionAddress(this->SmartIsMouseEnabled, "exp_isMouseEnabled");
this->Remote->FunctionAddress(this->SmartGetMousePos, "exp_getMousePos");
this->Remote->FunctionAddress(this->SmartHoldMouse, "exp_holdMouse");
this->Remote->FunctionAddress(this->SmartReleaseMouse, "exp_releaseMouse");
this->Remote->FunctionAddress(this->SmartHoldMousePlus, "exp_holdMousePlus");
this->Remote->FunctionAddress(this->SmartReleaseMousePlus, "exp_releaseMousePlus");
this->Remote->FunctionAddress(this->SmartMoveMouse, "exp_moveMouse");
this->Remote->FunctionAddress(this->SmartWindMouse, "exp_windMouse");
this->Remote->FunctionAddress(this->SmartClickMouse, "exp_clickMouse");
this->Remote->FunctionAddress(this->SmartClickMousePlus, "exp_clickMousePlus");
this->Remote->FunctionAddress(this->SmartIsMouseButtonHeld, "exp_isMouseButtonHeld");
this->Remote->FunctionAddress(this->SmartSendKeys, "exp_sendKeys");
this->Remote->FunctionAddress(this->SmartHoldKey, "exp_holdKey");
this->Remote->FunctionAddress(this->SmartReleaseKey, "exp_releaseKey");
this->Remote->FunctionAddress(this->SmartIsKeyDown, "exp_isKeyDown");
this->Remote->FunctionAddress(this->SmartSetMode, "exp_setMode");
}
}

void Smart::SetPlugins(const std::string &plugins)
{
this->Plugins = plugins;
}

std::vector<std::string> Smart::GetParameters()
{
return std::move(Explode(Between("<iframe id=\"game\" src=\"", "\"", GetPage("http://www.runescape.com/game.ws?j=1")), ","));
}

std::vector<int> Smart::GetClientIDs()
{
std::vector<int> Result;
Result.SetLength(SmartGetClients(true));
for (std::size_t I = 0; I < Result.Size(); ++I)
{
Result[I] = SmartClientID(I);
}
return std::move(Result);
}

void Smart::KillClients()
{
std::vector<int> IDs = this->GetClientIDs();
for (int I = IDs.Size(); I > 0; --I)
{
if (this->SmartKillClient(IDs[I]))
{
tcout<<_T("Killed Client: ")<<IDs[I]<<"\n";
}
else
{
tcout<<_T("Failed To Kill Client: ")<<IDs[I]<<"\n";
}
}
}

bool Smart::Pair(int ID)
{
std::vector<int> IDs = this->GetClientIDs();
for (std::size_t I = 0; I < IDs.Size(); ++I)
{
if (IDs[I] == ID && this->SmartPairClient(ID))
{
std::cout<<"Paired with SMART["<<ID<<"]\n";
return true;
}
}
std::cout<<"Failed To Pair Client: "<<ID<<"\n";
return false;
}

int Smart::Init(bool ForceNew)
{
if (!ForceNew)
{
std::vector<int> IDs = this->GetClientIDs();
for (std::size_t I = 0; I < IDs.Size(); ++I)
{
if (this->SmartPairClient(IDs[I]))
{
std::cout<<"Paired with SMART["<<IDs[I]<<"]\n";
return IDs[I];
}
}
}

std::vector<std::string> Params = this->GetParameters();
if (Params.Size() != 2)
{
std::cout<<"Failed To Grab Smart Parameters\n";
std::cout<<"Please check your internet connection/firewall\n";
}

#ifndef GLHOOK
char InitSeq[] = {'S'};
#else
char* InitSeq = nullptr;
#endif

int Result = this->SmartSpawnClient(const_cast<char*>("C:/Simba/Plugins/"), const_cast<char*>(Params[0].c_str()), const_cast<char*>(("," + Params[1]).c_str()), 765, 553, InitSeq, nullptr, nullptr, const_cast<char*>(this->Plugins.c_str()));
if (Result > 0)
{
std::cout<<"Loading SMART: "<<Params[0]<<Params[1]<<"\n";
}
else
{
std::cout<<"Smart Cannot Spawn Clients\n";
}
return Result;
}


And it would load Smart.. and I'd just create a bitmap from its buffer and do whatever I wanted with that.. If you need help on anything else, just post or something or tag me if you like.


For loading almost all types of Images that Simba has, you can load PNG, BMP, TGA, etc.. using the following (partially cross platform):

Image.hpp:

enum IMAGE_TYPE {GIF = 0, PNG, BMP, JPG, JPEG, TIFF, TGA};

class Image
{
private:
std::vector<RGBA> Pixels;
BITMAPINFO Info;
bool ImageCompressed;
std::uint32_t width, height, size;
BITMAPFILEHEADER bFileHeader;

std::uint32_t BitsPerPixel, ImageType;
int bitdepth, colortype, interlacetype, channels;
static int ValidImage(std::uint8_t* ImageBytes);
static void ReadFromStream(png_structp PngPointer, std::uint8_t* Data, std::size_t Length);
static void WriteToStream(png_structp PngPointer, std::uint8_t* Data, std::size_t Length);

protected:
HDC DC;
HBITMAP ImageHandle;
void Swap(Image& Img);

public:
~Image();
Image(const Image& Img);
Image(Image && Img);
explicit Image(const char* FilePath);

explicit Image(HBITMAP Bmp);
explicit Image(std::uint16_t ResourceID);
explicit Image(std::vector<std::uint8_t> &Pointer, int Width, int Height, std::uint32_t BitsPerPixel = 32, IMAGE_TYPE Value = BMP);
explicit Image(const void* Pointer, int Width, int Height, std::uint32_t BitsPerPixel = 32, IMAGE_TYPE Value = BMP);
explicit Image(int Width, int Height, std::uint32_t BitsPerPixel = 32);
explicit Image(HWND Window, Box Area = Box(0, 0, 0, 0), std::uint32_t BitsPerPixel = 32);
explicit Image(HDC hdc, Box Area = Box(0, 0, 0, 0), std::uint32_t BitsPerPixel = 32);

bool Save(const char* FilePath, IMAGE_TYPE Value = BMP);

Image& operator = (Image Img);

HBITMAP ToHBitmap();

void Draw(HWND Hwnd, HBITMAP hBmp = nullptr, int X = 0, int Y = 0, int Width = 0, int Height = 0, int SourceX = 0, int SourceY = 0, DWORD RasterType = SRCCOPY);

void Draw(HDC hDC, HBITMAP hBmp = nullptr, int X = 0, int Y = 0, int Width = 0, int Height = 0, int SourceX = 0, int SourceY = 0, DWORD RasterType = SRCCOPY);

tstring ToString();

Image& FromString(tstring Data, std::uint32_t BitsPerPixel = 32);

int Width() const;

int Height() const;

int Size() const;

void Size(int& Width, int& Height) const;

int GetBitsPerPixel() const;

void SetBitsPerPixel(std::uint32_t Depth);

RGBA& operator [](int I);

const RGBA& operator [](int I) const;

std::vector<RGBA>::iterator PixelsBegin();

std::vector<RGBA>::const_iterator PixelsBegin() const;

std::vector<RGBA>::iterator PixelsEnd();

std::vector<RGBA>::const_iterator PixelsEnd() const;

std::pair<std::vector<RGBA>::iterator, std::vector<RGBA>::iterator> PixelsRange();

std::pair<std::vector<RGBA>::const_iterator, std::vector<RGBA>::const_iterator> PixelsRange() const;

RGBA GetPixel(int X, int Y) const;

void SetPixel(int X, int Y, RGBA Color);
};

Image.cpp:

#include "Images.hpp"

namespace cxx
{
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));
}


/** Image Class **/

Image::~Image()
{
Pixels.clear();
DeleteDC(DC);
DeleteObject(ImageHandle);
ImageHandle = nullptr;
}

int Image::ValidImage(std::uint8_t* ImageBytes)
{
const static std::vector<std::uint8_t> GIFBytesOne = { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 };
const static std::vector<std::uint8_t> GIFBytesTwo = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 };
const static std::vector<std::uint8_t> PNGBytes = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
const static std::vector<std::uint8_t> BMPBytes = { 0x42, 0x4D };
const static std::vector<std::uint8_t> JPGBytes = { 0xFF, 0xD8, 0xFF };
const static std::vector<std::uint8_t> JPEGBytes = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20 };
const static std::vector<std::uint8_t> TIFFMonoChrome = { 0x0C, 0xED };
const static std::vector<std::uint8_t> TIFFOne = { 0x49, 0x20, 0x49 };
const static std::vector<std::uint8_t> TIFFTwo = { 0x49, 0x49, 0x2A, 0x00 };
const static std::vector<std::uint8_t> TIFFThree = { 0x4D, 0x4D, 0x00, 0x2A };
const static std::vector<std::uint8_t> TIFFFour = { 0x4D, 0x4D, 0x00, 0x2B };
const static std::vector<std::uint8_t> CompressedTGA = {0x0, 0x0, 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
const static std::vector<std::uint8_t> DeCompressedTGA = {0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};

const static std::vector<std::vector<std::uint8_t>> All = {GIFBytesOne, GIFBytesTwo, PNGBytes, BMPBytes, JPGBytes, JPEGBytes, TIFFMonoChrome, TIFFOne, TIFFTwo, TIFFThree, TIFFFour, CompressedTGA, DeCompressedTGA};

auto BytesMatch = [](std::vector<std::uint8_t> One, std::uint8_t * Two) -> bool
{
for (size_t I = 0; I < One.size(); ++I)
{
if (One[I] != Two[I])
return false;
}
return true;
};

int I = 0;
for (std::vector<std::uint8_t> Bytes : All)
{
if (BytesMatch(Bytes, ImageBytes))
return I;
++I;
}
return -1;
}

void Image::ReadFromStream(png_structp PngPointer, std::uint8_t* Data, std::size_t Length)
{
std::ifstream *Stream = reinterpret_cast<std::ifstream*>(png_get_io_ptr(PngPointer));
Stream->read(reinterpret_cast<char*>(Data), Length);
}

void Image::WriteToStream(png_structp PngPointer, std::uint8_t* Data, std::size_t Length)
{
std::ofstream *Stream = reinterpret_cast<std::ofstream*>(png_get_io_ptr(PngPointer));
Stream->write(reinterpret_cast<char*>(Data), Length);
}

Image::Image(const Image& Img) : Pixels(Img.Pixels), Info(Img.Info), ImageCompressed(Img.ImageCompressed), width(Img.width), height(Img.height), size(Img.size), bFileHeader(Img.bFileHeader), BitsPerPixel(Img.BitsPerPixel), ImageType(Img.ImageType), bitdepth(Img.bitdepth), colortype(Img.colortype), interlacetype(Img.interlacetype), channels(Img.channels), DC(nullptr), ImageHandle(nullptr) {}

Image::Image(Image && Img) : Pixels(), Info(), ImageCompressed(false), width(0), height(0), size(0), bFileHeader(), BitsPerPixel(32), ImageType(0), bitdepth(8), colortype(PNG_COLOR_TYPE_RGBA), interlacetype(0), channels(4), DC(nullptr), ImageHandle(nullptr)
{
this->Swap(Img);
Img.Pixels.clear();
}

Image::Image(const char* FilePath) : Pixels(0), Info(), ImageCompressed(false), width(0), height(0), size(0), bFileHeader(), BitsPerPixel(32), ImageType(0), bitdepth(8), colortype(PNG_COLOR_TYPE_RGBA), interlacetype(0), channels(4), DC(nullptr), ImageHandle(nullptr)
{
Pixels.clear();
std::fstream hFile(FilePath, std::ios::in | std::ios::binary);

if (!hFile.is_open())
{
throw std::invalid_argument("File Not Found.");
}

std::uint8_t Header[18] = {0};
hFile.read(reinterpret_cast<char*>(&Header), sizeof(Header));
hFile.seekg(0, std::ios::beg);

int IMAGETYPE = ValidImage(Header);

if (IMAGETYPE == 2) //It's a PNG..
{
hFile.seekg(8);
if (png_sig_cmp(Header, 0, 8))
{
hFile.close();
throw std::invalid_argument("Error: Invalid File Format. Required: Png.");
}

png_structp PngPointer = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (!PngPointer)
{
hFile.close();
throw std::runtime_error("Error: Cannot Create Read Structure.");
}

png_infop InfoPointer = png_create_info_struct(PngPointer);
if (!InfoPointer)
{
hFile.close();
png_destroy_read_struct(&PngPointer, nullptr, nullptr);
throw std::runtime_error("Error: Cannot Create InfoPointer Structure.");
}

png_infop EndInfo = png_create_info_struct(PngPointer);
if (!EndInfo)
{
hFile.close();
png_destroy_read_struct(&PngPointer, &InfoPointer, nullptr);
throw std::runtime_error("Error: Cannot Create EndInfo Structure.");
}

if (setjmp(png_jmpbuf(PngPointer)))
{
hFile.close();
png_destroy_read_struct(&PngPointer, &InfoPointer, nullptr);
throw std::runtime_error("Error: Cannot Set Jump Pointer.");
}

png_set_sig_bytes(PngPointer, 8);
png_set_read_fn(PngPointer, &hFile, ReadFromStream);
png_read_info(PngPointer, InfoPointer);

channels = png_get_channels(PngPointer, InfoPointer);
png_get_IHDR(PngPointer, InfoPointer, &width, &height, &bitdepth, &colortype, &interlacetype, nullptr, nullptr);
png_set_interlace_handling(PngPointer);
png_set_strip_16(PngPointer);
png_set_packing(PngPointer);

switch (colortype)
{
case PNG_COLOR_TYPE_GRAY:
{
png_set_expand_gray_1_2_4_to_8(PngPointer);
png_set_gray_to_rgb(PngPointer);
png_set_bgr(PngPointer);
break;
}

case PNG_COLOR_TYPE_PALETTE:
{
png_set_palette_to_rgb(PngPointer);
if (png_get_valid(PngPointer, InfoPointer, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(PngPointer);
else
png_set_filler(PngPointer, 0xFF, PNG_FILLER_AFTER);
png_set_bgr(PngPointer);
BitsPerPixel = 24;
break;
}

case PNG_COLOR_TYPE_GRAY_ALPHA:
{
png_set_gray_to_rgb(PngPointer);
break;
}

case PNG_COLOR_TYPE_RGB:
{
png_set_bgr(PngPointer);
png_set_filler(PngPointer, 0xFF, PNG_FILLER_AFTER);
BitsPerPixel = 24;
break;
}

case PNG_COLOR_TYPE_RGBA:
{
png_set_bgr(PngPointer);
BitsPerPixel = 32;
break;
}

default:
png_destroy_read_struct(&PngPointer, &InfoPointer, nullptr);
throw std::runtime_error("Error: Png Type not supported.");
break;
}

png_read_update_info(PngPointer, InfoPointer);
channels = png_get_channels(PngPointer, InfoPointer);
png_get_IHDR(PngPointer, InfoPointer, &width, &height, &bitdepth, &colortype, &interlacetype, nullptr, nullptr);

Pixels.resize(width * height);
std::vector<std::uint8_t*> RowPointers(height);
std::uint8_t* BuffPos = reinterpret_cast<std::uint8_t*>(Pixels.data());

for (size_t I = 0; I < height; ++I)
{
RowPointers[I] = BuffPos + (I * width * sizeof(RGBA));
}

png_read_image(PngPointer, RowPointers.data());
png_read_end(PngPointer, InfoPointer);
png_destroy_read_struct(&PngPointer, &InfoPointer, nullptr);
hFile.close();
}
else if (IMAGETYPE == 3) //It's a Bitmap.
{
std::memset(&Info, 0, sizeof(BITMAPINFO));
hFile.read(reinterpret_cast<char*>(&bFileHeader), sizeof(bFileHeader));
hFile.read(reinterpret_cast<char*>(&Info.bmiHeader), sizeof(Info.bmiHeader));

if (((Info.bmiHeader.biBitCount != 24) && (Info.bmiHeader.biBitCount != 32)) || (bFileHeader.bfType != 0x4D42) || (Info.bmiHeader.biCompression != BI_RGB))
{
hFile.close();
throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
}

width = Info.bmiHeader.biWidth;
height = std::abs(Info.bmiHeader.biHeight);
size = Info.bmiHeader.biSizeImage;

std::vector<std::uint8_t> TEMP(size);
hFile.seekg (bFileHeader.bfOffBits, std::ios::beg);
hFile.read(reinterpret_cast<char*>(TEMP.data()), size);
hFile.close();

std::uint8_t* BuffPos = TEMP.data();
Pixels.resize(width * height);

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 = (Info.bmiHeader.biBitCount > 24 ? * (BuffPos++) : 0xFF);
}
if(Info.bmiHeader.biBitCount == 24)
BuffPos += (-width * 3) & 3; //(4 - ((width * 3) % 4)) % 4;
}
}
else if (IMAGETYPE == 11 || IMAGETYPE == 12) //It's a TGA Image.
{
hFile.seekg(sizeof(Header));
std::vector<std::uint8_t> ImageData;

if (IMAGETYPE == 11)
{
BitsPerPixel = Header[16];
width = (Header[13] << 8) + Header[12];
height = (Header[15] << 8) + Header[14];
size = ((width * BitsPerPixel + 31) / 32) * 4 * height;

if ((BitsPerPixel != 24) && (BitsPerPixel != 32))
{
hFile.close();
throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
}

RGBA Pixel = {0};
int CurrentByte = 0;
std::size_t CurrentPixel = 0;
ImageCompressed = true;
std::uint8_t ChunkHeader = {0};
int BytesPerPixel = (BitsPerPixel / 8);
ImageData.resize(width * height * sizeof(RGBA));

do
{
hFile.read(reinterpret_cast<char*>(&ChunkHeader), sizeof(ChunkHeader));

if(ChunkHeader < 128)
{
++ChunkHeader;
for(int I = 0; I < ChunkHeader; ++I, ++CurrentPixel)
{
hFile.read(reinterpret_cast<char*>(&Pixel), BytesPerPixel);

ImageData[CurrentByte++] = Pixel.B;
ImageData[CurrentByte++] = Pixel.G;
ImageData[CurrentByte++] = Pixel.R;
if (BitsPerPixel > 24) ImageData[CurrentByte++] = Pixel.A;
}
}
else
{
ChunkHeader -= 127;
hFile.read(reinterpret_cast<char*>(&Pixel), BytesPerPixel);

for(int I = 0; I < ChunkHeader; ++I, ++CurrentPixel)
{
ImageData[CurrentByte++] = Pixel.B;
ImageData[CurrentByte++] = Pixel.G;
ImageData[CurrentByte++] = Pixel.R;
if (BitsPerPixel > 24) ImageData[CurrentByte++] = Pixel.A;
}
}
}
while(CurrentPixel < (width * height));
}
else if (IMAGETYPE == 12)
{
BitsPerPixel = Header[16];
width = (Header[13] << 8) + Header[12];
height = (Header[15] << 8) + Header[14];
size = ((width * BitsPerPixel + 31) / 32) * 4 * height;

if ((BitsPerPixel != 24) && (BitsPerPixel != 32))
{
hFile.close();
throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
}

ImageData.resize(size);
ImageCompressed = false;
hFile.read(reinterpret_cast<char*>(ImageData.data()), size);
}
else
{
hFile.close();
throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit TGA File.");
}

hFile.close();
std::uint8_t* BuffPos = ImageData.data();
Pixels.resize(width * height);

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);
}
}
}
else
{
hFile.close();
throw std::invalid_argument("Error: Invalid File Format!");
}

std::memset(&Info, 0, sizeof(BITMAPINFO));
Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
Info.bmiHeader.biPlanes = 1;
Info.bmiHeader.biBitCount = BitsPerPixel;
Info.bmiHeader.biCompression = BI_RGB;
Info.bmiHeader.biWidth = width;
Info.bmiHeader.biHeight = height;
Info.bmiHeader.biSizeImage = size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
bFileHeader.bfType = 0x4D42;
bFileHeader.bfSize = bFileHeader.bfOffBits + size;
bFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(Info.bmiHeader);
channels = (BitsPerPixel > 24 ? 4 : 3);
colortype = (BitsPerPixel > 24 ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB);
}

Image::Image(HBITMAP BitmapHandle) : Pixels(0), Info(), ImageCompressed(false), width(0), height(0), size(0), bFileHeader(), BitsPerPixel(32), ImageType(0), bitdepth(8), colortype(PNG_COLOR_TYPE_RGBA), interlacetype(0), channels(4), DC(nullptr), ImageHandle(nullptr)
{
if (BitmapHandle == nullptr)
{
throw std::logic_error("Null Pointer Exception. BitmapHandle is Null.");
}
BITMAP Bmp;
Pixels.clear();
DC = CreateCompatibleDC(nullptr);
std::memset(&Info, 0, sizeof(BITMAPINFO));
HBITMAP OldBitmap = (HBITMAP)SelectObject(DC, BitmapHandle);
GetObject(BitmapHandle, sizeof(Bmp), &Bmp);

Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
Info.bmiHeader.biWidth = width = Bmp.bmWidth;
Info.bmiHeader.biHeight = height = Bmp.bmHeight;
Info.bmiHeader.biPlanes = 1;
Info.bmiHeader.biBitCount = Bmp.bmBitsPixel;
Info.bmiHeader.biCompression = BI_RGB;
Info.bmiHeader.biSizeImage = size = ((width * Bmp.bmBitsPixel + 31) / 32) * 4 * height;
bFileHeader.bfType = 0x4D42;
bFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(Info.bmiHeader);
bFileHeader.bfSize = bFileHeader.bfOffBits + size;
channels = (BitsPerPixel > 24 ? 4 : 3);
colortype = (BitsPerPixel > 24 ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB);

std::vector<std::uint8_t> ImageData(size);
GetDIBits(DC, BitmapHandle, 0, height, ImageData.data(), &Info, DIB_RGB_COLORS);
SelectObject(DC, OldBitmap);

height = std::abs(height);
std::uint8_t* BuffPos = ImageData.data();
Pixels.resize(width * height);

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 = (Info.bmiHeader.biBitCount > 24 ? *(BuffPos++) : 0xFF);
}
if(Info.bmiHeader.biBitCount == 24)
BuffPos += (-width * 3) & 3;
}
}

Image::Image(std::uint16_t ResourceID) : Pixels(0), Info(), ImageCompressed(false), width(0), height(0), size(0), bFileHeader(), BitsPerPixel(32), ImageType(0), bitdepth(8), colortype(PNG_COLOR_TYPE_RGBA), interlacetype(0), channels(4), DC(nullptr), ImageHandle(nullptr)
{
BITMAP Bmp;
Pixels.clear();
DC = CreateCompatibleDC(nullptr);
std::memset(&Info, 0, sizeof(BITMAPINFO));
ImageHandle = (HBITMAP)LoadImage(GetModuleHandle(nullptr), MAKEINTRESOURCE(ResourceID), IMAGE_BITMAP, 0, 0, LR_SHARED);
HBITMAP OldBitmap = (HBITMAP)SelectObject(DC, ImageHandle);
GetObject(ImageHandle, sizeof(Bmp), &Bmp);

Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
Info.bmiHeader.biWidth = width = Bmp.bmWidth;
Info.bmiHeader.biHeight = height = Bmp.bmHeight;
Info.bmiHeader.biPlanes = 1;
Info.bmiHeader.biBitCount = Bmp.bmBitsPixel;
Info.bmiHeader.biCompression = BI_RGB;
Info.bmiHeader.biSizeImage = size = ((width * Bmp.bmBitsPixel + 31) / 32) * 4 * height;
bFileHeader.bfType = 0x4D42;
bFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(Info.bmiHeader);
bFileHeader.bfSize = bFileHeader.bfOffBits + size;
channels = (BitsPerPixel > 24 ? 4 : 3);
colortype = (BitsPerPixel > 24 ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB);

std::vector<std::uint8_t> ImageData(size);
GetDIBits(DC, ImageHandle, 0, height, ImageData.data(), &Info, DIB_RGB_COLORS);
SelectObject(DC, OldBitmap);

height = std::abs(height);
std::uint8_t* BuffPos = ImageData.data();
Pixels.resize(width * height);

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 = (Info.bmiHeader.biBitCount > 24 ? * (BuffPos++) : 0xFF);
}
if(Info.bmiHeader.biBitCount == 24)
BuffPos += (-width * 3) & 3;
}
}

Image::Image(std::vector<std::uint8_t> &Pointer, int Width, int Height, std::uint32_t BitsPerPixel, IMAGE_TYPE Value) : Pixels(0), Info(), ImageCompressed(false), width(Width), height(Height), size(0), bFileHeader(), BitsPerPixel(32), ImageType(Value), bitdepth(8), colortype(PNG_COLOR_TYPE_RGBA), interlacetype(0), channels(4), DC(nullptr), ImageHandle(nullptr)
{
Pixels.clear();
if (Pointer.empty())
{
throw std::logic_error("NullPointer Exception. Pointer is NULL.");
}
if (Width < 1 || Height < 1)
{
throw std::invalid_argument("Invalid Arguments. Width and Height cannot be less than or equal to 0.");
}

if (Value == BMP)
{
if ((BitsPerPixel != 24) && (BitsPerPixel != 32))
{
throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
}
if ((BitsPerPixel > 24) && (Pointer.size() < static_cast<std::size_t>(((width * 32 + 31) / 32) * 4 * height)))
{
throw std::logic_error("Invalid Pointer Size. Pointer may not contain an Alpha channel.");
}
std::memset(&Info, 0, sizeof(BITMAPINFO));
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;
bFileHeader.bfType = 0x4D42;
bFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(Info.bmiHeader);
bFileHeader.bfSize = bFileHeader.bfOffBits + size;
channels = (BitsPerPixel > 24 ? 4 : 3);
colortype = (BitsPerPixel > 24 ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB);

height = std::abs(height);
std::uint8_t* BuffPos = Pointer.data();
Pixels.resize(width * height);
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 = (Info.bmiHeader.biBitCount > 24 ? * (BuffPos++) : 0xFF);
}
if(Info.bmiHeader.biBitCount == 24)
BuffPos += (-width * 3) & 3;
}
}
else if (Value == PNG)
{
if ((BitsPerPixel != 24) || (BitsPerPixel != 32))
{
throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
}

std::memset(&Info, 0, sizeof(BITMAPINFO));
Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
Info.bmiHeader.biPlanes = 1;
Info.bmiHeader.biBitCount = BitsPerPixel;
Info.bmiHeader.biCompression = BI_RGB;
Info.bmiHeader.biWidth = width;
Info.bmiHeader.biHeight = height;
Info.bmiHeader.biSizeImage = size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
bFileHeader.bfType = 0x4D42;
bFileHeader.bfSize = bFileHeader.bfOffBits + size;
bFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(Info.bmiHeader);
channels = (BitsPerPixel > 24 ? 4 : 3);
colortype = (BitsPerPixel > 24 ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB);
Pixels.resize(width * height * sizeof(RGBA));
std::vector<RGBA>::iterator jt = Pixels.begin();

for (std::vector<std::uint8_t>::iterator it = Pointer.begin(); it != Pointer.end(); ++jt)
{
jt->B = *(it++);
jt->G = *(it++);
jt->R = *(it++);
jt->A = (BitsPerPixel > 24 ? * (it++) : 0xFF);
}
}
}

Image::Image(const void* Pointer, int Width, int Height, std::uint32_t BitsPerPixel, IMAGE_TYPE Value) : Pixels(0), Info(), ImageCompressed(false), width(Width), height(Height), size(0), bFileHeader(), BitsPerPixel(32), ImageType(Value), bitdepth(8), colortype(PNG_COLOR_TYPE_RGBA), interlacetype(0), channels(4), DC(nullptr), ImageHandle(nullptr)
{
Pixels.clear();
if (Pointer == nullptr)
{
throw std::logic_error("Null Pointer Exception. Pointer is NULL.");
}
if (Width < 1 || Height < 1)
{
throw std::invalid_argument("Invalid Arguments. Width and Height cannot be less than or equal to 0.");
}

if (Value == BMP)
{
if ((BitsPerPixel != 24) && (BitsPerPixel != 32))
{
throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
}
std::memset(&Info, 0, sizeof(BITMAPINFO));
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;
bFileHeader.bfType = 0x4D42;
bFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(Info.bmiHeader);
bFileHeader.bfSize = bFileHeader.bfOffBits + size;
channels = (BitsPerPixel > 24 ? 4 : 3);
colortype = (BitsPerPixel > 24 ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB);

height = std::abs(height);
const std::uint8_t* BuffPos = static_cast<const std::uint8_t*>(Pointer);
Pixels.resize(width * height);

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 = (Info.bmiHeader.biBitCount > 24 ? * (BuffPos++) : 0xFF);
}
if(Info.bmiHeader.biBitCount == 24)
BuffPos += (-width * 3) & 3;
}
}
else if (Value == PNG)
{
if ((BitsPerPixel != 24) || (BitsPerPixel != 32))
{
throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
}
std::memset(&Info, 0, sizeof(BITMAPINFO));
Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
Info.bmiHeader.biPlanes = 1;
Info.bmiHeader.biBitCount = BitsPerPixel;
Info.bmiHeader.biCompression = BI_RGB;
Info.bmiHeader.biWidth = width;
Info.bmiHeader.biHeight = height;
Info.bmiHeader.biSizeImage = size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
bFileHeader.bfType = 0x4D42;
bFileHeader.bfSize = bFileHeader.bfOffBits + size;
bFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(Info.bmiHeader);
channels = (BitsPerPixel > 24 ? 4 : 3);
colortype = (BitsPerPixel > 24 ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB);

const std::uint8_t* BuffPos = static_cast<const std::uint8_t*>(Pointer);
Pixels.resize(width * height * sizeof(RGBA));

for (std::vector<RGBA>::iterator it = Pixels.begin(); it != Pixels.end(); ++it)
{
it->B = *(BuffPos++);
it->G = *(BuffPos++);
it->R = *(BuffPos++);
it->A = (BitsPerPixel > 24 ? * (BuffPos++) : 0xFF);
}
}
}

Image::Image(int Width, int Height, std::uint32_t BitsPerPixel) : Pixels(0), Info(), ImageCompressed(false), width(Width), height(Height), size(0), bFileHeader(), BitsPerPixel(32), ImageType(0), bitdepth(8), colortype(PNG_COLOR_TYPE_RGBA), interlacetype(0), channels(4), DC(nullptr), ImageHandle(nullptr)
{
Pixels.clear();
if ((BitsPerPixel != 24) && (BitsPerPixel != 32))
{
throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
}
if (Width < 1 || Height < 1)
{
throw std::invalid_argument("Invalid Arguments. Width and Height cannot be less than or equal to 0.");
}
std::memset(&Info, 0, sizeof(BITMAPINFO));
Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
Info.bmiHeader.biPlanes = 1;
Info.bmiHeader.biBitCount = BitsPerPixel;
Info.bmiHeader.biCompression = BI_RGB;
Info.bmiHeader.biWidth = width;
Info.bmiHeader.biHeight = height;
Info.bmiHeader.biSizeImage = size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
bFileHeader.bfType = 0x4D42;
bFileHeader.bfSize = bFileHeader.bfOffBits + size;
bFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(Info.bmiHeader);
channels = (BitsPerPixel > 24 ? 4 : 3);
colortype = (BitsPerPixel > 24 ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB);
Pixels.resize(width * height);
}

Image::Image(HWND Window, Box Area, std::uint32_t BitsPerPixel) : Pixels(0), Info(), ImageCompressed(false), width(0), height(0), size(0), bFileHeader(), BitsPerPixel(32), ImageType(0), bitdepth(8), colortype(PNG_COLOR_TYPE_RGBA), interlacetype(0), channels(4), DC(nullptr), ImageHandle(nullptr)
{
Pixels.clear();
if ((BitsPerPixel != 24) && (BitsPerPixel != 32))
{
throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
}
std::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)
tcout << ErrorMessage(ERROR_INVALID_WINDOW_HANDLE);

Box BMBox;
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);
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;
bFileHeader.bfType = 0x4D42;
bFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(Info.bmiHeader);
bFileHeader.bfSize = bFileHeader.bfOffBits + size;
channels = (BitsPerPixel > 24 ? 4 : 3);
colortype = (BitsPerPixel > 24 ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB);

std::vector<std::uint8_t> ImageData(size);
GetDIBits(SDC, hSBmp, 0, height, ImageData.data(), &Info, DIB_RGB_COLORS);

DeleteDC(SDC);
DeleteObject(hSBmp);
ReleaseDC(nullptr, MemDC);
ReleaseDC(Window, DC);

height = std::abs(height);
std::uint8_t* BuffPos = ImageData.data();
Pixels.resize(width * height);

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 = (Info.bmiHeader.biBitCount > 24 ? * (BuffPos++) : 0xFF);
}
if(Info.bmiHeader.biBitCount == 24)
BuffPos += (-width * 3) & 3;
}
}

Image::Image(HDC Hdc, Box Area, std::uint32_t BitsPerPixel) : Pixels(0), Info(), ImageCompressed(false), width(0), height(0), size(0), bFileHeader(), BitsPerPixel(32), ImageType(0), bitdepth(8), colortype(PNG_COLOR_TYPE_RGBA), interlacetype(0), channels(4), DC(Hdc), ImageHandle(nullptr)
{
Pixels.clear();
if ((BitsPerPixel != 24) && (BitsPerPixel != 32))
{
throw std::invalid_argument("Invalid File Format. Required: 24 or 32 Bit Image.");
}
BITMAP Bmp;
HBITMAP hBmp = (HBITMAP)GetCurrentObject(DC, OBJ_BITMAP);
std::memset(&Info, 0, sizeof(BITMAPINFO));

if (GetObject(hBmp, sizeof(BITMAP), &Bmp) == 0)
tcout << ErrorMessage(ERROR_DC_NOT_FOUND);

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);
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;
bFileHeader.bfType = 0x4D42;
bFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(Info.bmiHeader);
bFileHeader.bfSize = bFileHeader.bfOffBits + size;
channels = (BitsPerPixel > 24 ? 4 : 3);
colortype = (BitsPerPixel > 24 ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB);

std::vector<std::uint8_t> ImageData(size);
GetDIBits(SDC, hSBmp, 0, height, ImageData.data(), &Info, DIB_RGB_COLORS);

DeleteDC(SDC);
DeleteObject(hSBmp);
ReleaseDC(nullptr, MemDC);

height = std::abs(height);
std::uint8_t* BuffPos = ImageData.data();
Pixels.resize(width * height);

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 = (Info.bmiHeader.biBitCount > 24 ? * (BuffPos++) : 0xFF);
}
if(Info.bmiHeader.biBitCount == 24)
BuffPos += (-width * 3) & 3;
}
}

bool Image::Save(const char* FilePath, IMAGE_TYPE Value)
{
std::fstream hFile(FilePath, std::ios::out | std::ios::binary);
if (!hFile.is_open())
{
throw std::invalid_argument("Cannot open file for writing.");
}
SetBitsPerPixel(BitsPerPixel);

if (Value == BMP)
{
size = Info.bmiHeader.biSizeImage = ((width * Info.bmiHeader.biBitCount + 31) / 32) * 4 * height;
std::vector<std::uint8_t> ImageData(size);
std::uint8_t* BuffPos = ImageData.data();

for (std::size_t I = 0; I < height; ++I)
{
for (std::size_t J = 0; J < width; ++J)
{
//Flip The ScanLines/Rows back to normal.
*(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 (Info.bmiHeader.biBitCount > 24)
*(BuffPos++) = Pixels[(height - 1 - I) * width + J].A;
}
if(Info.bmiHeader.biBitCount == 24)
BuffPos += (-width * 3) & 3;
}

hFile.write(reinterpret_cast<char*>(&bFileHeader), sizeof(BITMAPFILEHEADER));
hFile.write(reinterpret_cast<char*>(&Info.bmiHeader), sizeof (BITMAPINFOHEADER));
hFile.write(reinterpret_cast<char*>(ImageData.data()), size);
hFile.close();
}
else if (Value == PNG)
{
png_structp PngPointer = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (!PngPointer)
{
hFile.close();
throw std::runtime_error("Error: Cannot Create Write Structure.");
}

png_infop InfoPointer = png_create_info_struct(PngPointer);
if (!InfoPointer)
{
hFile.close();
png_destroy_write_struct(&PngPointer, nullptr);
throw std::runtime_error("Error: Cannot Create InfoPointer Structure.");
}

if (setjmp(png_jmpbuf(PngPointer)))
{
hFile.close();
png_destroy_write_struct(&PngPointer, &InfoPointer);
throw std::runtime_error("Error: Cannot Set Jump Pointer.");
}

png_set_write_fn(PngPointer, &hFile, WriteToStream, nullptr);
png_set_IHDR (PngPointer, InfoPointer, width, height, bitdepth, BitsPerPixel == 24 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_write_info(PngPointer, InfoPointer);

png_set_bgr(PngPointer);
png_set_packing(PngPointer);
png_set_interlace_handling(PngPointer);
if (colortype == PNG_COLOR_TYPE_RGB) png_set_filler(PngPointer, 0xFF, PNG_FILLER_AFTER);

std::vector<std::uint8_t*> RowPointers(height);
std::uint8_t* BuffPos = reinterpret_cast<std::uint8_t*>(Pixels.data());

for (std::size_t I = 0; I < height; ++I)
{
RowPointers[I] = BuffPos + (I * width * sizeof(RGBA));
}

png_write_image(PngPointer, RowPointers.data());
png_write_end(PngPointer, InfoPointer);
png_destroy_write_struct(&PngPointer, &InfoPointer);
hFile.close();
}
else if (Value == TGA)
{
size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
std::vector<std::uint8_t> ImageData(size);
std::uint8_t* BuffPos = ImageData.data();

for (std::size_t I = 0; I < height; ++I)
{
for (std::size_t J = 0; J < width; ++J)
{
//Flip The ScanLines/Rows back to normal.
*(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;
}

static std::uint8_t DeCompressed[12] = {0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
static std::uint8_t IsCompressed[12] = {0x0, 0x0, 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};

if (!ImageCompressed)
{
hFile.write(reinterpret_cast<char*>(&DeCompressed), sizeof(DeCompressed));
hFile.put((width & 0xFF));
hFile.put(((width >> 8) & 0xFF));
hFile.put((height & 0xFF));
hFile.put(((height >> 8) & 0xFF));
hFile.put(BitsPerPixel);
hFile.put(0x0);
hFile.write(reinterpret_cast<char*>(ImageData.data()), ImageData.size());
hFile.close();
}
else
{
hFile.write(reinterpret_cast<char*>(&IsCompressed), sizeof(IsCompressed));
hFile.put((width & 0xFF));
hFile.put(((width >> 8) & 0xFF));
hFile.put((height & 0xFF));
hFile.put(((height >> 8) & 0xFF));
hFile.put(BitsPerPixel);
hFile.put(0x0);
}
hFile.close();
}

return true;
}

Image& Image::operator = (Image Img)
{
Img.Swap(*this);
return *this;
}

void Image::Swap(Image& Img) //throw()
{
using std::swap;
swap(Pixels, Img.Pixels);
swap(Info, Img.Info);
swap(ImageCompressed, Img.ImageCompressed);
swap(width, Img.width);
swap(height, Img.height);
swap(size, Img.size);
swap(bFileHeader, Img.bFileHeader);
swap(BitsPerPixel, Img.BitsPerPixel);
swap(ImageType, Img.ImageType);
swap(bitdepth, Img.bitdepth);
swap(colortype, Img.colortype);
swap(interlacetype, Img.interlacetype);
swap(channels, Img.channels);
swap(DC, Img.DC);
swap(ImageHandle, Img.ImageHandle);
}

HBITMAP Image::ToHBitmap()
{
std::vector<std::uint8_t> ImageData(size);
std::uint8_t* BuffPos = ImageData.data();
std::uint8_t* Memory = ImageData.data();

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 (Info.bmiHeader.biBitCount > 24)
*(BuffPos++) = Pixels[(height - 1 - I) * width + J].A;
}
if(Info.bmiHeader.biBitCount == 24)
BuffPos += (-width * 3) & 3;
}

DeleteObject(ImageHandle);
return (ImageHandle = CreateDIBitmap(GetDC(nullptr), &Info.bmiHeader, CBM_INIT, Memory, &Info, DIB_RGB_COLORS));
}

void Image::Draw(HWND Hwnd, HBITMAP hBmp , int X , int Y , int Width , int Height , int SourceX , int SourceY, DWORD RasterType)
{
HDC hDC = GetDC(Hwnd);
Draw(hDC, hBmp, X, Y, Width, Height, SourceX, SourceY, RasterType);
ReleaseDC(Hwnd, hDC);
}

void Image::Draw(HDC hDC, HBITMAP hBmp , int X , int Y , int Width , int Height , int SourceX , int SourceY, DWORD RasterType)
{
BITMAP Bmp;
HDC MemDC = CreateCompatibleDC(hDC);
if (!ImageHandle) ToHBitmap();
HGDIOBJ hbmOld = SelectObject(MemDC, hBmp == nullptr ? ImageHandle : hBmp);
GetObject(hBmp == nullptr ? ImageHandle : hBmp, sizeof(Bmp), &Bmp);

BitBlt(hDC, X, Y, Width == 0 ? Bmp.bmWidth : Width, Height == 0 ? Bmp.bmHeight : Height, MemDC, SourceX, SourceY, RasterType);

SelectObject(MemDC, hbmOld);
DeleteDC(MemDC);
}

tstring Image::ToString()
{
std::string Data;
Data.resize(width * height * sizeof(RGBA));
PRGB Pointer = reinterpret_cast<PRGB>(&Data[0]);

for (std::vector<RGBA>::iterator it = Pixels.begin(); it != Pixels.end(); ++it)
{
Pointer->R = it->R;
Pointer->G = it->G;
Pointer->B = it->B;
++Pointer;
}

Data = CompressString(Data);
return EncodeBase64(_T("IMG.") + to_tstring(width) + _T("*") + to_tstring(height)) + _T(".") + EncodeBase64(tstring(Data.begin(), Data.end()));
}

Image& Image::FromString(tstring Data, std::uint32_t BitsPerPixel)
{
int PosDot = Pos(_T("."), Data, 0);
tstring DataHeader = DecodeBase64(Copy(Data, 0, PosDot));

if (Copy(DataHeader, 0, 4) != _T("IMG."))
{
throw std::logic_error("Error: Invalid Image String.");
}

int PosStar = Pos(_T("*"), DataHeader, 4);
width = ToNumber<int>(Copy(DataHeader, 5, PosStar - 4));
height = ToNumber<int>(Copy(DataHeader, PosStar + 2, PosDot - PosStar));
Data.erase(Data.begin(), Data.begin() + PosDot + 1);
Data = DecodeBase64(Data);
std::string DataBits = DecompressString(std::string(Data.begin(), Data.end()));

Pixels.clear();
std::memset(&Info, 0, sizeof(BITMAPINFO));
Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
Info.bmiHeader.biPlanes = 1;
Info.bmiHeader.biBitCount = BitsPerPixel;
Info.bmiHeader.biCompression = BI_RGB;
Info.bmiHeader.biWidth = width;
Info.bmiHeader.biHeight = height;
Info.bmiHeader.biSizeImage = size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
bFileHeader.bfType = 0x4D42;
bFileHeader.bfSize = bFileHeader.bfOffBits + size;
bFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(Info.bmiHeader);
Pixels.resize(width * height);
PRGB Pointer = reinterpret_cast<PRGB>(&DataBits[0]);

for (std::vector<RGBA>::iterator it = Pixels.begin(); it != Pixels.end(); ++it)
{
it->R = Pointer->R;
it->G = Pointer->G;
it->B = Pointer->B;
++Pointer;
}

return *this;
}

int Image::Width() const
{
return std::abs(width);
}

int Image::Height() const
{
return std::abs(height);
}

int Image::Size() const
{
return this->size;
}

void Image::Size(int& W, int& H) const
{
W = this->Width();
H = this->Height();
}

int Image::GetBitsPerPixel() const
{
return Info.bmiHeader.biBitCount;
}

void Image::SetBitsPerPixel(std::uint32_t Depth)
{
assert(Depth == 24 || Depth == 32);
bitdepth = 8;
Info.bmiHeader.biBitCount = BitsPerPixel = Depth;
channels = (BitsPerPixel > 24 ? 4 : 3);
colortype = (BitsPerPixel > 24 ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB);
}

RGBA& Image::operator [](int I)
{
assert(I >= 0 && !Pixels.empty());
return Pixels[I];
}

const RGBA& Image::operator [](int I) const
{
assert(I >= 0 && !Pixels.empty());
return Pixels[I];
}

std::vector<RGBA>::iterator Image::PixelsBegin()
{
return Pixels.begin();
}

std::vector<RGBA>::const_iterator Image::PixelsBegin() const
{
return Pixels.begin();
}

std::vector<RGBA>::iterator Image::PixelsEnd()
{
return Pixels.end();
}

std::vector<RGBA>::const_iterator Image::PixelsEnd() const
{
return Pixels.end();
}

std::pair<std::vector<RGBA>::iterator, std::vector<RGBA>::iterator> Image::PixelsRange()
{
return std::make_pair(Pixels.begin(), Pixels.end());
}

std::pair<std::vector<RGBA>::const_iterator, std::vector<RGBA>::const_iterator> Image::PixelsRange() const
{
return std::make_pair(Pixels.begin(), Pixels.end());
}

RGBA Image::GetPixel(int X, int Y) const
{
return Pixels[Y * width + X]; //return Pixels[(height - 1 - y) * width + x]; //If Not Flipped..
}

void Image::SetPixel(int X, int Y, RGBA Color)
{
Pixels[Y * width + X] = Color;
}
}




For Finder functions I did:

Client.hpp:

#ifndef CLIENT_HPP_INCLUDED
#define CLIENT_HPP_INCLUDED

class Client
{
private:
HDC DC;
HWND Window;
std::uint8_t* Buffer;
Image* 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 Image &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 FindImage(const Image &ImageToFind, int &X, int &Y);
bool FindImageIn(const Image &ImageToFind, int &X, int &Y, int X1, int Y1, int X2, int Y2);
bool FindImageToleranceIn(const Image &ImageToFind, int &X, int &Y, int X1, int Y1, int X2, int Y2, int Tolerance);
};

#endif // CLIENT_HPP_INCLUDED


Client.cpp:

#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 Image &Img)
{
this->Window = nullptr;
this->DC = nullptr;
this->Buffer = nullptr;
delete this->Target;
this->Target = new Image(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 Image(this->DC);
}
else if (this->Window != nullptr)
{
delete this->Target;
this->Target = new Image(this->Window);
}
else if (this->Buffer != nullptr)
{
delete this->Target;
std::vector<std::uint8_t> Buffer(765 * 553 * 4, 0);
FlipImageBytes(this->Buffer, Buffer.data(), 765, 553);
this->Target = new Image(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()) + ").");
}
RGBA Result = this->Target->GetPixel(X, Y);
return Rgba(Result.B, Result.G, Result.R).Colour;
}

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).Colour, 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, 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, Colour, Tolerance))
Points(Point(J, I));
}
}
return (!Points.Empty());
}

bool Client::FindImage(const Image &ImageToFind, int &X, int &Y)
{
return FindImageIn(ImageToFind, X, Y, 0, 0, Target->Width(), Target->Height());
}

bool Client::FindImageIn(const Image &ImageToFind, int &X, int &Y, int X1, int Y1, int X2, int Y2)
{
int TemporaryCTS = CTSValue;
bool Result = FindImageToleranceIn(ImageToFind, X, Y, X1, Y1, X2, Y2, 0);
SetColourToleranceSpeed(TemporaryCTS);
return Result;
}

bool Client::FindImageToleranceIn(const Image &ImageToFind, 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) - (ImageToFind.Width() - 1);
int dY = (Y2 - Y1) - (ImageToFind.Height() - 1);

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

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

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

DrChill

2013-07-22 03:41:10

Awesome man, you rock :D, will try all this in the morning(well, probably in the evening).

I am native C++, but using C because it came in the examples. Do you have Simba++ on github? It seems it would be great as a reference. I'm also curious about Library* and how you're loading it, is that a wrapper/custom/windows specific/deep dark corner of boost?

Brandon

2013-07-22 03:50:45

From: DrChill
Awesome man, you rock :D, will try all this in the morning(well, probably in the evening).

I am native C++, but using C because it came in the examples. Do you have Simba++ on github? It seems it would be great as a reference. I'm also curious about Library* and how you're loading it, is that a wrapper/custom/windows specific/deep dark corner of boost?


For loading PNG's I used libpng/zlib (only external library used for obvious reasons that the PNG reading I wrote only loaded 1-5 types of PNG and there's roughly 25 different "types" of images called PNG.. The code above will load all though).

Other than that, no sir. Lol. I write all my code myself. If I were to use external libraries, trust me it won't be that long.. Besides. I hate having code depend on external libraries too much. Where is the fun in that when everyone has to download a library to use it or you have to ship with that library and worry about copyrights and w/e else.. I don't use boost or anything. Just roll my own. Glad you like it though..

Also note in the image class that it has "ImageFromString and ImageToString" just like Simba does ;) *Shows off l33t st00f*

Uhh the wrapper for Library was done so that it'd be cross platform too. I wrote it as follows:

Library.hpp:

#ifndef LIBRARY_HPP_INCLUDED
#define LIBRARY_HPP_INCLUDED

#if defined _WIN32 || defined _WIN64
#include <windows.h>
#else
#include <dlfcn.h>
#endif

#include <string>
#include <cstdint>
#include <stdexcept>

class Library
{
private:
void* Module;

public:
Library(std::string Library);
~Library();

template<typename T>
T FunctionAddress(std::string FunctionName);

template<typename T>
bool FunctionAddress(T &FunctionDefinition, std::string FunctionName);

template<typename T, typename ...Args>
auto CallFunction(void* Function, Args... args) -> decltype(reinterpret_cast<T>(Function)(args...));
};

template<typename T>
T Library::FunctionAddress(std::string FunctionName)
{
#if defined _WIN32 || defined _WIN64
T Result = reinterpret_cast<T>(GetProcAddress(static_cast<HMODULE>(this->Module), FunctionName.c_str()));
return Result ? Result : throw std::runtime_error(FunctionName);
#else
T Result = reinterpret_cast<T>(dlsym(this->Module, FunctionName.c_str()));
return Result ? Result : throw std::runtime_error(FunctionName);
#endif
}

template<typename T>
bool Library::FunctionAddress(T &FunctionDefinition, std::string FunctionName)
{
return (FunctionDefinition = this->FunctionAddress<T>(FunctionName));
}

template<typename T, typename ...Args>
auto Library::CallFunction(void* Function, Args... args) -> decltype(reinterpret_cast<T>(Function)(args...))
{
return reinterpret_cast<T>(Function)(args...);
}
#endif // LIBRARY_HPP_INCLUDED

Library.cpp:

#include "Library.hpp"

Library::Library(std::string Library)
{
#if defined _WIN32 || defined _WIN64
this->Module = LoadLibrary(Library.c_str());
#else
this->Module = dlopen(Library.c_str(), RTLD_LAZY);
#endif
}

Library::~Library()
{
#if defined _WIN32 || defined _WIN64
FreeLibrary(static_cast<HMODULE>(this->Module));
#else
dlclose(this->Module);
#endif
}

/*
bool Library::xGetFunctionAddress(void* FunctionDefinition, std::string FunctionName)
{
return *static_cast<std::int64_t(__stdcall **)()>(FunctionDefinition) = this->xGetProcAddress(FunctionName.c_str());
}
*/


For types like Simba has.. Like TIntegerArray, TVariantArray, etc.. I created a class that takes infinite parameters (any amount) with a vector at its core.

Maybe I'll upload it all to github eventually.. Too lazy.. Atm it isn't on there. I stopped working on it a while back because it got boring but I might start it again if people were interested.. Even wrote part of the IDE as well. Still, the current Simba has came far from when I started on this and that's the main reason I stopped. I was satisfied with how far the current Simba got and the features it had I thought it would be better to work on Simba that to just continue the Simba++ project. When I started this, Simba had a lot missing..

DrChill

2013-07-22 12:56:50

I would be very interested in the C++ interface, it looks like you have done the C++ in C++11 fashion, which I have been wanting to study more, if you posted it I would probably just write the bindings based on Simba++ (or fork it to SMART++, and take only the SMART functions (or keep a basic toolset)). Either way I would love to see the entire source of it, so, I support a release, even if it's not an active project :)

How windows specific is it? I am running Linux right now, and it would be nice if I didn't have to reboot. The lib reading part supports Linux, and I like how it's written :)

Brandon

2013-07-22 13:22:51

Yeah. I had some time last year to port the whole thing to C++x11. It was amazing doing that. I learned so much and I love the new features. Pretty much opened up a lot of possibilities.

Uhh it isn't that windows specific. Some of the bindings such as BitmapFromDC is windows only because Device Contexts only exist on Windows. Displays are the linux equivalent.

For other things, I used #ifdef pre-processor stuff to keep it as cross-platform as possible. I used all of the std/stl library basically. Very little platform specific stuff. It is more updated for windows as when I was writing some parts, I got lazy part way through.. I had to constantly switch back and forth to test code on both platforms. It's 100% unicode support as well. So the things where you see std::string.. I replaced it just to post it so you can compile. The actual project uses a "tstring" which is a unicode #ifdef for std::string and std::wstring. Things like the form builder is windows only. I can easily in a couple hours port that to wxWidgets for cross platform.

All platform specific stuff has an #ifdef and they're in files like: "Platforms.hpp" and "System.hpp". Other files are 100% cross-platform. Still better to use windows since it does have all working code. Linux part is missing a couple functions (not much at all).

I'll upload it later. I got some other stuff to attend to atm.

You can just watch my github if you wish and you'll see when it goes up. Or I'll write a release thread or something. I did have it uploaded last year but only a few people cared or looked at it.

DrChill

2013-07-22 17:10:41

Is this correct? It seems to not be returning anything:


typedef struct{
unsigned char B, G, R, A;
} RGBA;
typedef RGBA* (*GetImageArray)();

...

GetImageArray getImageArray = (GetImageArray)dlsym(libsmart, "exp_getImageArray");

RGBA *tmpget = getImageArray();
std::cout << "Color test: " << tmpget[1].R << std::endl;

Also, how do you get C++ code colorings?

Brandon

2013-07-22 18:02:06

From: DrChill
Is this correct? It seems to not be returning anything:


typedef struct{
unsigned char B, G, R, A;
} RGBA;
typedef RGBA* (*GetImageArray)();

...

GetImageArray getImageArray = (GetImageArray)dlsym(libsmart, "exp_getImageArray");

RGBA *tmpget = getImageArray();
std::cout << "Color test: " << tmpget[1].R << std::endl;

Also, how do you get C++ code colorings?



That is right actually. When I do it, it prints: 194. You are forgetting to cast "tempget[1].R" to an int. Remember that it is all bytes and if a byte doesn't lie within the alpha range, it will display weirdly.

I did:


#include "SmartParams.hpp"

typedef struct
{
unsigned char B, G, R, A;
} RGBA;

int main(int argc, char* argv[])
{
#if defined _WIN32 || defined _WIN64
Smart S("LibSmartRemote64.dll");
#else
Smart S("LibSmartRemote64.so");
#endif
S.Init(false);

RGBA* Pixels = (RGBA*)S.SmartGetImageArray();

std::cout<<(int)Pixels[0].R;
Sleep(100);

return 0;
}

Prints 194 as the R-Value. If you made a bitmap from it then you could do:
Bmp.GetPixel(250, 250);
to get the pixel at the middle of the screen.

DrChill

2013-07-22 18:45:26

:spot::spot::spot: It's Working! :spot::spot::spot:

And successfully exported to Lua :D

I will now begin making a login script for RS3, many thanks :)

Still interested in the C++ lib, I may do the heavier calculations in C++, and leave scripting to primarily game logic.

Brandon

2013-07-22 20:21:43

From: DrChill
:spot::spot::spot: It's Working! :spot::spot::spot:

And successfully exported to Lua :D

I will now begin making a login script for RS3, many thanks :)

Still interested in the C++ lib, I may do the heavier calculations in C++, and leave scripting to primarily game logic.



Glad it works lol.