Here's my attempt (ugly GDI+ and GDI mix...)
// ...
BYTE pixels[BMP_WIDTH * BMP_HEIGHT * BMP_BPP];
HBITMAP hBitmap;
Gdiplus::Bitmap cBitmap(BMP_WIDTH, BMP_HEIGHT, PixelFormat32bppRGB);
Gdiplus::Graphics cGraphics(&cBitmap);
Gdiplus::Pen cPen(Gdiplus::Color(255, 255, 0, 0));
cGraphics.DrawRectangle(&cPen, 0, 0, cBitmap.GetWidth() - 1, cBitmap.GetHeight() - 1);
// and here it get's real ugly, I'd like to change that...
cBitmap.GetHBITMAP(Gdiplus::Color(255, 255, 255), &hBitmap);
GetBitmapBits(hBitmap, sizeof(pixels), pixels);
// ...
Someone told me to use LockBits but I really didn't understand how. I tried it, but I failed so I'm not going to post that attempt, too.
You could use Bitmap::LockBits to get access to a raw array of data. Here you could read about how to use Bitmap::LockBits.
Here's something I wrote that returns a vector of vectors (with the contained vectors representing columns of pixels in the image) when passed a file path:
#include <vector>
std::vector<std::vector<unsigned>> getPixels(const wchar_t *filename, int &width, int &height) {
Gdiplus::Bitmap bitmap(filename);
//Pass up the width and height, as these are useful for accessing pixels in the vector o' vectors.
width = bitmap.GetWidth();
height = bitmap.GetHeight();
auto *bitmapData = new Gdiplus::BitmapData;
//Lock the whole bitmap so we can read pixel data easily.
Gdiplus::Rect rect(0, 0, width, height);
bitmap.LockBits(&rect, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, bitmapData);
//Get the individual pixels from the locked area.
auto *pixels = static_cast<unsigned *>(bitmapData->Scan0);
//Vector of vectors; each vector is a column.
std::vector<std::vector<unsigned>> resultPixels(width, std::vector<unsigned>(height));
const int stride = abs(bitmapData->Stride);
for(int x = 0; x < width; x++) {
for(int y = 0; y < height; y++) {
//Get the pixel colour from the pixels array which we got earlier.
const unsigned pxColor = pixels[y * stride / 4 + x];
//Get each individual colour component. Bitmap colours are in reverse order.
const unsigned red = (pxColor & 0xFF0000) >> 16;
const unsigned green = (pxColor & 0xFF00) >> 8;
const unsigned blue = pxColor & 0xFF;
//Combine the values in a more typical RGB format (as opposed to the bitmap way).
const int rgbValue = RGB(red, green, blue);
//Assign this RGB value to the pixel location in the vector o' vectors.
resultPixels[x][y] = rgbValue;
}
}
//Unlock the bits that we locked before.
bitmap.UnlockBits(bitmapData);
return resultPixels;
}
Here is how I would do it using GDIPlus Bitmap.LockBits method defined in the header GdiPlusBitmap.h:
Notice that since bitmaps are usually DWORD aligned you may want to discard this unused data that was needed for the alignment, as malat correctly commented..
Gdiplus::BitmapData bitmapData;
Gdiplus::Rect rect(0, 0, bitmap.GetWidth(), bitmap.GetHeight());
//get the bitmap data
if(Gdiplus::Ok == bitmap.LockBits(
&rect, //A rectangle structure that specifies the portion of the Bitmap to lock.
Gdiplus::ImageLockModeRead | Gdiplus::ImageLockModeWrite, //ImageLockMode values that specifies the access level (read/write) for the Bitmap.
bitmap.GetPixelFormat(),// PixelFormat values that specifies the data format of the Bitmap.
&bitmapData //BitmapData that will contain the information about the lock operation.
))
{
//get the lenght of the bitmap data in bytes
int len = bitmapData.Height * std::abs(bitmapData.Stride);
BYTE* buffer = new BYTE[len];
memcpy(bitmapData.Scan0, buffer, len);//copy it to an array of BYTEs
//...
//cleanup
bitmap.UnlockBits(&bitmapData);
delete []buffer;
}
Have you tried supplying the bytes when you create the bitmap:
int width = BMP_WIDTH;
int height = BMP_HEIGHT;
int stride = 4 * width;
BYTE bytes[stride * height];
Gdiplus::Bitmap cBitmap(width, height, stride, PixelFormat32bppRGB, bytes);
Related
I want to make a red texture image buffer. Would anyone help me to make it in right way. I have tried following but could not copy the buffer into ID3D11Texture2D. I need help:
std::vector<BYTE> redTexture(w*h*4);
const auto stride = w * 4;
BYTE* buf = redTexture.data();
for (int i = 0; i < h; ++i)
{
const auto redValue = Gdiplus::Color::Red;
memcpy(buf, &redValue, stride);
buf += stride;
}
If the DXGI_FORMAT of your texture is R8G8B8A8_UNORM, you can do it like this;
std::vector<BYTE> redTexture(w*h*4);
for(int i=0; i<redTexture.size(); i++)
{
if(i%4==0)
{
redTexture[i]=255;
}
else if(i%4==3)
{
redTexture[i]=255;
}
else
{
redTexture[i]=0;
}
}
Since each pixel consists of RGBA 4 byte value, you should assign first and last bytes to 255, other bytes to 0 to get the red color.
Thanks #HMD. I have solved the issue by doing the following:
CImage m_cImage;
// create a test image
m_cImage.Create(w, -h, 8 * 4); // 8 bpp * 4 channel
auto hdc = m_cImage.GetDC();
Gdiplus::Graphics graphics(hdc);
// Create a SolidBrush object.
Gdiplus::SolidBrush redBrush(Gdiplus::Color::Red);
// Fill the rectangle.
Gdiplus::Status status = graphics.FillRectangle(&redBrush, 0, 0, w, h);
TRY_CONDITION(status == Gdiplus::Status::Ok);
....
// Then saved the m_cImage.GetBits() to bmp file using Gdiplus::Bitmap
// and my expected texture is found
I am trying to create a program that will capture a full screen directx application, look for a specific set of pixels on the screen and if it finds it then draw an image on the screen.
I have been able to set up the application to capture the screen the directx libraries using the code the answer for this question Capture screen using DirectX
In this example the code saves to the harddrive using the IWIC libraries. I would rather manipulate the pixels instead of saving it.
After I have captured the screen and have a LPBYTE of the entire screen pixels I am unsure how to crop it to the region I want and then being able to manipulate the pixel array. Is it just a multi dimensional byte array?
The way I think I should do it is
Capture screen to IWIC bitmap (done).
Convert IWIC bitmap to ID2D1 bitmap using ID2D1RenderTarget::CreateBitmapFromWicBitmap
Create new ID2D1::Bitmap to store partial image.
Copy region of the ID2D1 bitmap to a new bitmap using ID2D1::CopyFromBitmap.
Render back onto screen using ID2D1 .
Any help on any of this would be so much appreciated.
Here is a modified version of the original code that only captures a portion of the screen into a buffer, and also gives back the stride. Then it browses all the pixels, dumps their colors as a sample usage of the returned buffer.
In this sample, the buffer is allocated by the function, so you must free it once you've used it:
// sample usage
int main()
{
LONG left = 10;
LONG top = 10;
LONG width = 100;
LONG height = 100;
LPBYTE buffer;
UINT stride;
RECT rc = { left, top, left + width, top + height };
Direct3D9TakeScreenshot(D3DADAPTER_DEFAULT, &buffer, &stride, &rc);
// In 32bppPBGRA format, each pixel is represented by 4 bytes
// with one byte each for blue, green, red, and the alpha channel, in that order.
// But don't forget this is all modulo endianness ...
// So, on Intel architecture, if we read a pixel from memory
// as a DWORD, it's reversed (ARGB). The macros below handle that.
// browse every pixel by line
for (int h = 0; h < height; h++)
{
LPDWORD pixels = (LPDWORD)(buffer + h * stride);
for (int w = 0; w < width; w++)
{
DWORD pixel = pixels[w];
wprintf(L"#%02X#%02X#%02X#%02X\n", GetBGRAPixelAlpha(pixel), GetBGRAPixelRed(pixel), GetBGRAPixelGreen(pixel), GetBGRAPixelBlue(pixel));
}
}
// get pixel at 50, 50 in the buffer, as #ARGB
DWORD pixel = GetBGRAPixel(buffer, stride, 50, 50);
wprintf(L"#%02X#%02X#%02X#%02X\n", GetBGRAPixelAlpha(pixel), GetBGRAPixelRed(pixel), GetBGRAPixelGreen(pixel), GetBGRAPixelBlue(pixel));
SavePixelsToFile32bppPBGRA(width, height, stride, buffer, L"test.png", GUID_ContainerFormatPng);
LocalFree(buffer);
return 0;;
}
#define GetBGRAPixelBlue(p) (LOBYTE(p))
#define GetBGRAPixelGreen(p) (HIBYTE(p))
#define GetBGRAPixelRed(p) (LOBYTE(HIWORD(p)))
#define GetBGRAPixelAlpha(p) (HIBYTE(HIWORD(p)))
#define GetBGRAPixel(b,s,x,y) (((LPDWORD)(((LPBYTE)b) + y * s))[x])
int main()
HRESULT Direct3D9TakeScreenshot(UINT adapter, LPBYTE *pBuffer, UINT *pStride, const RECT *pInputRc = nullptr)
{
if (!pBuffer || !pStride) return E_INVALIDARG;
HRESULT hr = S_OK;
IDirect3D9 *d3d = nullptr;
IDirect3DDevice9 *device = nullptr;
IDirect3DSurface9 *surface = nullptr;
D3DPRESENT_PARAMETERS parameters = { 0 };
D3DDISPLAYMODE mode;
D3DLOCKED_RECT rc;
*pBuffer = NULL;
*pStride = 0;
// init D3D and get screen size
d3d = Direct3DCreate9(D3D_SDK_VERSION);
HRCHECK(d3d->GetAdapterDisplayMode(adapter, &mode));
LONG width = pInputRc ? (pInputRc->right - pInputRc->left) : mode.Width;
LONG height = pInputRc ? (pInputRc->bottom - pInputRc->top) : mode.Height;
parameters.Windowed = TRUE;
parameters.BackBufferCount = 1;
parameters.BackBufferHeight = height;
parameters.BackBufferWidth = width;
parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
parameters.hDeviceWindow = NULL;
// create device & capture surface (note it needs desktop size, not our capture size)
HRCHECK(d3d->CreateDevice(adapter, D3DDEVTYPE_HAL, NULL, D3DCREATE_SOFTWARE_VERTEXPROCESSING, ¶meters, &device));
HRCHECK(device->CreateOffscreenPlainSurface(mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface, nullptr));
// get pitch/stride to compute the required buffer size
HRCHECK(surface->LockRect(&rc, pInputRc, 0));
*pStride = rc.Pitch;
HRCHECK(surface->UnlockRect());
// allocate buffer
*pBuffer = (LPBYTE)LocalAlloc(0, *pStride * height);
if (!*pBuffer)
{
hr = E_OUTOFMEMORY;
goto cleanup;
}
// get the data
HRCHECK(device->GetFrontBufferData(0, surface));
// copy it into our buffer
HRCHECK(surface->LockRect(&rc, pInputRc, 0));
CopyMemory(*pBuffer, rc.pBits, rc.Pitch * height);
HRCHECK(surface->UnlockRect());
cleanup:
if (FAILED(hr))
{
if (*pBuffer)
{
LocalFree(*pBuffer);
*pBuffer = NULL;
}
*pStride = 0;
}
RELEASE(surface);
RELEASE(device);
RELEASE(d3d);
return hr;
}
i am doing a program where you take a screenshot of a window and then scan every pixel of that picture. But I have a problem assigning RGBQUAD array to the taken screen. Every pixel has the same RGB which is 205. Here is a piece of my code:
RGBQUAD *pixel = malloc((ssWidth * ssHeight)* sizeof(RGBQUAD));
hdcScreen = GetDC(gameHandle);
hdc = CreateCompatibleDC(hdcScreen);
hBmp = CreateCompatibleBitmap(hdcScreen, ssWidth, ssHeight);
SelectObject(hdc, hBmp);
BitBlt(hdc, 0, 0, ssWidth, ssHeight, hdcScreen, xCenter, yCenter, SRCCOPY);
GetDIBits(hdc, hBmp, 0, ssHeight, pixel, &bmpInfo, DIB_RGB_COLORS);
int p = -1;
for(y_var = 0; y_var < ssWidth; y_var++)
{
for(x_var = 0; x_var < ssHeight; x_var++)
{
if(ComparePixel(&pixel[++p]))
{
SetCursorPos(xCenter + x_var + 3, yCenter + y_var + 3);
}
}
}
bool ComparePixel(RGBQUAD *pixel)
{
printf("%d, %d, %d\n"; pixel -> rgbRed, pixel -> rgbGreen, pixel -> rgbBlue);
return false;
}
ComparePixel(RGBQUAD *pixel) function just checks the RGB values. How do i assign the RGBQUAD to the bitmap of the screenshot?
Multiple issues.
The RGBQUAD **pixel = malloc(... and free(*pixel) appear to be the problem. I think you want RGBQUAD *pixel = malloc((ssWidth * ssHeight)* sizeof(RGBQUAD)); (only 1 *)
Suspect the pixels in GetDIBits() s/b pixel.
I think you want y_var = 0; (x_var = 0; also)
ComparePixel() is not defined, but I think you want something closer to if(ComparePixel(pixel[x_var+(y_var*ssWidth)], the_pixel_to_compare_against))
The free(*pixel); s/b _after the 2 for loops and should be free(pixel);
I'm using StretchDIBits to print an image and it is failing when the image is at certain sizes for some unknown reason.
The image data is loaded into an unsigned int array from some some other image source in 24-bit BGR format. I've already verified that the image and buffer are perfectly fine since, like I said, it works at some sizes but not at all.
The current size I'm testing with is 638x1014. If I change the height to 1013 it works fine, but for some reason it just flat out fails if it's 1014.
Here's some code to show you how it's all being setup:
unsigned int * buffer = new unsigned int[width * height * 3];
// Fill buffer with image data...
BITMAPINFOHEADER bi = { 0 };
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = width;
bi.biHeight = height;
bi.biPlanes = 1;
bi.biBitCount = 24;
bi.biCompression = BI_RGB;
bi.biSizeImage = width * height * 3; // Specifying this value because if I don't it will crash trying to read outside of the buffer.
StartPage(hdcPrint);
SetMapMode(hdcPrint, MM_ISOTROPIC);
SetWindowExtEx(hdcPrint, width, height, NULL);
SetViewportExtEx(hdcPrint, width, height, NULL);
SetViewportOrgEx(hdcPrint, 0, 0, NULL);
StretchDIBits(hdcPrint, 0, 0, width, width, 0, 0, width, height, buffer, (BITMAPINFO *) &bi, DIB_RGB_COLORS, SRCCOPY);
StretchDIBits returns zero when it fails and the print result is a blank page.
I have a vague idea of what the problem is because, like it says in the comment, if I don't specify biSizeImage and leave it at zero then StretchDIBits will cause a crash because it tries to read past the end of the buffer. Even so, I have no idea how to diagnose exactly why it's doing that since it works at some sizes but not others.
Your width is the wrong number of bytes. Windows requires that each line be a multiple of 4 bytes; 638*3 is 1914, which is 2 bytes shy.
I searched and I understood I'll have to use GetDIBits(). I don't know what to do with the LPVOID lpvBits out parameter.
Could someone please explain it to me? I need to get the pixel color information in a two dimensional matrix form so that I can retrieve the information for a particular (x,y) coordinate pair.
I am programming in C++ using Win32 API.
first you need a bitmap and open it
HBITMAP hBmp = (HBITMAP) LoadImage(GetModuleHandle(NULL), _T("test.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if(!hBmp) // failed to load bitmap
return false;
//getting the size of the picture
BITMAP bm;
GetObject(hBmp, sizeof(bm), &bm);
int width(bm.bmWidth),
height(bm.bmHeight);
//creating a bitmapheader for getting the dibits
BITMAPINFOHEADER bminfoheader;
::ZeroMemory(&bminfoheader, sizeof(BITMAPINFOHEADER));
bminfoheader.biSize = sizeof(BITMAPINFOHEADER);
bminfoheader.biWidth = width;
bminfoheader.biHeight = -height;
bminfoheader.biPlanes = 1;
bminfoheader.biBitCount = 32;
bminfoheader.biCompression = BI_RGB;
bminfoheader.biSizeImage = width * 4 * height;
bminfoheader.biClrUsed = 0;
bminfoheader.biClrImportant = 0;
//create a buffer and let the GetDIBits fill in the buffer
unsigned char* pPixels = new unsigned char[(width * 4 * height)];
if( !GetDIBits(CreateCompatibleDC(0), hBmp, 0, height, pPixels, (BITMAPINFO*) &bminfoheader, DIB_RGB_COLORS)) // load pixel info
{
//return if fails but first delete the resources
DeleteObject(hBmp);
delete [] pPixels; // delete the array of objects
return false;
}
int x, y; // fill the x and y coordinate
unsigned char r = pPixels[(width*y+x) * 4 + 2];
unsigned char g = pPixels[(width*y+x) * 4 + 1];
unsigned char b = pPixels[(width*y+x) * 4 + 0];
//clean up the bitmap and buffer unless you still need it
DeleteObject(hBmp);
delete [] pPixels; // delete the array of objects
so in short, the lpvBits out parameter is the pointer to the pixels
but if it is only 1 pixel you need i suggest to use getpixel to
I'm not sure if this is what you're looking for but GetPixel does pretty much what you need ...at least from i can tell from the function's description