diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/.DS_Store differ diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..b46d0de --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,59 @@ +--- +Checks: > + -*, + bugprone-assert-side-effect, + bugprone-assignment-in-if-condition, + bugprone-bool-pointer-implicit-conversion, + bugprone-dangling-handle, + bugprone-dynamic-static-initializers, + bugprone-infinite-loop, + bugprone-integer-division, + bugprone-macro-repeated-side-effects, + bugprone-misplaced-operator-in-strlen-in-alloc, + bugprone-misplaced-pointer-arithmetic-in-alloc, + bugprone-misplaced-widening-cast, + bugprone-not-null-terminated-result, + bugprone-posix-return, + bugprone-redundant-branch-condition, + bugprone-string-literal-with-embedded-nul, + bugprone-suspicious-memset-usage, + bugprone-suspicious-semicolon, + bugprone-suspicious-string-compare, + bugprone-too-small-loop-variable, + bugprone-unused-return-value, + cert-err33-c, + clang-analyzer-core.*, + clang-analyzer-valist.*, + clang-analyzer-unix.Malloc, + clang-diagnostic-*, + google-readability-casting, + misc-misleading-bidirectional, + misc-misleading-identifier, + misc-misplaced-const, + misc-redundant-expression, + objc-*, + performance-type-promotion-in-math-fn, + readability-avoid-const-params-in-decls, + readability-braces-around-statements, + readability-const-return-type, + readability-duplicate-include, + readability-inconsistent-declaration-parameter-name, + readability-misplaced-array-index, + readability-non-const-parameter, + readability-redundant-control-flow, + readability-redundant-declaration, + readability-redundant-function-ptr-dereference, + readability-redundant-preprocessor, + readability-simplify-boolean-expr + +CheckOptions: + - key: bugprone-assert-side-effect.AssertMacros + value: "SDL_assert, SDL_assert_release, SDL_assert_paranoid, SDL_assert_always, SDL_COMPILE_TIME_ASSERT" + - key: bugprone-misplaced-widening-cast.CheckImplicitCasts + value: true + - key: bugprone-not-null-terminated-result.WantToUseSafeFunctions + value: false # Do not recommend _s functions + +FormatStyle: "file" +HeaderFilterRegex: "*.h$" +WarningsAsErrors: "" diff --git a/assets/.DS_Store b/assets/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/assets/.DS_Store differ diff --git a/src/main.c b/src/main.c index 8bfbf3f..01e3014 100644 --- a/src/main.c +++ b/src/main.c @@ -11,6 +11,7 @@ #define BYTE_B 0x000000FF #define BYTE_RGB 0x00FFFFFF #define BYTE 0xFF +#define LONG_BYTE 0xFFFF #define SHIFT_8 8 #define SHIFT_16 16 @@ -19,10 +20,10 @@ #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 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) | \ @@ -43,7 +44,7 @@ typedef struct { int width, height, len; const char* name; - char fmt[4]; + char fmt; } PPM; typedef struct @@ -53,10 +54,33 @@ typedef struct char* name; } JPEG; -static inline unsigned int -set_alpha(const unsigned int clr, const unsigned char alpha_value) +// Decodes int to a long +static inline unsigned long +u32to_u64(const unsigned int clr) { - return (clr & BYTE_RGB) | alpha_value << SHIFT_24; + 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 +set_alpha(const unsigned int clr, const unsigned char alpha) +{ + return (clr & BYTE_RGB) | alpha << SHIFT_24; } static inline unsigned int @@ -75,9 +99,9 @@ swap_rb(const unsigned int clr) static inline unsigned int to_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 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; @@ -85,12 +109,13 @@ to_grayscale(const unsigned int clr) } static inline unsigned int -apply_threshold(const unsigned int clr, const unsigned char limit) +apply_threshold(const unsigned int clr, const unsigned char max) { - unsigned int mask = 0; - if (decode_b(to_grayscale(clr)) < limit) mask = ENCODE_RGB(0, 0, 0); - else mask = ENCODE_RGB(MAX, MAX, MAX); - return BYTE_A | mask; + if (DECODE_B(to_grayscale(clr)) < max) + { + return ENCODE_ARGB(0, 0, 0, 0); + } + return ENCODE_ARGB(MAX, MAX, MAX, MAX); } static inline unsigned int @@ -98,34 +123,34 @@ adjust_brightness(const unsigned int clr, const float factor) { unsigned int alpha = (clr & BYTE_A); unsigned int red = - CLAMP_MAX((unsigned int)((float)decode_r(clr) * factor)); + CLAMP_MAX((unsigned int)((float)DECODE_R(clr) * factor)); unsigned int green = - CLAMP_MAX((unsigned int)((float)decode_g(clr) * factor)); + CLAMP_MAX((unsigned int)((float)DECODE_G(clr) * factor)); unsigned int blue = - CLAMP_MAX((unsigned int)((float)decode_b(clr) * factor)); + CLAMP_MAX((unsigned int)((float)DECODE_B(clr) * factor)); return (clr & BYTE_A) | (red << SHIFT_16) | (green << SHIFT_8) | blue; } static inline unsigned int apply_tint(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; + 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 set_alpha(ENCODE_RGB(red, green, blue), decode_a(clr_a)); + return ENCODE_ARGB(DECODE_A(clr_a), red, green, blue); } static inline unsigned int apply_opacity(const unsigned clr_a, const unsigned int clr_b, const float ratio) { - unsigned char blue = (unsigned char)((decode_b(clr_a) * (1 - ratio)) + - ((decode_b(clr_b) * ratio))); - unsigned char green = (unsigned char)((decode_g(clr_a) * (1 - ratio)) + - ((decode_g(clr_b) * ratio))); - unsigned char red = (unsigned char)((decode_r(clr_a) * (1 - ratio)) + - ((decode_r(clr_b) * ratio))); - return set_alpha(ENCODE_RGB(red, green, blue), decode_a(clr_a)); + unsigned char blue = (unsigned char)((DECODE_B(clr_a) * (1 - ratio)) + + ((DECODE_B(clr_b) * ratio))); + unsigned char green = (unsigned char)((DECODE_G(clr_a) * (1 - ratio)) + + ((DECODE_G(clr_b) * ratio))); + unsigned char red = (unsigned char)((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 @@ -152,10 +177,13 @@ channel_mask(const unsigned int clr, const char keep) static inline unsigned int chromatic_circular_shift(const unsigned int clr, const int n) { - unsigned int red = decode_r(clr); - unsigned int green = decode_g(clr); - unsigned int blue = decode_b(clr); - if (n > 8) return 0; + 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; @@ -166,7 +194,10 @@ static inline unsigned int* flip_horizontal(const unsigned int* buf, const PPM img) { unsigned int* flip = malloc(img.len * sizeof(unsigned int)); - if (!flip) return NULL; + if (!flip) + { + return NULL; + } int idx = 0; for (int i = 0; i < img.height; i++) { @@ -184,7 +215,10 @@ static inline unsigned int* flip_vertical(const unsigned int* buf, const PPM img) { unsigned int* flip = malloc(img.len * sizeof(unsigned int)); - if (!flip) return NULL; + if (!flip) + { + return NULL; + } int idx = 0; for (int i = img.height - 1; i >= 0; i--) @@ -204,11 +238,11 @@ constrast_adjustment(unsigned int clr, float factor) { unsigned int mask = 0; int red = CLAMP_MIN(CLAMP_MAX( - (int)(factor * (float)(int)(decode_r(clr) - HALF_MAX)) + HALF_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)(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)); + (int)(factor * (float)(int)(DECODE_B(clr) - HALF_MAX)) + HALF_MAX)); return BYTE_A | (red << SHIFT_16) | (green << SHIFT_8) | blue; } @@ -237,96 +271,101 @@ rotate(const unsigned int* buf, PPM* img, const int len) static inline int is_valid(const int x, const int y, const int width, const int height) { - if (x < 0 || x >= width) return 0; - if (y < 0 || y >= height) return 0; + if (x < 0 || x >= width || y < 0 || y >= height) + { + return 0; + } return 1; } static inline unsigned int brightness_mirror(const unsigned int clr) { - unsigned char red = decode_r(clr); - unsigned char blue = decode_b(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); } +// Used strictly for box_blur +static inline unsigned int +get_pixel_x(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]; +} + +// Used strictly for box_blur +static inline unsigned int +get_pixel_y(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]; +} + static inline unsigned int* box_blur(const unsigned int* src_buf, const PPM* img, const int radius) { - unsigned int* tmp_buf = malloc(img->len * sizeof(unsigned int)); - if (!tmp_buf) return 0; + 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(tmp_buf); + free(first_pass); return 0; } - printf("Pass 1: ...\n"); for (int y = 0; y < img->height; y++) { for (int x = 0; x < img->width; x++) { - unsigned long cpi = 0, red = 0, green = 0, blue = 0; - int count = 0; - int edge = (x < radius || x >= img->width - radius); - if (edge) continue; - cpi = src_buf[y * img->width + x]; - - for (int dx = -radius; dx <= radius; dx++) - { - int px = x + dx; - if (px < 0 || px >= img->width) continue; - unsigned int p = src_buf[y * img->width + px]; - red += decode_r(p); - green += decode_g(p); - blue += decode_b(p); - count++; - } - - if (count > 0) - { - tmp_buf[y * img->width + x] = - ENCODE_RGB((red / count), (green / count), - (blue / count)); - } - else tmp_buf[y * img->width + x] = cpi; + first_pass[y * img->width + x] = + get_pixel_x(src_buf, x, y, img, radius); } } - printf("Pass 1: Successful\n"); - printf("Pass 2: ...\n"); for (int y = 0; y < img->height; y++) { for (int x = 0; x < img->width; x++) { - unsigned long cpi = 0, red = 0, green = 0, blue = 0; - int count = 0; - int edge = (y < radius || y >= img->height - radius); - if (edge) continue; - cpi = src_buf[y * img->width + x]; - - for (int dy = -radius; dy <= radius; dy++) - { - int py = y + dy; - if (py < 0 || py >= img->height) continue; - unsigned int p = tmp_buf[py * img->width + x]; - red += decode_r(p); - green += decode_g(p); - blue += decode_b(p); - count++; - } - - if (count > 0) - { - dst_buf[y * img->width + x] = - ENCODE_RGB((red / count), (green / count), - (blue / count)); - } - else dst_buf[y * img->width + x] = cpi; + dst_buf[y * img->width + x] = + get_pixel_y(first_pass, x, y, img, radius); } } - printf("Pass 2: Successful\n"); - free(tmp_buf); + free(first_pass); return dst_buf; } @@ -356,6 +395,7 @@ ppm_decode(const char* file, PPM* img) return NULL; } fscanf(fptr, "%s", fmt); + img->fmt = fmt[1]; fscanf(fptr, "%d %d", &img->width, &img->height); img->len = img->width * img->height; fscanf(fptr, "%d", &max_val); @@ -368,7 +408,7 @@ ppm_decode(const char* file, PPM* img) free(img_buf); return NULL; } - if (fmt[1] == '3') + if (img->fmt == '3') { for (int i = 0; i < img->len; i++) { @@ -376,7 +416,7 @@ ppm_decode(const char* file, PPM* img) clr_buf[i] = ENCODE_RGB(red, green, blue); } } - else if (fmt[1] == '6') + else if (img->fmt == '6') { fread(img_buf, 3, img->len, fptr); @@ -405,22 +445,28 @@ ppm_encode(const unsigned int* buf, const PPM img) printf("Starting encode...\n"); FILE* fptr; - if (img.fmt[1] == '3') fptr = fopen(img.name, "w"); - if (img.fmt[1] == '6') fptr = fopen(img.name, "wb"); + if (img.fmt == '3') + { + fptr = fopen(img.name, "w"); + } + if (img.fmt == '6') + { + fptr = fopen(img.name, "wb"); + } if (!fptr) { free(dst_buf); return; } - fprintf(fptr, "%s\n%d %d\n255\n", img.fmt, img.width, img.height); - if (img.fmt[1] == '3') + 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]); + 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) { @@ -428,13 +474,13 @@ ppm_encode(const unsigned int* buf, const PPM img) } } } - if (img.fmt[1] == '6') + 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]); + 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); } @@ -450,7 +496,10 @@ jpeg_decode(JPEG* attr) tjhandle tjdecompress = tj3Init(TJINIT_DECOMPRESS); unsigned char* jpeg_buf = NULL; FILE* infile = fopen(attr->name, "rb"); - if (!infile) return NULL; + if (!infile) + { + return NULL; + } fseek(infile, 0, SEEK_END); attr->len = ftell(infile); fseek(infile, 0, SEEK_SET); @@ -526,21 +575,16 @@ jpeg_to_ppm(JPEG* input, PPM* out) int main() { - JPEG jpeg = { - .width = 3, - .height = 2, - .len = 0, - .name = - "/Users/andrew/personal/Projects/C/image_processor/assets/wp12450035.jpg" + PPM out = { + .name = "../test.ppm", }; - unsigned int* src_buf = jpeg_decode(&jpeg); - if(src_buf) { - for(int i = 0; i < jpeg.width * jpeg.height; i++) { - src_buf[i] = channel_mask(src_buf[i], 'b'); - } - } - jpeg_encode(src_buf, &jpeg, "../test.jpg"); + unsigned int* src_buf = ppm_decode("../assets/8ktest.ppm", &out); + if (src_buf) + { + src_buf = box_blur(src_buf, &out, 10); + } + ppm_encode(src_buf, out); if (src_buf) { free(src_buf);