|
@@ -0,0 +1,315 @@
|
|
|
+#include <stdlib.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include "cbmp.h"
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+#define BITS_PER_BYTE 8
|
|
|
+
|
|
|
+#define BLUE 0
|
|
|
+#define GREEN 1
|
|
|
+#define RED 2
|
|
|
+#define ALPHA 3
|
|
|
+
|
|
|
+#define PIXEL_ARRAY_START_BYTES 4
|
|
|
+#define PIXEL_ARRAY_START_OFFSET 10
|
|
|
+
|
|
|
+#define WIDTH_BYTES 4
|
|
|
+#define WIDTH_OFFSET 18
|
|
|
+
|
|
|
+#define HEIGHT_BYTES 4
|
|
|
+#define HEIGHT_OFFSET 22
|
|
|
+
|
|
|
+#define DEPTH_BYTES 2
|
|
|
+#define DEPTH_OFFSET 28
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+void _throw_error(char* message);
|
|
|
+unsigned int _get_int_from_buffer(unsigned int bytes,
|
|
|
+ unsigned int offset,
|
|
|
+ unsigned char* buffer);
|
|
|
+unsigned int _get_file_byte_number(FILE* fp);
|
|
|
+unsigned char* _get_file_byte_contents(FILE* fp, unsigned int file_byte_number);
|
|
|
+int _validate_file_type(unsigned char* file_byte_contents);
|
|
|
+int _validate_depth(unsigned int depth);
|
|
|
+unsigned int _get_pixel_array_start(unsigned char* file_byte_contents);
|
|
|
+int _get_width(unsigned char* file_byte_contents);
|
|
|
+int _get_height(unsigned char* file_byte_contents);
|
|
|
+unsigned int _get_depth(unsigned char* file_byte_contents);
|
|
|
+void _update_file_byte_contents(BMP* bmp, int index, int offset, int channel);
|
|
|
+void _populate_pixel_array(BMP* bmp);
|
|
|
+void _map(BMP* bmp, void (*f)(BMP* bmp, int, int, int));
|
|
|
+void _get_pixel(BMP* bmp, int index, int offset, int channel);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+BMP* bopen(char* file_path)
|
|
|
+{
|
|
|
+ FILE* fp = fopen(file_path, "rb");
|
|
|
+
|
|
|
+ if (fp == NULL)
|
|
|
+ {
|
|
|
+ perror("Error opening file");
|
|
|
+ exit(EXIT_FAILURE);
|
|
|
+ }
|
|
|
+
|
|
|
+ BMP* bmp = (BMP*) malloc(sizeof(BMP));
|
|
|
+ bmp->file_byte_number = _get_file_byte_number(fp);
|
|
|
+ bmp->file_byte_contents = _get_file_byte_contents(fp, bmp->file_byte_number);
|
|
|
+ fclose(fp);
|
|
|
+
|
|
|
+ if(!_validate_file_type(bmp->file_byte_contents))
|
|
|
+ {
|
|
|
+ _throw_error("Invalid file type");
|
|
|
+ }
|
|
|
+
|
|
|
+ bmp->pixel_array_start = _get_pixel_array_start(bmp->file_byte_contents);
|
|
|
+
|
|
|
+ bmp->width = _get_width(bmp->file_byte_contents);
|
|
|
+ bmp->height = _get_height(bmp->file_byte_contents);
|
|
|
+ bmp->depth = _get_depth(bmp->file_byte_contents);
|
|
|
+
|
|
|
+ if(!_validate_depth(bmp->depth))
|
|
|
+ {
|
|
|
+ _throw_error("Invalid file depth");
|
|
|
+ }
|
|
|
+
|
|
|
+ _populate_pixel_array(bmp);
|
|
|
+
|
|
|
+ return bmp;
|
|
|
+}
|
|
|
+
|
|
|
+BMP* b_deep_copy(BMP* to_copy)
|
|
|
+{
|
|
|
+ BMP* copy = (BMP*) malloc(sizeof(BMP));
|
|
|
+ copy->file_byte_number = to_copy->file_byte_number;
|
|
|
+ copy->pixel_array_start = to_copy->pixel_array_start;
|
|
|
+ copy->width = to_copy->width;
|
|
|
+ copy->height = to_copy->height;
|
|
|
+ copy->depth = to_copy->depth;
|
|
|
+
|
|
|
+ copy->file_byte_contents = (unsigned char*) malloc(copy->file_byte_number * sizeof(unsigned char));
|
|
|
+
|
|
|
+ unsigned int i;
|
|
|
+ for (i = 0; i < copy->file_byte_number; i++)
|
|
|
+ {
|
|
|
+ copy->file_byte_contents[i] = to_copy->file_byte_contents[i];
|
|
|
+ }
|
|
|
+
|
|
|
+ copy->pixels = (pixel*) malloc(copy->width * copy->height * sizeof(pixel));
|
|
|
+
|
|
|
+ unsigned int x, y;
|
|
|
+ int index;
|
|
|
+ for (y = 0; y < copy->height; y++)
|
|
|
+ {
|
|
|
+ for (x = 0; x < copy->width; x++)
|
|
|
+ {
|
|
|
+ index = y * copy->width + x;
|
|
|
+ copy->pixels[index].red = to_copy->pixels[index].red;
|
|
|
+ copy->pixels[index].green = to_copy->pixels[index].green;
|
|
|
+ copy->pixels[index].blue = to_copy->pixels[index].blue;
|
|
|
+ copy->pixels[index].alpha = to_copy->pixels[index].alpha;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return copy;
|
|
|
+}
|
|
|
+
|
|
|
+int get_width(BMP* bmp)
|
|
|
+{
|
|
|
+ return bmp->width;
|
|
|
+}
|
|
|
+
|
|
|
+int get_height(BMP* bmp)
|
|
|
+{
|
|
|
+ return bmp->height;
|
|
|
+}
|
|
|
+
|
|
|
+unsigned int get_depth(BMP* bmp)
|
|
|
+{
|
|
|
+ return bmp->depth;
|
|
|
+}
|
|
|
+
|
|
|
+void get_pixel_rgb(BMP* bmp, int x, int y, unsigned char* r, unsigned char* g, unsigned char* b)
|
|
|
+{
|
|
|
+ int index = y * bmp->width + x;
|
|
|
+ *r = bmp->pixels[index].red;
|
|
|
+ *g = bmp->pixels[index].green;
|
|
|
+ *b = bmp->pixels[index].blue;
|
|
|
+}
|
|
|
+
|
|
|
+void set_pixel_rgb(BMP* bmp, int x, int y, unsigned char r, unsigned char g, unsigned char b)
|
|
|
+{
|
|
|
+ int index = y * bmp->width + x;
|
|
|
+ bmp->pixels[index].red = r;
|
|
|
+ bmp->pixels[index].green = g;
|
|
|
+ bmp->pixels[index].blue = b;
|
|
|
+}
|
|
|
+
|
|
|
+void bwrite(BMP* bmp, char* file_name)
|
|
|
+{
|
|
|
+ _map(bmp, _update_file_byte_contents);
|
|
|
+
|
|
|
+ FILE* fp = fopen(file_name, "wb");
|
|
|
+ fwrite(bmp->file_byte_contents, sizeof(char), bmp->file_byte_number, fp);
|
|
|
+ fclose(fp);
|
|
|
+}
|
|
|
+
|
|
|
+void bclose(BMP* bmp)
|
|
|
+{
|
|
|
+ free(bmp->pixels);
|
|
|
+ bmp->pixels = NULL;
|
|
|
+ free(bmp->file_byte_contents);
|
|
|
+ bmp->file_byte_contents = NULL;
|
|
|
+ free(bmp);
|
|
|
+ bmp = NULL;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+void _throw_error(char* message)
|
|
|
+{
|
|
|
+ fprintf(stderr, "%s\n", message);
|
|
|
+ exit(1);
|
|
|
+}
|
|
|
+
|
|
|
+unsigned int _get_int_from_buffer(unsigned int bytes,
|
|
|
+ unsigned int offset,
|
|
|
+ unsigned char* buffer)
|
|
|
+{
|
|
|
+ unsigned char* _buffer = (unsigned char*) malloc(bytes * sizeof(unsigned char));
|
|
|
+
|
|
|
+ unsigned int i;
|
|
|
+ for (i = 0; i < bytes; i++)
|
|
|
+ {
|
|
|
+ _buffer[i] = buffer[i + offset];
|
|
|
+ }
|
|
|
+
|
|
|
+ unsigned int value = *(unsigned int*) _buffer;
|
|
|
+ free(_buffer);
|
|
|
+ return value;
|
|
|
+}
|
|
|
+
|
|
|
+unsigned int _get_file_byte_number(FILE* fp)
|
|
|
+{
|
|
|
+ unsigned int byte_number;
|
|
|
+ fseek(fp, 0, SEEK_END);
|
|
|
+ byte_number = ftell(fp);
|
|
|
+ rewind(fp);
|
|
|
+ return byte_number;
|
|
|
+}
|
|
|
+
|
|
|
+unsigned char* _get_file_byte_contents(FILE* fp, unsigned int file_byte_number)
|
|
|
+{
|
|
|
+ unsigned char* buffer = (unsigned char*) malloc(file_byte_number * sizeof(char));
|
|
|
+ unsigned int result = fread(buffer, 1, file_byte_number, fp);
|
|
|
+
|
|
|
+ if (result != file_byte_number)
|
|
|
+ {
|
|
|
+ _throw_error("There was a problem reading the file");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ return buffer;
|
|
|
+}
|
|
|
+
|
|
|
+int _validate_file_type(unsigned char* file_byte_contents)
|
|
|
+{
|
|
|
+ return file_byte_contents[0] == 'B' && file_byte_contents[1] == 'M';
|
|
|
+}
|
|
|
+
|
|
|
+int _validate_depth(unsigned int depth)
|
|
|
+{
|
|
|
+ return depth == 24 || depth == 32;
|
|
|
+}
|
|
|
+
|
|
|
+unsigned int _get_pixel_array_start(unsigned char* file_byte_contents)
|
|
|
+{
|
|
|
+ return _get_int_from_buffer(PIXEL_ARRAY_START_BYTES, PIXEL_ARRAY_START_OFFSET, file_byte_contents);
|
|
|
+}
|
|
|
+
|
|
|
+int _get_width(unsigned char* file_byte_contents)
|
|
|
+{
|
|
|
+ return (int) _get_int_from_buffer(WIDTH_BYTES, WIDTH_OFFSET, file_byte_contents);
|
|
|
+}
|
|
|
+
|
|
|
+int _get_height(unsigned char* file_byte_contents)
|
|
|
+{
|
|
|
+ return (int) _get_int_from_buffer(HEIGHT_BYTES, HEIGHT_OFFSET, file_byte_contents);
|
|
|
+}
|
|
|
+
|
|
|
+unsigned int _get_depth(unsigned char* file_byte_contents)
|
|
|
+{
|
|
|
+ return _get_int_from_buffer(DEPTH_BYTES, DEPTH_OFFSET, file_byte_contents);
|
|
|
+}
|
|
|
+
|
|
|
+void _update_file_byte_contents(BMP* bmp, int index, int offset, int channel)
|
|
|
+{
|
|
|
+ char value;
|
|
|
+ switch(channel)
|
|
|
+ {
|
|
|
+ case BLUE:
|
|
|
+ value = bmp->pixels[index].blue;
|
|
|
+ break;
|
|
|
+ case GREEN:
|
|
|
+ value = bmp->pixels[index].green;
|
|
|
+ break;
|
|
|
+ case RED:
|
|
|
+ value = bmp->pixels[index].red;
|
|
|
+ break;
|
|
|
+ case ALPHA:
|
|
|
+ value = bmp->pixels[index].alpha;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ bmp->file_byte_contents[offset + channel] = value;
|
|
|
+}
|
|
|
+
|
|
|
+void _populate_pixel_array(BMP* bmp)
|
|
|
+{
|
|
|
+ bmp->pixels = (pixel*) malloc(bmp->width * bmp->height * sizeof(pixel));
|
|
|
+ _map(bmp, _get_pixel);
|
|
|
+}
|
|
|
+
|
|
|
+void _map(BMP* bmp, void (*f)(BMP*, int, int, int))
|
|
|
+{
|
|
|
+ int channels = bmp->depth / (sizeof(unsigned char) * BITS_PER_BYTE);
|
|
|
+ int row_size = ((int) (bmp->depth * bmp->width + 31) / 32) * 4;
|
|
|
+ int padding = row_size - bmp->width * channels;
|
|
|
+
|
|
|
+ int c;
|
|
|
+ unsigned int x, y, index, offset;
|
|
|
+ for (y = 0; y < bmp->height; y++)
|
|
|
+ {
|
|
|
+ for (x = 0; x < bmp->width; x++)
|
|
|
+ {
|
|
|
+ index = y * bmp->width + x;
|
|
|
+ offset = bmp->pixel_array_start + index * channels + y * padding;
|
|
|
+ for (c = 0; c < channels; c++)
|
|
|
+ {
|
|
|
+ (*f)(bmp, index, offset, c);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void _get_pixel(BMP* bmp, int index, int offset, int channel)
|
|
|
+{
|
|
|
+ unsigned char value = _get_int_from_buffer(sizeof(unsigned char), offset + channel, bmp->file_byte_contents);
|
|
|
+ switch(channel)
|
|
|
+ {
|
|
|
+ case BLUE:
|
|
|
+ bmp->pixels[index].blue = value;
|
|
|
+ break;
|
|
|
+ case GREEN:
|
|
|
+ bmp->pixels[index].green = value;
|
|
|
+ break;
|
|
|
+ case RED:
|
|
|
+ bmp->pixels[index].red = value;
|
|
|
+ break;
|
|
|
+ case ALPHA:
|
|
|
+ bmp->pixels[index].alpha = value;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|