commit f5949f9d44e6ec3ac7e23f959fcd9bfd516e6f88 Author: Théo LUDWIG Date: Mon Dec 4 23:27:48 2023 +0100 chore: initial commit diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..5a6a40d --- /dev/null +++ b/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: "Google" +ColumnLimit: 0 diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..8d81c0c --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +libcproject +assets +build +bin diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..7993f3d --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +CC = gcc +CC_FLAGS = -Wall -Wextra -Wfloat-equal -Wundef -Werror -std=c17 -pedantic -pedantic-errors -O3 -I./ +CC_SANITIZER_FLAGS = -fsanitize=address -fsanitize=undefined +LIB_SOURCES = $(wildcard lib/*.c) +LIB_OBJECTS = $(patsubst %.c, %.o, $(LIB_SOURCES)) +HEADER_FILES = $(wildcard lib/*.h) +MAIN_EXECUTABLE = bin/main +LIBCPROJECT_PATH = ./libcproject/build/libcproject.a +LIBCPROJECT_CC_FLAGS = -L. -l:${LIBCPROJECT_PATH} + +${MAIN_EXECUTABLE}: ./main.c ${HEADER_FILES} $(addprefix build/, ${LIB_OBJECTS}) ${LIBCPROJECT_PATH} + mkdir --parents ./bin + mkdir --parents ./assets + ${CC} ${CC_FLAGS} ${CC_SANITIZER_FLAGS} $^ -o $@ ${LIBCPROJECT_CC_FLAGS} + +.PHONY: run +run: ${MAIN_EXECUTABLE} + ./$< ${ARGS} + +build/lib: + mkdir --parents ./build/lib + +build/lib/%.o: lib/%.c ${HEADER_FILES} | build/lib + ${CC} ${CC_FLAGS} -c $< -o $@ + +.PHONY: lint +lint: + clang-format --Werror --dry-run ${LIB_SOURCES} ${HEADER_FILES} ./main.c + +.PHONY: clean +clean: + rm --recursive --force ./bin ./build ./assets diff --git a/README.md b/README.md new file mode 100755 index 0000000..74451ab --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# fileformatsproject + +It needs [libcproject v4.1.1](https://github.com/theoludwig/libcproject/releases/tag/v4.1.1) static library to compile and execute. + +```sh +make # to compile +make run # to run main +make lint # to lint the code +make clean # to clean up +``` + +## Ideas/TODOs + +- [ ] Make functions `image_from_ppm_binary`, `image_from_ppm_text`, which receive `bytes_t*` and convert accordingly to `struct image*` +- [ ] Try to convert PPM to another format more famous (JPG, PNG, GIF, WEBP) and vice-versa +- [ ] Explore other file formats (not only images, but also audio, video, etc.) diff --git a/lib/image.c b/lib/image.c new file mode 100644 index 0000000..fdba78b --- /dev/null +++ b/lib/image.c @@ -0,0 +1,33 @@ +#include "image.h" + +struct image* image_initialization(size_t width, size_t height) { + struct image* image = malloc(sizeof(struct image)); + if (image == NULL) { + perror("Error (image_initialization)"); + exit(EXIT_FAILURE); + } + image->height = height; + image->width = width; + image->pixels = malloc(sizeof(struct pixel*) * image->height); + for (size_t row = 0; row < image->height; row++) { + image->pixels[row] = malloc(sizeof(struct pixel) * image->width); + for (size_t column = 0; column < image->width; column++) { + image->pixels[row][column].red = 0; + image->pixels[row][column].green = 0; + image->pixels[row][column].blue = 0; + } + } + return image; +} + +void image_free(struct image* image) { + if (image == NULL) { + perror("Error (image_free)"); + exit(EXIT_FAILURE); + } + for (size_t row = 0; row < image->height; row++) { + free(image->pixels[row]); + } + free(image->pixels); + free(image); +} diff --git a/lib/image.h b/lib/image.h new file mode 100644 index 0000000..17a4be1 --- /dev/null +++ b/lib/image.h @@ -0,0 +1,33 @@ +#ifndef __IMAGE__ +#define __IMAGE__ + +#include +#include +#include + +#include "../libcproject/libcproject.h" + +struct pixel { + byte_t red; + byte_t green; + byte_t blue; +}; + +struct image { + struct pixel** pixels; + size_t width; + size_t height; +}; + +/** + * @brief Initializes a new image (with all colors defaulting to 0 => black). + * + * @param width + * @param height + * @return struct image* + */ +struct image* image_initialization(size_t width, size_t height); + +void image_free(struct image* image); + +#endif diff --git a/lib/image_ppm.c b/lib/image_ppm.c new file mode 100644 index 0000000..7dfb4c7 --- /dev/null +++ b/lib/image_ppm.c @@ -0,0 +1,69 @@ +#include "image_ppm.h" + +byte_t* image_to_ppm_text(struct image* image, off_t* size) { + if (image == NULL || image->pixels == NULL || image->width == 0 || image->height == 0) { + errno = EINVAL; + perror("Error (image_to_ppm_text)"); + exit(EXIT_FAILURE); + } + string_t width = convert_number_to_string(image->width); + string_t height = convert_number_to_string(image->height); + string_t result = string_copy("P3\n"); + string_concatenate(&result, width); + free(width); + string_concatenate(&result, " "); + string_concatenate(&result, height); + free(height); + string_concatenate(&result, "\n255\n"); + for (size_t row = 0; row < image->height; row++) { + for (size_t column = 0; column < image->width; column++) { + struct pixel current_pixel = image->pixels[row][column]; + string_t red = convert_number_to_string(current_pixel.red); + string_t green = convert_number_to_string(current_pixel.green); + string_t blue = convert_number_to_string(current_pixel.blue); + string_concatenate(&result, red); + free(red); + string_concatenate(&result, " "); + string_concatenate(&result, green); + free(green); + string_concatenate(&result, " "); + string_concatenate(&result, blue); + free(blue); + string_concatenate(&result, " "); + } + string_concatenate(&result, "\n"); + } + (*size) = string_get_length(result); + return (byte_t*)result; +} + +byte_t* image_to_ppm_binary(struct image* image, off_t* size) { + if (image == NULL || image->pixels == NULL || image->width == 0 || image->height == 0) { + errno = EINVAL; + perror("Error (image_to_ppm_binary)"); + exit(EXIT_FAILURE); + } + size_t pixel_data_size = (image->width * image->height * 3); + string_t width = convert_number_to_string(image->width); + string_t height = convert_number_to_string(image->height); + size_t header_size = 14 + string_get_length(width) + string_get_length(height); + (*size) = header_size + pixel_data_size; + byte_t* result = (byte_t*)malloc(*size); + if (result == NULL) { + perror("Error (image_to_ppm_binary)"); + exit(EXIT_FAILURE); + } + byte_t* pixel_data_start = result; + pixel_data_start += sprintf((string_t)pixel_data_start, "P6\n%s\n%s\n255\n", width, height); + free(width); + free(height); + for (size_t row = 0; row < image->height; row++) { + for (size_t column = 0; column < image->width; column++) { + struct pixel current_pixel = image->pixels[row][column]; + *pixel_data_start++ = current_pixel.red; + *pixel_data_start++ = current_pixel.green; + *pixel_data_start++ = current_pixel.blue; + } + } + return result; +} diff --git a/lib/image_ppm.h b/lib/image_ppm.h new file mode 100644 index 0000000..0a601b9 --- /dev/null +++ b/lib/image_ppm.h @@ -0,0 +1,23 @@ +#ifndef __IMAGE_PPM__ +#define __IMAGE_PPM__ + +#include +#include +#include + +#include "../libcproject/libcproject.h" +#include "image.h" + +/** + * @brief Convert image to PPM (Portable PixMap format) defined by the Netpbm project (Plain/Raw text ASCII format). + * @see https://netpbm.sourceforge.net/doc/ppm.html + */ +byte_t* image_to_ppm_text(struct image* image, off_t* size); + +/** + * @brief Convert image to PPM (Portable PixMap format) defined by the Netpbm project (binary format). + * @see https://netpbm.sourceforge.net/doc/ppm.html + */ +byte_t* image_to_ppm_binary(struct image* image, off_t* size); + +#endif diff --git a/main.c b/main.c new file mode 100755 index 0000000..247edfe --- /dev/null +++ b/main.c @@ -0,0 +1,52 @@ +#include +#include +#include + +#include "lib/image.h" +#include "lib/image_ppm.h" +#include "libcproject/libcproject.h" + +int main() { + printf("Generating images...\n"); + size_t images_count = 0; + for (size_t red = 0; red < 256; red += 51) { + for (size_t green = 0; green < 256; green += 51) { + for (size_t blue = 0; blue < 256; blue += 51) { + struct image* image = image_initialization(100, 100); + for (size_t row = 0; row < image->height; row++) { + for (size_t column = 0; column < image->width; column++) { + image->pixels[row][column].red = red; + image->pixels[row][column].green = green; + image->pixels[row][column].blue = blue; + } + } + string_t red_string = convert_number_to_string((long long)red); + string_t green_string = convert_number_to_string((long long)green); + string_t blue_string = convert_number_to_string((long long)blue); + + string_t path = string_copy("./assets/"); + string_concatenate(&path, "image-red-"); + string_concatenate(&path, red_string); + string_concatenate(&path, "-green-"); + string_concatenate(&path, green_string); + string_concatenate(&path, "-blue-"); + string_concatenate(&path, blue_string); + string_concatenate(&path, ".ppm"); + free(red_string); + free(green_string); + free(blue_string); + + off_t file_size = 0; + byte_t* image_ppm = image_to_ppm_binary(image, &file_size); + filesystem_write(path, image_ppm, file_size); + images_count += 1; + + image_free(image); + free(path); + free(image_ppm); + } + } + } + printf("Generated %zu images in `./assets`.\n", images_count); + return EXIT_SUCCESS; +}