fileiopp

File IO ++ or fileiopp serves as a utility that interprets files in ways that I deem useful
Log | Files | Refs | README | LICENSE

commit 15358da14443624050aed83dc0779ebfeffc6ef3
parent 36e6257f38b8832a4d8ddd8a7cdba74c9cc10fb4
Author: M. Yamanaka <myamanaka@live.com>
Date:   Mon,  9 Nov 2020 19:29:57 -0500

basic bitmap capabilities

Diffstat:
Abmpfileiopp.c | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abmpfileiopp.h | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Avarbyterw.c | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Avarbyterw.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); +

Generated using stagit (https://codemadness.org/stagit.html)