From b4918934dc3d9da8473b9622dae5eb97d91875c0 Mon Sep 17 00:00:00 2001 From: theBreadCompany Date: Sat, 9 Dec 2023 03:51:00 +0100 Subject: [PATCH] init with content --- CMakeLists.txt | 66 +++++++++ LICENSE | 21 +++ README.md | 28 ++++ src/.DS_Store | Bin 0 -> 10244 bytes src/bpic/.DS_Store | Bin 0 -> 6148 bytes src/bpic/common_utils.c | 37 +++++ src/bpic/decode.c | 212 ++++++++++++++++++++++++++++ src/bpic/encode.c | 243 ++++++++++++++++++++++++++++++++ src/bpic/include/bpic.h | 15 ++ src/bpic/include/common_utils.h | 78 ++++++++++ src/bpic/include/decode.h | 43 ++++++ src/bpic/include/encode.h | 31 ++++ src/bpicdecode/main.c | 74 ++++++++++ src/bpicdecode_test/main.c | 5 + src/bpicencode/main.c | 121 ++++++++++++++++ src/bpicencode_test/main.c | 8 ++ 16 files changed, 982 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 README.md create mode 100644 src/.DS_Store create mode 100644 src/bpic/.DS_Store create mode 100644 src/bpic/common_utils.c create mode 100644 src/bpic/decode.c create mode 100644 src/bpic/encode.c create mode 100644 src/bpic/include/bpic.h create mode 100644 src/bpic/include/common_utils.h create mode 100644 src/bpic/include/decode.h create mode 100644 src/bpic/include/encode.h create mode 100644 src/bpicdecode/main.c create mode 100644 src/bpicdecode_test/main.c create mode 100644 src/bpicencode/main.c create mode 100644 src/bpicencode_test/main.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..abe52b6 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,66 @@ +set() +cmake_minimum_required(VERSION 3.12) +project(libbpic VERSION 1.0.0 LANGUAGES C DESCRIPTION "encode and decode any file to an image") + +# dependencies + +find_package(PNG REQUIRED) +message(STATUS ${PNG_INCLUDE_DIR}) +include_directories(${PNG_INCLUDE_DIRS}) + +# sources + +set(BPIC_SOURCES + src/bpic/common_utils.c + src/bpic/encode.c + src/bpic/decode.c) +set(BPIC_HEADERS + src/bpic/include/bpic.h + src/bpic/include/common_utils.h + src/bpic/include/decode.h + src/bpic/include/encode.h) + +set(BPICENCODE_SOURCES + src/bpicencode/main.c) + +set(BPICDECODE_SOURCES + src/bpicdecode/main.c) + +set(BPICENCODE_TEST_SOURCES + src/bpicencode_test/main.c) + +set(BPICDECODE_TEST_SOURCES + src/bpicdecode_test/main.c) + +# building + +add_library(bpic ${BPIC_HEADERS} ${BPIC_SOURCES}) +set_target_property(TARGET bpic PROPERTY C_STANDARD 90) + +target_include_directories(bpic PUBLIC src/bpic/include) +set_target_properties(bpic PROPERTIES PUBLIC_HEADER bpic.h) + +add_executable(bpicencode ${BPICENCODE_SOURCES}) +add_executable(bpicdecode ${BPICDECODE_SOURCES}) +add_executable(bpicencode_test ${BPICENCODE_TEST_SOURCES}) +add_executable(bpicdecode_test ${BPICDECODE_TEST_SOURCES}) + + +# linking +message(STATUS ${PNG_LIBRARIES}) +target_link_libraries(bpic ${PNG_LIBRARIES}) +target_link_libraries(bpicencode bpic) +target_link_libraries(bpicdecode bpic) +target_link_libraries(bpicdecode_test bpic) +target_link_libraries(bpicencode_test bpic) + + +# installing + +install( + TARGETS bpic + EXPORT bpic + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + PUBLIC_HEADER DESTINATION include/${PROJECT_NAME} +) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cf8863b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 theBreadCompany + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6f58103 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# libbpic + +**P**NG **V**isual **S**torage **F**format library to encode data to images and back. + +## disclaimer + +This library is not considered to be safe for production and is not delivered under any kind of warranty! Use with caution and only for educational and research purposes! + +## building + +Simply call: + +```bash +git clone https://github.com/thebreadcompany/libbpic +cd libbpic +mkdir build && cd build +cmake .. && cmake --build . +``` + +You can find the library + +## installing + + + +## usage + +## use cases \ No newline at end of file diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..0904b3849209655895f244a26f6b02b661643aa1 GIT binary patch literal 10244 zcmeHMU2GIp6uxI#+F3f-Tc85NwyabEp-Q(P|H}4YC}^WE>v?7d3}f##GFBqTrx5%FS@N_Z<$SUcqvTsg)iHz(k3KEU-1?hXYU?>K(I zQ6~`M(z|;IcnC~JfbHE&NsNpVjl^^J@3B!`v+Rb3t00t=&M7O$Qc?bRc*M+v?Wmoy znxn~KR_ifMJ(hir#`-ih6QK)ROv8?9hMIH?G(|;~ef^rDn3-lXWhj>4((Xon^e;ne}CN6QwhV0(5iS;kEisgtv2a8Q^vMRE1ooUeXnV0wq_cG1GakD z&JL8P`Oe<6$ba7h ztJkjI{8-D*J$tWI(yI9j7E00(f^OK_eoamGW;Def8H`$S)lf9!Kz~}Zw3x2;7}11U z1#Lc`W3_mREDxk&+M$$YKTYdI(P^!zm8E?OPNdUR*k}qpCvyGewX!_SdRmey-WAti z#Y$P~u@D3rbrsjtOH$84WCgWcR}Dc)?$x6)Ri}!N`*~!8B=@IzbhN{5+_F`Y2XGA& z7>ZQ|^=|X_kSssJJnxBGcGswC;H)m8`}wr0Qm&QTL~)SyHYtj#@WytzL!_6*A!{U> zRJ$_B*lc^3C^}1v?QFCiie{VRonh8VH6yKIG9c_cCwz6GL!4E0q1AjD>bM}p!PH0D zcdmx4Ae-=u+C%melN=`}$tm(7`IuZFUy-ZiXL5u5Np6#W06>5ekf0hC!D3hf%V8}9 zp%K=>HrNI2up7cK07Ebg`#^=KK!*evkbz_H42;2XH~}xitMD4U4yWKPcpKh97vUG2L&q>Zyag?_k)Cr$Oeivlc@Ha*%rPCCxgEHQKG?r=OW zGaQaP1SlZq6-=$A{u*gnHuDNIvKNy>JuEVY({ir8T)JPx-`&jA_ZRAB5m_w;?YuG-7YoxSk6=8)cvjgHW8DDiZio% zT26mUu955HCb>m!Lm5;6g=Me`8ejv2pbd5+i#wnb`k)^Mk;{z9`(Xq$I0!bRkj;nT z2s{hV!SnC}yoii`1v$+aeHz|?Gw>#yh4Ml^D}6OpZc+n3db}7A8k8KR8l;f-|hB z)BKR8*DVsr1YkM-r(BrsyoD)hQ84#k!i5Vnh1CST(8nEG6^RFpz2YI@A>bk4A#j%v zDBi=V}dv5 zI3A^Q9FG;YgIa|b({)dzoad0+HpZo6jyjGx%Kz!lfIB=w^Y;JV{vRpno{ksn$R?c+ L3eSBiM)s>@te#jTQ?7aAR;r6 ze3Q&ik`J16h={lIxFKqYXpJVwqEtkttEMw^z5sH`G1Ex*<6%hsR7vy~r)2M!^hD1z zP@<##Pf51>#gra4-7xL;dXnbl)MGcbZJ(wd7UArzn_q8gUgI~s_K(M_MWk_Y2AlzB zz!`7`e$D{iY?1YqqW8{#GvEw-G9dRuKog9HS+QIl=t>CyTw%Hhbg3mICKyJ;tOyH) zH590!Y$XP3IOc=-MZ>J9;lx&au&w-2yl`0^`9pChj*8wp1J1ybfpZ-;^8VlAlc^T@ z%Mc$q1J1xdV}Ms}*KTnrds|PACvR;)yF(KZzbpy_`r;=51Gz`8i&5=CbofQXtSDJz SzJvq)N1zbmoip$Y47>wU + +void throw_err(const char* message, int error) { + char *category = error ? "ERROR:" : "NOTE:"; + fprintf(stderr, "%s %s", category, message); +} + +void debug(FILE *pipe, const char *message, ...) { +#if DEBUG || debug + fprintf(pipe, "%s", message); +#endif +} + +bpic_message bpic_error_input_file_null = "Input file is null!"; +bpic_message bpic_error_input_buffer_null = "Input buffer is null!"; +bpic_message bpic_error_input_buffer_small = "Input buffer too small!"; +bpic_message bpic_warn_encoding_invalid = "Encoding is invalid, defaulting to bitwise (0)"; +bpic_message bpic_warn_pxsize_null = "Pixel size is null, defaulting to 2"; + +uint16_t fletcher16(const unsigned char *data, size_t size) { + uint16_t sum1 = 0; + uint16_t sum2 = 0; + + for (int index = 0; index < size; index++) { + sum1 = (sum1 + data[index]) % 0xFF; + sum2 = (sum2 + sum1) % 0xFF; + } + + return (sum2 << 8) | sum1; +} diff --git a/src/bpic/decode.c b/src/bpic/decode.c new file mode 100644 index 0000000..781c8f8 --- /dev/null +++ b/src/bpic/decode.c @@ -0,0 +1,212 @@ +// +// decode.c +// bpic +// +// Created by theBreadCompany on 04.03.23. +// + +#include "decode.h" + +void read_to_buffer(png_structp png_ptr, png_bytep data, png_size_t length); + +decoded_data *bpic_decode_memory(encoded_data *data, bool *checksum_matching) { + + if(!data->buffer) { // is there even data + throw_err(bpic_error_input_buffer_null, 0); + return (void*)bpic_error_input_buffer_null; + } + if(data->size < 7) { // is there enough data to hold at least a header + throw_err(bpic_error_input_buffer_small, 0); + return (void*)bpic_error_input_buffer_small; + } + + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_infop png_info = png_create_info_struct(png_ptr); + + //printf("%zu\n", data->size); + /* + mem_decode *input = malloc(sizeof(mem_decode)); + input->buffer = data->buffer; + input->size = data->size; + input->pos = 0; + png_set_read_fn(png_ptr, input, read_to_buffer); + */ + + decoded_data *output = bpic_decode_file(fmemopen(data->buffer, data->size, "r"), checksum_matching); + + png_free(png_ptr, png_info); + png_destroy_read_struct(&png_ptr, NULL, NULL); + + //free(png_ptr); + return output; +} + +decoded_data* bpic_decode_file(FILE *input, bool *checksum_matching) { + fseek(input, 0, SEEK_SET); + + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_infop png_info = png_create_info_struct(png_ptr); + + if(input) { + png_init_io(png_ptr, input); + } else { + printf("Failed to init png with file!\n"); + return NULL; + } + + image_data *output = bpic_decode_png(png_ptr, png_info, checksum_matching); + + png_free(png_ptr, png_info); + png_destroy_read_struct(&png_ptr, NULL, NULL); + + return output; +} + +decoded_data* bpic_decode_png(png_structp png_ptr, png_infop png_info, bool *checksum_matching) { + + png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_IF_SAFE, (png_bytep)"pvSF", 1); + + png_read_info(png_ptr, png_info); + + printf("%u x %u\n", png_get_image_height(png_ptr, png_info), png_get_image_width(png_ptr, png_info)); + + png_unknown_chunkp chunks; + int chunk_count = png_get_unknown_chunks(png_ptr, png_info, &chunks); + + bpic_info bpic_chunk; + bpic_chunk.length = 0; + if(chunk_count) { // are there unknown chunks + for(uint8_t i = 0; i < chunk_count; i++) { // for every unknown chunk + png_unknown_chunk chunk = chunks[i]; // store chunk + + char *name = malloc(5); // alloc name + memcpy(name, chunk.name, 5); // copy name (same procedure as setting it + if(!strcmp(name, "pvSF") && chunk.size == 7) { // if name is pvSF header name and size is 12 bytes + bpic_chunk.px_size = pow(2, chunk.data[0] >> 3); // remember, the stored pxsize is a power of 2 leftshifted by 2 + bpic_chunk.encoding = chunk.data[0] & 0b11; + memcpy(&bpic_chunk.length, chunk.data + 1, 4); + memcpy(&bpic_chunk.fletcher16, chunk.data + 5, 2); + + for(int byte = 0; byte < 7; byte++) { + for(int bit = 1; bit < 9; bit++) { + printf("%s", chunk.data[byte] >> (8 - bit) & 1 ? "1" : "0"); + } + printf(" "); + } + printf("\n"); + } + free(name); + free(chunk.data); + } + } else { + throw_err("input is missing unknown chunks, meaning that the pvSF chunk is also missing!", 0); + return (void*)"input is missing unknown chunks, meaning that the pvSF chunk is also missing!"; + } + + if(!bpic_chunk.length) { + fprintf(stderr, "Missing data length in bpic chunk!\n"); + return NULL; + } + + free(chunks); + printf("pxsize: %u; encoding: %u; length: %zu; checksum: %u\n", bpic_chunk.px_size, bpic_chunk.encoding, bpic_chunk.length, bpic_chunk.fletcher16); + + int width = png_get_image_width(png_ptr, png_info);// * bpic_chunk.px_size; + int height = png_get_image_height(png_ptr, png_info);// * bpic_chunk.px_size; + + png_bytep *row_data = png_malloc(png_ptr, sizeof(png_bytep) * height * bpic_chunk.px_size); + for(int y = 0; y < height; y++) row_data[y] = png_malloc(png_ptr, sizeof(png_byte) * width * bpic_chunk.px_size); + png_set_rows(png_ptr, png_info, row_data); + png_read_image(png_ptr, row_data); + png_read_end(png_ptr, NULL); + + + int bits = png_get_bit_depth(png_ptr, png_info); + + // create a filebuffer that can hold as many bytes as needed according to the header + unsigned char *filebuffer = malloc(bpic_chunk.length);//(width/bits * height/bits);//(bpic_chunk.length); + // if encoding is bitwise + if(!bpic_chunk.encoding) { + // if the data is encoded in an 8 bit grayscale image + if(bits == 8) { + // for every [px_size]th in the data; also init a bit count + for(int y = 0, bit_i = 0; y < height; y += bpic_chunk.px_size) { + // for every [px_size]th column; also increment the bit count + for(int x = 0; x < width && (bit_i >> 3) < bpic_chunk.length; x += bpic_chunk.px_size, bit_i++) { + // store the pixel data + png_byte px = row_data[y][x * bpic_chunk.px_size]; + // store the corresponding the data + uint8_t value = px & (0x80 >> (bit_i & 0b111)); + // add the data in the byte of the buffer; 8 bit = 1 byte, so divide the bit count by 8 to get the byte position + filebuffer[bit_i >> 3] += value; + } + } + // if the data is encoded as a 1 bit grayscale image + } else if(bits == 1) { + // the image widths are required to be divisible by 8 to make parsing easier + // basically same algorithm, but now + for(int y = 0, bit_i = 0; y < height; y += bpic_chunk.px_size) { + // for every [px_size]th column; also increment the bit count + for(int x = 0; x < width && (bit_i >> 3) < bpic_chunk.length; x += bpic_chunk.px_size, bit_i++) { + // store the pixel data + png_byte px = row_data[y][x * bpic_chunk.px_size]; + // store the corresponding the data + uint8_t value = px & (0x80 >> (bit_i & 0b111)); + // add the data in the byte of the buffer; 8 bit = 1 byte, so divide the bit count by 8 to get the byte position + filebuffer[bit_i >> 3] += value; + } + } + // unknown bit count, cancel... + } else { + + } + // othrwise the encoding is bytewise + } else { + // this is far more straightforward as the rows literally represent the data + if(bpic_chunk.px_size == 1) { + // every row, except the last as it may break the available range of the file buffer + for(int y = 0; y < height - 1; y++) { + // copy the row to the filebuffer with an offset of the number of already processed bytes + memcpy(filebuffer + y * width, row_data[y], width); + } + // the required data size to be copied is calculated as the required length minus the already parsed length + memcpy(filebuffer + (height - 1) * width, row_data[height-1], bpic_chunk.length - width * (height - 1)); + // except if the pixel size is > 1, then only every [px_size]th pixel is valid data + } else { + // for every [px_size]th row except the last + for(int y = 0; y < height - bpic_chunk.px_size; y += bpic_chunk.px_size) { + // for every [px_size]th column + for(int x = 0; x < width; x += bpic_chunk.px_size) { + // copy every [px_size]th byte to the buffer at offset of the current byte + memcpy(filebuffer + y / bpic_chunk.px_size * width + x / bpic_chunk.px_size, row_data[y] + x * bpic_chunk.px_size, 1); + } + } + // separatly go through the last row as it may contain more data than required + for(int x = 0; x < bpic_chunk.length - (height - 1) * width; x += bpic_chunk.px_size) { + memcpy(filebuffer + (height - 1) * width + x / bpic_chunk.px_size, row_data[height - 1] + x * bpic_chunk.px_size, 1); + } + } + } + for(int y = 0; y < height; y++) png_free(png_ptr, row_data[y]); + png_free(png_ptr, row_data); + + uint16_t checksum = fletcher16(filebuffer, bpic_chunk.length); + printf("Checksum of decoded data: %u\n", checksum); + *checksum_matching = bpic_chunk.fletcher16 == checksum; + + + decoded_data *output = malloc(sizeof(decoded_data)); + output->buffer = filebuffer; + output->size = bpic_chunk.length; + + return output; +} + +void read_to_buffer(png_structp png_ptr, png_bytep data, png_size_t length) { + mem_decode *data_info = png_get_io_ptr(png_ptr); + if(data_info->size < data_info->pos + length) { + printf("How... The input is bigger than expected, already at %lu/%zu\n", data_info->pos + length, data_info->size); + } + memcpy(data, data_info->buffer + data_info->pos, length); + data_info->pos += length; +} diff --git a/src/bpic/encode.c b/src/bpic/encode.c new file mode 100644 index 0000000..ab64f66 --- /dev/null +++ b/src/bpic/encode.c @@ -0,0 +1,243 @@ +// +// encode.c +// bpic +// +// Created by theBreadCompany on 04.03.23. +// + +#include "encode.h" + +void write_to_buffer(png_structp png_ptr, png_bytep data, png_size_t length); + +png_bytepp convert_bitwise_1bit_grayscale(image_data *data, png_structp png_ptr, png_infop info_ptr); +png_bytepp convert_bitwise_8bit_grayscale(image_data *data, png_structp png_ptr, png_infop info_ptr); +png_bytepp convert_bytewise_8bit_rgb(image_data *data, png_structp png_ptr, png_infop info_ptr); + +png_bytepp convert_bitwise_1bit_grayscale(image_data *data, png_structp png_ptr, png_infop info_ptr) { + int width = round(sqrt((double)(data->size << 3))); + int height = round((data->size << 3) / (double)width); + + png_bytepp row_data = convert_bytewise_8bit_rgb(data, png_ptr, info_ptr); + + png_set_IHDR(png_ptr, info_ptr, width, height, 1, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + return row_data; +} + +png_bytepp convert_bitwise_8bit_grayscale(image_data *data, png_structp png_ptr, png_infop info_ptr) { + // width as the squareroot of the size times 8 because each bit requires a separate pixel; round app for + int width = round(sqrt((double)(data->size << 3))); + // height is size divided by width, + //int height = round(data->size * 8 / (double)width); + int height = width + (width * width < data->size << 3); + // embed image information + png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + // alloc enough memory to hold the pointers to the individual rows + png_byte **row_data = calloc(height, sizeof(png_bytep)); + // for every row + for(int y = 0; y < height; y++) { + // alloc row memory + row_data[y] = calloc(width, sizeof(png_byte)); + // for every column + for(int x = 0; x < width; x++) { + // get bit index + int px_i = y * width + x; + // check if already at EOF/EOB + if(px_i == (data->size << 3)) return row_data; + // get on/of state at bit index + uint8_t value = data->buffer[px_i >> 3] >> (7 - px_i % 8) & 1; + // store value at the current position in the image data (assigning 266 to uint8 results in 0, as 256 > uint8) + row_data[y][x] = value * 0xFF; + } + } + + return row_data; +} + +png_bytepp convert_bytewise_8bit_rgb(image_data *data, png_structp png_ptr, png_infop info_ptr) { + int width = round(sqrt((double)data->size)); + int height = width; + + png_bytep *row_data = calloc(height-1, sizeof(png_bytep)); + + for(int y = 0; y < height - 1; y++) { + // alloc row memory + row_data[y] = malloc(width * sizeof(png_byte)); + // literally copy chunks of the source to the PNG + memcpy(row_data[y], data->buffer + y * width, width); + } + // copy the last chunk separatly as it may be smaller than the width -> prevent random data + row_data[height-1] = calloc(width, sizeof(png_byte)); + printf("Copying last chunk to row %d of length %lu, starting at index %d in buffer\n", height-1, data->size - width*(height-1), (height-1)*width); + memcpy(row_data[height-1], data->buffer + (height-1)*width, data->size - width*(height-1)); + + png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + return row_data; +} + +image_data *bpic_encode_memory(image_data *data, bpic_info* info) { + int width = 0; + int height = 0; + + if(!data->buffer) { + throw_err(bpic_error_input_buffer_null, 0); + return (void*)bpic_error_input_buffer_null; + } + if(!info) info = (bpic_info*) malloc(sizeof(bpic_info)); + + + if(info->encoding != bpic_bitwise_encoding && info->encoding != bpic_bytewise_encoding) info->encoding = bpic_bitwise_encoding; + //if(!info->px_size) info->px_size = 4; + //if(!info->px_size) info->px_size = 1; + info->px_size = 1; + + //if(!data->size) data->size = data->size; + + if(info->encoding == bpic_bitwise_encoding) { + width = round(sqrt((double)(data->size << 3))); + //width += width % 8; + height = width;//round(data->size * 8 / (double)width); + } else if(info->encoding == bpic_bytewise_encoding) { + width = sqrt((double)data->size); + height = width + (width * width < data->size); + } else { + return NULL; + } + + printf("%u x %u -> %u x %u\n", width, height, width * info->px_size, height * info->px_size); + + info->fletcher16 = fletcher16(data->buffer, data->size); + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_infop info_ptr = png_create_info_struct(png_ptr); + + //png_bytep image_data = malloc(sizeof(png_byte) * width * height); + png_byte **row_data = NULL; + if(!info->encoding) { + clock_t start_bit8 = clock(); + row_data = convert_bitwise_8bit_grayscale(data, png_ptr, info_ptr); // works + clock_t end_bit8 = clock(); + printf("8-bit / grayscale; time: %f sec\n", (double)(end_bit8 - start_bit8)/CLOCKS_PER_SEC); + } else { + clock_t start_byte = clock(); + row_data = convert_bytewise_8bit_rgb(data, png_ptr, info_ptr); // why u no work + clock_t end_byte = clock(); + printf("1-byte / grayscale; time: %f sec\n", (double)(end_byte - start_byte)/CLOCKS_PER_SEC); + } + + png_byte **scaled_data = NULL; + + // then scale it to its actual size + if(info->px_size > 1) { + // cache row size + int row_size = sizeof(png_byte) * width * info->px_size * 2; + // create a pointer to the scaled up data + scaled_data = malloc(height * info->px_size * sizeof(png_bytep)); + // alloc memory here as rows will be allocated that normally would not be in range with the row index + for(int y = 0; y < height * info->px_size; y++) scaled_data[y] = malloc(row_size); + // for every [pixelsize]th row + for(int y = 0; y < height * info->px_size; y+=info->px_size) { + // for every [pixelsize]th column + for(int x = 0; x < width * info->px_size; x+=info->px_size) { + // copy the pixeldata from the 1x1 version to the scaled version; i.e. px_size=4, y=4, x=4 results in row_data[1][1] + memcpy(&scaled_data[y][x], &row_data[(y / info->px_size)][x / info->px_size], sizeof(png_byte)); + // fill the following pixels with the data at the position in scaled_data, in the previous example y=4 and x=5,6,7 + for(int x_offset = 1; x_offset < info->px_size; x_offset++) memcpy(&scaled_data[y][x+x_offset], &scaled_data[y][x], sizeof(png_byte)); + } + // copy the previously calculated row "down", meaning the data from y=4 to y=5,6,7 + for(int y_offset = 1; y_offset < info->px_size; y_offset++) memcpy(scaled_data[y+y_offset], scaled_data[y], row_size); + } + } else { + // if the pixel size is actually 1, just point to the already calculated data + scaled_data = row_data; + } + + png_bytep bpic_data = calloc(7, sizeof(png_byte)); + + bpic_data[0] += (uint8_t)sqrt(info->px_size) << 2; + bpic_data[0] += info->encoding; + memcpy(bpic_data + 1, &data->size, sizeof(uint32_t)); + memcpy(bpic_data + 5, &info->fletcher16, sizeof(uint16_t)); + + for(int byte = 0; byte < 7; byte++) { + for(int bit = 1; bit < 9; bit++) { + printf("%s", bpic_data[byte] >> (8 - bit) & 1 ? "1" : "0"); + } + printf(" "); + } + printf("\n"); + + printf("pxsize: %u; encoding: %u; length: %zu; checksum: %u\n", info->px_size, info->encoding, data->size, info->fletcher16); + + // Finally, set up the libpng stuff for writing + + mem_encode *target = malloc(sizeof(mem_encode)); + target->buffer = NULL; + target->size = 0; + png_set_write_fn(png_ptr, target, write_to_buffer, NULL); + + png_bytep chunk_data = calloc(7, sizeof(png_byte)); + memcpy(chunk_data, bpic_data, 7); + + png_size_t size = 7; + png_bytep type = (png_bytep)"pvSF"; + + png_write_info(png_ptr, info_ptr); + png_write_chunk(png_ptr, type, bpic_data, size); + png_write_image(png_ptr, scaled_data); + png_write_end(png_ptr, info_ptr); + + //fclose(out); + + png_destroy_info_struct(png_ptr, &info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + + + if(info->px_size > 1) { + for(int y = 0; y < height; y++) free(scaled_data[y]); + free(scaled_data); + } + for(int y = 0; y < height / info->px_size; y++) free(row_data[y]); + free(row_data); + free(bpic_data); + free(chunk_data); + free(info); + + return target; +} + +image_data *bpic_encode_file(FILE *input, bpic_info* info) { + if(!input) throw_err(bpic_error_input_file_null, 1); + + // prepare file + fseek(input, 0, SEEK_END); + size_t flen = ftell(input); + fseek(input, 0, SEEK_SET); + + image_data *data = malloc(sizeof(image_data)); + data->buffer = malloc(flen); + fread(data->buffer, sizeof(unsigned char), flen, input); + data->size = flen; + + encoded_data *output = bpic_encode_memory(data, info); + + free(data->buffer); + free(data); + + return output; +} + +void write_to_buffer(png_structp png_ptr, png_bytep data, png_size_t length) { + mem_encode* data_info = png_get_io_ptr(png_ptr); + size_t size = data_info->size + length; + + if(data_info->buffer) { + data_info->buffer = realloc(data_info->buffer, size); + } else { + data_info->buffer = malloc(size); + } + + memcpy(data_info->buffer + data_info->size, data, length); + data_info->size += length; +} diff --git a/src/bpic/include/bpic.h b/src/bpic/include/bpic.h new file mode 100644 index 0000000..d7d1522 --- /dev/null +++ b/src/bpic/include/bpic.h @@ -0,0 +1,15 @@ +// +// bpic.h +// VisualStorageKit +// +// Created by theBreadCompany on 04.03.23. +// + +#ifndef bpic_h +#define bpic_h + +#include "encode.h" +#include "decode.h" + + +#endif /* bpic_h */ diff --git a/src/bpic/include/common_utils.h b/src/bpic/include/common_utils.h new file mode 100644 index 0000000..bf1dadd --- /dev/null +++ b/src/bpic/include/common_utils.h @@ -0,0 +1,78 @@ +// +// common_utils.h +// bpic +// +// Created by theBreadCompany on 04.03.23. +// + +#ifndef common_utils_h +#define common_utils_h + +#include +#include +#include +#include +#include +#include +#include + + +#pragma mark - Header Information + +#define BPIC_BITWISE_ENCODING 0b00 +#define BPIC_BYTEWISE_ENCODING 0b01 + +#define BPIC_UNCOMPRESSED 0b00 +#define BPIC_COMPRESSED 0b10 + +enum bpic_encoding { + bpic_bitwise_encoding, + bpic_bytewise_encoding, + //bpic_bitwise_zipped_encoded, + //bpic_bytewise_zipped_encoded +}; + + +typedef struct { + uint8_t px_size; // first 6 bits (limits px size to up to 2^6-1 = 63, which should be more than sufficient + enum bpic_encoding encoding; // 7th and 8th bit, allowing for 4 encodings (only two used for now + size_t length; // 9th - 40 th bit (4 bytes) + uint16_t fletcher16; // 41st - 56th bit (2 bytes) + +} bpic_info; + +#pragma mark - Helper structs + +typedef struct { + unsigned char *buffer; + size_t size; +} mem_encode; + +typedef mem_encode image_data; +typedef image_data encoded_data; +typedef image_data decoded_data; + +typedef struct { + unsigned char *buffer; + size_t size; + png_uint_32 pos; +} mem_decode; + +#pragma mark - Crypto + +uint16_t fletcher16(const unsigned char *data, size_t size); // + +#pragma mark - Error Handling + +typedef const char* bpic_message; +extern bpic_message bpic_error_input_file_null; +extern bpic_message bpic_error_input_buffer_null; +extern bpic_message bpic_error_input_buffer_small; +extern bpic_message bpic_warn_encoding_invalid; +extern bpic_message bpic_warn_pxsize_null; + + +void throw_err(bpic_message message, int error); +void debug(FILE *pipe, const char *message, ...); + +#endif /* common_utils_h */ diff --git a/src/bpic/include/decode.h b/src/bpic/include/decode.h new file mode 100644 index 0000000..58d3c39 --- /dev/null +++ b/src/bpic/include/decode.h @@ -0,0 +1,43 @@ +// +// decode.h +// bpic +// +// Created by theBreadCompany on 04.03.23. +// + +#ifndef decode_h +#define decode_h + +#include "common_utils.h" + + +/** + decode memory + + @param data The buffer to decode + @param checksum_matching A pointer that stores stores whether the checksum matches the one encoded in the image + @return A pointer to the decoded memory or a bpic\_message on failure. + */ +decoded_data *bpic_decode_memory(encoded_data *data, bool *checksum_matching); +/** + decode a file + + @param input A pointer to a file to decode; NULL is invalid + @param checksum_matching A pointer that stores stores whether the checksum matches the one encoded in the image + @returns A pointer to the decoded memory or a bpic\_message on failure. + */ +decoded_data *bpic_decode_file(FILE *input, bool *checksum_matching); + +/** + Decode a png + + This method may only be invoced via `bpic_decode_file(FILE *input)` or `bpic_decode_memory(encoded_data *data)`. + + @param png_ptr A pointer to the png_ptr that is to be decoded. The input has to be set up, either via `png_set_read_fn` or `png_init_io` + @param png_info A pointer to a `png_info` struct if already available + @param checksum_matching A pointer that stores stores whether the checksum matches the one encoded in the image + @return A pointer to the decoded data + */ +decoded_data *bpic_decode_png(png_structrp png_ptr, png_inforp png_info, bool *checksum_matching); + +#endif /* decode_h */ diff --git a/src/bpic/include/encode.h b/src/bpic/include/encode.h new file mode 100644 index 0000000..5383ba5 --- /dev/null +++ b/src/bpic/include/encode.h @@ -0,0 +1,31 @@ +// +// encode.h +// bpic +// +// Created by theBreadCompany on 04.03.23. +// + +#ifndef encode_h +#define encode_h + +#include "common_utils.h" + +/** + encode memory + + @param data The memory buffer to encode + @param info A bpic_info struct containing parameters like encoding and any calculated variables on return; NULL is valid. + @returns A pointer to the encoded memory or a bpic\_message on if an error occured. + */ +encoded_data *bpic_encode_memory(image_data *data, bpic_info* info); + +/** + encode a file + + @param input A pointer to the file to encode; NULL is invalid + @param info A bpic_info struct containing parameters like encoding and any calculated variables on return; NULL is valid. + @returns A pointer to the encoded memory or a bpic\_message on if an error occured. + */ +encoded_data *bpic_encode_file(FILE *input, bpic_info* info); + +#endif /* encode_h */ diff --git a/src/bpicdecode/main.c b/src/bpicdecode/main.c new file mode 100644 index 0000000..7b8a5ea --- /dev/null +++ b/src/bpicdecode/main.c @@ -0,0 +1,74 @@ +// +// main.m +// bpicdecode +// +// Created by theBreadCompany on 28.02.23. +// + +#include +#include +#include "bpic.h" + + +int main(int argc, const char * argv[]) { + FILE *inputFile; + FILE *outputFile; + + if(argc < 2) { + fprintf(stderr, "Missing path to file!\n"); + return 1; + } + + inputFile = fopen(argv[1], "rb"); + if(!inputFile) { + fprintf(stderr, "Could not open file %s\n", argv[2]); + return 1; + } + + + // input as memory + encoded_data *input = malloc(sizeof(encoded_data)); + fseek(inputFile, 0, SEEK_END); + input->size = ftell(inputFile); + fseek(inputFile, 0, SEEK_SET); + input->buffer = malloc(input->size); + fread(input->buffer, sizeof(unsigned char), input->size, inputFile); + + + clock_t start = clock(); + bool checksum_matching = false; + //encoded_data *data = bpic_decode_file(inputFile); + encoded_data *data = bpic_decode_memory(input, &checksum_matching); + clock_t end = clock(); + + free(input->buffer); + free(input); + fclose(inputFile); + printf("Done in %f seconds.\n", (double)(end - start) / CLOCKS_PER_SEC); + + uint8_t len = strlen(argv[1]) + 5; + char *oname = calloc(len, sizeof(const char)); + strcpy(oname, argv[1]); + strcpy(oname + len - 5, ".png"); + outputFile = fopen(oname, "wb"); + + int error = 0; + if(fwrite(data->buffer, sizeof(unsigned char), data->size, outputFile) != data->size * sizeof(unsigned char)) { + fprintf(stderr, "Failed writing output file!"); + error = 1; + } + + fclose(outputFile); + + //free(inputFile); + free(oname); + free(data->buffer); + free(data); + //free(outputFile); + inputFile = NULL; + outputFile = NULL; + + return error; +} + + diff --git a/src/bpicdecode_test/main.c b/src/bpicdecode_test/main.c new file mode 100644 index 0000000..f58c9fe --- /dev/null +++ b/src/bpicdecode_test/main.c @@ -0,0 +1,5 @@ +#include + +int main(int argc, const char *argv[]) { + return 0; +} \ No newline at end of file diff --git a/src/bpicencode/main.c b/src/bpicencode/main.c new file mode 100644 index 0000000..eb70bb2 --- /dev/null +++ b/src/bpicencode/main.c @@ -0,0 +1,121 @@ +// +// main.m +// bpicencode +// +// Created by theBreadCompany on 28.02.23. +// + +#include +#include +#include +#include +#include +#include +#include +#include "bpic.h" + +// prototype for earlier use +void printHelper(void); +bool processArgv(int argc, const char *argv[], unsigned int *encoding, const char **input, const char **output, unsigned int *bit_depth); + +void printHelp(void) { + printf( + "Usage: bpicencode [-o -e -b ]\n" + " the file to encode\n" + "--output,-o sets a destination file name; default: .png\n" + "--encoding,-o sets a encoding (0 for bitwise, 1 for bytewise); default: 0\n" + "--bitdepth,-b sets a bit depth (1 (doesnt work with bytewise) or 8); default: 8\n" + "file sizes: 8 bit > 1 bit > bytewise encoding; safety is the other way around\n" + ); +} + +int main(int argc, const char * argv[]) { + int encoding; + FILE *inputFile; + FILE *outputFile; + + + if(argc == 1 || strlen(argv[1]) != 1 || !(strcmp(&argv[1][0], "0") || strcmp(&argv[1][0], "1"))) { + fprintf(stderr, "Encoding has to be either '0' for bitwise or '1' for bytewise!\n"); + return 1; + } + encoding = strcmp(argv[1], "0"); + + inputFile = fopen(argv[2], "rb"); + if(!inputFile) { + fprintf(stderr, "Could not open file %s!\n", argv[2]); + return 1; + } + + clock_t start = clock(); + + bpic_info *info = malloc(sizeof(bpic_info)); + info->encoding = encoding; + + decoded_data *data = bpic_encode_file(inputFile, info); + + clock_t end = clock(); + printf("Done in %f seconds.\n", (double)(end - start) / CLOCKS_PER_SEC); + + uint8_t len = strlen(argv[2]); + char *oname = malloc(len + 4 + 1); // 4 bytes for ".png" and 1 byte for \0 + strcpy(oname, argv[2]); + strcpy(oname + len, ".png"); + outputFile = fopen(oname, "wb"); + int code = 0; + if(fwrite(data->buffer, sizeof(unsigned char), data->size, outputFile) != data->size * sizeof(unsigned char)) { + fprintf(stderr, "Failed writing output file!"); + code = 1; + } + + fclose(outputFile); + free(oname); + free(data->buffer); + free(data); + + return code; +} + +bool processArgv(int argc, const char *argv[], unsigned int *encoding, const char **input, const char **output, unsigned int *bit_depth) { + for(int i = 0; i < argc; i++) { + if(strcmp(argv[i], "--output") || strcmp(argv[i], "-o")) { + if(i+1 <= argc || strncmp(argv[i+1], "-", 1)) { + fprintf(stderr, "missing output value!\n"); + return false; + } else { + i++; + *output = argv[i]; + } + } else if(strcmp(argv[i], "--bit-depth") || strcmp(argv[i], "-b")) { + if(i+1 <= argc || strncmp(argv[i+1], "-", 1)) { + fprintf(stderr, "missing bit depth value!\n"); + return false; + } else { + i++; + *bit_depth = (int)strtol(argv[i], NULL, 10); + } + } else if(strcmp(argv[i], "--encoding") || strcmp(argv[i], "-e")) { + if(i+1 <= argc || strncmp(argv[i+1], "-", 1)) { + fprintf(stderr, "missing encoding value!\n"); + return false; + } else { + i++; + *encoding = (int)strtol(argv[i], NULL, 10); + } + } else if(strncmp(argv[i], "--", 2)) { + fprintf(stderr, "unknown flag '%s'!\n", argv[i]); + return false; + } else { + FILE *fin = fopen(argv[i], "rb"); + if(fin) { + *input = argv[i]; + fclose(fin); + } else { + fprintf(stderr, "input file does not exist!\n"); + return false; + } + } + } + + return true; +} diff --git a/src/bpicencode_test/main.c b/src/bpicencode_test/main.c new file mode 100644 index 0000000..5afdf9c --- /dev/null +++ b/src/bpicencode_test/main.c @@ -0,0 +1,8 @@ +#include + +int main(int argc, const char *argv[]) { + + + + return 0; +} \ No newline at end of file