在BMP文件中查找隐藏的消息

时间:2018-04-19 01:02:49

标签: c++ file-io bmp

我正在开展一个学校项目,我们需要读取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;
}

1 个答案:

答案 0 :(得分:0)

你是在滥用指针和数组。

你根本不应该在C ++中使用fopen(),而是使用std::ifstreamstd::ofstream

您没有考虑到位图的高度可能是正数(自下而上)或负数(自上而下),或者clue.bmp文件中的每个像素都是3个字节,或者每行像素被填充到4个字节的偶数倍。当你试图清除像素字节时,你会陷入无限循环。

有关BMP文件布局的详细信息,请参阅Bitmap StorageBMP 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;
}