commit 15358da14443624050aed83dc0779ebfeffc6ef3
parent 36e6257f38b8832a4d8ddd8a7cdba74c9cc10fb4
Author: M. Yamanaka <myamanaka@live.com>
Date: Mon, 9 Nov 2020 19:29:57 -0500
basic bitmap capabilities
Diffstat:
A | bmpfileiopp.c | | | 113 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | bmpfileiopp.h | | | 52 | ++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | varbyterw.c | | | 70 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | varbyterw.h | | | 43 | +++++++++++++++++++++++++++++++++++++++++++ |
4 files changed, 278 insertions(+), 0 deletions(-)
diff --git a/bmpfileiopp.c b/bmpfileiopp.c
@@ -0,0 +1,113 @@
+/*
+ Bitmap File IO ++ Implementation File
+ "bmpfileiopp.c"
+ M. Yamanaka
+ email: myamanaka@live.com
+ website: csmyamanaka.com
+ license: MIT (See included "LICENSE" file for details)
+*/
+
+/*
+ According to the wikipedia page (full URL in the header file), the bitmap header requires:
+ - 2 bytes for the BMP file signature
+ - 4 bytes for the file size
+ - 4 bytes for some reason
+ - 4 bytes that points to the location of the actual data
+ - 4 bytes just to say 40?
+ - 4 bytes for the image width
+ - 4 bytes for the image height
+ - 2 bytes for the image plane (apparently we can just set this to 1)??
+ - 2 bytes for bits per pixel 8 bits for 0-255 so for RGB, it should be 24 bits
+ - 4 bytes for the compression, pretty sure we don't have to do any compression so probably just set this to 0
+ - 4 bytes for idkwtf. apparently i can set this to 0 as well if we set the compression to 0
+ - 4 bytes for pixels per meter for x. idk 1000 might be a good number. that's 1 pixel = 1 mm
+ - 4 bytes for pixels per meter for y. same here, probably just 1000
+ - 4 bytes for "Colors Used". It looks like I can set this to 0
+ - 4 bytes for "Important Colors" it says we can just set this to 0
+
+ According to "Example 1" on the wikipedia page, it seems like I don't need the color table section
+ or at least it is outisde the scope at least for now to have to handle any of that.
+
+ Whew that was a lot so that's 14 bytes for the first section and 40 bytes for the second so the 54th bytes is where the image data begins.
+
+ The total file size is therefore 54 bytes + number of elements in the data array
+*/
+
+#include "bmpfileiopp.h"
+
+int readBMP(BMPIS* bfi, char** bd){
+ /*
+ fn ... bmp file name
+ bfi ... bitmap file information
+ bd ... bitmap image data
+ */
+
+ FILE* imf = fopen(bfi->fn, "r");
+ char rb[64]; //read buffer
+
+ //check if the file in question is indeed a bitmap file by checking the header
+ fread(rb, sizeof(char), 2, imf);
+ if(rb[0] != 'B' || rb[1] != 'M') return -1;
+
+ int details[4]; //temporary storage for important variables: image date location, width, height, and number of cnannels
+ int ds[4] = {4, 4, 4, 2}; //the byte sizes of each
+ int adv[7] = {8, 4, 4, 4, 4, 2, 2}; //read sizes. Some reads will contain the above variables. Others are ignored.
+ for(int i = 0, di = 0; i < 7; i++){
+ fread(rb, sizeof(char), adv[i], imf);
+ if(i == 1|| i == 3 || i == 4 || i == 6){
+ rDataFromChar(rb, details + di, ds[di], 0);
+ di++;
+ }
+ }
+
+ bfi->w = details[1];
+ bfi->h = details[2];
+ bfi->c = details[3]/8;
+
+ //the file pointer is at byte 30 at this point so we need bring this to details[0]
+ fread(rb, sizeof(char), details[0] - 30, imf);
+
+ //the actual data size (assuming there's no compression) is width*height*channels
+ int dsz = (bfi->w)*(bfi->h)*(bfi->c); //data size
+ *bd = (char*)malloc(sizeof(char)*dsz);
+ fread(*bd, sizeof(char), dsz, imf);
+ fclose(imf);
+ return 0;
+}
+
+int writeBMP(BMPIS bfi, char* bd){
+ /*
+ bfi: bitmap file information
+ bd: bitmap data array
+ */
+
+ //the size of the data array whould be height x width x number of channels
+ int datsz = bfi.h*bfi.w*bfi.c;
+
+ //create an array of bytes that represents the file
+ char* file_contents = (char*)malloc(sizeof(char)*(54 + datsz));
+
+ //but starting with byte 54, the entirety of array a1 is copied onto the file contents
+ for(int i = 0; i < datsz; i++) file_contents[i + 54] = bd[i];
+
+ //for the BMP signature, set byte 0 to 'B' and byte 1 to 'M' as indicated by ece.ualberta.ca
+ file_contents[0] = 'B';
+ file_contents[1] = 'M';
+
+ int cb = 2; //the current byte position is 2 after having read 'B' and 'M'
+ int details[14] = {54 + datsz, 0, 54, 40, bfi.w, bfi.h, 1, bfi.c*8, 0, 0, 1000, 1000, 0, 0};
+ int bsz[14] = {4, 4, 4, 4, 4, 4, 2, 2, 4, 4, 4, 4, 4, 4};
+
+ for(int i = 0; i < 14; cb += bsz[i++]) wDataToChar(details + i, file_contents + cb, bsz[i], 0);
+
+ //Write the char array into the output file
+ FILE* bmpfile = fopen(bfi.fn, "w");
+ for(int i = 0; i < 54 + datsz; i++) fputc(file_contents[i], bmpfile);
+ fclose(bmpfile);
+
+ //deallocate memory
+ free(file_contents);
+ return 0;
+}
+
+
diff --git a/bmpfileiopp.h b/bmpfileiopp.h
@@ -0,0 +1,52 @@
+/*
+ Bitmap File IO ++ header file
+ bmpfileiopp.h
+ M. Yamanaka
+ email: myamanaka@live.com
+ website: http://www.csmyamanaka.com
+ license: MIT (See included "LICENSE" file for details)
+*/
+
+#pragma once
+
+#include "varbyterw.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+/*
+ Bitmap Information Struct
+ This struct contains information that is required to interpret bitmap files assuming no compression.
+ The attributes from left to right are:
+ h ... height
+ w ... width
+ c ... number of channels (e.g. 1 for grayscale, 3 for RGB)
+ fn ... file name
+*/
+typedef struct BMPIS{int h, w, c; const char* fn;} BMPIS;
+
+/*
+ The functions below were written using wikipedia as a reference for the bitmap file structure:
+ <https://en.wikipedia.org/wiki/BMP_file_format>
+ Please see the implementation file "bmpfileiopp.c" for more details
+*/
+
+/*
+ Read Bitmap
+ The details such as width, height, number of channels and the image data are extracted
+ and stored into objects referenced by the arguments to this function.
+ The arguments from left to right are:
+ BMPIS* ... Bitmap information struct pointer
+ char** ... the address of a char (effectively byte) array will accept the image data of the bitmap
+*/
+int readBMP(BMPIS*, char**);
+
+/*
+ Write Bitmap
+ A bitmap file is created using details provided by the objects referenced by the arguments to this function.
+ The arguments from left to right are:
+ BMPIS* ... Bitmap information struct pointer with all components (h, w, c, fn) defined
+ char** ... the image data that will be written onto the bitmap file
+*/
+int writeBMP(BMPIS, char*);
+
+
diff --git a/varbyterw.c b/varbyterw.c
@@ -0,0 +1,70 @@
+/*
+ Variable-Byte Reader/Writer implemetation file
+ varbyterw.c
+ M. Yamanaka
+ email: myamanaka@live.com
+ website: csmyamanaka.com
+ license: MIT (See included "LICENSE" file for details)
+*/
+
+#include "varbyterw.h"
+
+
+int wDataToChar(void* ipt, char* dst, int b, int e){
+ /*
+ ipt ... input data
+ dst ... destination array
+ b ... number of bytes
+ e ... endian-ness. 0 indicates little-endian and 1, big-endian
+ */
+
+ /*
+ I could be wrong but I'm pretty sure gcc (or at least the version that I'm using) stores variables as big-endian
+ I came to this conclusion because I tested out the following code:
+
+ int main(){
+ int n = 65;
+ int* pn = &n;
+ char* pcn = (char*)pn;
+ if(*pcn == '\0' && *(pcn + 3) == 'A') printf("big endian\n");
+ else if(*pcn == 'A' && *(pcn + 3) == '\0') printf("little endian\n");
+ else printf("something probably went wrong\n");
+ return 0;
+ }
+
+ and got the second print statement.
+ I don't know if this varies from compiler to compiler.
+ If it helps, I tested this on Void Linux using GCC 9.3.0
+ */
+
+ if(e != 0 && e != 1) return -1;
+
+ /*
+ Assuming that the variables are indeed stored in little-endian format,
+ if match the indices as the bytes are being written,
+ the resulting byte array should also be little-endian.
+ If one of the indices is reversed, the result should be big-endian.
+
+ The for-loop below may look a little convoluted but it should handle both cases:
+ i.e. when "e" = 0, "i" and "j" should match whereas "i" would count up from 0 to "b" - 1
+ and "j" would count down from "b" - 1 to 0 when "e" = 1.
+ It saves me from having to write two for loops or an if statement in the loop
+ thereby making the code shorter suckless style
+ */
+ for(int i = 0, j = (b - 1)*e; i < b; i++, j += 1 - 2*e) dst[i] = *((char*)ipt + j);
+ return 0;
+}
+
+int rDataFromChar(char* ipt, void* dst, int b, int e){
+ /*
+ ipt ... input array
+ dst ... destination data
+ b ... number of bytes to interpret
+ e ... endian-ness, 0 for little-endian and 1 for big-endian
+ */
+
+ if(e != 0 && e != 1) return -1;
+ for(int i = 0, j = (b - 1)*e; i < b; i++, j += 1 - 2*e) *((char*)dst + i) = ipt[i];
+ return 0;
+}
+
diff --git a/varbyterw.h b/varbyterw.h
@@ -0,0 +1,43 @@
+/*
+ Variable-Byte Data Reader/Writer header file
+ varbyterw.h
+ M. Yamanaka
+ email: myamanaka@live.com
+ website: http://www.csmyamanaka.com
+ License: MIT (See included "LICENSE" file for details)
+*/
+
+#pragma once
+
+#include <stdlib.h>
+
+/*
+ Write Data to Char Array
+ "char" is a stand-in for byte because C doesn't have a byte type at least on the compiler that I am using at this time (GCC 9.3.0).
+ Therefore, this function writes the byte representation of a given input data onto a char (byte) array.
+ This can be done in either endian format controlled by the last argument of this function.
+ The arguments from left to right are:
+ void* ... input data
+ char* ... destination char array
+ int ... number of bytes
+ int ... endian-ness, 0 for little-endian, 1 for big-endian
+ The return values are 0 or -1, where -1 means something definitely went wrong.
+ Be warned that a return value of 0 does not necessarily mean that everything went smoothly.
+ This is C after all.
+
+*/
+int wDataToChar(void*, char*, int, int);
+
+/*
+ Read Data from Char Array
+ Again, "char" is just byte.
+ This function reads a series of bytes in either endian form and extracts the data and interprets it as a given variable..
+ The arguments from left to right are:
+ char* ... byte data
+ void* ... destination variable
+ int ... number of bytes to interpret
+ int ... endian-ness, 0 for little, 1 for big
+ A return value of -1 indicates a definite error but 0 does not indicate that there was no error.
+*/
+int rDataFromChar(char*, void*, int, int);
+