Ok, I'm trying to make a program that finds the position of a colored pixel within the desktop. To do this I make a screenshot of the desktop then go through the pixels and search for the one that has the matching RGB as i need. The only problem is that my program returs strange coordonates X,Y for the found pixel...
#include <stdio.h>
#include <windows.h>
#include <atlimage.h>
#include <iostream>
using namespace std;
struct rgbcolor{
int red;
int green;
int blue;} myColor;
struct point{
int x;
int y;
};
point SearchPixel(int r,int g, int b){
CImage bitmapzor;
bitmapzor.Load(("C:\\1.bmp"));
COLORREF PixColor=0; //This is a color data
int R=0,G=0,B=0; //These are the channel values
BYTE* byteptr = (BYTE*)bitmapzor.GetBits();
int ok=0;
int pitch = bitmapzor.GetPitch(); //This is a pointer offset to get new line of the bitmap
//Go through every pixel and compare the RGB code
for (int i=0; i<bitmapzor.GetWidth();i++)
for (int j=0; j<bitmapzor.GetHeight();j++)
{
B= *(byteptr+pitch*j+3*i);
G= *(byteptr+pitch*j+3*i+1);
R= *(byteptr+pitch*j+3*i+2);
if(R==r&&G==g&&B==b)
{ point p;
p.x=i;
p.y=j;
cout<<"First pixel found at:\n X:"<<p.x<<"\n Y:"<<p.y<<"\n-----------------\n";
return p;
}
}
bitmapzor.Destroy(); //destroy the bitmap
point p;
p.x=-1;
p.y=-1;
cout<<"Pixel not found!\n";
return p;
}
bool ScreenCapture(int x, int y, int width, int height, char *filename){
// get a DC compat. w/ the screen
HDC hDc = CreateCompatibleDC(0);
// make a bmp in memory to store the capture in
HBITMAP hBmp = CreateCompatibleBitmap(GetDC(0), width, height);
// join em up
SelectObject(hDc, hBmp);
// copy from the screen to my bitmap
BitBlt(hDc, 0, 0, width, height, GetDC(0), x, y, SRCCOPY);
CImage image;
image.Attach(hBmp);
image.Save(("C:\\1.bmp"), Gdiplus::ImageFormatBMP);
SearchPixel(myColor.red,myColor.green,myColor.blue);
// free the bitmap memory
DeleteObject(hBmp);
return 1;
}
int main()
{ //RGB for the searched color
myColor.red=200;
myColor.green=191;
myColor.blue=231;
int count=0;
while(true){
ScreenCapture(0, 0, 1366, 768, "c:\\1.bmp");
count++;
cout<<"Number of searches:"<<count<<"\n\n";
Sleep(500);
}
system("pause");
return 0;
}
Well, this code could be simplified quite a lot, but first I'd suggest you try a much, much simpler case, like finding a known integer in a small array of integers. Once you have that working, move up to more complex cases.
EDIT:
You have enough knowledge to do this? Cristy, please don't take this the wrong way, but of all the programmers I've had to deal with, the worst have been the ones who thought they had nothing to learn. I didn't actually look for the bug in your code because your code is overcomplicated, and if you had gone from simple to complex when building it, you would have caught your error a lot sooner.
I suggest that you print out the RGB values of the first few pixel to make sure you're even grabbing the pixel data correctly. If your offsets are wrong, you'll never get this to work.
Ok, finally I found a mistake that I think is the cause of this problem.
The pointer arithmetics:
B= *(byteptr+pitch*j+3*i);
G= *(byteptr+pitch*j+3*i+1);
R= *(byteptr+pitch*j+3*i+2);
Are done for 24bits images, but the screenshot is a 32bits image that means I have to change the multiplier to 4 insteand of 3. That means:
B= *(byteptr+pitch*j+4*i);
G= *(byteptr+pitch*j+4*i+1);
R= *(byteptr+pitch*j+4*i+2);
I think... I'll be back with an edit if this works or not.
EDIT: Yes it works, it gives the corect pixel position. Thanks everyone for your help :) .
Related
I make a game in c++ using SFML library with 2d world made of tiles but when I display it, the last lines are wrong size as you can see:
The size of world is 100x100 tiles. I tried to check last tile width or displaying only first 10x10 tiles but the problem still exist so this is not problem with size of last tiles.
Here you can see:
Here is a class of Tile
class Square{
public:
int x, y, object_type;
sf::RectangleShape rect;
sf::Sprite object;
Square(sf::Color col, int b, int c){
rect = sf::RectangleShape(sf::Vector2f(200, 200));
rect.setFillColor(col);
rect.setOutlineColor(sf::Color(0, 0, 0, 0));
rect.setOutlineThickness(1);
object_type = 0;
x = b;
y = c;
rect.setPosition(790+15*x, 440+15*y);
}
...
}
This is code of filling the world without changing colors of the tiles(it's only cosmetic)
vector<vector<Square>> world;
for(int i=0;i<100;i++){
vector<Square> v;
for(int j=0;j<100;j++){
Square sq(sf::Color(255, 255, 255), j, i);
v.push_back(sq);
}
world.push_back(v);
}
And this is how am I displaying it
window.clear();
for(int i=0;i<world.size();i++) for(int j=0;j<world.at(i).size();j++){
window.draw(world.at(i).at(j).rect);
if(world.at(i).at(j).object_type) window.draw(world.at(i).at(j).object);
}
window.display();
XDDDDDDDD
I cannot belive how terrible idiot I am
rect = sf::RectangleShape(sf::Vector2f(200, 200));
rect.setPosition(790+15*x, 440+15*y);
I just made wrond position details 15 != 200
I will not delete this question even if it's shaming for my programming(and brain-using) skills because someone can has similar problem
My C++/SDL2 program uses a Sprites class with a std::map of pointers to SDL_Textures and member functions for loading and rendering images, however when the program ran none of the loaded images showed up in the window.
I've found that the Sprites instance is given a correct pointer to the renderer and IMG_LoadTexture did not return 0. The renderer is also working properly since primitives like SDL_RenderDrawLine are drawn just fine.
Interestingly when I wanted to check if SDL_QueryTexture gave correct dimensions by printing the source rectangle's width and height, the program rendered the images properly. The printed dimensions were also correct, and trying to print the x and y fixed it too.
Why does this happen?
Here's the two important member functions in the Sprites class.
void Sprites::load(std::string id, std::string filename, int length) {
SDL_Texture* tex = IMG_LoadTexture(renderer, filename.c_str());
if (tex == 0) {
printf("Texture %s could not be loaded\n", filename.c_str());
}
sprmap[id] = tex;
lengthmap[id] = length;
printf("%s should be loaded now\n", id.c_str());
}
void Sprites::render(std::string id, float x, float y, int frame) {
SDL_Rect src;
SDL_Rect dest;
SDL_QueryTexture(sprmap[id], NULL, NULL, &src.w, &src.h);
src.w /= lengthmap[id];
printf("src x: %s, src y %s\n", std::to_string(src.x).c_str(), std::to_string(src.y).c_str());
//Images do not render when above line is omitted
src.x = frame*src.w;
dest.x = x;
dest.y = y;
dest.w = src.w;
dest.h = src.h;
SDL_RenderCopy(renderer, sprmap[id], &src, &dest);
}
Your SDL_Rect is not correctly initialised. Printing "fixes" it, but only by chance.
Your SDL_QueryTexture allows you to initialise w and h, but x and y have completely random values. As to why EXACTLY printing the values fixes things is beyond me, but I suspect that the random values don't go well with SDL_RenderCopy (especially negative values).
If you give proper values to x and y, your problem should go away.
If you want to read more about how variables are initialised this SO post should be a good place to start.
An alternative could be zero initialisation to make sure your structs always have a default value of 0.
The simplest way should be like this : SDL_Rect src{};
I made a program that has two different states, one is for menu display-"Menu State", and the other state is for drawing some stuff-"Draw State".
But I came across a weird thing, if i load certain png for texture and copy them to renderer to display , then leave "Menu State" to enter "Draw State". The texture will somehow make the rectangle color not display properly (for example make green go dark).
In my code, switching to a new state(invoke MenuState::onExit()) will erase the texture map(map of texture smart pointer indexing with std::string)
So the texutre loaded doesn't even exist in the "Drawing State".
I couldn't figure out what went wrong. Here is some of my codes
void TextureManager::DrawPixel(int x, int y, int width, int height, SDL_Renderer *pRenderer)
{
SDL_Rect rect;
rect.x = x;
rect.y = y;
rect.w = width;
rect.h = height;
SDL_SetRenderDrawColor(pRenderer, 0, 255, 0, 255);//same color value
SDL_RenderFillRect(pRenderer, &rect);
}
static bool TextureManagerLoadFile(std::string filename, std::string id)
{
return TextureManager::Instance().Load(filename, id, Game::Instance().GetRenderer());
}
bool TextureManager::Load(std::string filename, std::string id, SDL_Renderer *pRenderer)
{
if(m_textureMap.count(id) != 0)
{
return false;
}
SDL_Surface *pTempSurface = IMG_Load(filename.c_str());
SDL_Texture *pTexutre = SDL_CreateTextureFromSurface(pRenderer, pTempSurface);
SDL_FreeSurface(pTempSurface);
if(pTexutre != 0)
{
m_textureMap[id] = std::make_unique<textureData>(pTexutre, 0, 0);
SDL_QueryTexture(pTexutre, NULL, NULL, &m_textureMap[id]->width, &m_textureMap[id]->height);
return true;
}
return false;
}
void TextureManager::ClearFromTextureMap(std::string textureID)
{
m_textureMap.erase(textureID);
}
bool MenuState::onEnter()
{
if(!TextureManagerLoadFile("assets/Main menu/BTN PLAY.png", "play_button"))
{
return false;
}
if(!TextureManagerLoadFile("assets/Main menu/BTN Exit.png", "exit_button"))
//replace different png file here will also affect the outcome
{
return false;
}
if(!TextureManagerLoadFile("assets/Main menu/BTN SETTINGS.png", "setting_button"))
{
return false;
}
int client_w,client_h;
SDL_GetWindowSize(Game::Instance().GetClientWindow(),&client_w, &client_h);
int playBtn_w = TextureManager::Instance().GetTextureWidth("play_button");
int playBtn_h = TextureManager::Instance().GetTuextureHeight("play_button");
int center_x = (client_w - playBtn_w) / 2;
int center_y = (client_h - playBtn_h) / 2;
ParamsLoader pPlayParams(center_x, center_y, playBtn_w, playBtn_h, "play_button");
int settingBtn_w = TextureManager::Instance().GetTextureWidth("setting_button");
int settingBtn_h = TextureManager::Instance().GetTuextureHeight("setting_button");
ParamsLoader pSettingParams(center_x , center_y + (playBtn_h + settingBtn_h) / 2, settingBtn_w, settingBtn_h, "setting_button");
int exitBtn_w = TextureManager::Instance().GetTextureWidth("exit_button");
int exitBtn_h = TextureManager::Instance().GetTuextureHeight("exit_button");
ParamsLoader pExitParams(10, 10, exitBtn_w, exitBtn_h, "exit_button");
m_gameObjects.push_back(std::make_shared<MenuUIObject>(&pPlayParams, s_menuToPlay));
m_gameObjects.push_back(std::make_shared<MenuUIObject>(&pSettingParams, s_menuToPlay));
m_gameObjects.push_back(std::make_shared<MenuUIObject>(&pExitParams, s_menuExit));
//change order of the 3 line code above
//or replace different png for exit button, will make the rectangle color different
std::cout << "Entering Menu State" << std::endl;
return true;
}
bool MenuState::onExit()
{
for(auto i : m_gameObjects)
{
i->Clean();
}
m_gameObjects.clear();
TextureManager::Instance().ClearFromTextureMap("play_button");
TextureManager::Instance().ClearFromTextureMap("exit_button");
TextureManager::Instance().ClearFromTextureMap("setting_button");
std::cout << "Exiting Menu State" << std::endl;
return true;
}
void Game::Render()
{
SDL_SetRenderDrawColor(m_pRenderer, 255, 255, 255, 255);
SDL_RenderClear(m_pRenderer);
m_pGameStateMachine->Render();
SDL_RenderPresent(m_pRenderer);
}
Menu State Figure
Correct Color
Wrong Color
edit :Also, I found out that this weird phenomenon only happens when the renderer was created with 'SDL_RENDERER_ACCELERATED' flag and -1 or 0 driver index, i.e SDL_CreateRenderer(m_pWindow, 1, SDL_RENDERER_ACCELERATED); or SDL_CreateRenderer(m_pWindow, -1, SDL_RENDERER_SOFTWARE);works fine!
I have been plagued by this very same issue. The link provided by ekodes is how I resolved it, as order of operations had no effect for me.
I was able to pull the d3d9Device via SDL_RenderGetD3D9Device(), then SetTextureStageState as described in ekodes d3d blending link.
I was having the same issue. I got a vibrant green color when trying to render a light gray.
The combination of the parameters that are fixing the issue for you pertain to the driver to be used. -1 selects the first driver that meets the criteria, int this case it needs to be accelerated.
Using SDL_GetRendererInfo I was able to see this happens when using the "direct3d" driver.
I found this question talking about blending in direct3d.
I figured it out eventually. In addition to Alpha Blending there is a Color Blending. So DirectX merges color of the last texture with the last primitive.
The question does provide a fix for this in DirectX, however I'm not sure how to apply that it in regards to SDL. I also have not been able to find a solution for this problem in SDL.
I was drawing Green text with SDL_ttf, which uses a texture. Then drawing a gray rectangle for another component elsewhere on the screen.
What's strange is it doesn't seem to happen all the time. However, mine seems to predominantly happen with SDL_ttf. At first I thought it may be a byproduct of TTF_RenderText_Blended however, it happens with the other ones as well. It also does not appear to be affected by the blend mode of the Texture generated by those functions
So in my case, I was able to change the order of the operations to get the correct color.
Alternatively, using the OpenGL driver appeared to fix this as well. Similar to what you mentioned. (This was driver index 1 for me)
I'm not sure this classifies as an "Answer" but hopefully it helps someone out or points them in the right direction.
Not sure if this code has already been uploaded since I haven't able to find one. I have more experience reading code than writing it so any help is appreciated.
First you have to know colour theory and how to detect whether two colours are the "same" or "similar" (different filters will have different outcomes too).. then you'll have to know what image formats you wish to parse into raw RGB pixels.
Finally, you'll compare the colours in the image to the colours you want to find..
First.. Colour similarity is measured with Euclidean distance or "Pythagorean distance" given as:
distance = squareRoot(of: abs(r1^2 - r2^2) + abs(g1^2 - g2^2) + abs(b1^2 - b2^2))
where r1g1b1 is the first colours and r2g2b2 is the second colour in RGB space..
Then you compare that distance to some tolerance.. IE some threshold that you're okay with as an angle of error..
For example the colour white.. 16777215 as a UInt is the same as 255, 255, 255 in RGB..
Then there is 16777214 as a UInt is the same as 255, 255, 254..
the two colours are white to the eye and extremely similar to the human eye but to the computer they are different! Therefore you use the formula and you compare the distance to some tolerance let's say "10".. why? because if the distance is 0, the colours are an exact match. If not, then you know they are not perfect, but they may be similar depending on how much of a threshold you're willing to accept.. That is what the distance represents. Otherwise compare for exact matches by comparing the bytes.
https://en.wikipedia.org/wiki/Color_difference
Now, you don't have to do it in RGB space.. you can use HSL and XYZ and CIELAB, etc.. at the end of the day, it will be fairly similar results.
I have written code that does exactly that: https://github.com/Brandon-T/CMML/blob/master/src/finder.c#L252
and https://github.com/Brandon-T/CMML/blob/master/src/finder.c#L30
#include <stdio.h>
#include <stdlib.h>
#include "bitmap.h"
#include "finder.h"
void TestOne(CTSInfo* info)
{
rgb32 colour = {144, 240, 255, 0};
PointArray pts;
initPointArray(&pts);
if (findColours(info, &pts, &colour, 0, 0, info->targetImage->width, info->targetImage->height))
{
Point *p = pts.p;
int I = 0;
for (; I < pts.size; ++I, ++p)
printf("Colour found at: (%d, %d)\n", p->x, p->y);
}
freePointArray(&pts);
}
void TestTwo(CTSInfo* info, bitmap* bmp_to_find)
{
int x, y;
if (findImageToleranceIn(info, bmp_to_find, &x, &y, 0, 0, info->targetImage->width, info->targetImage->height, 0))
{
printf("Image found at: (%d, %d)\n", x, y);
}
}
int main()
{
CTSInfo info = {0};
bitmap bmp_to_find = {0};
bitmap bmp_target = {0};
defaultCTS(&info);
bitmap_from_file(&bmp_to_find, "C:/Users/Brandon/Desktop/find.bmp");
bitmap_from_file(&bmp_target, "C:/Users/Brandon/Desktop/target.bmp");
info.targetImage = &bmp_target;
TestOne(&info);
printf("\n");
TestTwo(&info, &bmp_to_find);
printf("\n");
freebmp(&bmp_to_find);
freebmp(&bmp_target);
return 0;
}
If you are planning on doing this in pure C++ you might have a harder time as you'll either have to learn X11 or WinAPI to get screenshots from windows/games to detect colours within.. The best program I know that does this is located at: https://villavu.com/forum and it's called "Simba". It's written in Pascal and is used to bot Runescape with colours. It's quite successful and I've used it myself.
Someone there wrote a tutorial on CTS (Colour Tolerance Space): https://villavu.com/forum/showthread.php?t=74908
I am having some problems finding a solution on how to retrieve a specific color of a pixel on a SDL_Texture...
To be bit more specific: I am trying to calculate the average amount of color used in a given texture. Later on I want to devide for example the number of red pixels by the total amount of pixels. For this task I will need a method, which will get me each pixel color...
I tried to search for some functions, but unfortunately I wasnt able to figure it out..
I saw methods like SDL_RenderReadPixels and SDL_GetPixelFormatName, but none of those helped me out...
Do you have a solution for me?
To access an SDL_Texture's pixels, you must create a blank texture using SDL_CreateTexture() and pass in SDL_TEXTUREACCESS_STREAMING for the access parameter, then copy the pixels of a surface into it. Once that's done, you can use the SDL_LockTexture() function to retrieve a pointer to the pixel data which can then be accessed and modified. To save your changes, you'd call SDL_UnlockTexture(). Try something like this:
SDL_Texture *t;
int main()
{
// Init SDL
SDL_Surface * img = IMG_Load("path/to/file");
SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, img->w, img->h);
void * pixels;
SDL_LockTexture(t, &img->clip_rect, &pixels, img->pitch);
memcpy(pixels, img->pixels, img->w * img->h);
Uint32 * upixels = (Uint32 *) pixels;
// get or modify pixels
SDL_UnlockTexture(t);
return 0;
}
Uint32 get_pixel_at(Uint32 * pixels, int x, int y, int w)
{
return pixels[y * w + x];
}
You can get the colors from a pixel like this:
Uint32 pixel = get_pixel_at(pixels, x, y, img->w);
Uint8 * colors = (Uint8 *) pixel;
// colors[0] is red, 1 is green, 2 is blue, 3 is alpha (assuming you've set the blend mode on the texture to SDL_BLENDMODE_BLEND
If you want more information, then check out these SDL 2.0 tutorials: http://lazyfoo.net/tutorials/SDL/index.php. Tutorial 40 deals specifically with this problem.
Let me know if you have any questions or something is unclear.
Good luck!