Seg Fault when calling SDL_BlitSurface a second time - c++

I'm using SDL and SDL_Image to load images to be used as textures for opengl.
I'm trying to load a spritesheet with multiple images arranged in a horizontal row (in the same image)
void load_spritesheet(std::string key, const char *file_name, int width, int height, int nframes) {
GLuint *texture = new GLuint[nframes];
auto src = IMG_Load(file_name);
auto dstrect = new SDL_Rect{0, 0, width, height};
for(int i = 0; i < nframes; i++) {
auto dst = SDL_CreateRGBSurface(0, width, height, 1, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
auto rect = new SDL_Rect { i*width, 0, width, height };
SDL_BlitSurface(src, rect, dst, dstrect);
load_gltex(dst, &texture[i]);
SDL_FreeSurface(dst);
}
SPRITESHEET_CACHE[key] = texture;
SDL_FreeSurface(src);
}
I stepped through the code, and on the first iteration of the loop it works fine. On the second iteration I get a seg fault on the call to SDL_BlitSurface, none of the pointers passed in are NULL and none of the surfaces are locked or anything like that. I'm sure that my rectangles are within the bounds of each surface.
Here's some values from gdb at the point right before it segfaults:
print i
1
print *src
{flags = 0, format = 0x847100, w = 416, h = 32, pitch = 1664, pixels = 0x87c5f0, userdata = 0x0, locked = 0, lock_data = 0x0, clip_rect = {x = 0, y = 0, w = 416, h = 32}, map = 0x8537b0, refcount = 1}
print *dst
{flags = 0, format = 0x855ec0, w = 32, h = 32, pitch = 4, pixels = 0x84d1f0, userdata = 0x0, locked = 0, lock_data = 0x0, clip_rect = {x = 0, y = 0, w = 32, h = 32}, map = 0x6bdfc0, refcount = 1}
print *rect
{x = 32, y = 0, w = 32, h = 32}
print *dstrect
{x = 0, y = 0, w = 32, h = 32}
Is it unsafe to call SDL_BlitSurface twice on the same surface or something like that? Thanks.

Ah, the error was caused by improperly setting the depth on the call to SDL_CreateRGBSurface
I was passing in a 1 when I really should've been passing the correct value (32 in this case)
Once I corrected that the segfault went away.
https://wiki.libsdl.org/SDL_CreateRGBSurface#Remarks

Related

Convert Gdiplus::Region to ID2D1Geometry* for clipping

I am trying to migrate my graphics interface project from Gdiplus to Direct2D.
Currently, I have a code that calculates clipping area for an rendering object:
Graphics g(hdc);
Region regC = Rect(x, y, cx + padding[2] + padding[0], cy + padding[3] + padding[1]);
RecursRegPos(this->parent, &regC);
RecursRegClip(this->parent, &regC);
g.setClip(g);
...
inline void RecursRegClip(Object *parent, Region* reg)
{
if (parent == CARD_PARENT)
return;
if (parent->overflow != OVISIBLE)
{
Rect regC(parent->getAbsoluteX(), parent->getAbsoluteY(), parent->getCx(), parent->getCy()); // Implementation of these function is not necceassary
GraphicsPath path;
path.Reset();
GetRoundRectPath(&path, regC, parent->borderRadius[0]);
// source https://stackoverflow.com/a/71431813/15220214, but if diameter is zero, just plain rect is returned
reg->Intersect(&path);
}
RecursRegClip(parent->parent, reg);
}
inline void RecursRegPos(Object* parent, Rect* reg)
{
if (parent == CARD_PARENT)
return;
reg->X += parent->getX() + parent->padding[0];
reg->Y += parent->getY() + parent->padding[1];
if (parent->overflow == OSCROLL || parent->overflow == OSCROLLH)
{
reg->X -= parent->scrollLeft;
reg->Y -= parent->scrollTop;
}
RecursRegPos(parent->parent, reg);
}
And now I need to convert it to Direct2D. As You may notice, there is no need to create Graphics object to get complete calculated clipping region, so I it would be cool if there is way to just convert Region to ID2D1Geometry*, that, as far, as I understand from msdn article need to create clipping layer.
Also, there is probably way to convert existing code (RecursRegClip, RecursRegPos) to Direct2D, but I am facing problems, because I need to work with path, but current functions get region as an argument.
Update 1
There is Region::GetData method that returns, as I understand array of points, so maybe there is possibility to define either ID2D1PathGeometry or ID2D1GeometrySink by points?
Update 2
Oh, maybe
ID2D1GeometrySink::AddLines(const D2D1_POINT_2F *points, UINT32 pointsCount)
is what do I need?
Unfortunately, GetData of region based on just (0,0,4,4) rectangle returns 36 mystique values:
Region reg(Rect(0, 0, 4, 4));
auto so = reg.GetDataSize();
BYTE* are = new BYTE[so];
UINT fi = 0;
reg.GetData(are, so, &fi);
wchar_t ou[1024]=L"\0";
for (int i = 0; i < fi; i++)
{
wchar_t buf[10] = L"";
_itow_s(are[i], buf, 10, 10);
wcscat_s(ou, 1024, buf);
wcscat_s(ou, 1024, L", ");
}
// ou - 28, 0, 0, 0, 188, 90, 187, 128, 2, 16, 192, 219, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 64, 0, 0, 128, 64,
I rewrote the solution completely, it seems to be working:
// zclip is ID2D1PathGeometry*
inline void Render(ID2D1HwndRenderTarget *target)
{
ID2D1RoundedRectangleGeometry* mask = nullptr;
ID2D1Layer* clip = nullptr;
if(ONE_OF_PARENTS_CLIPS_THIS || THIS_HAS_BORDER_RADIUS)
{
Region reg = Rect(x, y, cx + padding[2] + padding[0], cy + padding[3] + padding[1]);
RecursRegPos(this->parent, &reg);
D2D1_ROUNDED_RECT maskRect;
maskRect.rect.left = reg.X;
maskRect.rect.top = reg.Y;
maskRect.rect.right = reg.X + reg.Width;
maskRect.rect.bottom = reg.Y + reg.Height;
maskRect.radiusX = this->borderRadius[0];
maskRect.radiusY = this->borderRadius[1];
factory->CreateRoundedRectangleGeometry(maskRect, &mask);
RecursGeoClip(this->parent, mask);
target->CreateLayer(NULL, &clip);
if(zclip)
target->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), zclip), clip);
else
target->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), mask), clip);
SafeRelease(&mask);
}
// Draw stuff here
if (clip)
{
target->PopLayer();
SafeRelease(&clip);
SafeRelease(&mask);
SafeRelease(&zclip);
}
}
...
inline void RecursGeoClip(Object* parent, ID2D1Geometry* geo)
{
if (parent == CARD_PARENT)
return;
ID2D1RoundedRectangleGeometry* maskParent = nullptr;
if (parent->overflow != OVISIBLE)
{
Rect regC(parent->getAbsoluteX(), parent->getAbsoluteY(), parent->getCx(), parent->getCy());
ID2D1GeometrySink* sink = nullptr;
ID2D1PathGeometry* path = nullptr;
SafeRelease(&path);
factory->CreatePathGeometry(&path);
D2D1_ROUNDED_RECT maskRect;
maskRect.rect.left = regC.X;
maskRect.rect.top = regC.Y;
maskRect.rect.right = regC.X + regC.Width;
maskRect.rect.bottom = regC.Y + regC.Height;
maskRect.radiusX = parent->borderRadius[0];
maskRect.radiusY = parent->borderRadius[1];
path->Open(&sink);
factory->CreateRoundedRectangleGeometry(maskRect, &maskParent);
geo->CombineWithGeometry(maskParent, D2D1_COMBINE_MODE_INTERSECT, NULL, sink);
sink->Close();
SafeRelease(&sink);
SafeRelease(&this->zclip);
this->zclip = path;
RecursGeoClip(parent->parent, this->zclip);
}
else
RecursGeoClip(parent->parent, geo);
SafeRelease(&maskParent);
}
Now I can enjoy drawing one image and two rectangles in more than 60 fps, instead of 27 (in case of 200x200 image size, higher size - lower fps) with Gdi+ -_- :

