From: wantonman
hello soo uh… JAVA is great was trying to find a way way to get anything perhaps even just a green window to run as an applet in 16 color mode for windows xp. thats 4 bits of gpu computing power! dont ask why i want to acheive this… i tried looking at the api and saw a function for funking with the color space. is that the right direction?
my website is aimed for older graphic cards so this applet fix is vital for me… once again getting the proper fix to run a graphical app in 16 color mode..
LOL.. This again? K here's the deal.. You program your Applet in any version of windows.. You run it, and your graphics card should take care of the rest.. I do not think there is a way to "FORCE" the applet to display in 16-bit mode alone.. I know you can force Java to use JRE 32 instead of 64 but I don't think you can just force an applet to display in a specific graphics mode as it depends entirely on the user's graphics card..
Also I noticed you keep bringing up 16-bit programming for images/graphics and applications.. Well here's something from a library/Simba++ I wrote a while back.. Reads 8, 16, 24, & 32 bit PNG's, TGA, BMP, etc.. At the end will have code for finding all types of images.. I don't know why you need everything 16-bit so often.. Do what you wish with it.
Is there any reason you seriously need 16-bit anything? As well as 256-Colour mode? I'm just curious as it is the only thing I noticed that interests you quite a lot and no one seems to answer because it is sort of pointless (Sorry if that seems a bit harsh).. In other words, there isn't any benefits.
I don't mind helping you with graphics programming or 16-bit programming in any language but I'm just not seeing the big picture as to "why" you insist on 16-bit stuff if they take up the "same" CPU processing as 32 bit (24 takes more than 16/32).
Here's something most programmers DO NOT know.. From one of my posts 7 Months ago (Read Mike's answer):
http://www.daniweb.com/software-development/cpp/threads/425643/size_t-vs-unsigned-intsshorts-pointersOn a 32 bit computer, the bus accepts 32 bit values. So when someone decides to use a short rather than an int, the compiler must then "Pad" it.
Think of this. You decide that "short" takes up less space than an integer so you decide to use it. Your Bus takes only 32 bit values. What does it have to do? Well an int is 4 bytes and a short is 2. The compiler has to pad the short with 2 blank bytes so the bus accepts it. Structs are also padded unless you specify pragma pack. This is the way someone explained it to me. It's of course more complex than that.
Guess what? It takes MORE work to pad the short rather than to use the int in the first place. How often do you see someone doing "for (short I = 0; ……)" instead of (for int I = 0;…..)?? You use short when you want to restrict the limit of values the user can enter to 65535. Also use short for algorithms that require cache-ing and saving of memory.
It's the same thing. You do not see anyone using 16 bit images very often except when uploading to websites that require such. In this case, a 16-bit bitmap takes less power to read than a 24-bit bitmap which requires padding. It takes the exact same amount as a 32-bit bitmap though because both require same amount because both read straight without any shifting and extra operations.. In some cases such as for PNG, a 16 bit image requires PADDING and converting.. It is usually converted to 24-bit PNG's with Padding of invisible bits on either the end OR the beginning. Why? Because the formats requires 4-byte boundaries aka DWORD aligned boundaries.
Yes I understand your applications seem to run faster in 256 colour mode than true colour mode but that is because there is less pixels to draw to your screen. Your bitmap/application only has 256 colours to draw.. No more, no less. Reading one byte however takes no longer than reading 1 bit when it comes to bitmaps. You either have to shift or pad so the work comes in there.
I don't wish to see you give up on 16-bit loading of images or applets or applications.. Whatever floats your boat but I'm just trying to save you the hassle.. Unless of course it's for learning experience..
Anyway, here is some answers to your previous "256 Colour mode thread".. These are only the relative parts that you need for any image processing.. If you still plan on getting this stuff added into Simba/making Simba work in 16-bit mode, you might have to get help translating it as I'm pretty busy lately.
static int 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;
}
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 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 = nullptr, int X = 0, int Y = 0, int Width = 0, int Height = 0, int SourceX = 0, int SourceY = 0, DWORD RasterType = SRCCOPY);
std::string ToString();
Image& FromString(std::string, 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::~Image()
{
Pixels.clear();
DeleteDC(DC);
DeleteObject(ImageHandle);
ImageHandle = nullptr;
}
void Image::ReadFromStream(png_structp PngPointer, std::uint8_t* Data, std::size_t Length)
{
std::ifstream *Stream = (std::ifstream*)png_get_io_ptr(PngPointer);
Stream->read((char*)Data, Length);
}
void Image::WriteToStream(png_structp PngPointer, std::uint8_t* Data, std::size_t Length)
{
std::ofstream *Stream = (std::ofstream*)png_get_io_ptr(PngPointer);
Stream->write((char*)Data, Length);
}
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
{
hFile.close();
throw std::invalid_argument("Error: Invalid File Format!");
}
channels = (BitsPerPixel > 24 ? 4 : 3);
colortype = (BitsPerPixel > 24 ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB);
}
For reading TGA images:
class Tga
{
private:
std::vector<RGB> Pixels;
bool ImageCompressed;
std::uint32_t width, height, size, BitsPerPixel;
public:
Tga(const char* FilePath);
void Save(const char* FilePath);
};
Tga::Tga(const char* FilePath)
{
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};
std::vector<std::uint8_t> ImageData;
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};
hFile.read(reinterpret_cast<char*>(&Header), sizeof(Header));
if (!std::memcmp(DeCompressed, &Header, sizeof(DeCompressed)))
{
BitsPerPixel = Header[16];
width = Header[13] * 0xFF + Header[12];
height = Header[15] * 0xFF + 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 if (!std::memcmp(IsCompressed, &Header, sizeof(IsCompressed)))
{
BitsPerPixel = Header[16];
width = Header[13] * 0xFF + Header[12];
height = Header[15] * 0xFF + 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.");
}
RGB 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(RGB));
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.RGBA.B;
ImageData[CurrentByte++] = Pixel.RGBA.G;
ImageData[CurrentByte++] = Pixel.RGBA.R;
if (BitsPerPixel > 24) ImageData[CurrentByte++] = Pixel.RGBA.A;
}
}
else
{
ChunkHeader -= 127;
hFile.read(reinterpret_cast<char*>(&Pixel), BytesPerPixel);
for(int I = 0; I < ChunkHeader; ++I, ++CurrentPixel)
{
ImageData[CurrentByte++] = Pixel.RGBA.B;
ImageData[CurrentByte++] = Pixel.RGBA.G;
ImageData[CurrentByte++] = Pixel.RGBA.R;
if (BitsPerPixel > 24) ImageData[CurrentByte++] = Pixel.RGBA.A;
}
}
} while(CurrentPixel < (width * height));
}
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].RGBA.B = *(BuffPos++);
Pixels[(height - 1 - I) * width + J].RGBA.G = *(BuffPos++);
Pixels[(height - 1 - I) * width + J].RGBA.R = *(BuffPos++);
Pixels[(height - 1 - I) * width + J].RGBA.A = (BitsPerPixel > 24 ? *(BuffPos++) : 0xFF);
}
if(BitsPerPixel == 24)
BuffPos += (-width * 3) & 3;
}
}
void Tga::Save(const char* FilePath)
{
std::fstream hFile(FilePath, std::ios::out | std::ios::binary);
if (!hFile.is_open()) {throw std::invalid_argument("Cannot open file for writing.");}
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].RGBA.B;
*(BuffPos++) = Pixels[(height - 1 - I) * width + J].RGBA.G;
*(BuffPos++) = Pixels[(height - 1 - I) * width + J].RGBA.R;
if (BitsPerPixel > 24)
*(BuffPos++) = Pixels[(height - 1 - I) * width + J].RGBA.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 & 0xFF) / 0xFF);
hFile.put((height & 0xFF));
hFile.put(((height & 0xFF) / 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 & 0xFF) / 0xFF);
hFile.put((height & 0xFF));
hFile.put(((height & 0xFF) / 0xFF));
hFile.put(BitsPerPixel);
hFile.put(0x0);
}
hFile.close();
}
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];
}
}
For Reading Bitmaps:
#include <iostream>
#include <sstream>
#include <algorithm>
/** Definitions **/
#define LEN(a) (sizeof(a)/sizeof(*a))
#define HIGH(a) ((sizeof(a)/sizeof(*a)) - 1)
#define BitSize(x) (sizeof(x)*8)
#define Chr(n) ((char)(n))
#define Ord(c) ((int)(std::uint8_t)(c))
#define BmPitch(bmWidth) (((std::uint32_t)(bmWidth + 3) & ~3))
#define BmStride(Pitch, bmBitCount) (((Pitch * bmBitCount) >> 3))
#define Repeat do{
#define Until(condition) }while(!(condition));
#define Write(Output) (std::cout<<(Output))
#define WriteLn(Output) (std::cout<<(Output)<<"\n")
template<typename T, typename... Args>
inline T Max(Args... args) {return std::max({args...});}
template<typename T, typename... Args>
inline T Min(Args... args) {return std::min({args...});}
template<typename T>
inline T Inc(T& Value, int Amount = 1){return (Value += Amount);}
template<typename T>
inline T Dec(T& Value, int Amount = 1){return (Value -= Amount);}
inline bool Odd(int Value){return (Value & 1);}
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;
inline XYZ Xyz(double X, double Y, double Z){return {X, Y, Z};}
inline HSL Hsl(double Hue, double Sat, double Lum) {return {Hue, Sat, Lum};}
inline LAB Lab(double L, double A, double B) {return {L, A, B};}
inline RGBA Rgba(std::uint32_t R, std::uint32_t G, std::uint32_t B) {return {((R << 16) & 0x00FF0000) | ((G << 8) & 0x0000FF00) | (B & 0x000000FF)};}
inline RGBA Rgba(std::uint32_t Colour) {return {((Colour & 0xFF000000)|((Colour & 0xFF0000) >> 16)|(Colour & 0x00FF00)|((Colour & 0x0000FF) << 16))};}
inline std::uint32_t ColourSwap(std::uint32_t Colour){return ((Colour & 0xFF000000)|((Colour & 0xFF0000) >> 16)|(Colour & 0x00FF00)|((Colour & 0x0000FF) << 16));}
inline RGBA ColourSwap(RGBA Rgba){return {((Rgba.Colour & 0xFF000000)|((Rgba.Colour & 0xFF0000) >> 16)|(Rgba.Colour & 0x00FF00)|((Rgba.Colour & 0x0000FF) << 16))};}
inline 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];}}
//return (((Colour & 0xFF) << 16) + (((Colour & 0x00FF00) >> 8) << 8) + ((Colour & 0xFF0000) >> 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 = (std::uint8_t)std::round(Red * 255.0);
Result.G = (std::uint8_t)std::round(Green * 255.0);
Result.B = (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 = (std::uint8_t)HSLToRGBHelper(I, J, H + (1.0 / 3.0));
Result.G = (std::uint8_t)HSLToRGBHelper(I, J, H);
Result.B = (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));}
class PBitmap
{
private:
std::vector<RGBA> Pixels;
std::uint32_t width, height;
std::uint16_t BitsPerPixel;
public:
PBitmap(const char* FilePath);
void Save(const char* FilePath);
inline int Width() const {return width;}
inline int Height() const {return height;}
inline RGBA GetPixel(int X, int Y) const {return Pixels[Y * width + X];}
inline void SetPixel(int X, int Y, RGBA Color) {Pixels[Y * width + X] = Color;}
};
PBitmap::PBitmap(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::uint32_t size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
hFile.seekg (PixelsOffset, std::ios::beg);
hFile.read(reinterpret_cast<char*>(FileInfo.data()), size);
hFile.close();
std::uint8_t* BuffPos = FileInfo.data();
Pixels.resize(width * height);
for (std::uint16_t I = 0; I < height; ++I)
{
for (std::uint16_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 PBitmap::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> 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)
{
*(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;
}
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*>(ImageData.data()), size);
hFile.close();
}
/** Functions **/
void FlipImageBytes(void* In, void* Out, int width, int height, std::uint16_t Bpp)
{
std::uint32_t Chunk = (Bpp > 24 ? width * 4 : width * 3 + width % 4);
std::uint8_t* Destination = static_cast<std::uint8_t*>(Out);
std::uint8_t* Source = static_cast<std::uint8_t*>(In) + Chunk * (height - 1);
while(Source != In)
{
std::memcpy(Destination, Source, Chunk);
Destination += Chunk;
Source -= Chunk;
}
}
For Finding Images:
class ImageFinder
{
private:
int CTSValue;
double TMod, HueMod, SatMod;
Image* TargetImage;
protected:
bool (ImageFinder::*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);
public:
ImageFinder();
~ImageFinder();
ImageFinder(const ImageFinder& ImgFinder) = delete;
ImageFinder& operator = (const ImageFinder& ImgFinder) = delete;
void SetTargetImage(Image IMG);
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(RGBA FirstColour, RGBA SecondColour, int Tolerance);
bool SimilarColours(std::uint32_t FirstColour, std::uint32_t SecondColour, int Tolerance);
std::uint32_t CountColour(RGBA Colour, int X1, int Y1, int X2, int Y2);
std::uint32_t CountColourTolerance(RGBA Colour, int X1, int Y1, int X2, int Y2, int Tolerance);
bool FindColour(int &X, int &Y, RGBA Colour, int X1, int Y1, int X2, int Y2);
bool FindColours(PointArray &Points, RGBA Colour, int X1, int Y1, int X2, int Y2);
bool FindColourTolerance(int &X, int &Y, RGBA Colour, int X1, int Y1, int X2, int Y2, int Tolerance);
bool FindColoursTolerance(PointArray &Points, RGBA Colour, int X1, int Y1, int X2, int Y2, int Tolerance);
bool FindImage(Image ImageToFind, int &X, int &Y);
bool FindImageIn(Image ImageToFind, int &X, int &Y, int X1, int Y1, int X2, int Y2);
bool FindImageToleranceIn(Image ImageToFind, int &X, int &Y, int X1, int Y1, int X2, int Y2, int Tolerance);
};
void ImageFinder::SetTargetImage(Image IMG)
{
delete TargetImage;
TargetImage = new Image(IMG);
}
ImageFinder::~ImageFinder() {delete TargetImage;}
ImageFinder::ImageFinder() : CTSValue(1), TMod(4.0), HueMod(0.2), SatMod(0.2), TargetImage(nullptr), CTS(&ImageFinder::CTS1) {}
void ImageFinder::Reset() {CTSValue = 1; CTS = &ImageFinder::CTS1; TMod = 4.0; HueMod = 0.2; SatMod = 0.2;}
int ImageFinder::GetColourToleranceSpeed() {return this->CTSValue;}
void ImageFinder::SetColourToleranceSpeed(int Value)
{
switch (Value)
{
case -1: CTSValue = -1; CTS = &ImageFinder::CTSN; break;
case 0: CTSValue = 0; CTS = &ImageFinder::CTS0; break;
case 1: CTSValue = 1; CTS = &ImageFinder::CTS1; break;
case 2: CTSValue = 2; CTS = &ImageFinder::CTS2; break;
case 3: CTSValue = 3; CTS = &ImageFinder::CTS3; break;
default: CTSValue = 1; CTS = &ImageFinder::CTS1; break;
}
}
void ImageFinder::GetSpeed2Modifiers(double &HueMod, double &SatMod) {HueMod = this->HueMod; SatMod = this->SatMod;}
void ImageFinder::SetSpeed2Modifiers(double HueMod, double SatMod) {this->HueMod = HueMod; this->SatMod = SatMod;}
double ImageFinder::GetSpeed3Modifier() {return TMod;}
void ImageFinder::SetSpeed3Modifier(double TMod) {this->TMod = TMod;}
bool ImageFinder::SimilarColours(RGBA FirstColour, RGBA SecondColour, int Tolerance)
{
return (this->*ImageFinder::CTS)(FirstColour, SecondColour, Tolerance);
}
bool ImageFinder::SimilarColours(std::uint32_t FirstColour, std::uint32_t SecondColour, int Tolerance)
{
return (this->*ImageFinder::CTS)(Rgba(FirstColour), Rgba(SecondColour), Tolerance);
}
bool ImageFinder::CTSN(RGBA FirstCol, RGBA SecondCol, int Tol)
{
return (FirstCol.Colour == SecondCol.Colour);
}
bool ImageFinder::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 ImageFinder::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 ImageFinder::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 ImageFinder::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 ImageFinder::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 > TargetImage->Width())
{
throw std::out_of_range("Error: X2 out of bounds. X2 must be within the search area.");
}
if (Y2 > TargetImage->Height())
{
throw std::out_of_range("Error: Y2 out of bounds. Y2 must be within the search area.");
}
}
std::uint32_t ImageFinder::CountColour(RGBA Colour, int X1, int Y1, int X2, int Y2)
{
int TemporaryCTS = this->CTSValue;
SetColourToleranceSpeed(-1);
bool Result = CountColourTolerance(Colour, X1, Y1, X2, Y2, 0);
SetColourToleranceSpeed(TemporaryCTS);
return Result;
}
std::uint32_t ImageFinder::CountColourTolerance(RGBA Colour, int X1, int Y1, int X2, int Y2, int Tolerance)
{
int Result = 0;
ValidateRange(X1, Y1, X2, Y2);
for (int I = Y1; I < Y2; ++I)
{
for (int J = X1; J < X2; ++J)
{
if (SimilarColours(Colour, TargetImage->GetPixel(J, I), Tolerance))
{
++Result;
}
}
}
return Result;
}
bool ImageFinder::FindColour(int &X, int &Y, RGBA 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 ImageFinder::FindColours(PointArray &Points, RGBA 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 ImageFinder::FindColourTolerance(int &X, int &Y, RGBA Colour, int X1, int Y1, int X2, int Y2, int Tolerance)
{
ValidateRange(X1, Y1, X2, Y2);
for (int I = Y1; I < Y2; ++I)
{
for (int J = X1; J < X2; ++J)
{
if (SimilarColours(TargetImage->GetPixel(J, I), Colour, Tolerance))
{
X = J; Y = I;
return true;
}
}
}
return false;
}
bool ImageFinder::FindColoursTolerance(PointArray &Points, RGBA Colour, int X1, int Y1, int X2, int Y2, int Tolerance)
{
ValidateRange(X1, Y1, X2, Y2);
for (int I = Y1; I < Y2; ++I)
{
for (int J = X1; J < X2; ++J)
{
if (SimilarColours(TargetImage->GetPixel(J, I), Colour, Tolerance))
Points(Point(J, I));
}
}
return (!Points.Empty());
}
bool ImageFinder::FindImage(Image ImageToFind, int &X, int &Y)
{
return FindImageIn(ImageToFind, X, Y, 0, 0, TargetImage->Width(), TargetImage->Height());
}
bool ImageFinder::FindImageIn(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 ImageFinder::FindImageToleranceIn(Image ImageToFind, int &X, int &Y, int X1, int Y1, int X2, int Y2, int Tolerance)
{
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), TargetImage->GetPixel(XX + J, YY + I), Tolerance))
{
goto Skip;
}
}
}
}
X = J + X1; Y = I + Y1;
return true;
Skip: continue;
}
}
X = -1; Y = -1;
return false;
}