C++ bitmap editing - c++

I am trying to open a bitmap file, edit it, and then save the edited version as a new file. This is eventually to mess with using steganography. I am trying to save the bitmap information now but the saved file will not open. No errors in compilation or run time. It opens fine and the rest of the functions work.
void cBitmap::SaveBitmap(char * filename)
{
// attempt to open the file specified
ofstream fout;
// attempt to open the file using binary access
fout.open(filename, ios::binary);
unsigned int number_of_bytes(m_info.biWidth * m_info.biHeight * 4);
BYTE red(0), green(0), blue(0);
if (fout.is_open())
{
// same as before, only outputting now
fout.write((char *)(&m_header), sizeof(BITMAPFILEHEADER));
fout.write((char *)(&m_info), sizeof(BITMAPINFOHEADER));
// read off the color data in the bass ackwards MS way
for (unsigned int index(0); index < number_of_bytes; index += 4)
{
red = m_rgba_data[index];
green = m_rgba_data[index + 1];
blue = m_rgba_data[index + 2];
fout.write((const char *)(&blue), sizeof(blue));
fout.write((const char *)(&green), sizeof(green));
fout.write((const char *)(&red), sizeof(red));
}
}
else
{
// post file not found message
cout <<filename << " not found";
}
// close the file
fout.close();
}

You're missing the padding bytes after each RGB row. The rows have to be a multiple of 4 bytes each.
Also, are you supposed to be writing a 24 or 32-bit bmp file? If you're writing 24-bit, you're just missing padding. If you're writing 32-bit, then you're missing each extra byte (alpha). Not enough information to fix your code sample short of writing a complete bmp writer that would support all possible options.

Related

Loading Wave File but there is random nonsense at the end of the data rather than the expected samples

I've got a simple wav header reader i found online a long time ago, i've gotten back round to using it but it seems to replace around 1200 samples towards the end of the data chunk with a single random repeated number, eg -126800. At the end of the sample is expected silence so the number should be zero.
Here is the simple program:
void main() {
WAV_HEADER* wav = loadWav(".\\audio\\test.wav");
double sample_count = wav->SubChunk2Size * 8 / wav->BitsPerSample;
printf("Sample count: %i\n", (int)sample_count);
vector<int16_t> samples = vector<int16_t>();
for (int i = 0; i < wav->SubChunk2Size; i++)
{
int val = ((wav->data[i] & 0xff) << 8) | (wav->data[i + 1] & 0xff);
samples.push_back(val);
}
printf("done\n");
}
And here is the Wav reader:
typedef struct
{
//riff
uint32_t Chunk_ID;
uint32_t ChunkSize;
uint32_t Format;
//fmt
uint32_t SubChunk1ID;
uint32_t SubChunk1Size;
uint16_t AudioFormat;
uint16_t NumberOfChanels;
uint32_t SampleRate;
uint32_t ByteRate;
uint16_t BlockAlignment;
uint16_t BitsPerSample;
//data
uint32_t SubChunk2ID;
uint32_t SubChunk2Size;
//Everything else is data. We note it's offset
char data[];
} WAV_HEADER;
#pragma pack()
inline WAV_HEADER* loadWav(const char* filePath)
{
long size;
WAV_HEADER* header;
void* buffer;
FILE* file;
fopen_s(&file,filePath, "r");
assert(file);
fseek(file, 0, SEEK_END);
size = ftell(file);
rewind(file);
std::cout << "Size of file: " << size << std::endl;
buffer = malloc(sizeof(char) * size);
fread(buffer, 1, size, file);
header = (WAV_HEADER*)buffer;
//Assert that data is in correct memory location
assert((header->data - (char*)header) == sizeof(WAV_HEADER));
//Extra assert to make sure that the size of our header is actually 44 bytes
assert((header->data - (char*)header) == 44);
fclose(file);
return header;
}
Im not sure what the problem is, i've confirmed that there is no meta data, nor is there a mis match between the numbers read from the header of the file and the actual file. Im assuming its a size/offset misallignment on my side, but i cannot see it.
Any help welcomed.
Sulkyoptimism
WAV is just a container for different audio sample formats.
You're making assumptions on a wav file that would have been OK on Windows 3.11 :) These don't hold in 2021.
Instead of rolling your own Wav file reader, simply use one of the available libraries. I personally have good experiences using libsndfile, which has been around roughly forever, is very slim, can deal with all prevalent WAV file formats, and with a lot of other file formats as well, unless you disable that.
This looks like a windows program (one notices by the fact you're using very WIN32API style capital struct names – that's a bit oldschool); so, you can download libsndfile's installer from the github releases and directly use it in your visual studio (another blind guess).
Apple (macOS and iOS) software often does not create WAVE/RIFF files with just a canonical Microsoft 44-byte header at the beginning. Those Wave files can instead can use a longer header followed by a padding block.
So you need to use the full WAVE RIFF format parsing specification instead of just reading from a fixed size 44 byte struct.

