#define _CRT_SECURE_NO_WARNINGS    // fopen 보안 경고로 인한 컴파일 에러 방지

#include <stdio.h>    // fopen, fread, fseek, fprintf, fclose 함수가 선언된 헤더 파일

#include <stdlib.h>   // malloc, free 함수가 선언된 헤더 파일


#pragma pack(push, 1)                // 구조체를 1바이트 크기로 정렬


typedef struct _BITMAPFILEHEADER     // BMP 비트맵 파일 헤더 구조체

{

    unsigned short bfType;           // BMP 파일 매직 넘버

    unsigned int   bfSize;           // 파일 크기

    unsigned short bfReserved1;      // 예약

    unsigned short bfReserved2;      // 예약

    unsigned int   bfOffBits;        // 비트맵 데이터의 시작 위치

} BITMAPFILEHEADER;


typedef struct _BITMAPINFOHEADER     // BMP 비트맵 정보 헤더 구조체(DIB 헤더)

{

    unsigned int   biSize;           // 현재 구조체의 크기

    int            biWidth;          // 비트맵 이미지의 가로 크기

    int            biHeight;         // 비트맵 이미지의 세로 크기

    unsigned short biPlanes;         // 사용하는 색상판의 수

    unsigned short biBitCount;       // 픽셀 하나를 표현하는 비트 수

    unsigned int   biCompression;    // 압축 방식

    unsigned int   biSizeImage;      // 비트맵 이미지의 픽셀 데이터 크기

    int            biXPelsPerMeter;  // 그림의 가로 해상도(미터당 픽셀)

    int            biYPelsPerMeter;  // 그림의 세로 해상도(미터당 픽셀)

    unsigned int   biClrUsed;        // 색상 테이블에서 실제 사용되는 색상 수

    unsigned int   biClrImportant;   // 비트맵을 표현하기 위해 필요한 색상 인덱스 수

} BITMAPINFOHEADER;


typedef struct _RGBTRIPLE            // 24비트 비트맵 이미지의 픽셀 구조체

{

    unsigned char rgbtBlue;          // 파랑

    unsigned char rgbtGreen;         // 초록

    unsigned char rgbtRed;           // 빨강

} RGBTRIPLE;


#pragma pack(pop)


#define PIXEL_SIZE   3    // 픽셀 한 개의 크기 3바이트(24비트)

#define PIXEL_ALIGN  4    // 픽셀 데이터 가로 한 줄은 4의 배수 크기로 저장됨


int main()

