Wrote a simple BMP generation code but instead of outputting what I want (red that gradually goes to black (from left to right)) it returns an image with some some weird layout far from what I expect to see. Was inspecting the way header and pixels written in the memory and everything seems alright
Output
#include <iostream>
#include <fstream>
size_t width = 1000, height = 1000, CPP = 3;
//this creates an array of pixels
//supposed to be from red to black from left to right
uint8_t* createBitMapI(const size_t& width, const size_t& height)
{
uint8_t _color[3] = { 255, 0, 0 };
uint8_t* bitMap = new uint8_t[width * height * 3];
//creating one row
for (float i = 0; i < width; i++)
{
*(bitMap + (int)(i * 3)) = (uint8_t)_color[0] * (1 - i / width);
*(bitMap + 1 + (int)(i * 3)) = 0;//(uint8_t)_color[1] * (1 - i / width);
*(bitMap + 2 + (int)(i * 3)) = 0;//(uint8_t)_color[2] * (1 - i / width);
}
//copying previously created row to others
for (size_t i = 1; i < height; i++)
{
memcpy(bitMap + (width * 3) * i, bitMap, width * 3);
}
return bitMap;
}
//creates BMP file and writes contents into it
class BMP
{
public:
BMP(const size_t& width, const size_t& height,
uint8_t* arr, const char* name)
{
const char pad_[3] = { 0, 0, 0 };
char padding = (4 - width % 4) % 4;
fullSize = (width + padding) * height * 3 + 54;
image.open(name);
writeHeader();
image.write((const char*)header, 54);
for (size_t i = 0; i < height; i++)
{
image.write((const char*)arr + (i * width), width * 3);
image.write(pad_, padding);
}
image.close();
}
void writeHeader()
{
memcpy(header, "BM", 2);
*(size_t*)(header + 2) = fullSize;
*(size_t*)(header + 10) = 54;
*(size_t*)(header + 14) = 40;
*(size_t*)(header + 18) = width;
*(size_t*)(header + 22) = height;
*(uint16_t*)(header + 26) = 1;
*(uint16_t*)(header + 28) = 24;
}
private:
std::ofstream image;
uint8_t header[54];
uint8_t* pixels;
size_t fullSize;
};
int main()
{
uint8_t* arr = createBitMapI(width, height);
BMP newImage(width, height, arr, "image.bmp");
delete[] arr;
}
upd: changing image.open(name) to image.open(name, std::ios::binary) gives us output2
Related
Reference with this Question & answer by #Decade Moon
How can i use that method for generate image from byte array instead of image file.
i tried like below but nothing works. no image are shown
std::vector<char> data= std::vector<char>(imgx->Height * imgx->Width * 4);
int offset;
for (int row = 0; row < imgx->Height; row++)
{
for (int col = 0; col < imgx->Width; col++)
{
offset = (row * (int)(imgx->Width * 4)) + (col * 4);
data[offset] = 0x58; // Red
data[offset + 1] = 0x58; // Green
data[offset + 2] = 0x58; // Blue
data[offset + 3] = 0x58; // Alpha
}
};
My approach is little bit different from the reply you reffered to, but it works pretty well.
#include <wrl.h>
#include <robuffer.h>
using namespace Windows::UI::Xaml::Media::Imaging;
using namespace Windows::Storage::Streams;
using namespace Microsoft::WRL;
typedef uint8 byte;
byte* GetPointerToPixelData(IBuffer^ pixelBuffer, unsigned int *length)
{
if (length != nullptr)
{
*length = pixelBuffer ->Length;
}
// Query the IBufferByteAccess interface.
ComPtr<IBufferByteAccess> bufferByteAccess;
reinterpret_cast<IInspectable*>(pixelBuffer)->QueryInterface(IID_PPV_ARGS(&bufferByteAccess));
// Retrieve the buffer data.
byte* pixels = nullptr;
bufferByteAccess->Buffer(&pixels);
return pixels;
}
MainPage::MainPage()
{
InitializeComponent();
auto bitmap = ref new WriteableBitmap(50, 50);
image->Source = bitmap;
unsigned int length;
byte* sourcePixels = GetPointerToPixelData(bitmap->PixelBuffer, &length);
const unsigned int width = bitmap->PixelWidth;
const unsigned int height = bitmap->PixelHeight;
create_async([this, width, height, sourcePixels] {
byte* temp = sourcePixels;
// generate RED - BLUE gradient
for(unsigned int k = 0; k < height; k++) {
for (unsigned int i = 0; i < (width * 4); i += 4) {
int pos = k * (width * 4) + (i);
temp[pos] = (byte)(0xFF * k / (float)height); // B
temp[pos + 1] = 0x0; // G
temp[pos + 2] = 0xFF - (byte)(0xFF * k / (float)height); // R
temp[pos + 3] = 0xFF; // A
}
}
});
}
I made a program in C++ which calculates the mandelbrot-set. Now I want to visualize it (save it in a picture). But when I try to save a 64k picture some problems come up. So what is the best way to save a picture of the pixels or at least to visual it?
Edit:
When I want to create a for Example 64K (61440 * 34560) image there will be the error "Access violation while writing at the position 0x0..." (originally on German and translated) and the program stops. This error appears with very high resolution. On lower resolutions the program works as it is supposed to.
#include <SFML\Graphics.hpp>
#include <stdlib.h>
#include <complex>
#include <cmath>
#include <thread>
//4K : 3840 * 2160
//8K : 7680 * 4320
//16K: 15360 * 8640
//32K: 30720 * 17280
//64K: 61440 * 34560
//128K:122880 * 69120
const unsigned long width = 61440; //should be dividable by ratioX & numberOfThreads!
const unsigned long height = 34560; //should be dividable by ratioY & numberOfThreads!
const unsigned int maxIterations = 500;
const unsigned int numberOfThreads = 6;
const int maxWidth = width / 3;
const int maxHeight = height / 2;
const int minWidth = -maxWidth * 2;
const int minHeight = -maxHeight;
const double ratioX = 3.0 / width;
const double ratioY = 2.0 / height;
sf::Image img = sf::Image();
int getsGreaterThan2(std::complex<double> z, int noIterations) {
double result;
std::complex<double> zTmp = z;
std::complex<double> c = z;
for (int i = 1; i != noIterations; i++) {
zTmp = std::pow(z, 2) + c;
if (zTmp == z) {
return 0;
}
z = std::pow(z, 2) + c;
result = std::sqrt(std::pow(z.real(), 2) + std::pow(z.imag(), 2));
if (result > 2) {
return i;
}
}
return 0;
}
void fillPixelArrayThreadFunc(int noThreads, int threadNr) { //threadNr ... starts from 0
double imgNumber;
double realNumber;
double tmp;
long startWidth = ((double)width) / noThreads * threadNr + minWidth;
long endWidth = startWidth + width / noThreads;
for (long x = startWidth; x < endWidth; x++) {
imgNumber = x * ratioX;
for (long y = minHeight; y < maxHeight; y++) {
realNumber = y * ratioY;
long xArray = x - minWidth;
long yArray = y - minHeight;
tmp = getsGreaterThan2(std::complex<double>(imgNumber, realNumber), maxIterations);
if (tmp == 0) {
img.setPixel(xArray, yArray, sf::Color(0, 0, 0, 255));
}
else {
img.setPixel(xArray, yArray, sf::Color(tmp / maxIterations * 128, tmp / maxIterations * 128, tmp / maxIterations * 255, 255));
}
}
}
}
int main() {
img.create(width, height, sf::Color::Black);
std::thread *threads = new std::thread[numberOfThreads];
for (int i = 0; i < numberOfThreads; i++) {
threads[i] = std::thread(std::bind(fillPixelArrayThreadFunc, numberOfThreads, i));
}
for (int i = 0; i < numberOfThreads; i++) {
threads[i].join();
}
img.saveToFile("filename.png");
return 1;
}
Your program fails during the call img.create(width, height, sf::Color::Black);.
When you step into the sf::Image::create function you end up here where the newPixels vector is created, this simply fails when width * height is too big as in your case:
////////////////////////////////////////////////////////////
void Image::create(unsigned int width, unsigned int height, const Color& color)
{
if (width && height)
{
// Create a new pixel buffer first for exception safety's sake
std::vector<Uint8> newPixels(width * height * 4);
^61440* ^34560 = 8'493'465'600 bytes !!
Conclusion: SFML cannot handle huge images.
Ok guys, it's the third time I'm posting the same question (previous are here and here).
Now at this time I will try to explain what's my problem:
So first them all, I need to rotate a .bmp image and it's not rotate correctly. But I don't need to rotate a random image with extension .bmp, I need to rotate this one. I've tried with many other images and all of them was rotated correctly, except mine.
In this moment my code it works just for 180-degree, how could make it to works on any degree which is multiple of 90-degree (I need to rotate my image just with 90, 180 or 270 degrees, not more).
I don't need any kind of external library for this code like CImage, OpenCV, ImageMagik and so on... I need to make this code to work.
So yeh, that's it. And here you can find my actual result.
CODE:
#include <array>
using namespace std;
struct BMP {
int width;
int height;
unsigned char header[54];
unsigned char *pixels;
int row_padded;
int size_padded;
};
void writeBMP(string filename, BMP image) {
string fileName = "Output Files\\" + filename;
FILE *out = fopen(fileName.c_str(), "wb");
fwrite(image.header, sizeof(unsigned char), 54, out);
unsigned char tmp;
for (int i = 0; i < image.height; i++) {
for (int j = 0; j < image.width * 3; j += 3) {
//Convert(B, G, R) to(R, G, B)
tmp = image.pixels[j];
image.pixels[j] = image.pixels[j + 2];
image.pixels[j + 2] = tmp;
}
}
fwrite(image.pixels, sizeof(unsigned char), image.size_padded, out);
fclose(out);
}
BMP readBMP(string filename) {
BMP image;
string fileName = "Input Files\\" + filename;
FILE *in = fopen(fileName.c_str(), "rb");
fread(image.header, sizeof(unsigned char), 54, in); // read the 54-byte header
// extract image height and width from header
image.width = *(int *) &image.header[18];
image.height = *(int *) &image.header[22];
image.row_padded = (image.width * 3 + 3) & (~3); // ok size of a single row rounded up to multiple of 4
image.size_padded = image.row_padded * image.height; // padded full size
image.pixels = new unsigned char[image.size_padded]; // yeah !
if (fread(image.pixels, sizeof(unsigned char), image.size_padded, in) == image.size_padded) {
unsigned char tmp;
for (int i = 0; i < image.height; i++) {
for (int j = 0; j < image.width * 3; j += 3) {
//Convert (B, G, R) to (R, G, B)
tmp = image.pixels[j];
image.pixels[j] = image.pixels[j + 2];
image.pixels[j + 2] = tmp;
}
}
}
fclose(in);
return image;
}
BMP rotate(BMP image, double degree) {
BMP newImage = image;
unsigned char *pixels = new unsigned char[image.size_padded];
int height = image.height;
int width = image.width;
for (int x = 0; x < height; x++) {
for (int y = 0; y < width; y++) {
pixels[(x * width + y) * 3 + 0] = image.pixels[((height - 1 - x) * width + (width - 1 - y)) * 3 + 0];
pixels[(x * width + y) * 3 + 1] = image.pixels[((height - 1 - x) * width + (width - 1 - y)) * 3 + 1];
pixels[(x * width + y) * 3 + 2] = image.pixels[((height - 1 - x) * width + (width - 1 - y)) * 3 + 2];
}
}
newImage.pixels = pixels;
return newImage;
}
int main() {
BMP image = readBMP("Input-1.bmp");
image = rotate(image, 180);
writeBMP("Output.bmp", image);
return 0;
}
You have major memory leak. pixels = new unsigned char[size]; must be freed otherwise there is potentially several megabytes leak with every rotation. You have to rewrite the function to keep track of memory allocations.
When you rotate the image by 90 or 270 of the image, the widht/height of image changes. The size may change too because of padding. The new dimension has to be recorded in header file.
In C++ you can use fopen, but std::fstream is preferred.
Here is an example which works in Windows for 24bit images only. In Big-endian systems you can't use memcpy the way I used it below.
Note, this is for practice only. As #datenwolf explained you should use a library for real applications. Most standard libraries such Windows GDI library (basic drawing functions) offer solution for these common tasks.
#include <iostream>
#include <fstream>
#include <string>
#include <Windows.h>
bool rotate(char *src, char *dst, BITMAPINFOHEADER &bi, int angle)
{
//In 24bit image, the length of each row must be multiple of 4
int padw = 4 - ((bi.biWidth * 3) % 4);
if(padw == 4) padw = 0;
int padh = 4 - ((bi.biHeight * 3) % 4);
if(padh == 4) padh = 0;
int pad2 = 0;
if(padh == 1 || padh == 3) pad2 = 2;
bi.biHeight += padh;
int w = bi.biWidth;
int h = bi.biHeight;
if(angle == 90 || angle == 270)
{
std::swap(bi.biWidth, bi.biHeight);
}
else
{
bi.biHeight -= padh;
}
for(int row = 0; row < h; row++)
{
for(int col = 0; col < w; col++)
{
int n1 = 3 * (col + w * row) + padw * row;
int n2 = 0;
switch(angle)
{
case 0: n2 = 3 * (col + w * row) + padw * row; break;
case 90: n2 = 3 * ((h - row - 1) + h * col) + pad2 * col; break;
case 180: n2 = 3 * (col + w * (h - row - 1)) + padw * (h - row - 1); break;
case 270: n2 = 3 * (row + h * col) + pad2 * col; break;
}
dst[n2 + 0] = src[n1 + 0];
dst[n2 + 1] = src[n1 + 1];
dst[n2 + 2] = src[n1 + 2];
}
}
for(int row = 0; row < bi.biHeight; row++)
for(int col = 0; col < padw; col++)
dst[bi.biWidth * 3 + col] = 0;
bi.biSizeImage = (bi.biWidth + padw) * bi.biHeight * 3;
return true;
}
int main()
{
std::string input = "input.bmp";
std::string output = "output.bmp";
BITMAPFILEHEADER bf = { 0 };
BITMAPINFOHEADER bi = { sizeof(BITMAPINFOHEADER) };
std::ifstream fin(input, std::ios::binary);
if(!fin) return 0;
fin.read((char*)&bf, sizeof(bf));
fin.read((char*)&bi, sizeof(bi));
int size = 3 * (bi.biWidth + 3) * (bi.biHeight + 3);
char *src = new char[size];
char *dst = new char[size];
fin.read(src, bi.biSizeImage);
//use 0, 90, 180, or 270 for the angle
if(rotate(src, dst, bi, 270))
{
bf.bfSize = 54 + bi.biSizeImage;
std::ofstream fout(output, std::ios::binary);
fout.write((char*)&bf, 14);
fout.write((char*)&bi, 40);
fout.write((char*)dst, bi.biSizeImage);
}
delete[]src;
delete[]dst;
return 0;
}
The BMP file format is a complicated, convoluted beast and there's no such thing as a "simple" BMP file reader. The code you have there makes certain hard coded assumptions on the files you're trying to read (24bpp true color, tightly packed, no compression) that it will flat (on its face) when it encounters anything that isn't that specific format. Unfortunately, for you, the majority of BMP files out there is not of that kind. To give you an idea of what a fully conforming BMP reader must support have a look at this page:
http://entropymine.com/jason/bmpsuite/bmpsuite/html/bmpsuite.html
And the code you have up there does not even check if there's a valid file magic bytes signature and if the header is valid. So that's your problem right there: You don't have a BMP file reader. You have something that actually spits out pixels if you're lucky enough the feed it something that by chance happens to be in the right format.
I want to find a small bmp file from another bigger bmp file (the bigger one is captured from screen and called Sample.bmp , the small bmp file is called Button.bmp . Thing is the when comparing the images the file can't be found anywhere.
the compare code :
for (int i=0;i<SCREEN_WIDTH-width;++i)
{
for (int j=0;j<SCREEN_HEIGHT-height;++j)
{
boolean isequal = true;
for(int qqq=i;qqq<i+width;++qqq)
{
for (int kkk=j;kkk<j+height;++kkk)
{
if (PI[qqq][kkk]!=NPI[qqq-i][kkk-j]) isequal = false;
if (isequal == false)
{
qqq = i + width + 1;
kkk = j + height + 1;
}
}
}
if (isequal==true)
{
MidX = i;
MidY = j;
return;
}
}
}
note : Screen_width and Screen_height are for the bigger image and width and height are for the smaller one
Full Code:
void readBMP()
{
int i;
FILE* f = fopen("Sample.bmp", "rb");
unsigned char info[54];
fread(info, sizeof(unsigned char), 54, f); // read the 54-byte header
// extract image height and width from header
int width = *(int*)&info[18];
int height = *(int*)&info[22];
int size = 3 * width * height;
unsigned char* data = new unsigned char[size]; // allocate 3 bytes per pixel
fread(data, sizeof(unsigned char), size, f); // read the rest of the data at once
fclose(f);
for(int qq=0;qq<SCREEN_WIDTH;++qq)
for (int kk=0;kk<SCREEN_HEIGHT;++kk)
{
PI[qq][kk][0] = data[kk * width + qq];
PI[qq][kk][1] = data[kk * width + qq + 1];
PI[qq][kk][2] = data[kk * width + qq + 2];
}
}
void FindImageInScreen(char* FileName)
{
FILE* f = fopen(FileName, "rb");
unsigned char info[54];
fread(info, sizeof(unsigned char), 54, f); // read the 54-byte header
// extract image height and width from header
int width = *(int*)&info[18];
int height = *(int*)&info[22];
int size = 3 * width * height;
unsigned char* data = new unsigned char[size]; // allocate 3 bytes per pixel
fread(data, sizeof(unsigned char), size, f); // read the rest of the data at once
fclose(f);
for(int qq=0;qq<width;++qq)
for (int kk=0;kk<height;++kk)
{
NPI[qq][kk][0] = data[kk * width + qq];
NPI[qq][kk][1] = data[kk * width + qq + 1];
NPI[qq][kk][2] = data[kk * width + qq + 2];
}
for (int i=0;i<SCREEN_WIDTH-width;++i)
{
for (int j=0;j<SCREEN_HEIGHT-height;++j)
{
boolean isequal = true;
for(int qqq=i;qqq<i+width;++qqq)
{
for (int kkk=j;kkk<j+height;++kkk)
{
if (PI[qqq][kkk][0]!=NPI[qqq-i][kkk-j][0]) isequal = false;
if (isequal == false)
{
qqq = i + width + 1;
kkk = j + height + 1;
}
}
}
if (isequal==true)
{
MidX = i;
MidY = j;
return;
}
}
}
MidX = -1;
MidY = -1;
return;
}
definition of arrays (added because of request) , This is before functions execute :
PI = new unsigned int**[SCREEN_WIDTH];
for (int i=0;i<SCREEN_WIDTH;++i)
PI[i] = new unsigned int*[SCREEN_HEIGHT];
for (int i=0;i<SCREEN_WIDTH;++i)
for (int j=0;j<SCREEN_HEIGHT;++j)
PI[i][j] = new unsigned int[3];
NPI = new unsigned int**[SCREEN_WIDTH];
for (int i=0;i<SCREEN_WIDTH;++i)
NPI[i] = new unsigned int*[SCREEN_HEIGHT];
for (int i=0;i<SCREEN_WIDTH;++i)
for (int j=0;j<SCREEN_HEIGHT;++j)
NPI[i][j] = new unsigned int[3];
The First function executes then the second. and sorry for some bad programming because I did thousands of changes to make it work!
PI[qq][kk][0] = data[kk * width + qq];
From how PI and NPI are filled in, it appears that they are 3-dimensional arrays (it would help if you included their definition in the code sample). But
if (PI[qqq][kkk]!=NPI[qqq-i][kkk-j]) isequal = false;
which is only indexing 2 dimensions of each. PI[a][b] is the address of the array containing PI[a][b][0..2] and will certainly never match the address of NPI[x][y], so this statement is always returning false I expect.
Lets get you started. Here is a better LoadBMP.
Yours, among other thing, read the size, and uses SCREEN_HEIGHT anyway.
Using this for loading both images is probably easier.
#include <vector>
#include <cstdio>
#include <string>
using namespace std;
typedef unsigned char UC;
struct RGB { UC r,g,b; };
bool operator == ( const RGB& p1, const RGB& p2 ) { return p1.r==p2.r && p1.g==p2.g && p1.b==p2.b; }
struct BMP
{
int width;
int height;
vector<RGB> pixels;
RGB& Pix(int x,int y) { return pixels[ y*width + x ]; }
};
void LoadBMP( BMP& bmp, const char* filename )
{
FILE* f = fopen(filename, "rb");
UC info[54];
fread(info, 1, 54, f); // read the 54-byte header
// extract image height and width from header
bmp.width = *(int*) (info+18);
bmp.height = *(int*) (info+22);
// scanlines are always multiple of 4, padded with 0-3 bytes
int scanlinesize = 3*bmp.width;
while( scanlinesize % 4 ) ++scanlinesize;
int size = scanlinesize * bmp.height;
UC* data = new UC[size];
fread(data, 1, size, f);
fclose(f);
bmp.pixels.clear();
bmp.pixels.reserve(bmp.height*bmp.width);
for(int yy=0;yy<bmp.height;++yy)
{
UC* p = data+scanlinesize*yy;
for (int xx=0;xx<bmp.width;++xx)
{
RGB rgb;
rgb.b = *p++;
rgb.g = *p++;
rgb.r = *p++;
bmp.pixels.push_back(rgb);
}
}
delete[] data;
}
This may be a long post but I really need to know how to Convert between 24 and 32 bit bitmaps. For the sake of the length of this post, I removed the PNG part of my question.
Here goes:
I have a struct like the one below that holds all pixel information:
typedef union RGB
{
uint32_t Color;
struct
{
unsigned char B, G, R, A;
} RGBA;
} *PRGB;
std::vector<RGB> Pixels; //Holds all pixels.
All of the bitmap writing works except when going from 24 to 32 or vice-versa. I don't know what I'm doing wrong or why 24-32 conversions don't work. My bitmap reading and writing code is as follows:
Bitmap(const void* Pointer, int Width, int Height, uint32_t BitsPerPixel) //Constructor initialization here...
{
Pixels.clear();
if (Pointer == nullptr) {throw std::logic_error("Null Pointer Exception. Pointer is NULL.");}
if (Width < 1 || Height < 1) {throw std::invalid_argument("Invalid Arguments. Width and Height cannot equal 0.");}
std::memset(&Info, 0, sizeof(BITMAPINFO));
size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
Info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
Info.bmiHeader.biWidth = width;
Info.bmiHeader.biHeight = height;
Info.bmiHeader.biPlanes = 1;
Info.bmiHeader.biBitCount = BitsPerPixel;
Info.bmiHeader.biCompression = BI_RGB;
Info.bmiHeader.biSizeImage = size;
bFileHeader.bfType = 0x4D42;
bFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(Info.bmiHeader);
bFileHeader.bfSize = bFileHeader.bfOffBits + size;
const unsigned char* BuffPos = static_cast<const unsigned char*>(Pointer);
height = (height < 0 ? -height : height);
Pixels.resize(width * height);
for (int I = 0; I < height; I++)
{
for (int 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 = (Info.bmiHeader.biBitCount > 24 ? *(BuffPos++) : 0);
}
if(Info.bmiHeader.biBitCount == 24)
BuffPos += width % 4;
}
}
bool SaveBitmap(const char* FilePath)
{
std::vector<unsigned char> ImageData(size);
unsigned char* BuffPos = ImageData.data();
for (int I = 0; I < height; ++I)
{
for (int J = 0; J < width; ++J)
{
*(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 (Info.bmiHeader.biBitCount > 24)
*(BuffPos++) = Pixels[(height - 1 - I) * width + J].RGBA.A;
}
if(Info.bmiHeader.biBitCount == 24)
BuffPos += width % 4;
}
std::fstream hFile(FilePath, std::fstream::out | std::ofstream::binary);
if (!hFile.is_open()) return false;
hFile.write(reinterpret_cast<char*>(&bFileHeader), sizeof(BITMAPFILEHEADER));
hFile.write(reinterpret_cast<char*>(&Info.bmiHeader), sizeof (BITMAPINFOHEADER));
hFile.write(reinterpret_cast<char*>(&ImageData[0]), Size());
hFile.close();
return true;
}
Any idea what the two problems could be? I want it so that if I called Bitmap(24BmpBuff, W, H, 32); It'll save as 32. If I do Bitmap(32BmpBuff, W, H, 24) it'll save as 24 bit. I just can't see it so I'm hoping one of you will.
I also tried making helper functions:
Convert From 24 bit to 32 bit.
void T24To32(std::vector<RGB> &Input, std::vector<RGB> &Output, int Width, int Height)
{
Output.resize(Input.size());
for (int I = 0; I < Height; ++I)
{
for (int J = 0; J < Width; ++J)
{
Output[J].RGBA.B = Input[J].RGBA.B;
Output[J].RGBA.G = Input[J].RGBA.G;
Output[J].RGBA.R = Input[J].RGBA.R;
Output[J].RGBA.A = 0;
}
}
}
Take the unsigned char* of pixels and store them upside down within the struct.
void Pack(int width, int height, int BPP, unsigned char* Input, std::vector<RGB> &Pixels)
{
unsigned char* BuffPos = Input;
height = (height < 0 ? -height : height);
Pixels.resize(width * height);
for (int I = 0; I < height; I++)
{
for (int 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 = (BPP > 24 ? *(BuffPos++) : 0);
}
if(BPP == 24)
BuffPos += width % 4;
}
}
Take the struct of pixels and store them upright in the unsigned char*.
void Unpack(int width, int height, int BPP, std::vector<RGB> Pixels, unsigned char* &Output)
{
unsigned char* BuffPos = Output;
for (int I = 0; I < height; ++I)
{
for (int J = 0; J < width; ++J)
{
*(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 (BPP > 24)
*(BuffPos++) = Pixels[(height - 1 - I) * width + J].RGBA.A;
}
if(BPP == 24)
BuffPos += width % 4;
}
}
I use all of the above like so.. Input image(32 bit):
Code:
void Bitmap32ToBitmap24(int Width, int Height)
{
Bitmap Image("C:/Images/Bitmap32.bmp");
std::vector<unsigned char> Pixels(((Width * 32 + 31) / 32) * 4 * Height); //Array large enough to hold 32 bit bmp.
unsigned char* BuffPos = Pixels.data();
Unpack(Width, Height, 32, Image.Get(), BuffPos); //Fill the array of unsigned char with image pixels being upright
Bitmap BMP(Pixels.data(), Width, Height, 24); //Convert image to 24 bit bmp and save it.
BMP.Save("C:/Images/Output/Bitmap32ToBitmap24.png");
}
Output image (24 bit):
24 to 32 results in:
In all your code snippets
if(Info.bmiHeader.biBitCount == 24)
BuffPos += width % 4;
or
if(BPP == 24)
BuffPos += width % 4;
occur. I assume this should add the padding value to each line. But it isn't the padding, it is the number of pixels per line %4.
The correct adding value is (4 - ((width * 3) % 4)) % 4. The width*3 is the number of bytes in that line. The %4 calculates the number of bytes which are to many for a 4 byte padding, but to fill up to the next higher limes we need 4-this value. This again is 4 if no padding offset is needed -> %4 to avoid that.
A faster way to compute the same value is (-width * 3) & 3. See wiki.