C++ binary files not read correctly

I am reading a file that is written in high endian on a little endian intel processor in c++. The file is a generic file written in binary. I have tried reading it using open() and fopen() both but they both seem to get the same thing wrong. The file is a binary file for training images from the MNIST dataset. It contains 4 headers, each 32 bits in size and stored in high endian. My code is working, it is just not giving the right value for the 2nd header. It works for the rest of the headers. I even opened the file in a hex editor to see if the value might be wrong but it is right. The program, for some weird reason, reads only the value of the second header wrong:
Here is the code that deals with reading the headers only:
void DataHandler::readInputData(std::string path){
uint32_t headers[4];
char bytes[4];
std::ifstream file;
//I tried both open() and fopen() as seen below
file.open(path.c_str(), std::ios::binary | std::ios::in);
//FILE* f = fopen(path.c_str(), "rb");
if (file)
{
int i = 0;
while (i < 4)//4 headers
{
//if (fread(bytes, sizeof(bytes), 1, f))
//{
// headers[i] = format(bytes);
// ++i;
//}
file.read(bytes, sizeof(bytes));
headers[i++] = format(bytes);
}
printf("Done getting images file header.\n");
printf("magic: 0x%08x\n", headers[0]);
printf("nImages: 0x%08x\n", headers[1]);//THIS IS THE ONE THAT IS GETTING READ WRONG
printf("rows: 0x%08x\n", headers[2]);
printf("cols: 0x%08x\n", headers[3]);
exit(1);
//reading rest of the file code here
}
else
{
printf("Invalid Input File Path\n");
exit(1);
}
}
//converts high endian to little indian (required for Intel Processors)
uint32_t DataHandler::format(const char * bytes) const
{
return (uint32_t)((bytes[0] << 24) |
(bytes[1] << 16) |
(bytes[2] << 8) |
(bytes[3]));
}
Output I am getting is:
Done getting images file header.
magic: 0x00000803
nImages: 0xffffea60
rows: 0x0000001c
cols: 0x0000001c
nImages should be 60,000 or (0000ea60)h in hex but it is reading it as ffff... for some reason.
Here is the file opened in a hex editor:
As we can see, the 2nd 32 bit number is 0000ea60 but it is reading it wrong...
It seems that char is signed in your environment and therefore 0xEA in the data is sign-extended to 0xFFFFFFEA.
This will break the higher digits.
To prevent this, you should use unsigned char instead of char. (for both of element type of bytes and the argument of format())

Using visual studio C++ trying to copy the data part of a WAV file using ifstream::read(). The stream jumps around weirdly

This is the first code I used:
ifstream wavFile;
wavFile.open("saxaphone.wav");
char buffer[10000];
wavFile.seekg(44); //data starts at the 44 byte of a wav file
wavFile.read(buffer, 10000);
//file has more than 13000 data bytes so there should
//be plenty to fill the buffer
When I played this buffer with the windows waveOut API it only played coherent sound for about 5000 of the buffer values.
This is the code I used to try and debug:
ifstream wavFile;
wavFile.open("saxaphone.wav");
char buffer[10000];
wavFile.seekg(44); //data starts at the 44 byte of a wav file
for(unsigned int i = 0; i < 10000; i++){
wavFile.read(&buffer[i], 1);
cout << wavFile.tellg() << '\n';
}
This output values from 45 to somewhere in the 4000s before it jumped up to somewhere in the 8000s then started counting by 1 again. I tried different files to see if that was the problem and it does the same thing. How do I fix this?

