I am developing an application using c++
I am facing a problem when trying to capture screen. then edit some of its pixels
and save the image
My code works absolutely fine when when i select the platform as Win32
but as soon as i change the platform from Win32 to x64, the code fails
It start giving access violation when trying to access the pixels
I checked that under both platforms, size of int is 4 bytes and imageData.Stride is coming as -5528
when i do (row*stride/4 + col) i get same value on both platforms
imageData.getPixelFormat() returns 139273 which is PixelFormat32bppRGB
under both platforms
I am posting the code below
please help me out, i have done lot of google, but nothing helps
The access violation error comes at this line
UINT curColor = pixels[row * iStride / 4 + col];
when row value is >0
void BitmapToJpg(HBITMAP hbmpImage, int width, int height)
{
p_bmp = Bitmap::FromHBITMAP(hbmpImage, NULL);
CLSID pngClsid;
int result = GetEncoderClsid(L"image/jpeg", &pngClsid);
if (result != -1)
std::cout << "Encoder succeeded" << std::endl;
else
std::cout << "Encoder failed" << std::endl;
//***************************Testing Lockbits********************************//
// successfull results and position is also correct
BitmapData imageData;
Rect rect(0, 0, width, height);
p_bmp->LockBits(
&rect,
ImageLockModeWrite,
p_bmp->GetPixelFormat(),
//PixelFormat24bppRGB,
&imageData);
cout << p_bmp->GetPixelFormat();
UINT* pixels;
pixels = (UINT*)imageData.Scan0;
int iStride = imageData.Stride;
int x = sizeof(int);
byte red = 0;
byte green = 0;
byte blue = 255;
byte alpha = 0;
for (int row = 0; row < height; ++row)
{
for (int col = 0; col < width; ++col)
{
///Some code to get color
UINT curColor = pixels[row * iStride / 4 + col];
int b = curColor & 0xff;
int g = (curColor & 0xff00) >> 8;
int r = (curColor & 0xff0000) >> 16;
int a = (curColor & 0xff000000) >> 24;
//result_pixels[col][row] = RGB(r, g, b);
if (b>15 && b < 25 && g<5 && r>250)
{
//Red found
//Code to change color, generate ARGB from provided RGB values
UINT32 rgb = (alpha << 24) + (red << 16) + (green << 8) + (blue);
curColor = rgb;
b = curColor & 0xff;
g = (curColor & 0xff00) >> 8;
r = (curColor & 0xff0000) >> 16;
a = (curColor & 0xff000000) >> 24;
cout << "Red found" << endl;
pixels[row * iStride / 4 + col]=rgb;
}
}
}
p_bmp->UnlockBits(&imageData);
//*****************************Till Here*************************************//
p_bmp->Save(L"screen.jpg", &pngClsid, NULL);
delete p_bmp;
}
Related
const int WIDTH = 1920;
const int HEIGHT = 1200;
const int MAX_ITERATIONS = 500;
uint32_t image[HEIGHT][WIDTH];
struct ThreadArgs { int id; int delay; };
void myThreadFunc(ThreadArgs args)
{
for (int i = 0; i < 1; i++) {
sleep_for(seconds(args.delay));
cout << args.id;
}
}
void write_tga(const char *filename)
{
ofstream outfile(filename, ofstream::binary);
uint8_t header[18] = {
0, // no image ID
0, // no colour map
2, // uncompressed 24-bit image
0, 0, 0, 0, 0, // empty colour map specification
0, 0, // X origin
0, 0, // Y origin
WIDTH & 0xFF, (WIDTH >> 8) & 0xFF, // width
HEIGHT & 0xFF, (HEIGHT >> 8) & 0xFF, // height
24, // bits per pixel
0, // image descriptor
};
outfile.write((const char *)header, 18);
for (int y = 0; y < HEIGHT; ++y)
{
for (int x = 0; x < WIDTH; ++x)
{
uint8_t pixel[3] = {
image[y][x] & 0xFF, // blue channel
(image[y][x] >> 8) & 0xFF, // green channel
(image[y][x] >> 16) & 0xFF, // red channel
};
outfile.write((const char *)pixel, 3);
}
}
outfile.close();
if (!outfile)
{
cout << "Error writing to " << filename << endl;
exit(1);
}
}
void compute_mandelbrot(double left, double right, double top, double bottom)
{
for (int y = 0; y < HEIGHT; ++y)
{
for (int x = 0; x < WIDTH; ++x)
{
complex<double> c(left + (x * (right - left) / WIDTH),
top + (y * (bottom - top) / HEIGHT));
complex<double> z(0.0, 0.0);
int iterations = 0;
while (abs(z) < 2.0 && iterations < MAX_ITERATIONS)
{
z = (z * z) + c;
++iterations;
}
if (iterations == MAX_ITERATIONS)
{
image[y][x] = 0x000000; // black
}
else
{
image[y][x] = 0xFFFFFF; // white
}
}
}
}
this is most of the code and this works but i want to make it run faster using more threads.
i tried splitting the height portions of the compute_mandelbrot function into two separate functions but could not get it to not flag errors. the errors i got were: "expression must be a modifiable lvalue" and "array type 'uint32_t[1920]' is not assignable" on the line "image[x] = 0x000000 ' the same happened on the other lines mentioning image[x] or image[y] as I'd changed those lines to split the function between x axis and y axis. The above code does not have this change
is there any way to do this or something like this to split this function between two threads? if so please explain
Compilers don't guess at your code. uint32_t image[HEIGHT][WIDTH]; is a 2D array of pixels. image[y][x] is single pixel. image[y] is a row of pixels. And you can't assign a whole row of pixels. image[x] is still a row of pixels. The compile won't turn that into a column just because you used x.
It's indeed easy to split the function in two threads. Just calculate the upper and lower half of the image separately, changing just the for(int y ... part.
First off, this is not a duplicate. I have already read Converting 1-bit bmp file to array in C/C++ and my question is about an inconsistency I'm seeing in the formulas provided with the one that works for me.
The Issue
I am trying to read in a 1-bit Bitmap image that was created in MS Paint. I've used the code provided by other answers on this site, but there are a few things I had to change to get it to work, and I want to understand why,
Change 1: lineSize must be doubled
Original
int lineSize = (w / 8 + (w / 8) % 4);
Mine:
int lineSize = (w/ 8 + (w / 8) % 4) * 2;
Change 2: Endianness must be reversed
Original:
for(k = 0 ; k < 8 ; k++)
... (data[fpos] >> k ) & 1;
Mine:
for (int k = 7; k >= 0; --k) {
... (data[rawPos] >> k) & 1;
Full Code
NOTE: This code works. There are some changes from the original, but the core read part is the same.
vector<vector<int>> getBlackAndWhiteBmp(string filename) {
BmpHeader head;
ifstream f(filename, ios::binary);
if (!f) {
throw "Invalid file given";
}
int headSize = sizeof(BmpHeader);
f.read((char*)&head, headSize);
if (head.bitsPerPixel != 1) {
f.close();
throw "Invalid bitmap loaded";
}
int height = head.height;
int width = head.width;
// Lines are aligned on a 4-byte boundary
int lineSize = (width / 8 + (width / 8) % 4) * 2;
int fileSize = lineSize * height;
vector<unsigned char> rawFile(fileSize);
vector<vector<int>> img(head.height, vector<int>(width, -1));
// Skip to where the actual image data is
f.seekg(head.offset);
// Read in all of the file
f.read((char*)&rawFile[0], fileSize);
// Decode the actual boolean values of the pixesl
int row;
int reverseRow; // Because bitmaps are stored bottom to top for some reason
int columnByte;
int columnBit;
for (row = 0, reverseRow = height - 1; row < height; ++row, --reverseRow) {
columnBit = 0;
for (columnByte = 0; columnByte < ceil((width / 8.0)); ++columnByte) {
int rawPos = (row * lineSize) + columnByte;
for (int k = 7; k >= 0 && columnBit < width; --k, ++columnBit) {
img[reverseRow][columnBit] = (rawFile[rawPos] >> k) & 1;
}
}
}
f.close();
return img;
}
#pragma pack(1)
struct BmpHeader {
char magic[2]; // 0-1
uint32_t fileSize; // 2-5
uint32_t reserved; // 6-9
uint32_t offset; // 10-13
uint32_t headerSize; // 14-17
uint32_t width; // 18-21
uint32_t height; // 22-25
uint16_t bitsPerPixel; // 26-27
uint16_t bitDepth; // 28-29
};
#pragma pack()
Potentially relevant information:
I'm using Visual Studio 2017
I'm compiling for C++14
I'm on a Windows 10 OS
Thanks.
Both of those line size formulas are incorrect.
For example, for w = 1, (w / 8 + (w / 8) % 4) results in zero. It's still zero if you multiply by two. It's expected to be 4 for width = 1.
The correct formula for line size (or bytes per line) is
((w * bpp + 31) / 32) * 4 where bpp is bits per pixel, in this case it is 1.
By coincidence the values are sometimes the same, for some smaller width values.
See also MSDN example:
DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
Also, 1-bit image has 2 palette entries, for a total of 8 bytes. It seems you are ignoring the palette and assuming that 0 is black, and 1 is white, always.
The part where you flip the bits is correct, the other code appears to be incorrect.
Lets say we have a single byte 1000 0000 This is mean to be a single row, starting with 7 zeros and ending in 1.
Your code is a bit confusing for me (but seems okay when you fix linesize). I wrote my own version:
void test(string filename)
{
BmpHeader head;
ifstream f(filename, ios::binary);
if(!f.good())
return;
int headsize = sizeof(BmpHeader);
f.read((char*)&head, headsize);
if(head.bitsPerPixel != 1)
{
f.close();
throw "Invalid bitmap loaded";
}
int height = head.height;
int width = head.width;
int bpp = 1;
int linesize = ((width * bpp + 31) / 32) * 4;
int filesize = linesize * height;
vector<unsigned char> data(filesize);
//read color table
uint32_t color0;
uint32_t color1;
uint32_t colortable[2];
f.seekg(54);
f.read((char*)&colortable[0], 4);
f.read((char*)&colortable[1], 4);
printf("colortable: 0x%06X 0x%06X\n", colortable[0], colortable[1]);
f.seekg(head.offset);
f.read((char*)&data[0], filesize);
for(int y = height - 1; y >= 0; y--)
{
for(int x = 0; x < width; x++)
{
int pos = y * linesize + x / 8;
int bit = 1 << (7 - x % 8);
int v = (data[pos] & bit) > 0;
printf("%d", v);
}
printf("\n");
}
f.close();
}
Test image:
(33 x 20 monochrome bitmap)
Output:
colortable: 0x000000 0xFFFFFF
000000000000000000000000000000000
000001111111111111111111111111110
000001111111111111111111111111110
000001111111111111111111111111110
000001111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111111110
011111111111111111111111111110010
011111111111111111111111111110010
011111111111111111111111111111110
000000000000000000000000000000000
Notice this line in above code:
int pos = y * linesize + x / 8;
int bit = 1 << (7 - x % 8);
int v = (data[pos] & bit) > 0;
printf("%d", v);
First I wrote it as
int bit = 1 << (x % 8);
But this shows the bits in the wrong order, so I had to change to 1 << (7 - x % 8) which is basically what you did also. I don't know why it's designed like that. There must be some historical reasons for it!
(above code is for little-endian machines only)
I would like to convert a hardware pixel buffer that is in the format X8B8G8R8 into unsigned int 24 bit memory buffer.
Here is my attempt:
// pixels is uin32_t;
src.pixels = new pixel_t[src.width*src.height];
readbuffer->lock( Ogre::HardwareBuffer::HBL_DISCARD );
const Ogre::PixelBox &pb = readbuffer->getCurrentLock();
/// Update the contents of pb here
/// Image data starts at pb.data and has format pb.format
uint32 *data = static_cast<uint32*>(pb.data);
size_t height = pb.getHeight();
size_t width = pb.getWidth();
size_t pitch = pb.rowPitch; // Skip between rows of image
for ( size_t y = 0; y<height; ++y )
{
for ( size_t x = 0; x<width; ++x )
{
src.pixels[pitch*y + x] = data[pitch*y + x];
}
}
This should do
uint32_t BGRtoRGB(uint32_t col) {
return (col & 0x0000ff00) | ((col & 0x000000ff) << 16) | ((col & 0x00ff0000) >> 16)
}
With
src.pixels[pitch*y + x] = BGRtoRGB(data[pitch*y + x]);
Note: BGRtoRGB here converts both ways if you want it to, but remember it throws away whatever you have in the X8 bits (alpha?), but it should keep the values themselves.
To convert the other way around with an alpha of 0xff
uint32_t RGBtoXBGR(uint32_t col) {
return 0xff000000 | (col & 0x0000ff00) | ((col & 0x000000ff) << 16) | ((col & 0x00ff0000) >> 16)
}
The aim of the following function is to get the R,G,B values of each pixel from a Bitmap loaded from file and increase them by 10.
void PerformTransformation(Gdiplus::Bitmap* bitmap, LPCTSTR SaveFileName) {
Gdiplus::BitmapData* bitmapData = new Gdiplus::BitmapData;
UINT Width = bitmap->GetWidth();
UINT Height = bitmap->GetHeight();
Gdiplus::Rect rect(0, 0,Width,Height );
bitmap->LockBits(&rect, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, bitmapData);
byte* pixels = (byte*)bitmapData->Scan0;
INT iStride = abs(bitmapData->Stride);
for (UINT col = 0; col < Width; ++col)
for (UINT row = 0; row < Height; ++row)
{
unsigned int curColor = pixels[row * iStride / 4 + col];
int b = curColor & 0xff;
int g = (curColor & 0xff00) >> 8;
int r = (curColor & 0xff0000) >> 16;
if ((r + 10) > 255) r = 255; else r += 10;
if ((g + 10) > 255) g = 255; else g += 10;
if ((b + 10) > 255) b = 255; else b += 10;
pixels[curColor & 0xff ] = b;
pixels[curColor & 0xff00 >> 8] = g;
pixels[curColor & 0xff0000 >> 16] = r;
}
bitmap->UnlockBits(bitmapData);
CLSID pngClsid;
GetEncoderClsid(L"image/png", &pngClsid);
bitmap->Save(SaveFileName, &pngClsid, NULL);
}
However when checking the save file, the brightness has not increased. I have tried to increase the values to update each R,G,B value to be 100 each but the image remains the same, Seems like i'm not setting the new values correctly.
Can anyone show me what im doing wrong?
EDIT:
After following some guidance i now have the image brightening but only brightening a quarter of the image.
Changed Code
void PerformTransformation(Gdiplus::Bitmap* bitmap, LPCTSTR SaveFileName) {
Gdiplus::BitmapData* bitmapData = new Gdiplus::BitmapData;
UINT Width = bitmap->GetWidth();
UINT Height = bitmap->GetHeight();
Gdiplus::Rect rect(0, 0,Width,Height );
// Lock a 5x3 rectangular portion of the bitmap for reading.
bitmap->LockBits(&rect, Gdiplus::ImageLockModeWrite,
PixelFormat32bppARGB, bitmapData);
byte* Pixels = (byte*)bitmapData->Scan0;
INT stride_bytes_count = abs(bitmapData->Stride);
UINT row_index, col_index;
byte pixel[4];
for (col_index = 0; col_index < Width; ++col_index) {
for (row_index = 0; row_index < Height; ++row_index)
{
unsigned int curColor = Pixels[row_index * stride_bytes_count /
4 + col_index];
int b = curColor & 0xff;
int g = (curColor & 0xff00) >> 8;
int r = (curColor & 0xff0000) >> 16;
if ((r + 10) > 255) r = 255; else r += 10;
if ((g + 10) > 255) g = 255; else g += 10;
if ((b + 10) > 255) b = 255; else b += 10;
pixel[0] = b;
pixel[1] = g;
pixel[2] = r;
Pixels[row_index * stride_bytes_count / 4 + col_index] = *pixel;
}
}
bitmap->UnlockBits(bitmapData);
::DeleteObject(bitmapData);
CLSID pngClsid;
GetEncoderClsid(L"image/png", &pngClsid);
bitmap->Save(SaveFileName, &pngClsid, NULL);
}
};
You never check return codes.
You access bitmap data in reading mode (Gdiplus::ImageLockModeRead)
You are indexing pixel channel values by color value pixels[curColor & 0xff]
You never delete allocated bitmapData object
I'm trying to create an algorithm in C/C++, which applies a uniform transparent gradient from left to right to a pixel buffer. As seen on the next image:
Next is so far my implementation. But the resulting image is not even close to what I need to achieve. Anyone can spot what I'm doing wrong? Thanks
void alphaGradient(uint32_t* pixelsBuffer, const int width, const int height)
{
const short OPAQUE = 255;
int pixelOffsetY, pixelIndex;
short A, R, G, B;
for (int y = 0; y < height; y++)
{
A = OPAQUE;
pixelOffsetY = y * height;
for (int x = 0; x < width; x++)
{
pixelIndex = pixelOffsetY + x;
A = (int)(OPAQUE - ((OPAQUE * x) / width));
R = (pixelsBuffer[pixelIndex] & 0x00FF0000) >> 16;
G = (pixelsBuffer[pixelIndex] & 0x0000FF00) >> 8;
B = (pixelsBuffer[pixelIndex] & 0x000000FF);
pixelsBuffer[pixelIndex] = (A << 24) + (R << 16) + (G << 8) + B;
}
}
}
I haven't tried this code out but something like this should work :
void alphaGradient(uint32_t* pixelBuffer, const int width, const int height)
{
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
const DWORD src = pixelBuffer[i + j * width];
const DWORD dst = MYBACKGROUNDCOLOR;
const unsigned char src_A = (width - i) * 255 / width;
const unsigned char src_R = (src & 0x00FF0000) >> 16;
const unsigned char src_G = (src & 0x0000FF00) >> 8;
const unsigned char src_B = (src & 0x000000FF);
//const unsigned char dst_Alpha = (src & 0xFF000000) >> 24;
const unsigned char dst_R = (dst & 0x00FF0000) >> 16;
const unsigned char dst_G = (dst & 0x0000FF00) >> 8;
const unsigned char dst_B = (dst & 0x000000FF);
const unsigned char rlt_R = (src_R * src_A + dst_R * (255 - src_A)) / 255;
const unsigned char rlt_G = (src_G * src_A + dst_G * (255 - src_A)) / 255;
const unsigned char rlt_B = (src_B * src_A + dst_B * (255 - src_A)) / 255;
//pixelBuffer[i + j*width] = (DWORD)(((255) << 24) | (((rlt_R)& 0xff) << 16) | (((rlt_G)& 0xff) << 8) | ((rlt_B)& 0xff));
// or if you want to save the transparancy then
//pixelBuffer[i + j*width] = (DWORD)(((src_A) << 24) | (((src_R)& 0xff) << 16) | (((src_G)& 0xff) << 8) | ((src_B)& 0xff));
}
}
}
But personally, I would try to use DirectX or OpenGL for this and write a good PixelShader. It would make this ALOT faster.
As a suggestion, since you only want to modify the alpha channel, you do not need to do anything with the colors. So the following would work too:
char *b((char *) pixelBuffer);
for(int j = 0; j < height; ++j)
{
for(int i = 0; i < width; ++i, b += 4)
{
*b = (width - i) * 255 / width;
}
}
That's it. You could also eliminate the computation for each line by duplicating the data of the first line in the following lines:
// WARNING: code expects height > 0!
char *b((char *) pixelBuffer);
for(int i = 0; i < width; ++i, b += 4)
{
*b = (width - i) * 255 / width;
}
int offset = width * -4;
for(int j = 1; j < height; ++j)
{
for(int i = 0; i < width; ++i, b += 4)
{
*b = b[offset];
}
}
I will leave as an exercise to you to change this double for() loop in a single for() loop, which would make it a little faster yet (because you'd have a single counter (variable b) instead of three).
Note that I do not understand how Mikael's answer would work as he uses the * 255 in the wrong place in his computation of the alpha channel. With integer arithmetic, that's very important. So this should return 0 or 255:
(width - i) / width * 255
because if value < width then value / width == 0. And (width - i) is either width or a value smaller than width...