API: moved functions to files to make it easier to import to other projects

This commit is contained in:
Andrew Haynes
2026-03-22 17:24:40 -04:00
parent 05a15d38af
commit 64cf77bbe3
11 changed files with 678 additions and 572 deletions
+3 -1
View File
@@ -1,2 +1,4 @@
build/
.cache/
.cache/
assets/
.DS_Store
+10 -11
View File
@@ -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})
+44
View File
@@ -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
+24
View File
@@ -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
+15
View File
@@ -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
+60
View File
@@ -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
+217
View File
@@ -0,0 +1,217 @@
#include <stdlib.h>
// #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;
}
+208
View File
@@ -0,0 +1,208 @@
#include "imgio.h"
#include <stdio.h>
#include <stdlib.h>
#include <turbojpeg.h>
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);
}
+7 -560
View File
@@ -1,560 +1,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <turbojpeg.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;
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;
}
+67
View File
@@ -0,0 +1,67 @@
#include <stdlib.h>
#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;
}
+23
View File
@@ -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));
}