GetPixel() not working correctly Windows API C++

I'm writing a program that reads each pixel of a window and store it in an array of bytes as black and white, each bit of the bytes is a black/white value.
But GetPixel() doesn't seem to work the way I expected. Here's the part of the code for reading pixels and storing them:
byte *colors = new byte[250000 / 8 + 1];
ZeroMemory(colors, 250000 / 8 + 1);
HDC hdc = GetDC(hwnd);
HDC memDC = CreateCompatibleDC(hdc);
HBITMAP memBitmap = CreateCompatibleBitmap(hdc, 500, 500);
SelectObject(memDC, memBitmap);
BitBlt(memDC, 0, 0, 500, 500, hdc, 0, 0, SRCCOPY);
for (int y = 0; y < 500; y++) {
for (int x = 0; x < 500; x++) {
COLORREF pxcolor = GetPixel(memDC, x, y);
if (pxcolor == CLR_INVALID) {
MessageBox(hwnd, _T("Oops..."), NULL, NULL);
}
int r = GetRValue(pxcolor);
int g = GetGValue(pxcolor);
int b = GetBValue(pxcolor);
int average = (r + g + b) / 3;
bool colorBW = average >= 128;
int currentIndex = y * 500 + x;
if (colorBW) {
SetBit(colors, currentIndex);
}
}
}
ReleaseDC(hwnd, hdc);
DeleteDC(memDC);
DeleteObject(memBitmap);
delete[] colors;
SetBit():
inline VOID SetBit(byte *bytes, int index, bool state = true) {
byte byteToSet = bytes[index / 8];
int bitNumber = index % 8;
bytes[index / 8] = state ? (byteToSet | (0b1000'0000 >> bitNumber)) : (byteToSet & ((0b1111'1111 >> (bitNumber + 1)) | (0b1111'1111 << (8 - bitNumber - 1))));
}
Every pixel read in by GetPixel() seems to give me 0x000000, or pure black.
My code used to call GetPixel() with the first parameter being hdc, without all the bitmap and memory DC stuff, but that way every pixel returns CLR_INVALID. I came across this question, and the above code is after I have changed it into using memory DCs and bitmaps. But it just went from returning CLR_INVALID to 0x000000 for each pixel.
If I add this line before I use GetPixel():
SetPixel(memDC, x, y, RGB(255, 255, 255));
GetPixel() returns the correct result. Why is it functioning this way?

SDL: IMG_load returns wrong data

The problem comes from the "IMG_load" fonction of the SDL_image, with language C.
It raises no error during execution, but when it comes to the line
SDL_Surface* image1 = IMG_Load("image.bmp");
the function returns a data that I can't use: for instance, SDL_BlitSurface(image1, NULL, screen, &position) won't do anything.
However, it works fine when using the function SDL_LoadBMP.
I have used the debugger to try to understand the difference between the data returned by the two functions:
Here is what IMG_Load("image.bmp") returns:
{ //Doesn't work
flags = 0,
format = 0x3c07c0,
w = 40,
h = 40,
pitch = 120,
pixels = 0x3c07f0,
offset = 0,
hwdata = 0x0,
clip_rect = {
x = 0,
y = 0,
w = 0,
h = 0
},
unused1 = 0,
locked = 40,
map = 0x28,
format_version = 3939000,
refcount = 1
}
And here is what SDL_LoadBMP("image.bmp") returns:
{ //Works fine
flags = 0,
hwdata = 0x0,
clip_rect = {
x = 0,
y = 0,
w = 40,
h = 40
},
unused1 = 0,
locked = 0,
map = 0x3458e8,
format_version = 7,
refcount = 1
}
(Note that the image is the same, and it is a 40x40px square).
Why can't I use SDL_BlitSurface on a SDL_Surface returned by the function IMG_Load?
edit: you also have to know that this is the first time a function from the SDL_Image package is used in the program.

Why is locked bits returning -842150451 for all pixel values?

I am trying to get the bits in a bitmap, but I keep getting this output (PS. I tested the whole array as well):
-842150451 // Array before lockbits
-842150451 // Array after lockbits
This is my code to get the lockedBits.
BitmapData * getLockedBitmapData()
{
float squareSideLength = 50 * 4;
Bitmap * src = new Bitmap(squareSideLength , squareSideLength);
Graphics * graphics = Graphics::FromImage(solid);
SolidBrush blackBrush(Color(255, 0, 0, 0));
graphics->FillRectangle(&blackBrush, FLOAT_ZERO, FLOAT_ZERO, squareSideLength, squareSideLength);
int srcWidth = src->GetWidth();
int srcHeight = src->GetHeight();
UINT * pixels = new UINT[srcWidth * srcHeight];
// _RPT1(0, "%d\n", pixels[55]);
BitmapData * bitmapData = new BitmapData();
bitmapData->Width = srcWidth;
bitmapData->Height = srcHeight;
bitmapData->Stride = 4 * srcWidth;
bitmapData->PixelFormat = PixelFormat32bppARGB;
bitmapData->Scan0 = (VOID*) pixels;
bitmapData->Reserved = NULL;
src->LockBits(new Rect(0, 0, srcWidth, srcHeight),
ImageLockMode::ImageLockModeRead | ImageLockMode::ImageLockModeWrite,
src->GetPixelFormat(),
bitmapData);
// _RPT1(0, "%d\n", pixels[55]);
return bitmapData;
}
You are using it wrong, it returns a BitmapData. So it needs to be:
BitmapData bitmapData;
Status ret = src->LockBits(new Rect(0, 0, srcWidth, srcHeight),
ImageLockMode::ImageLockModeRead | ImageLockMode::ImageLockModeWrite,
src->GetPixelFormat(),
&bitmapData);
if (ret != Ok) {
// Report error
//...
}
Not not skip error checking.

Get Pixel color fastest way?

I'm trying to make an auto-cliker for an windows app. It works well, but it's incredibly slow!
I'm currently using the method "getPixel" which reloads an array everytime it's called.
Here is my current code:
hdc = GetDC(HWND_DESKTOP);
bx = GetSystemMetrics(SM_CXSCREEN);
by = GetSystemMetrics(SM_CYSCREEN);
start_bx = (bx/2) - (MAX_WIDTH/2);
start_by = (by/2) - (MAX_HEIGHT/2);
end_bx = (bx/2) + (MAX_WIDTH/2);
end_by = (by/2) + (MAX_HEIGHT/2);
for(y=start_by; y<end_by; y+=10)
{
for(x=start_bx; x<end_bx; x+=10)
{
pixel = GetPixel(*hdc, x, y);
if(pixel==RGB(255, 0, 0))
{
SetCursorPos(x,y);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
Sleep(50);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
Sleep(25);
}
}
}
So basically, it just scan a range of pixel in the screen and starts a mouse event if it detects a red button.
I know there are other ways to get the pixel color, such as bitblt. But I've made some researches, and I don't understand how I'm supposed to do, in order to scan a color array. I need something which scans screen very fast in order to catch the button.
Could you please help me?
Thanks.
I found a perfect way which is clearly faster than the GetPixel one:
HDC hdc, hdcTemp;
RECT rect;
BYTE* bitPointer;
int x, y;
int red, green, blue, alpha;
while(true)
{
hdc = GetDC(HWND_DESKTOP);
GetWindowRect(hWND_Desktop, &rect);
int MAX_WIDTH = rect.right;
int MAX_HEIGHT = rect.bottom;
hdcTemp = CreateCompatibleDC(hdc);
BITMAPINFO bitmap;
bitmap.bmiHeader.biSize = sizeof(bitmap.bmiHeader);
bitmap.bmiHeader.biWidth = MAX_WIDTH;
bitmap.bmiHeader.biHeight = MAX_HEIGHT;
bitmap.bmiHeader.biPlanes = 1;
bitmap.bmiHeader.biBitCount = 32;
bitmap.bmiHeader.biCompression = BI_RGB;
bitmap.bmiHeader.biSizeImage = MAX_WIDTH * 4 * MAX_HEIGHT;
bitmap.bmiHeader.biClrUsed = 0;
bitmap.bmiHeader.biClrImportant = 0;
HBITMAP hBitmap2 = CreateDIBSection(hdcTemp, &bitmap, DIB_RGB_COLORS, (void**)(&bitPointer), NULL, NULL);
SelectObject(hdcTemp, hBitmap2);
BitBlt(hdcTemp, 0, 0, MAX_WIDTH, MAX_HEIGHT, hdc, 0, 0, SRCCOPY);
for (int i=0; i<(MAX_WIDTH * 4 * MAX_HEIGHT); i+=4)
{
red = (int)bitPointer[i];
green = (int)bitPointer[i+1];
blue = (int)bitPointer[i+2];
alpha = (int)bitPointer[i+3];
x = i / (4 * MAX_HEIGHT);
y = i / (4 * MAX_WIDTH);
if (red == 255 && green == 0 && blue == 0)
{
SetCursorPos(x,y);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
Sleep(50);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
Sleep(25);
}
}
}
I hope this could help someone else.
The simple answer is that if this is the method you insist on using then there isn't much to optimize. As others have pointed out in comments, you should probably use a different method for locating the area to click. Have a look at using FindWindow, for example.
If you don't want to change your method, then at least sleep your thread for a bit after each complete screen scan.