81.6 연습문제: 비트맵 파일을 아스키 아트로 변환하기

다음 소스 코드를 완성하여 24비트 비트맵 파일을 아스키 아트로 변환하는 프로그램을 만드세요.

practice_bitmap_asciiart.c

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

#pragma pack(push, 1)

typedef struct _BITMAPFILEHEADER
{
    unsigned short bfType;
    unsigned int   bfSize;
    unsigned short bfReserved1;
    unsigned short bfReserved2;
    unsigned int   bfOffBits;
} BITMAPFILEHEADER;

typedef struct _BITMAPINFOHEADER
{
    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
{
    unsigned char rgbtBlue;
    unsigned char rgbtGreen;
    unsigned char rgbtRed;
} RGBTRIPLE;

#pragma pack(pop)

#define PIXEL_SIZE   3
#define PIXEL_ALIGN  4

int main()
{
    FILE *fpBmp;
    FILE *fpTxt;
    BITMAPFILEHEADER fileHeader;
    BITMAPINFOHEADER infoHeader;

    unsigned char *image;
    int size;
    int width, height;
    int padding;

    char ascii[] = { '#', '#', '@', '%', '=', '+', '*', ':', '-', '.', ' ' };

    fpBmp = fopen("Lenna80x80.bmp", "rb");
    if (fpBmp == NULL)
        return 0;

    if (________________________________________________ < 1)
    {
        fclose(fpBmp);
        return 0;
    }

    if (____________________________)
    {
        fclose(fpBmp);
        return 0;
    }

    if (fread(&infoHeader, sizeof(BITMAPINFOHEADER), 1, fpBmp) < 1)
    {
        fclose(fpBmp);
        return 0;
    }

    if (infoHeader.biBitCount != 24)
    {
        fclose(fpBmp);
        return 0;
    }

    size = infoHeader.biSizeImage;
    width = infoHeader.biWidth;
    height = infoHeader.biHeight;

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

    if (size == 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;
    }

    fclose(fpBmp);

    fpTxt = fopen("ascii.txt", "w");
    if (fpTxt == NULL)
    {
        free(image);
        return 0;
    }

    for (int y = height - 1; y >= 0; y--)
    {
        for (int x = 0; x < width; x++)
        {
            int index = (x * PIXEL_SIZE) + (y * (width * PIXEL_SIZE)) + (padding * y);

            RGBTRIPLE *pixel = (RGBTRIPLE *)&image[index];

            unsigned char blue = pixel->rgbtBlue;
            unsigned char green = pixel->rgbtGreen;
            unsigned char red = pixel->rgbtRed;

            unsigned char gray = (red + green + blue) / PIXEL_SIZE;

            _______________________________________________

            fprintf(fpTxt, "%c%c", c, c);
        }

        fprintf(fpTxt, "\n");
    }

    fclose(fpTxt);

    free(image);

    return 0;
}

정답

fread(&fileHeader, sizeof(BITMAPFILEHEADER), 1, fpBmp)fileHeader.bfType != 'MB'char c = ascii[gray * sizeof(ascii) / 256];

해설

먼저 fread 함수로 비트맵 파일 헤더를 읽은 뒤 bfType을 확인하여 'MB'가 맞는지 확인합니다. 파일에 저장된 상태는 'BM'이지만 리틀 엔디언으로 읽었으므로 'MB'가 됩니다.

RGB 값의 평균으로 구한 흑백값을 ASCII 문자 배열의 인덱스 변환하려면 흑백값에 ASCII 문자의 개수를 곱한 뒤 256으로 나눠주면 됩니다. 단, 255로 나누면 흑백값이 255일 때 ascii 배열의 인덱스를 벗어나게 되므로 주의해야 합니다.