{

    FILE* fpBmp;                    // 비트맵 파일 포인터

    FILE* fpBmpResult;

    BITMAPFILEHEADER fileHeader;    // 비트맵 파일 헤더 구조체 변수

    BITMAPINFOHEADER infoHeader;    // 비트맵 정보 헤더 구조체 변수


    unsigned char* image;    // 픽셀 데이터 포인터

    int size;                // 픽셀 데이터 크기

    int width, height;       // 비트맵 이미지의 가로, 세로 크기

    int padding;             // 픽셀 데이터의 가로 크기가 4의 배수가 아닐 때 남는 공간의 크기


    unsigned char password = 0x00;


    fpBmp = fopen("image.bmp", "rb");    // 비트맵 파일을 바이너리 모드로 열기

    if (fpBmp == NULL)    // 파일 열기에 실패하면

        return 0;         // 프로그램 종료


    // 비트맵 파일 헤더 읽기. 읽기에 실패하면 파일 포인터를 닫고 프로그램 종료

    if (fread(&fileHeader, sizeof(BITMAPFILEHEADER), 1, fpBmp) < 1)

    {

        fclose(fpBmp);

        return 0;

    }


    // 매직 넘버가 MB가 맞는지 확인(2바이트 크기의 BM을 리틀 엔디언으로 읽었으므로 MB가 됨)

    // 매직 넘버가 맞지 않으면 프로그램 종료

    if (fileHeader.bfType != 'MB')

    {

        fclose(fpBmp);

        return 0;

    }


    // 비트맵 정보 헤더 읽기. 읽기에 실패하면 파일 포인터를 닫고 프로그램 종료

    if (fread(&infoHeader, sizeof(BITMAPINFOHEADER), 1, fpBmp) < 1)

    {

        fclose(fpBmp);

        return 0;

    }


    // 24비트 비트맵이 아니면 프로그램 종료

    if (infoHeader.biBitCount != 24)

    {

        fclose(fpBmp);

        return 0;

    }


    size = infoHeader.biSizeImage;    // 픽셀 데이터 크기

    width = infoHeader.biWidth;       // 비트맵 이미지의 가로 크기

    height = infoHeader.biHeight;     // 비트맵 이미지의 세로 크기


    // 이미지의 가로 크기에 픽셀 크기를 곱하여 가로 한 줄의 크기를 구하고 4로 나머지를 구함

    // 그리고 4에서 나머지를 빼주면 남는 공간을 구할 수 있음.

    // 만약 남는 공간이 0이라면 최종 결과가 4가 되므로 여기서 다시 4로 나머지를 구함

    padding = (PIXEL_ALIGN - ((width * PIXEL_SIZE) % PIXEL_ALIGN)) % PIXEL_ALIGN;


    if (size == 0)    // 픽셀 데이터 크기가 0이라면

    {

        // 이미지의 가로 크기 * 픽셀 크기에 남는 공간을 더해주면 완전한 가로 한 줄 크기가 나옴

        // 여기에 이미지의 세로 크기를 곱해주면 픽셀 데이터의 크기를 구할 수 있음

        size = (width * PIXEL_SIZE + padding) * height;

    }


    image = malloc(size);    // 픽셀 데이터의 크기만큼 동적 메모리 할당


    // 파일 포인터를 픽셀 데이터의 시작 위치로 이동

    fseek(fpBmp, fileHeader.bfOffBits, SEEK_SET);


    // 파일에서 픽셀 데이터 크기만큼 읽음. 읽기에 실패하면 파일 포인터를 닫고 프로그램 종료

    if (fread(image, size, 1, fpBmp) < 1)

    {

        fclose(fpBmp);

        return 0;

    }


    fseek(fpBmp, 0, SEEK_SET);


    fpBmpResult = fopen("image_result.bmp", "wb");


    fwrite(&fileHeader, sizeof(BITMAPFILEHEADER), 1, fpBmpResult);

    fwrite(&infoHeader, sizeof(BITMAPINFOHEADER), 1, fpBmpResult);

    

    // 픽셀 데이터는 아래 위가 뒤집혀서 저장되므로 아래쪽부터 반복

    // 세로 크기만큼 반복

    for (int y = height - 1; y >= 0; y--)

    {

        int n = 0;


        // 가로 크기만큼 반복

        for (int x = 0; x < width; x++)

        {

            // 일렬로 된 배열에 접근하기 위해 인덱스를 계산

            // (x * 픽셀 크기)는 픽셀의 가로 위치

            // (y * (세로 크기 * 픽셀 크기))는 픽셀이 몇 번째 줄인지 계산

            // 남는 공간 * y는 줄별로 누적된 남는 공간

            int index = (x * PIXEL_SIZE) + (y * (width * PIXEL_SIZE)) + (padding * y);


            srand(n++);

            password = rand() % 256;

            ((RGBTRIPLE*)&image[index])->rgbtBlue ^= password;


            srand(n++);

            password = rand() % 256;

            ((RGBTRIPLE*)&image[index])->rgbtGreen ^= password;


            srand(n++);

            password = rand() % 256;

            ((RGBTRIPLE*)&image[index])->rgbtRed ^= password;


        }

    }


    fwrite(image, size, 1, fpBmpResult);


    fclose(fpBmp);

    fclose(fpBmpResult);


    free(image);      // 픽셀 데이터를 저장한 동적 메모리 해제


    return 0;

}



변경 전

변경 후


각 픽셀에 저장된 RGB값과 0~255 난수 값이랑 XOR연산 시켜서 이미지 바꾼 코드임.

변경 된 이미지를 다시 변환하면 원래 이미지로 돌아옴.

본인 의도는 형체도 못 알아보게 바꾸는 건데 형체는 알아 볼 수 있음.

어떻게 해야 형체도 못 알아보게 만들 수 있음?