bmp.c 3.4 KB
#include "../include/bmp.h"
#include "stdint.h"
#include <stdbool.h>

#define BFTYPE 19778
#define BIPLANES 1
#define BISIZE 40
#define BPC 24
#define COMPR 0
#define X_PPM 0
#define Y_PPM 0
#define IMP_COLORS 0
#define NUM_COLORS 0
#define HEADER_BITS 54

const char *read_status_message[] = {
        [READ_OK] = "The read was successful",
        [READ_ERROR] = "Read wasn't successful",
        [SIGNATURE_INVALID] = "Signature of BMP file is invalid",
        [HEADER_INVALID] = "BMP file header is invalid"
};

const char *write_status_message[] = {
        [WRITE_OK] = "The write was successful",
        [WRITE_ERROR] = "Unable to write BMP"
};

static size_t padding_size(const size_t width) {
    return width % 4 == 0 ? 0 : 4 - ((width * sizeof(struct pixel)) % 4);
}

static enum read_status read_head(FILE *file, struct bmp_header *header) {
    return fread(header, sizeof(struct bmp_header), 1, file);
}

static size_t picture_size(const struct picture *picture) {
    return (picture->width * sizeof(struct pixel) + padding_size(picture->width)) * picture->height;
}

static size_t file_size(const struct picture *picture) {
    return picture_size(picture) + sizeof(struct bmp_header);
}

static struct bmp_header create_header(const struct picture *picture) {
    return (struct bmp_header) {
            .bfType = BFTYPE,
            .bfileSize = file_size(picture),
            .bOffBits = HEADER_BITS,
            .biSize = BISIZE,
            .biWidth = picture->width,
            .biHeight = picture->height,
            .biPlanes = BIPLANES,
            .biBitCount = BPC,
            .biCompression = COMPR,
            .biSizeImage = picture_size(picture),
            .biXPelsPerMeter = X_PPM,
            .biYPelsPerMeter = Y_PPM,
            .biClrUsed = NUM_COLORS,
            .biClrImportant = IMP_COLORS
    };
}

static enum read_status header_check_valid(struct bmp_header *header) {
    return header->bfType == 0x4D42;
}


enum read_status from_bmp(FILE *in, struct picture *picture) {
    struct bmp_header header = {0};
    if (!read_head(in, &header)) return HEADER_INVALID;
    if (!header_check_valid(&header)) return SIGNATURE_INVALID;

    *picture = picture_create(header.biWidth, header.biHeight);

    const size_t padding = padding_size(picture->width);

    for (size_t i = 0; i < picture->height; ++i) {
        for (size_t j = 0; j < picture->width; ++j) {
            if (!fread(&(picture->data[picture->width * i + j]), sizeof(struct pixel), 1, in))
                return READ_ERROR;
        }
        if (fseek(in, (long) padding, SEEK_CUR))
            return READ_ERROR;
    }
    return READ_OK;
}

enum write_status to_bmp(FILE *out, const struct picture *picture) {
    struct bmp_header header = create_header(picture);
    if (!fwrite(&header, sizeof(struct bmp_header), 1, out))
        return WRITE_ERROR;
    fseek(out, header.bOffBits, SEEK_SET);
    const int8_t zero = 0;
    const size_t padding = padding_size(picture->width);
    if (picture->data != NULL) {
        for (size_t i = 0; i < picture->height; ++i) {
            if (fwrite(picture->data + i * picture->width, sizeof(struct pixel), picture->width, out) != picture->width)
                return WRITE_ERROR;
            for (size_t j = 0; j < padding; ++j) {
                if (fwrite(&zero, 1, 1, out) != 1)
                    return WRITE_ERROR;
            }
        }
    } else {
        return WRITE_ERROR;
    }
    return WRITE_OK;
}