Reading width and height of PNG header

I am experimenting with reading the width and height of a PNG file.
This is my code:
struct TImageSize {
int width;
int height;
};
bool getPngSize(const char *fileName, TImageSize &is) {
std::ifstream file(fileName, std::ios_base::binary | std::ios_base::in);
if (!file.is_open() || !file) {
file.close();
return false;
}
// Skip PNG file signature
file.seekg(9, std::ios_base::cur);
// First chunk: IHDR image header
// Skip Chunk Length
file.seekg(4, std::ios_base::cur);
// Skip Chunk Type
file.seekg(4, std::ios_base::cur);
__int32 width, height;
file.read((char*)&width, 4);
file.read((char*)&height, 4);
std::cout << file.tellg();
is.width = width;
is.height = height;
file.close();
return true;
}
If I try to read for example from this image from Wikipedia, I'm getting these wrong values:
252097920 (should be 800)
139985408 (should be 600)
Note that the function is not returning false so the contents of the width and height variables must come from the file.
It looks like you're off by a byte:
// Skip PNG file signature
file.seekg(9, std::ios_base::cur);
The PNG Specification says the header is 8 bytes long, so you want that "9" to be an "8" instead. Positions start at 0.
Also note that the spec says that integers are in network (big-endian) order, so you may want or need to use ntohl() or otherwise convert byte order if you're on a little-endian system.
It's probably worth using libpng or stb_image or something similar rather than attempting to parse the png yourself, though -- unless you're doing this to learn.
When you look at Portable Network Graphics Technical details, it says the signature is 8 bytes not 9.
Plus, are you sure your system has the same byte order as the PNG standard? ntohl(3) will ensure the correct byte order. It's available for windows also.

Can you help with creating a zip archive with the LZMA (7zip) SDK?

I am trying to use the LZMA SDK to create a zip archive (either .zip or .7z format). I've downloaded and built the SDK and I just want to use the dll exports to compress or decompress a few files. When I use the LzamCompress method, it returns 0 (SZ_OK) as if it worked correctly. However, after I write the buffer to file and try to open it, I get an error that the file cannot be opened as an archive.
Here is the code I am currently using. Any suggestions would be appreciated.
#include "lzmalib.h"
typedef unsigned char byte;
using namespace std;
int main()
{
int length = 0;
char *inBuffer;
byte *outBuffer = 0;
size_t outSize;
size_t outPropsSize = 5;
byte * outProps = new byte[outPropsSize];
fstream in;
fstream out;
in.open("c:\\temp\\test.exe", ios::in | ios::binary);
in.seekg(0, ios::end);
length = in.tellg();
in.seekg(0, ios::beg);
inBuffer = new char[length];
outSize = (size_t) length / 20 * 21 + ( 1 << 16 ); //allocate 105% of file size for destination buffer
if(outSize != 0)
{
outBuffer = (byte*)malloc((size_t)outSize);
if(outBuffer == 0)
{
cout << "can't allocate output buffer" << endl;
exit(1);
}
}
in.read(inBuffer, length);
in.close();
int ret = LzmaCompress(
outBuffer, /* output buffer */
&outSize, /* output buffer size */
reinterpret_cast<byte*>(inBuffer),/* input buffer */
length, /* input buffer size */
outProps, /* archive properties out buffer */
&outPropsSize,/* archive properties out buffer size */
5, /* compression level, 5 is default */
1<<24,/* dictionary size, 16MB is default */
-1, -1, -1, -1, -1/* -1 means use default options for remaining arguments */
);
if(ret != SZ_OK)
{
cout << "There was an error creating the archive." << endl;
exit(1);
}
out.open("test.zip", ios::out | ios::binary);
out.write(reinterpret_cast<char*>(outBuffer), (int)(outSize));
out.close();
delete inBuffer;
delete outBuffer;
}
I do not know about LZMA specifically, but from what I know of compression in general, it looks like you are writing a compressed bit stream to a file without any header information that would let a decompression program know how the bit stream is compressed.
The LzmaCompress() function probably writes this information to outProps. There should be another function in the SDK that will take the compressed bit stream in outBuffer and the properties in outProps and create a proper archive from them.