diff --git a/include/image/filters.h b/include/image/filters.h index aae33b8..59fe881 100644 --- a/include/image/filters.h +++ b/include/image/filters.h @@ -2,42 +2,149 @@ #define FILTERS_H #include "utils.h" +/** + * @brief Packs new alpha channel + * @details Takes a value from 0 - 255 for levels of opacity in image and packs + * it into the first byte. 0 is transparent and 255 is fully opaque. + * @param[in] clr Takes in a packed pixel + * @param[in] alpha One byte value to be packed for image + * @return unsigned int Packed color with new alpha channel. + */ unsigned int u32_put_alpha(const unsigned int clr, const unsigned char alpha); +/** + * @brief Inverts bits for color channels + * @details Takes a packed color and inverts the RGB channel bits from 0 to 1 + * and vice versa. Then saves the alpha channel back to new packed image. + * @param[in] clr Takes in a packed pixel + * @return unsigned int Packed color with inverted RGB channels. + */ unsigned int u32_invert(const unsigned int clr); +/** + * @brief Inverts RED and BLUE channels + * @details Takes a packed color and isolates the A and G channels, then shifts + * the clr Red channel 16 Bits right and B channel 16 Bits left. + * @param[in] clr Takes in a packed pixel + * @return unsigned int Packed color with inverted RB channels. + */ unsigned int u32_swap_rb(const unsigned int clr); +/** + * @brief Grays pixels from RGB channels + * @details Takes a packed color and merges RGB channels and sets all RGB + * channels to new gray. + * @param[in] clr Takes in a packed pixel + * @return unsigned int Packed color with gray RGB channels. + */ unsigned int u32_grayscale(const unsigned int clr); +/** + * @brief Grays pixels from RGB channels + * @details If greyscale value is less than Max then set color to transparent + * and black. If greater, set color to opaque white. channels to new gray. + * @param[in] clr Takes in a packed pixel + * @param[in] max Max gray value for split + * @return unsigned int Packed color with white or black RGB channels. + */ unsigned int u32_step(const unsigned int clr, const unsigned char max); +/** + * @brief Brightens Color + * @details Uses a factor to multiply RGB channels to increase values closer to + * 255 to raise brightness. Use x > 1 for a brighter pixel and x < 1 for a + * darker pixel + * @param[in] clr Takes in a packed pixel + * @param[in] factor Used to multiply against RGB channels. Can be 0 < x > 1 but + * NOT negative. + * @return unsigned int Packed color with brighter or darker pixel + */ unsigned int u32_brighten(const unsigned int clr, const float factor); +/** + * @brief Combines RGB values between 2 packed colors + * @details Takes and adds the respective RGB values between 2 colors and + * divides by 2 to make sure each channel is below 255. So, even if 2 pixels are + * white, the max values will stay 255. + * @param[in] clr_a Packed Pixel A + * @param[in] clr_b Packed Pixel B + * @return unsigned int Packed ARGB pixel. + */ unsigned int u32_mix(const unsigned int clr_a, const unsigned int clr_b); +/** + * @brief Combines RGB values between 2 packed colors + * @details Takes and adds the respective RGB values between 2 colors and + * divides by 2 to make sure each channel is below 255. So, even if 2 pixels are + * white, the max values will stay 255. + * @param[in] clr_a Packed Pixel A + * @param[in] clr_b Packed Pixel B + * @return unsigned int Packed ARGB pixel. + */ unsigned int u32_lerp(const unsigned clr_a, const unsigned int clr_b, const float ratio); +/** + * @brief Returns a Packed Pixel of one Channel + * @param[in] clr Packed Pixel + * @param[in] keep Character of 'r', 'g', 'b', keeping the channel respectively. + * @return unsigned int Packed Pixel with one channel kept, includes alpha auto + */ unsigned int u32_mask(const unsigned int clr, const char keep); +/** + * @brief Rotates the bits of each channel by n + * @details Moves bits of channels left by n with the max being 8 and + * recommended to use 0 < x < 8 instead of 8; 8 just returns the same pixel as + * it is just rotated back to original position. Cannot be greater than 8 + * because the calculations would result in a negative. + * @param[in] clr Packed Pixel + * @param[in] n Amount of times to move bits to left. + * @return unsigned int Packed Pixel with newly moved bits + */ unsigned int u32_rotl(const unsigned int clr, const int n); +/** + * @brief Raises Color Vibrance + * @details Raises the vibrance by essentially raising specific channels higher + * and lowering others. + * @param[in] clr Packed Pixel + * @param[in] factor Floating point to raise or lower vibrance by. + * @return unsigned int Packed Pixel with one channel kept, includes alpha auto + */ unsigned int u32_contrast(const unsigned int clr, const float factor); +/** + * @brief Averages Red and Blue Channels for Green Channel + * @param[in] clr Packed Pixel + * @return unsigned int Packed Pixel with green pixel averaged + */ unsigned int u32_avg_gb(const unsigned int clr); +/** + * @brief Blurs image in a radiance with a box + * @details Uses 2 loops to scan from left to right; selecting a pixel and + * mixing all of the pixels from "radius" left to "radius" right and averaging + * them. Then it moves to the next pixel doing the same. Then it does the second + * loop from top to bottom doing the same principle. This can take some time + * depending on hardware, image size, radius, etc. + * @param[in] src_buf The image buffer to be blurred + * @param[in] img the PPM image container + * @param[in] radius The amount of pixels around the left and right, then top + * and bottom to mix with the currently selected pixel + * @return unsigned int Pointer to new created image buffer. + */ unsigned int* box_blur(const unsigned int* src_buf, const PPM* img, const int radius); diff --git a/include/image/utils.h b/include/image/utils.h index 03d2aaa..825e661 100644 --- a/include/image/utils.h +++ b/include/image/utils.h @@ -115,7 +115,8 @@ typedef struct /** * @brief Converts an int to long * @details Takes in 32-Bit packed color and separates the 4 color values into a - * 64-Bit packed color for JPEG translations. + * 64-Bit packed color. Used for bigger image calculations with one channel and + * then dividing later. * @param[in] clr Takes a packed color, unsigned int * @return unsigned long Packed color in 4 ints instead of one * */ @@ -124,8 +125,7 @@ u32to_u64(const unsigned int clr); /** * @brief Converts a long to int - * @details Takes in a full packed color and compresses it back into 1 ints for - * ppm translations. + * @details Takes in a full packed color and compresses it to 1 Byte * @param[in] clr Takes a packed color, unsigned long * @param[in] u64_div Takes a divisor to break down to raw color * @return unsigned int Packed color in 1 int instead of 4 diff --git a/src/filters.c b/src/filters.c index b33c8f5..490733c 100644 --- a/src/filters.c +++ b/src/filters.c @@ -1,10 +1,8 @@ - - -#include - -// #include "utils.h" #include "image/filters.h" +#include +#include + unsigned int u32_put_alpha(const unsigned int clr, const unsigned char alpha) { @@ -30,6 +28,13 @@ u32_grayscale(const unsigned int clr) unsigned int red = DECODE_R(clr); unsigned int green = DECODE_G(clr); unsigned int blue = DECODE_B(clr); + /* + * WHY: The colors use the REC 601 Standard: Lamens terms they are + * weighed in how the human eye percieves color. So green is the largest + * because we are more sensitive to green and least to blue. + * The Shift_8 is to divide 256 to bring the color value back into range + of 0 - 255 + */ unsigned int gray = ((red * 77) + (green * 150) + (blue * 29)) >> SHIFT_8; @@ -49,7 +54,6 @@ u32_step(const unsigned int clr, const unsigned char 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); @@ -67,8 +71,14 @@ 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) +u32_lerp(const unsigned int clr_a, const unsigned int clr_b, const float ratio) { + /* + * A more powerful mix using floating points to more precisely and + * smoothly mix colors from 2 pixels. Doesn't really work on PPM images + * because they are whole number pixels. So jpeg are better because the + * numbers result in floating point colors. + * */ unsigned char blue = ((DECODE_B(clr_a) * (1 - ratio)) + ((DECODE_B(clr_b) * ratio))); unsigned char green = @@ -104,11 +114,41 @@ u32_rotl(const unsigned int clr, const int n) { if (n > 8) { - return 0; + printf( + "ERROR: %i is greater than 8 results in black " + "pixels, please use values less than 8\n", + n); + exit(10); } unsigned int red = DECODE_R(clr); unsigned int green = DECODE_G(clr); unsigned int blue = DECODE_B(clr); + /* + * Rotates the color channels. Essentially it moves bits left a certain + * n and then wraps if it leaves the left to the right. + * + * If you input 1 to n and have: + * 0 0 0 0 0 0 0 1 # 1 + * + * You will get: + * 0 0 0 0 0 0 1 0 # 2 + * + * But this gets complex with larger numbers: + * 0 1 0 1 1 1 0 0 # 4 + 8 + 16 + 64 = 92 + * + * Rotating by 2 will wrap and give: + * 0 1 1 1 0 0 0 1 # 1 + 16 + 32 + 64 = 113 + * + * This creates a pixelated looking image of random colors while still + * holding the original image's structure under. Almost a distored, + * broken tv look. + * + * N has a max of 8 because any number after 8 results in the bits + * shifting negatively due to the wrapping math, which cant happen and + * just makes the pixels black. If the number is 8 it just results in + * the pixels being the same as they are just rotated back to their + * original spot. + * */ red = ((red << n) | (red >> (SHIFT_8 - n))) & BYTE; green = ((green << n) | (green >> (SHIFT_8 - n))) & BYTE; blue = ((blue << n) | (blue >> (SHIFT_8 - n))) & BYTE; @@ -136,6 +176,19 @@ u32_avg_gb(const unsigned int clr) return ENCODE_RGB(red, green, blue); } +/** + * @brief Blurs column at (x,y) using -radius to radius + * @details Starts at the top extreme and moves to the bottom extreme, + * taking the pixel and adding each channel. Because each channel is separated + * into their own ints, it adds each int together and at the end divides each + * channel back below 256 and converts back to one int. + * @param[in] src Pointer to buffer image. + * @param[in] x Location in the image buffer for column of original pixel + * @param[in] y Location in the image buffer for row of original pixel + * @param[in] img Pointer to PPM container + * @param[in] radius The value to find -extreme and +extreme to add to pixel + * @return unsigned int Packed color with new alpha channel. + */ unsigned int blur_col(const unsigned int* src, const int x, const int y, const PPM* img, const int radius) @@ -152,13 +205,30 @@ blur_col(const unsigned int* src, const int x, const int y, const PPM* img, } } + // WHY: takes the count (cnt) of pixels from column and divides the + // point (pt) by (cnt) to get back below 256 and then converted back + // into an int from a long if (cnt > 0) { return u64to_u32(pt, cnt); } + return src[y * img->width + x]; } +/** + * @brief Blurs column at (x,y) using -radius to radius + * @details Starts at the left extreme and moves to the right extreme taking the + * pixel and adding each channel. Because each channel is separated into their + * own ints, it adds each int together and at the end divides each channel back + * below 256 and converts back to one int. + * @param[in] src Pointer to buffer image. + * @param[in] x Location in the image buffer for column of original pixel + * @param[in] y Location in the image buffer for row of original pixel + * @param[in] img Pointer to PPM container + * @param[in] radius The value to find -extreme and +extreme to add to pixel + * @return unsigned int Packed color with new alpha channel. + */ unsigned int blur_row(const unsigned int* src, const int x, const int y, const PPM* img, const int radius) @@ -175,6 +245,9 @@ blur_row(const unsigned int* src, const int x, const int y, const PPM* img, } } + // WHY: takes the count (cnt) of pixels from row and divides the + // point (pt) by (cnt) to get back below 256 and then converted back + // into an int from a long if (cnt > 0) { return u64to_u32(pt, cnt); diff --git a/src/main.c b/src/main.c index 3162d1c..8f2afaf 100644 --- a/src/main.c +++ b/src/main.c @@ -1,3 +1,4 @@ +#include #include #include "image/filters.h" @@ -7,6 +8,11 @@ // #include "image/transformations.h" #include "image/imgio.h" +/* + * Keep in mind any function that creates a new buffer and returns it can + * overwrite the old one, however it is lost. SO create a new variable and free + * the old one after use. + * */ int main() { @@ -15,17 +21,18 @@ main() // // }; PPM img = { - .name = "../IMG_1649.ppm", + .name = "../assets/test.ppm", }; unsigned int* buf = ppm_load(img.name, &img); - // for (int i = 0; i < img.len; i++) - // { - // // you need to replace the old value with the new updated value - // buf[i] = u32_grayscale(buf[i]); - // } - buf = u32_rotate(buf, &img); - ppm_save(buf, img, "../IMG_1649_new.ppm"); + for (int i = 0; i < img.len; i++) + { + // you need to replace the old value with the new updated value + buf[i] = u32_contrast(buf[i], 9); + } + img.fmt = '3'; + // buf = u32_rotate(buf, &img); + ppm_save(buf, img, "../assets/test_new.ppm"); // PPM out_2 = inherit_from_jpeg(&out_1, '6'); //