我正在开展一个学校项目,我们需要读取BMP文件,然后更改像素以显示隐藏的信息。
我首先在标题中读取并将其存储到数组中。我知道我只需要在RGB中显示红色以显示消息。此外,红色,蓝色和绿色各占一个字节的空间。
我的老师给了我们代码,以便从标题中获取高度和宽度。我认为我非常接近于弄清楚它,但是我一直遇到错误抛出bad_alloc
异常。我认为这意味着我在将值或其指针存储在内存中的方式上做错了。
在调试时,错误是从我的exe文件而不是从我的main.cpp
引发的。我试过搞乱不同的东西,但我不知道如何处理这个问题。我无法运行我的代码,看看我是否正在交换像素并正确输出。
哦,最后一件事,由于某种原因,RGB是蓝色,绿色,红色的顺序,但这就是我们的教授告诉我们的。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdint>
#include <cstdio>
#include <vector>
int main() {
FILE *fp = std::fopen("output.bmp", "wb+");
if (fp == nullptr) {
//Error
std::cerr << "Failed to open the output file\n";
return 1;
}
FILE *cp = std::fopen("clue.bmp", "r");
if (cp == nullptr) {
//Error
std::cout << "Failed to open the clue file\n";
return 1;
}
uint8_t* header = NULL;
header = new uint8_t[54];
uint8_t* buf;
buf = new uint8_t[54];
if (std::fread(&buf, sizeof(header), 1, cp) != 1) {
std::cout << "Failed to fread\n";
return EXIT_FAILURE;
}
buf == header;
if (std::fwrite(&header[0], sizeof(header), 1, fp) != 1) {
std::cerr << "Failed fwrite for output\n";
return EXIT_FAILURE;
}
int32_t width = *(static_cast<int *>(static_cast<void *>(&header[18])));
int32_t height = *(static_cast<int *>(static_cast<void *>(&header[22])));
uint64_t pixelCount;
pixelCount = width * height;
std::cout << "Number of pixels: " << pixelCount << "\n";
uint8_t *pixels;
pixels = new uint8_t[pixelCount];
uint16_t byte;
if (std::fseek(cp, sizeof(header), SEEK_SET) != 0) { //adjust pointer to avoid the header
std::cerr << "Failed fseek\n";
return EXIT_FAILURE;
}
if (std::fread(&pixels, pixelCount, 1, cp) != 1) {
std::cout << "Failed to fread\n";
return EXIT_FAILURE;
}
int i = 0;
while (true) {
for (i; i <= sizeof(byte)-1; i++) {
pixels[i] = 0;
}
}
if (std::fwrite(&pixels, pixelCount, 1, fp) != 1) {
std::cerr << "Failed to fwrite pixels\n";
return EXIT_FAILURE;
}
return 0;
}
答案 0 :(得分:0)
你是在滥用指针和数组。
你根本不应该在C ++中使用fopen()
,而是使用std::ifstream
和std::ofstream
。
您没有考虑到位图的高度可能是正数(自下而上)或负数(自上而下),或者clue.bmp
文件中的每个像素都是3个字节,或者每行像素被填充到4个字节的偶数倍。当你试图清除像素字节时,你会陷入无限循环。
有关BMP文件布局的详细信息,请参阅Bitmap Storage和BMP file format。
尝试更像这样的东西:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <fstream>
#include <vector>
#include <cstdint>
int main() {
std::ifstream cp("clue.bmp");
if (!cp.is_open()) {
//Error
std::cout << "Failed to open the clue file\n";
return 1;
}
uint8_t header[54];
if (!cp.read(reinterpret_cast<char*>(header), sizeof(header))) {
std::cout << "Failed to read\n";
return EXIT_FAILURE;
}
if ((header[0] != 0x42) || (header[1] != 0x4D)) {
std::cout << "clue file is not a BMP file\n";
return EXIT_FAILURE;
}
uint32_t size = *(reinterpret_cast<uint32_t*>(&header[14]));
if (size != 40) {
std::cout << "clue file is not a supported BMP format\n";
return EXIT_FAILURE;
}
uint32_t pixelsOffset = *(reinterpret_cast<uint32_t*>(&header[10]));
if (size != 54) {
std::cout << "clue file is not a supported BMP format\n";
return EXIT_FAILURE;
}
uint16_t planes = *(reinterpret_cast<uint16_t*>(&header[26]));
if (planes != 1) {
std::cout << "clue file is not a supported BMP format\n";
return EXIT_FAILURE;
}
uint16_t bitCount = *(reinterpret_cast<uint16_t*>(&header[28]));
if (bitCount != 24) {
std::cout << "clue file is not a supported BMP format\n";
return EXIT_FAILURE;
}
uint32_t compression = *(reinterpret_cast<uint16_t*>(&header[30]));
if (compression != 0) {
std::cout << "clue file is not a supported BMP format\n";
return EXIT_FAILURE;
}
int32_t width = *(reinterpret_cast<int32_t*>(&header[18]));
int32_t height = *(reinterpret_cast<int32_t*>(&header[22]));
if (width <= 0 || height == 0) {
std::cout << "clue file has no pixels\n";
return EXIT_FAILURE;
}
int32_t actualHeight = height;
if (actualHeight < 0) actualHeight *= -1;
uint64_t pixelCount = width * actualHeight;
std::cout << "Number of pixels: " << pixelCount << "\n";
uint32_t bytesPerLine = (((width * 24) + 31) & ~31) / 8;
std::vector<uint8_t> pixels(bytesPerLine * actualHeight);
if (!cp.read(reinterpret_cast<char*>(&pixels[0]), pixels.size())) {
std::cout << "Failed to read\n";
return EXIT_FAILURE;
}
cp.close();
int32_t row;
uint8_t *pixel;
for (int32_t i = 0; i < actualHeight; ++i) {
row = (height > 0) ? (actualHeight - i - 1) : i;
pixel = &pixels[row * bytesPerLine];
for (int32_t j = 0; j < width; ++j) {
*pixel++ = 0; // clear blue
*pixel++ = 0; // clear green
++pixel; // skip red
}
}
std::ofstream fp("output.bmp", std::ios_base::trunc);
if (!fp.is_open()) {
//Error
std::cerr << "Failed to open the output file\n";
return 1;
}
if (!fp.write(reinterpret_cast<char*>(header), sizeof(header))) {
std::cerr << "Failed write for output\n";
return EXIT_FAILURE;
}
if (!fp.write(reinterpret_cast<char*>(&pixels[0]), pixels.size())) {
std::cerr << "Failed to write pixels\n";
return EXIT_FAILURE;
}
return 0;
}
话虽如此,如果您使用预定义的结构和常量而不是硬编码的字节偏移和"magic numbers",这将更容易阅读和维护,例如:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <fstream>
#include <vector>
#include <cstdint>
#include <windows.h>
// or, if you want to compile for non-Windows platforms, manually
// define the relevant bitmap structures and constants, and make sure
// the structures are aligned to 1-byte boundaries, such as with
// #pragma pack, #pragma align, alignas(), etc, depending on compiler...
int main() {
std::ifstream cp("clue.bmp");
if (!cp.is_open()) {
//Error
std::cout << "Failed to open the clue file\n";
return 1;
}
uint8_t header[54];
if (!cp.read(reinterpret_cast<char*>(header), sizeof(header))) {
std::cout << "Failed to read\n";
return EXIT_FAILURE;
}
BITMAPFILEHEADER *bfh = reinterpret_cast<BITMAPFILEHEADER*>(header);
if ((bfh->bfType != 0x4D42) {
std::cout << "clue file is not a BMP file\n";
return EXIT_FAILURE;
}
BITMAPINFOHEADER *bih = reinterpret_cast<BITMAPINFOHEADER*>(bfh+1);
if (bih->biSize != sizeof(BITMAPINFOHEADER)) {
std::cout << "clue file is not a supported BMP format\n";
return EXIT_FAILURE;
}
if (bfh->bfOffBits != 54) {
std::cout << "clue file is not a supported BMP format\n";
return EXIT_FAILURE;
}
if (bih->biPlanes != 1) {
std::cout << "clue file is not a supported BMP format\n";
return EXIT_FAILURE;
}
if (bih->biBitCount != 24) {
std::cout << "clue file is not a supported BMP format\n";
return EXIT_FAILURE;
}
if (bih->biCompression != BI_RGB) {
std::cout << "clue file is not a supported BMP format\n";
return EXIT_FAILURE;
}
int32_t width = bih->biWidth;
int32_t height = bih->biHeight;
if (width <= 0 || height == 0) {
std::cout << "clue file has no pixels\n";
return EXIT_FAILURE;
}
if (height < 0) height *= -1;
uint64_t pixelCount = width * height;
std::cout << "Number of pixels: " << pixelCount << "\n";
uint32_t bytesPerLine = (((width * 24) + 31) & ~31) / 8;
std::vector<uint8_t> pixels(bytesPerLine * height);
if (!cp.read(reinterpret_cast<char*>(&pixels[0]), pixels.size())) {
std::cout << "Failed to read\n";
return EXIT_FAILURE;
}
cp.close();
int32_t row;
RGBTRIPLE *pixel;
for (int32_t i = 0; i < height; ++i) {
row = (bih->biHeight > 0) ? (height - i - 1) : i;
pixel = reinterpret_cast<RGBTRIPLE*>(&pixels[row * bytesPerLine]);
for (int32_t j = 0; j < width; ++j) {
pixel[j]->rgbtBlue = 0;
pixel[j]->rgbtGreen = 0;
}
}
std::ofstream fp("output.bmp", std::ios_base::trunc);
if (!fp.is_open()) {
//Error
std::cerr << "Failed to open the output file\n";
return 1;
}
if (!fp.write(reinterpret_cast<char*>(header), sizeof(header))) {
std::cerr << "Failed write for output\n";
return EXIT_FAILURE;
}
if (!fp.write(reinterpret_cast<char*>(&pixels[0]), pixels.size())) {
std::cerr << "Failed to write pixels\n";
return EXIT_FAILURE;
}
return 0;
}