init with content
This commit is contained in:
commit
b4918934dc
16 changed files with 982 additions and 0 deletions
66
CMakeLists.txt
Normal file
66
CMakeLists.txt
Normal file
|
@ -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}
|
||||
)
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -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.
|
28
README.md
Normal file
28
README.md
Normal file
|
@ -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
|
BIN
src/.DS_Store
vendored
Normal file
BIN
src/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
src/bpic/.DS_Store
vendored
Normal file
BIN
src/bpic/.DS_Store
vendored
Normal file
Binary file not shown.
37
src/bpic/common_utils.c
Normal file
37
src/bpic/common_utils.c
Normal file
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// common_utils.c
|
||||
// bpic
|
||||
//
|
||||
// Created by theBreadCompany on 04.03.23.
|
||||
//
|
||||
|
||||
#include <common_utils.h>
|
||||
|
||||
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;
|
||||
}
|
212
src/bpic/decode.c
Normal file
212
src/bpic/decode.c
Normal file
|
@ -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;
|
||||
}
|
243
src/bpic/encode.c
Normal file
243
src/bpic/encode.c
Normal file
|
@ -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;
|
||||
}
|
15
src/bpic/include/bpic.h
Normal file
15
src/bpic/include/bpic.h
Normal file
|
@ -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 */
|
78
src/bpic/include/common_utils.h
Normal file
78
src/bpic/include/common_utils.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// common_utils.h
|
||||
// bpic
|
||||
//
|
||||
// Created by theBreadCompany on 04.03.23.
|
||||
//
|
||||
|
||||
#ifndef common_utils_h
|
||||
#define common_utils_h
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <png.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <strings.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
#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 */
|
43
src/bpic/include/decode.h
Normal file
43
src/bpic/include/decode.h
Normal file
|
@ -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 */
|
31
src/bpic/include/encode.h
Normal file
31
src/bpic/include/encode.h
Normal file
|
@ -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 */
|
74
src/bpicdecode/main.c
Normal file
74
src/bpicdecode/main.c
Normal file
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// main.m
|
||||
// bpicdecode
|
||||
//
|
||||
// Created by theBreadCompany on 28.02.23.
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
5
src/bpicdecode_test/main.c
Normal file
5
src/bpicdecode_test/main.c
Normal file
|
@ -0,0 +1,5 @@
|
|||
#include <bpic.h>
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
return 0;
|
||||
}
|
121
src/bpicencode/main.c
Normal file
121
src/bpicencode/main.c
Normal file
|
@ -0,0 +1,121 @@
|
|||
//
|
||||
// main.m
|
||||
// bpicencode
|
||||
//
|
||||
// Created by theBreadCompany on 28.02.23.
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#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 <file> [-o <output> -e <encoding> -b <bitdepth>]\n"
|
||||
"<file> the file to encode\n"
|
||||
"--output,-o sets a destination file name; default: <file>.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;
|
||||
}
|
8
src/bpicencode_test/main.c
Normal file
8
src/bpicencode_test/main.c
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include <bpic.h>
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Reference in a new issue