diff --git a/.gitignore b/.gitignore index 5b79a54..e9ada21 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ build/ -.cache/ \ No newline at end of file +.cache/ +assets/ +.DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt index e8de43f..4d02ea3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,22 +4,21 @@ set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) include_directories(include) project(image_processor LANGUAGES C) -# find_package(JPEG REQUIRED) -# -# add_executable(image_processor src/main.c) -# -# target_include_directories(image_processor PRIVATE ${JPEG_INCLUDE_DIRS}) -# -# target_link_libraries(image_processor PRIVATE ${JPEG_LIBRARIES}) + +set(SOURCES + src/main.c + src/transformations.c + src/utils.c + src/filters.c + src/imgio.c + +) find_library(TURBOJPEG_LIB NAMES turbojpeg REQUIRED) find_path(TURBOJPEG_INCLUDE NAMES turbojpeg.h REQUIRED) -add_executable(image_processor src/main.c) +add_executable(image_processor ${SOURCES}) -# 2. Include the directories for the header files target_include_directories(image_processor PRIVATE ${TURBOJPEG_INCLUDE}) -# 3. Link the actual turbojpeg library target_link_libraries(image_processor PRIVATE ${TURBOJPEG_LIB}) - diff --git a/include/filters.h b/include/filters.h new file mode 100644 index 0000000..aae33b8 --- /dev/null +++ b/include/filters.h @@ -0,0 +1,44 @@ +#ifndef FILTERS_H +#define FILTERS_H +#include "utils.h" + +unsigned int +u32_put_alpha(const unsigned int clr, const unsigned char alpha); + +unsigned int +u32_invert(const unsigned int clr); + +unsigned int +u32_swap_rb(const unsigned int clr); + +unsigned int +u32_grayscale(const unsigned int clr); + +unsigned int +u32_step(const unsigned int clr, const unsigned char max); + +unsigned int +u32_brighten(const unsigned int clr, const float factor); + +unsigned int +u32_mix(const unsigned int clr_a, const unsigned int clr_b); + +unsigned int +u32_lerp(const unsigned clr_a, const unsigned int clr_b, const float ratio); + +unsigned int +u32_mask(const unsigned int clr, const char keep); + +unsigned int +u32_rotl(const unsigned int clr, const int n); + +unsigned int +u32_contrast(const unsigned int clr, const float factor); + +unsigned int +u32_avg_gb(const unsigned int clr); + +unsigned int* +box_blur(const unsigned int* src_buf, const PPM* img, const int radius); + +#endif diff --git a/include/imgio.h b/include/imgio.h new file mode 100644 index 0000000..c3db9bd --- /dev/null +++ b/include/imgio.h @@ -0,0 +1,24 @@ +#ifndef IMGIO_H +#define IMGIO_H + +#include "utils.h" + +unsigned int* +ppm_load(const char* file, PPM* m_attr); + +int +ppm_save(const unsigned int* buf, const PPM img); + +unsigned int* +jpeg_load(JPEG* m_attr); + +int +jpeg_save(const unsigned int* src_buf, const JPEG* attr, const char* name); + +void +ppm_to_jpeg(PPM* in, JPEG* out); + +void +jpeg_to_ppm(JPEG* input, PPM* out); + +#endif diff --git a/include/transformations.h b/include/transformations.h new file mode 100644 index 0000000..ea40ab4 --- /dev/null +++ b/include/transformations.h @@ -0,0 +1,15 @@ +#ifndef TRANSFORMATIONS_H +#define TRANSFORMATIONS_H + +#include "utils.h" + +unsigned int* +ppm_flip_h(const unsigned int* buf, const PPM img); + +unsigned int* +ppm_flip_v(const unsigned int* buf, const PPM img); + +unsigned int* +u32_rotate(const unsigned int* buf, PPM* m_img, const int len); + +#endif diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 0000000..29e0777 --- /dev/null +++ b/include/utils.h @@ -0,0 +1,60 @@ +#ifndef UTILS_H +#define UTILS_H + +#define BYTE_A 0xFF000000 +#define BYTE_R 0x00FF0000 +#define BYTE_G 0x0000FF00 +#define BYTE_B 0x000000FF +#define BYTE_RGB 0x00FFFFFF +#define BYTE 0xFF +#define LONG_BYTE 0xFFFF + +#define SHIFT_8 8 +#define SHIFT_16 16 +#define SHIFT_24 24 + +#define MAX 255 +#define HALF_MAX ((int)(MAX / 2) + 1) + +#define DECODE_A(clr) (((clr) >> SHIFT_24) & BYTE) +#define DECODE_R(clr) (((clr) >> SHIFT_16) & BYTE) +#define DECODE_G(clr) (((clr) >> SHIFT_8) & BYTE) +#define DECODE_B(clr) ((clr) & BYTE) + +#define ENCODE_RGB(r, g, b) \ + (((unsigned int)(MAX) << SHIFT_24) | ((unsigned int)(r) << SHIFT_16) | \ + ((unsigned int)(g) << SHIFT_8) | (unsigned int)(b)) + +#define ENCODE_ARGB(a, r, g, b) \ + (((unsigned int)(a) << SHIFT_24) | ((unsigned int)(r) << SHIFT_16) | \ + ((unsigned int)(g) << SHIFT_8) | (unsigned int)(b)) + +#define ENCODE_BGRA(b, g, r, a) \ + (((unsigned int)(b) << SHIFT_24) | ((unsigned int)(g) << SHIFT_16) | \ + ((unsigned int)(r) << SHIFT_8) | (unsigned int)(a)) + +#define CLAMP_MAX(x) (((x) > MAX) ? MAX : (x)) +#define CLAMP_MIN(x) (((x) < 0) ? 0 : (x)) + +typedef struct +{ + int width, height; + int len; + char* name; + char fmt; +} PPM; + +typedef struct +{ + int width, height; + int len; + char* name; +} JPEG; + +unsigned long +u32to_u64(const unsigned int clr); + +unsigned int +u64to_u32(const unsigned long clr, const unsigned long u64_div); + +#endif diff --git a/src/filters.c b/src/filters.c new file mode 100644 index 0000000..9394d9c --- /dev/null +++ b/src/filters.c @@ -0,0 +1,217 @@ + + +#include + +// #include "utils.h" +#include "filters.h" + +unsigned int +u32_put_alpha(const unsigned int clr, const unsigned char alpha) +{ + return (clr & BYTE_RGB) | alpha << SHIFT_24; +} + +unsigned int +u32_invert(const unsigned int clr) +{ + return ((~clr) & BYTE_RGB) | (clr & BYTE_A); +} + +unsigned int +u32_swap_rb(const unsigned int clr) +{ + return (clr & 0xFF00FF00) | ((clr & BYTE_R) >> SHIFT_16) | + ((clr & BYTE_B) << SHIFT_16); +} + +unsigned int +u32_grayscale(const unsigned int clr) +{ + unsigned int red = DECODE_R(clr); + unsigned int green = DECODE_G(clr); + unsigned int blue = DECODE_B(clr); + unsigned int gray = + ((red * 77) + (green * 150) + (blue * 29)) >> SHIFT_8; + + return (clr & BYTE_A) | (gray << SHIFT_16) | (gray << SHIFT_8) | gray; +} + +unsigned int +u32_step(const unsigned int clr, const unsigned char max) +{ + if (DECODE_B(u32_grayscale(clr)) < max) + { + return ENCODE_ARGB(0, 0, 0, 0); + } + return ENCODE_ARGB(MAX, MAX, MAX, MAX); +} + +unsigned int +u32_brighten(const unsigned int clr, const float factor) +{ + unsigned int alpha = (clr & BYTE_A); + unsigned int red = CLAMP_MAX(DECODE_R(clr) * factor); + unsigned int green = CLAMP_MAX(DECODE_G(clr) * factor); + unsigned int blue = CLAMP_MAX(DECODE_B(clr) * factor); + return (clr & BYTE_A) | (red << SHIFT_16) | (green << SHIFT_8) | blue; +} + +unsigned int +u32_mix(const unsigned int clr_a, const unsigned int clr_b) +{ + unsigned char blue = (DECODE_B(clr_a) + DECODE_B(clr_b)) / 2; + unsigned char green = (DECODE_G(clr_a) + DECODE_G(clr_b)) / 2; + unsigned char red = (DECODE_R(clr_a) + DECODE_R(clr_b)) / 2; + + return ENCODE_ARGB(DECODE_A(clr_a), red, green, blue); +} + +unsigned int +u32_lerp(const unsigned clr_a, const unsigned int clr_b, const float ratio) +{ + unsigned char blue = + ((DECODE_B(clr_a) * (1 - ratio)) + ((DECODE_B(clr_b) * ratio))); + unsigned char green = + ((DECODE_G(clr_a) * (1 - ratio)) + ((DECODE_G(clr_b) * ratio))); + unsigned char red = + ((DECODE_R(clr_a) * (1 - ratio)) + ((DECODE_R(clr_b) * ratio))); + return ENCODE_ARGB(DECODE_A(clr_a), red, green, blue); +} + +unsigned int +u32_mask(const unsigned int clr, const char keep) +{ + switch (keep) + { + case 'r': + return clr & (BYTE_A | BYTE_R); + break; + case 'g': + return clr & (BYTE_A | BYTE_G); + break; + case 'b': + return clr & (BYTE_A | BYTE_B); + break; + default: + return 0; + break; + } + return 0; +} + +unsigned int +u32_rotl(const unsigned int clr, const int n) +{ + if (n > 8) + { + return 0; + } + unsigned int red = DECODE_R(clr); + unsigned int green = DECODE_G(clr); + unsigned int blue = DECODE_B(clr); + red = ((red << n) | (red >> (SHIFT_8 - n))) & BYTE; + green = ((green << n) | (green >> (SHIFT_8 - n))) & BYTE; + blue = ((blue << n) | (blue >> (SHIFT_8 - n))) & BYTE; + return ENCODE_RGB(red, green, blue); +} + +unsigned int +u32_contrast(const unsigned int clr, const float factor) +{ + int red = CLAMP_MIN(CLAMP_MAX( + (int)(factor * (float)(int)(DECODE_R(clr) - HALF_MAX)) + HALF_MAX)); + int green = CLAMP_MIN(CLAMP_MAX( + (int)(factor * (float)(int)(DECODE_G(clr) - HALF_MAX)) + HALF_MAX)); + int blue = CLAMP_MIN(CLAMP_MAX( + (int)(factor * (float)(int)(DECODE_B(clr) - HALF_MAX)) + HALF_MAX)); + return BYTE_A | (red << SHIFT_16) | (green << SHIFT_8) | blue; +} + +unsigned int +u32_avg_gb(const unsigned int clr) +{ + unsigned char red = DECODE_R(clr); + unsigned char blue = DECODE_B(clr); + unsigned char green = (red + blue) / 2; + return ENCODE_RGB(red, green, blue); +} + +unsigned int +blur_col(const unsigned int* src, const int x, const int y, const PPM* img, + const int radius) +{ + unsigned long pt = 0, cnt = 0; + + for (int dx = -radius; dx <= radius; dx++) + { + int px = x + dx; + if (px >= 0 && px < img->width) + { + pt += u32to_u64(src[y * img->width + px]); + cnt++; + } + } + + if (cnt > 0) + { + return u64to_u32(pt, cnt); + } + return src[y * img->width + x]; +} + +unsigned int +blur_row(const unsigned int* src, const int x, const int y, const PPM* img, + const int radius) +{ + unsigned long pt = 0, cnt = 0; + + for (int dy = -radius; dy <= radius; dy++) + { + int py = y + dy; + if (py >= 0 && py < img->height) + { + pt += u32to_u64(src[py * img->width + x]); + cnt++; + } + } + + if (cnt > 0) + { + return u64to_u32(pt, cnt); + } + return src[y * img->width + x]; +} + +unsigned int* +box_blur(const unsigned int* src_buf, const PPM* img, const int radius) +{ + unsigned int* first_pass = malloc(img->len * sizeof(unsigned int)); + if (!first_pass) + { + return 0; + } + unsigned int* dst_buf = malloc(img->len * sizeof(unsigned int)); + if (!dst_buf) + { + free(first_pass); + return 0; + } + for (int y = 0; y < img->height; y++) + { + for (int x = 0; x < img->width; x++) + { + first_pass[y * img->width + x] = + blur_col(src_buf, x, y, img, radius); + } + } + for (int y = 0; y < img->height; y++) + { + for (int x = 0; x < img->width; x++) + { + dst_buf[y * img->width + x] = + blur_row(first_pass, x, y, img, radius); + } + } + free(first_pass); + return dst_buf; +} diff --git a/src/imgio.c b/src/imgio.c new file mode 100644 index 0000000..1e49259 --- /dev/null +++ b/src/imgio.c @@ -0,0 +1,208 @@ +#include "imgio.h" +#include +#include +#include + +unsigned int* +ppm_load(const char* file, PPM* m_attr) +{ + printf("Starting decode...\n"); + unsigned char red; + unsigned char green; + unsigned char blue; + char fmt[3]; + int max_val; + FILE* fptr; + fptr = fopen(file, "r"); + if (!fptr) + { + printf("Invalid file name: %s\n", file); + return NULL; + } + fscanf(fptr, "%s", fmt); + m_attr->fmt = fmt[1]; + fscanf(fptr, "%d %d", &m_attr->width, &m_attr->height); + m_attr->len = m_attr->width * m_attr->height; + fscanf(fptr, "%d", &max_val); + fgetc(fptr); + unsigned char* img_buf = malloc(m_attr->len * 3); + if (!img_buf) + { + return NULL; + } + unsigned int* clr_buf = malloc(m_attr->len * sizeof(unsigned int)); + if (!clr_buf) + { + free(img_buf); + return NULL; + } + if (m_attr->fmt == '3') + { + for (int i = 0; i < m_attr->len; i++) + { + fscanf(fptr, "%hhu %hhu %hhu", &red, &green, &blue); + clr_buf[i] = ENCODE_RGB(red, green, blue); + } + } + else if (m_attr->fmt == '6') + { + fread(img_buf, 3, m_attr->len, fptr); + + for (int i = 0; i < m_attr->len; i++) + { + red = img_buf[i * 3]; + green = img_buf[i * 3 + 1]; + blue = img_buf[i * 3 + 2]; + clr_buf[i] = ENCODE_RGB(red, green, blue); + } + } + + fclose(fptr); + free(img_buf); + printf("Decoding done!\n"); + return clr_buf; +} + +int +ppm_save(const unsigned int* buf, const PPM img) +{ + if (!buf) + { + return 0; + } + unsigned char red, green, blue; + unsigned char* dst_buf = malloc(img.len * 3); + if (!dst_buf) + { + return 0; + } + printf("Starting encode...\n"); + FILE* fptr; + + if (img.fmt == '3') + { + fptr = fopen(img.name, "w"); + } + if (img.fmt == '6') + { + fptr = fopen(img.name, "wb"); + } + if (!fptr) + { + free(dst_buf); + return 0; + } + + fprintf(fptr, "P%c\n%d %d\n255\n", img.fmt, img.width, img.height); + if (img.fmt == '3') + { + for (int i = 0; i < img.len; i++) + { + red = DECODE_R(buf[i]); + green = DECODE_G(buf[i]); + blue = DECODE_B(buf[i]); + fprintf(fptr, "%d %d %d ", red, green, blue); + if (i % 7 == 0) + { + fprintf(fptr, "\n"); + } + } + } + if (img.fmt == '6') + { + for (int i = 0; i < img.len; i++) + { + dst_buf[i * 3] = DECODE_R(buf[i]); + dst_buf[i * 3 + 1] = DECODE_G(buf[i]); + dst_buf[i * 3 + 2] = DECODE_B(buf[i]); + } + fwrite(dst_buf, 1, img.len * 3, fptr); + } + fclose(fptr); + free(dst_buf); + printf("Encode Done!\n"); + return 1; +} + +unsigned int* +jpeg_load(JPEG* m_attr) +{ + printf("Starting Decode...\n"); + tjhandle tjdecompress = tj3Init(TJINIT_DECOMPRESS); + unsigned char* jpeg_buf = NULL; + FILE* infile = fopen(m_attr->name, "rb"); + if (!infile) + { + return NULL; + } + fseek(infile, 0, SEEK_END); + m_attr->len = ftell(infile); + fseek(infile, 0, SEEK_SET); + jpeg_buf = (unsigned char*)malloc(m_attr->len); + fread(jpeg_buf, 1, m_attr->len, infile); + fclose(infile); + if (tj3DecompressHeader(tjdecompress, jpeg_buf, m_attr->len) < 0) + { + printf("Header error: %s\n", tj3GetErrorStr(tjdecompress)); + free(jpeg_buf); + return NULL; + } + m_attr->width = tj3Get(tjdecompress, TJPARAM_JPEGWIDTH); + m_attr->height = tj3Get(tjdecompress, TJPARAM_JPEGHEIGHT); + unsigned int* pixels = + malloc(m_attr->width * m_attr->height * sizeof(unsigned int)); + tj3Decompress8(tjdecompress, jpeg_buf, m_attr->len, + (unsigned char*)pixels, 0, TJPF_BGRA); + tj3Destroy(tjdecompress); + free(jpeg_buf); + printf("Decode Successful!\n"); + return pixels; +} + +int +jpeg_save(const unsigned int* src_buf, const JPEG* attr, const char* name) +{ + printf("Starting Decode...\n"); + tjhandle tjcompress = tj3Init(TJINIT_COMPRESS); + unsigned char* dst_buf = NULL; + size_t dst_size = 0; + tj3Set(tjcompress, TJPARAM_QUALITY, 80); + tj3Set(tjcompress, TJPARAM_SUBSAMP, TJSAMP_420); + if (tj3Compress8(tjcompress, (unsigned char*)src_buf, attr->width, 0, + attr->height, TJPF_BGRA, &dst_buf, &dst_size) < 0) + { + printf("TurboJPEG Error: %s\n", tj3GetErrorStr(tjcompress)); + } + FILE* fptr = fopen(name, "wb"); + if (!fptr || dst_buf == NULL || dst_size <= 0) + { + tj3Free(dst_buf); + return 0; + } + fwrite(dst_buf, 1, dst_size, fptr); + fclose(fptr); + + tj3Destroy(tjcompress); + tj3Free(dst_buf); + printf("Encode Successful!\n"); + return 1; +} + +void +ppm_to_jpeg(PPM* in, JPEG* out) +{ + unsigned int* src_buf = ppm_load(in->name, in); + out->width = in->width; + out->height = in->height; + jpeg_save(src_buf, out, out->name); +} + +void +jpeg_to_ppm(JPEG* input, PPM* out) +{ + unsigned int* src_buf = jpeg_load(input); + out->width = input->width; + out->height = input->height; + out->len = out->width * out->height; + ppm_save(src_buf, *out); +} diff --git a/src/main.c b/src/main.c index c61d6a2..7cf5540 100644 --- a/src/main.c +++ b/src/main.c @@ -1,560 +1,9 @@ -#include #include -#include #include -#include - -#define BYTE_A 0xFF000000 -#define BYTE_R 0x00FF0000 -#define BYTE_G 0x0000FF00 -#define BYTE_B 0x000000FF -#define BYTE_RGB 0x00FFFFFF -#define BYTE 0xFF -#define LONG_BYTE 0xFFFF - -#define SHIFT_8 8 -#define SHIFT_16 16 -#define SHIFT_24 24 - -#define MAX 255 -#define HALF_MAX ((int)(MAX / 2) + 1) - -#define DECODE_A(clr) (((clr) >> SHIFT_24) & BYTE) -#define DECODE_R(clr) (((clr) >> SHIFT_16) & BYTE) -#define DECODE_G(clr) (((clr) >> SHIFT_8) & BYTE) -#define DECODE_B(clr) ((clr) & BYTE) - -#define ENCODE_RGB(r, g, b) \ - (((unsigned int)(MAX) << SHIFT_24) | ((unsigned int)(r) << SHIFT_16) | \ - ((unsigned int)(g) << SHIFT_8) | (unsigned int)(b)) - -#define ENCODE_ARGB(a, r, g, b) \ - (((unsigned int)(a) << SHIFT_24) | ((unsigned int)(r) << SHIFT_16) | \ - ((unsigned int)(g) << SHIFT_8) | (unsigned int)(b)) - -#define ENCODE_BGRA(b, g, r, a) \ - (((unsigned int)(b) << SHIFT_24) | ((unsigned int)(g) << SHIFT_16) | \ - ((unsigned int)(r) << SHIFT_8) | (unsigned int)(a)) - -#define CLAMP_MAX(x) (((x) > MAX) ? MAX : (x)) -#define CLAMP_MIN(x) (((x) < 0) ? 0 : (x)) - -typedef struct -{ - int width, height; - int len; - char* name; - char fmt; -} PPM; - -typedef struct -{ - int width, height; - int len; - char* name; -} JPEG; - -static inline unsigned long -u32to_u64(const unsigned int clr) -{ - unsigned long mask = 0; - unsigned long alpha = DECODE_A(clr); - unsigned long red = DECODE_R(clr); - unsigned long green = DECODE_G(clr); - unsigned long blue = DECODE_B(clr); - return (alpha << 48) | (red << 32) | (green << 16) | (blue); -} - -static inline unsigned int -u64to_u32(const unsigned long clr, const unsigned long u64_div) -{ - unsigned int a = ((clr >> 48) & LONG_BYTE); - unsigned int r = ((clr >> 32) & LONG_BYTE); - unsigned int g = ((clr >> 16) & LONG_BYTE); - unsigned int b = (clr & LONG_BYTE); - return ENCODE_ARGB((a / u64_div), (r / u64_div), (g / u64_div), - (b / u64_div)); -} - -static inline unsigned int -u32_put_alpha(const unsigned int clr, const unsigned char alpha) -{ - return (clr & BYTE_RGB) | alpha << SHIFT_24; -} - -static inline unsigned int -u32_invert(const unsigned int clr) -{ - return ((~clr) & BYTE_RGB) | (clr & BYTE_A); -} - -static inline unsigned int -u32_swap_rb(const unsigned int clr) -{ - return (clr & 0xFF00FF00) | ((clr & BYTE_R) >> SHIFT_16) | - ((clr & BYTE_B) << SHIFT_16); -} - -static inline unsigned int -u32_grayscale(const unsigned int clr) -{ - unsigned int red = DECODE_R(clr); - unsigned int green = DECODE_G(clr); - unsigned int blue = DECODE_B(clr); - unsigned int gray = - ((red * 77) + (green * 150) + (blue * 29)) >> SHIFT_8; - - return (clr & BYTE_A) | (gray << SHIFT_16) | (gray << SHIFT_8) | gray; -} - -static inline unsigned int -u32_step(const unsigned int clr, const unsigned char max) -{ - if (DECODE_B(u32_grayscale(clr)) < max) - { - return ENCODE_ARGB(0, 0, 0, 0); - } - return ENCODE_ARGB(MAX, MAX, MAX, MAX); -} - -static inline unsigned int -u32_brighten(const unsigned int clr, const float factor) -{ - unsigned int alpha = (clr & BYTE_A); - unsigned int red = CLAMP_MAX(DECODE_R(clr) * factor); - unsigned int green = CLAMP_MAX(DECODE_G(clr) * factor); - unsigned int blue = CLAMP_MAX(DECODE_B(clr) * factor); - return (clr & BYTE_A) | (red << SHIFT_16) | (green << SHIFT_8) | blue; -} - -static inline unsigned int -u32_mix(const unsigned int clr_a, const unsigned int clr_b) -{ - unsigned char blue = (DECODE_B(clr_a) + DECODE_B(clr_b)) / 2; - unsigned char green = (DECODE_G(clr_a) + DECODE_G(clr_b)) / 2; - unsigned char red = (DECODE_R(clr_a) + DECODE_R(clr_b)) / 2; - - return ENCODE_ARGB(DECODE_A(clr_a), red, green, blue); -} - -static inline unsigned int -u32_lerp(const unsigned clr_a, const unsigned int clr_b, const float ratio) -{ - unsigned char blue = - ((DECODE_B(clr_a) * (1 - ratio)) + ((DECODE_B(clr_b) * ratio))); - unsigned char green = - ((DECODE_G(clr_a) * (1 - ratio)) + ((DECODE_G(clr_b) * ratio))); - unsigned char red = - ((DECODE_R(clr_a) * (1 - ratio)) + ((DECODE_R(clr_b) * ratio))); - return ENCODE_ARGB(DECODE_A(clr_a), red, green, blue); -} - -static inline unsigned int -u32_mask(const unsigned int clr, const char keep) -{ - switch (keep) - { - case 'r': - return clr & (BYTE_A | BYTE_R); - break; - case 'g': - return clr & (BYTE_A | BYTE_G); - break; - case 'b': - return clr & (BYTE_A | BYTE_B); - break; - default: - return 0; - break; - } - return 0; -} - -static inline unsigned int -u32_rotl(const unsigned int clr, const int n) -{ - if (n > 8) - { - return 0; - } - unsigned int red = DECODE_R(clr); - unsigned int green = DECODE_G(clr); - unsigned int blue = DECODE_B(clr); - red = ((red << n) | (red >> (SHIFT_8 - n))) & BYTE; - green = ((green << n) | (green >> (SHIFT_8 - n))) & BYTE; - blue = ((blue << n) | (blue >> (SHIFT_8 - n))) & BYTE; - return ENCODE_RGB(red, green, blue); -} - -static inline unsigned int* -ppm_flip_h(const unsigned int* buf, const PPM img) -{ - unsigned int* flip = malloc(img.len * sizeof(unsigned int)); - if (!flip) - { - return NULL; - } - int idx = 0; - for (int i = 0; i < img.height; i++) - { - for (int k = img.width - 1; k >= 0; k--) - { - flip[idx] = buf[(i * img.width) + k]; - idx++; - } - } - - return flip; -} - -static inline unsigned int* -ppm_flip_v(const unsigned int* buf, const PPM img) -{ - unsigned int* flip = malloc(img.len * sizeof(unsigned int)); - if (!flip) - { - return NULL; - } - - int idx = 0; - for (int i = img.height - 1; i >= 0; i--) - { - for (int k = 0; k < img.width; k++) - { - flip[idx] = buf[(i * img.width) + k]; - idx++; - } - } - - return flip; -} - -static inline unsigned int -u32_contrast(const unsigned int clr, const float factor) -{ - int red = CLAMP_MIN(CLAMP_MAX( - (int)(factor * (float)(int)(DECODE_R(clr) - HALF_MAX)) + HALF_MAX)); - int green = CLAMP_MIN(CLAMP_MAX( - (int)(factor * (float)(int)(DECODE_G(clr) - HALF_MAX)) + HALF_MAX)); - int blue = CLAMP_MIN(CLAMP_MAX( - (int)(factor * (float)(int)(DECODE_B(clr) - HALF_MAX)) + HALF_MAX)); - return BYTE_A | (red << SHIFT_16) | (green << SHIFT_8) | blue; -} - -static inline unsigned int* -u32_rotate(const unsigned int* buf, PPM* m_img, const int len) -{ - unsigned int* mask = malloc(len * sizeof(unsigned int)); - int old_width = m_img->width; - int old_height = m_img->height; - for (int row = 0; row < old_height; row++) - { - for (int col = 0; col < old_width; col++) - { - int new_height = col; - int new_width = (old_height - 1 - row); - - mask[(new_height * old_height) + new_width] = - buf[(row * old_width) + col]; - } - } - m_img->width = old_height; - m_img->height = old_width; - return mask; -} - -static inline unsigned int -u32_avg_gb(const unsigned int clr) -{ - unsigned char red = DECODE_R(clr); - unsigned char blue = DECODE_B(clr); - unsigned char green = (red + blue) / 2; - return ENCODE_RGB(red, green, blue); -} - -static inline unsigned int -blur_col(const unsigned int* src, const int x, const int y, const PPM* img, - const int radius) -{ - unsigned long pt = 0, cnt = 0; - - for (int dx = -radius; dx <= radius; dx++) - { - int px = x + dx; - if (px >= 0 && px < img->width) - { - pt += u32to_u64(src[y * img->width + px]); - cnt++; - } - } - - if (cnt > 0) - { - return u64to_u32(pt, cnt); - } - return src[y * img->width + x]; -} - -static inline unsigned int -blur_row(const unsigned int* src, const int x, const int y, const PPM* img, - const int radius) -{ - unsigned long pt = 0, cnt = 0; - - for (int dy = -radius; dy <= radius; dy++) - { - int py = y + dy; - if (py >= 0 && py < img->height) - { - pt += u32to_u64(src[py * img->width + x]); - cnt++; - } - } - - if (cnt > 0) - { - return u64to_u32(pt, cnt); - } - return src[y * img->width + x]; -} - -unsigned int* -box_blur(const unsigned int* src_buf, const PPM* img, const int radius) -{ - unsigned int* first_pass = malloc(img->len * sizeof(unsigned int)); - if (!first_pass) - { - return 0; - } - unsigned int* dst_buf = malloc(img->len * sizeof(unsigned int)); - if (!dst_buf) - { - free(first_pass); - return 0; - } - for (int y = 0; y < img->height; y++) - { - for (int x = 0; x < img->width; x++) - { - first_pass[y * img->width + x] = - blur_col(src_buf, x, y, img, radius); - } - } - for (int y = 0; y < img->height; y++) - { - for (int x = 0; x < img->width; x++) - { - dst_buf[y * img->width + x] = - blur_row(first_pass, x, y, img, radius); - } - } - free(first_pass); - return dst_buf; -} - -static inline unsigned int* -ppm_load(const char* file, PPM* m_attr) -{ - printf("Starting decode...\n"); - unsigned char red; - unsigned char green; - unsigned char blue; - char fmt[3]; - int max_val; - FILE* fptr; - fptr = fopen(file, "r"); - if (!fptr) - { - printf("Invalid file name: %s\n", file); - return NULL; - } - fscanf(fptr, "%s", fmt); - m_attr->fmt = fmt[1]; - fscanf(fptr, "%d %d", &m_attr->width, &m_attr->height); - m_attr->len = m_attr->width * m_attr->height; - fscanf(fptr, "%d", &max_val); - fgetc(fptr); - unsigned char* img_buf = malloc(m_attr->len * 3); - if (!img_buf) - { - return NULL; - } - unsigned int* clr_buf = malloc(m_attr->len * sizeof(unsigned int)); - if (!clr_buf) - { - free(img_buf); - return NULL; - } - if (m_attr->fmt == '3') - { - for (int i = 0; i < m_attr->len; i++) - { - fscanf(fptr, "%hhu %hhu %hhu", &red, &green, &blue); - clr_buf[i] = ENCODE_RGB(red, green, blue); - } - } - else if (m_attr->fmt == '6') - { - fread(img_buf, 3, m_attr->len, fptr); - - for (int i = 0; i < m_attr->len; i++) - { - red = img_buf[i * 3]; - green = img_buf[i * 3 + 1]; - blue = img_buf[i * 3 + 2]; - clr_buf[i] = ENCODE_RGB(red, green, blue); - } - } - - fclose(fptr); - free(img_buf); - printf("Decoding done!\n"); - return clr_buf; -} - -static inline int -ppm_save(const unsigned int* buf, const PPM img) -{ - if (!buf) - { - return 0; - } - unsigned char red, green, blue; - unsigned char* dst_buf = malloc(img.len * 3); - if (!dst_buf) - { - return 0; - } - printf("Starting encode...\n"); - FILE* fptr; - - if (img.fmt == '3') - { - fptr = fopen(img.name, "w"); - } - if (img.fmt == '6') - { - fptr = fopen(img.name, "wb"); - } - if (!fptr) - { - free(dst_buf); - return 0; - } - - fprintf(fptr, "P%c\n%d %d\n255\n", img.fmt, img.width, img.height); - if (img.fmt == '3') - { - for (int i = 0; i < img.len; i++) - { - red = DECODE_R(buf[i]); - green = DECODE_G(buf[i]); - blue = DECODE_B(buf[i]); - fprintf(fptr, "%d %d %d ", red, green, blue); - if (i % 7 == 0) - { - fprintf(fptr, "\n"); - } - } - } - if (img.fmt == '6') - { - for (int i = 0; i < img.len; i++) - { - dst_buf[i * 3] = DECODE_R(buf[i]); - dst_buf[i * 3 + 1] = DECODE_G(buf[i]); - dst_buf[i * 3 + 2] = DECODE_B(buf[i]); - } - fwrite(dst_buf, 1, img.len * 3, fptr); - } - fclose(fptr); - free(dst_buf); - printf("Encode Done!\n"); - return 1; -} - -unsigned int* -jpeg_load(JPEG* m_attr) -{ - printf("Starting Decode...\n"); - tjhandle tjdecompress = tj3Init(TJINIT_DECOMPRESS); - unsigned char* jpeg_buf = NULL; - FILE* infile = fopen(m_attr->name, "rb"); - if (!infile) - { - return NULL; - } - fseek(infile, 0, SEEK_END); - m_attr->len = ftell(infile); - fseek(infile, 0, SEEK_SET); - jpeg_buf = (unsigned char*)malloc(m_attr->len); - fread(jpeg_buf, 1, m_attr->len, infile); - fclose(infile); - if (tj3DecompressHeader(tjdecompress, jpeg_buf, m_attr->len) < 0) - { - printf("Header error: %s\n", tj3GetErrorStr(tjdecompress)); - free(jpeg_buf); - return NULL; - } - m_attr->width = tj3Get(tjdecompress, TJPARAM_JPEGWIDTH); - m_attr->height = tj3Get(tjdecompress, TJPARAM_JPEGHEIGHT); - unsigned int* pixels = - malloc(m_attr->width * m_attr->height * sizeof(unsigned int)); - tj3Decompress8(tjdecompress, jpeg_buf, m_attr->len, - (unsigned char*)pixels, 0, TJPF_BGRA); - tj3Destroy(tjdecompress); - free(jpeg_buf); - printf("Decode Successful!\n"); - return pixels; -} - -int -jpeg_save(const unsigned int* src_buf, const JPEG* attr, const char* name) -{ - printf("Starting Decode...\n"); - tjhandle tjcompress = tj3Init(TJINIT_COMPRESS); - unsigned char* dst_buf = NULL; - size_t dst_size = 0; - tj3Set(tjcompress, TJPARAM_QUALITY, 80); - tj3Set(tjcompress, TJPARAM_SUBSAMP, TJSAMP_420); - if (tj3Compress8(tjcompress, (unsigned char*)src_buf, attr->width, 0, - attr->height, TJPF_BGRA, &dst_buf, &dst_size) < 0) - { - printf("TurboJPEG Error: %s\n", tj3GetErrorStr(tjcompress)); - } - FILE* fptr = fopen(name, "wb"); - if (!fptr || dst_buf == NULL || dst_size <= 0) - { - tj3Free(dst_buf); - return 0; - } - fwrite(dst_buf, 1, dst_size, fptr); - fclose(fptr); - - tj3Destroy(tjcompress); - tj3Free(dst_buf); - printf("Encode Successful!\n"); - return 1; -} - -void -ppm_to_jpeg(PPM* in, JPEG* out) -{ - unsigned int* src_buf = ppm_load(in->name, in); - out->width = in->width; - out->height = in->height; - jpeg_save(src_buf, out, out->name); -} - -void -jpeg_to_ppm(JPEG* input, PPM* out) -{ - unsigned int* src_buf = jpeg_load(input); - out->width = input->width; - out->height = input->height; - out->len = out->width * out->height; - ppm_save(src_buf, *out); -} +#include "utils.h" +#include "filters.h" +// #include "transformations.h" +#include "imgio.h" int main() @@ -564,11 +13,9 @@ main() }; unsigned int* src_buf = ppm_load("../assets/8ktest.ppm", &out); - unsigned int* flip = ppm_flip_h(src_buf, out); - // unsigned int* blurred = box_blur(src_buf, &out, 10); - ppm_save(flip, out); + unsigned int* blurred = box_blur(src_buf, &out, 10); + ppm_save(blurred, out); free(src_buf); - // free(blurred); - free(flip); + free(blurred); return 0; } diff --git a/src/transformations.c b/src/transformations.c new file mode 100644 index 0000000..8cee62a --- /dev/null +++ b/src/transformations.c @@ -0,0 +1,67 @@ +#include +#include "transformations.h" + +unsigned int* +ppm_flip_h(const unsigned int* buf, const PPM img) +{ + unsigned int* flip = malloc(img.len * sizeof(unsigned int)); + if (!flip) + { + return NULL; + } + int idx = 0; + for (int i = 0; i < img.height; i++) + { + for (int k = img.width - 1; k >= 0; k--) + { + flip[idx] = buf[(i * img.width) + k]; + idx++; + } + } + + return flip; +} + +unsigned int* +ppm_flip_v(const unsigned int* buf, const PPM img) +{ + unsigned int* flip = malloc(img.len * sizeof(unsigned int)); + if (!flip) + { + return NULL; + } + + int idx = 0; + for (int i = img.height - 1; i >= 0; i--) + { + for (int k = 0; k < img.width; k++) + { + flip[idx] = buf[(i * img.width) + k]; + idx++; + } + } + + return flip; +} + +unsigned int* +u32_rotate(const unsigned int* buf, PPM* m_img, const int len) +{ + unsigned int* mask = malloc(len * sizeof(unsigned int)); + int old_width = m_img->width; + int old_height = m_img->height; + for (int row = 0; row < old_height; row++) + { + for (int col = 0; col < old_width; col++) + { + int new_height = col; + int new_width = (old_height - 1 - row); + + mask[(new_height * old_height) + new_width] = + buf[(row * old_width) + col]; + } + } + m_img->width = old_height; + m_img->height = old_width; + return mask; +} diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..cff2cd5 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,23 @@ +#include "utils.h" + +unsigned long +u32to_u64(const unsigned int clr) +{ + unsigned long mask = 0; + unsigned long alpha = DECODE_A(clr); + unsigned long red = DECODE_R(clr); + unsigned long green = DECODE_G(clr); + unsigned long blue = DECODE_B(clr); + return (alpha << 48) | (red << 32) | (green << 16) | (blue); +} + +unsigned int +u64to_u32(const unsigned long clr, const unsigned long u64_div) +{ + unsigned int a = ((clr >> 48) & LONG_BYTE); + unsigned int r = ((clr >> 32) & LONG_BYTE); + unsigned int g = ((clr >> 16) & LONG_BYTE); + unsigned int b = (clr & LONG_BYTE); + return ENCODE_ARGB((a / u64_div), (r / u64_div), (g / u64_div), + (b / u64_div)); +}