Compare commits

...
10 Commits
Author SHA1 Message Date
DarthMooMancer 54a2d44a53 REFACTOR: Expanded many parameter names for functions.
DOCS: Corrected comments to match new parameters.
CHORE: Moved some variable definition to top of function call.
2026-09-12 05:15:16 -04:00
DarthMooMancer 81c228beb8 CHORE: freeing old buf in box blur solving #2 2026-09-12 04:12:17 -04:00
DarthMooMancer c140c5b776 LICENSE: rename 2026-09-10 02:58:56 -04:00
DarthMooMancer 241ddd7f6d LICENSE: added MIT License 2026-09-10 02:57:51 -04:00
DarthMooMancer 8ed07bf8db README: Fixed many spelling problems and little nitpick things. 2026-09-10 02:52:53 -04:00
DarthMooMancer 328648f57f README: Added Prerequisites, more installation steps, updated
description, added Types, updated example
2026-09-10 02:43:22 -04:00
DarthMooMancer ee2180a905 DOCS: Spellchecked some words 2026-09-10 02:19:32 -04:00
DarthMooMancer 33bc2f29e6 DOCS: added documentation to find_extension and removed redundant
asterisks from comments
2026-09-09 17:32:22 -04:00
DarthMooMancer 39911cd662 REFACTOR: I forgot to actually change the function logic after rename;
The functions take in {width,height} to use instead of PPM image
containers. The function only pulled len, width and height so I just
imported them instead for all image types to be used.
2026-09-09 17:26:02 -04:00
DarthMooMancer d2557b84cf README: Updated to show pulling the project as dependency to other projects 2026-09-09 17:21:48 -04:00
12 changed files with 303 additions and 219 deletions
+1 -1
View File
@@ -29,6 +29,6 @@ target_link_libraries(imgproc PUBLIC PkgConfig::TURBOJPEG)
set_target_properties(imgproc PROPERTIES EXPORT_NAME imgproc)
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
add_executable(libimg src/main.c) # Assuming you have a main.c
add_executable(libimg src/main.c)
target_link_libraries(libimg PRIVATE imgproc)
endif()
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2011-2026 The Bootstrap Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+64 -7
View File
@@ -1,20 +1,75 @@
# LibIMG
_A "powerful" library that I have worked on allowing you to take a PPM image and apply filters and transformations and download the file. It also works for JPEG which you can also convert to PPM and vice versa_
_A lightweight, fast, and minimal library designed to be powerful for my current capabilities. This is a hobby project to learn bit operations and expanded to a full processor between two types --- PPM and JPEG. It's powerful for what it is and it is weak for what it could be._
> [Note]
> All documentation is in the code and files itself. So to save myself time I would recommend going through the code itself.
> [Warning]
> There are a lot of changes you can make to an image but this is not an extensive library to use mainstream. I would hope you understood that already. This Is for a self made project learning bitwise for c originally and now has become bigger and cooler than expected.
> There are a lot of changes you can make to an image but this is not an extensive library to use mainstream. I would hope you understood that already. This is for a self made project learning bitwise for c originally and now has become bigger and cooler than expected.
### Example
## Prerequisites
1. This library does require _```libturbojpeg```_ as manually writing the functions for JPEG would take a long time to figure out and I may come back to do later.
2. ```C23 and CMake 4.4.3```, as there are some new features from it. Tested only on my M1 Macbook for compatibility.
## Types
Available
1. PPM (Originally just to learn with)
2. JPEG (Added as having only PPM felt wrong)
Planned
3. PNG (Planned)
4. SVG (We Shall See)
## Installation
You can build manually or pull it into a project
_To Build_:
```bash
git clone https://git.moomancer.xyz/DarthMooMancer/libimg.git
cd libimg
cmake -S . -B build && cmake --build build
./build/libimg
```
_To pull, use FetchContent in your ```CMakeLists.txt```:_
```Make
include(FetchContent)
FetchContent_Declare(imgproc
GIT_REPOSITORY https://git.moomancer.xyz/DarthMooMancer/libimg.git
GIT_TAG main
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(imgproc)
target_link_libraries(<project> PRIVATE imgproc)
```
## Example
_While it is not an extensive example, it shows many of the patterns for how to use all functions throughout the codebase._
```c
/*
* 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.
*
* Functions (Specifically filters) that return unsigned int (not pointers) need
* to be used as an input to the same point in a buffer.
*
* For example:
*
* buf[i] = u32_grayscale(buf[i]);
*
* The buf[i] is replaced by the output of grayscale at buf[i]
*/
int
main()
@@ -29,13 +84,14 @@ main()
// Load the PPM image
unsigned int* buf = ppm_load(img.name, &img);
// Check for null image load. In case image doesnt exist or fails to
// Check for null image load. In case image doesn't exist or fails to
// load
if (!buf)
{
LOGERROR(ERR_BUF_NULL);
return ERR_BUF_NULL;
}
// Blur image and save to new pointer
unsigned int* blur = box_blur(buf, &img, 20);
@@ -43,7 +99,8 @@ main()
// ending or possibly leaking. Also it stays in heap and a large image
// can take a lot of memory so better to clear anyways.
free(buf);
// Check for null image load. In case image doesnt exist or fails to
// Check for null image load. In case image doesn't exist or fails to
// load
if (!blur)
{
@@ -57,10 +114,10 @@ main()
blur[i] = u32_rotl(blur[i], 4);
}
// Take the attributes from ppm image so jpeg file can be saved
// Take the attributes from ppm image so JPEG file can be saved
jimg = inherit_from_ppm(&img);
// Saves jpeg to location specified
// Saves JPEG to location specified
jpeg_save(blur, &jimg, "../assets/test_2_new.jpg");
// Make sure to free any buffer still active
+5 -5
View File
@@ -107,11 +107,11 @@ u32_mask(const unsigned int clr, const char keep);
* 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.
* @param[in] dist Distance from original position to move each bit to the left.
* @return unsigned int Packed Pixel with newly moved bits
*/
unsigned int
u32_rotl(const unsigned int clr, const int n);
u32_rotl(const unsigned int clr, const int dist);
/**
* @brief Raises Color Vibrance
@@ -139,13 +139,13 @@ u32_avg_gb(const unsigned int clr);
* 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] buf The image buffer to be blurred
* @param[in] metadata 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*
u32_box_blur(const unsigned int* src_buf, const PPM* img, const int radius);
u32_box_blur(const unsigned int* buf, const PPM* metadata, const int radius);
#endif
+20 -21
View File
@@ -5,67 +5,66 @@
/**
* @brief Open and Fill buffer from .ppm file
* @param [in] file Char pointer use to locate the image to be modified lays.
* @param [in,out] m_attr PPM pointer to store image information
* @param [in] filename Location of the image to be modified
* @param [in,out] metadata PPM pointer to store image information
*/
unsigned int*
read_ppm(const char* file, PPM* m_attr);
read_ppm(const char* filename, PPM* metadata);
/**
* @brief Open and write buffer to file
* @param [in] buf Information of pixels
* @param [in] img PPM pointer to store image information
* @param [in] char Name/Location for file
* @param [in] buffer Information of pixels
* @param [in] metadata PPM pointer to store image information
* @param [in] filename Name/Location for file
*/
int
write_ppm(const unsigned int* buf, const PPM img, const char* name);
write_ppm(const unsigned int* buffer, const PPM metadata, const char* filename);
/**
* @brief Open and Fill buffer from .jpg file
* @details Opens a .jpg file, creates a new buffer and extracts the pixels into
* said buffer as integers.
* @param [in] file Char pointer use to locate the image to be modified lays.
* @param [in,out] m_attr JPEG pointer to store image information
* @param [in,out] metadata JPEG pointer to store image information
*/
unsigned int*
read_jpeg(JPEG* m_attr);
read_jpeg(JPEG* metadata);
/**
* @brief Open and write buffer to file
* @param [in] buf Information of pixels
* @param [in] attr JPEG pointer to store image information
* @param [in] char Name/Location for file
* @param [in] metadata JPEG pointer to store image information
* @param [in] filename Name/Location for file
*/
int
write_jpeg(const unsigned int* src_buf, const JPEG* attr, const char* name);
write_jpeg(const unsigned int* buf, const JPEG* metadata, const char* filename);
/**
* @brief Converts PPM to JPEG
* @param [in] in Pointer to PPM container
* @param [in] location points to file to use
*/
void
transcode_to_jpeg(PPM* in);
transcode_to_jpeg(const char* location);
/**
* @brief Converts JPEG to PPM
* @param [in] in Pointer to JPEG container
* @param [in] location Pointer to JPEG container
*/
void
transcode_to_ppm(JPEG* in);
transcode_to_ppm(const char* location);
/**
* @brief Inherits the attributes from JPEG container
* @param [in] in Pointer to JPEG container
* @param [in] metadata Pointer to JPEG container
* @param [in] fmt Character for type of PPM image, binary or raw
*/
PPM
inherit_from_jpeg(const JPEG* in, const char fmt);
inherit_from_jpeg(const JPEG* metadata, const char fmt);
/**
* @brief Inherits the attributes from PPM container
* @param [in] in Pointer to PPM container
* @param [in] metadata Pointer to PPM container
*/
JPEG
inherit_from_ppm(const PPM* in);
inherit_from_ppm(const PPM* metadata);
#endif
+3 -3
View File
@@ -13,7 +13,7 @@
* @retval NULL If original buffer is invalid or if new buffer fails to create
* */
unsigned int*
u32_flop(const unsigned int* buf, const PPM img);
u32_flop(const unsigned int* buf, const int width, const int height);
/**
* @brief Flips image on Y-Axis
@@ -25,7 +25,7 @@ u32_flop(const unsigned int* buf, const PPM img);
* @retval NULL If original buffer is invalid or if new buffer fails to create
* */
unsigned int*
ppm_flip(const unsigned int* buf, const PPM img);
u32_flip(const unsigned int* buf, const int width, const int height);
/**
* @brief Rotates image on 90 Clockwise
@@ -38,6 +38,6 @@ ppm_flip(const unsigned int* buf, const PPM img);
* new buffer fails to create and is invalid
* */
unsigned int*
u32_rotate(const unsigned int* buf, PPM* m_img);
u32_rotate(const unsigned int* buf, PPM* metadata);
#endif
+22 -16
View File
@@ -47,7 +47,7 @@ extern const char* error_messages[];
* @brief Max value for any channel
* @details For alpha channel this is the max opacity setting and 0 is fully
* transparent, 255 is white for any color and 0 is black.
* */
*/
#define MAX 255
#define HALF_MAX \
((int)(MAX / 2) + 1) /**< Half Max Color Value for any channel */
@@ -70,7 +70,7 @@ extern const char* error_messages[];
* @param[in] g The green channel stored as char and packed into int
* @param[in] b The blue channel stored as char and packed into int
*
* */
*/
#define ENCODE_RGB(r, g, b) \
(((unsigned int)(MAX) << SHIFT_24) | ((unsigned int)(r) << SHIFT_16) | \
((unsigned int)(g) << SHIFT_8) | (unsigned int)(b))
@@ -85,7 +85,7 @@ extern const char* error_messages[];
* @param[in] g The green channel stored as char and packed into int
* @param[in] b The blue channel stored as char and packed into int
*
* */
*/
#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))
@@ -95,7 +95,7 @@ extern const char* error_messages[];
* @details Takes in a value of X and checks if it is Above the Max of 255. If
* so, then set to Max. If not, then simply set back to whatever X was before.
* @param[in] x Color channel: char
* */
*/
#define CLAMP_MAX(x) (((x) > MAX) ? MAX : (x))
/**
@@ -103,14 +103,14 @@ extern const char* error_messages[];
* @details Takes in a value of X and checks if it is Below 0. If so, then set
* to 0. If not, then simply set back to whatever X was before.
* @param[in] x Color channel: char
* */
*/
#define CLAMP_MIN(x) (((x) < 0) ? 0 : (x))
/**
* @brief Contains values for PPM Image
* @details Can be defined with just a name and filled later with ppm_load
* as it stores information for header of file not the pixels of the image
* */
*/
typedef struct
{
int width; /**< Image width in pixels */
@@ -124,7 +124,7 @@ typedef struct
* @brief Contains values for JPEG Image
* @details Can be defined with just a name and filled later with jpeg_load as
* it stores information for header of file not the pixels of the image
* */
*/
typedef struct
{
int width; /**< Image width in pixels */
@@ -140,7 +140,7 @@ typedef struct
* then dividing later.
* @param[in] clr Takes a packed color, unsigned int
* @return unsigned long Packed color in 4 ints instead of one
* */
*/
unsigned long
u32to_u64(const unsigned int clr);
@@ -150,7 +150,7 @@ u32to_u64(const unsigned int clr);
* @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
* */
*/
unsigned int
u64to_u32(const unsigned long clr, const unsigned long u64_div);
@@ -158,21 +158,27 @@ u64to_u32(const unsigned long clr, const unsigned long u64_div);
* @brief Prints quick debug info of container
* @details Prints metadata like Name, Format, Width, Height and Length
* important for debugging errors
* @param[in] src Pointer to src metadata of a PPM image
* */
* @param[in] metadata Pointer to information of a PPM image
*/
void __attribute__((overloadable))
print_attr(const PPM* src);
print_attr(const PPM* metadata);
/**
* @brief Prints quick debug info of container
* @details Prints metadata like Name, Width, Height and Length
* important for debugging errors
* @param[in] src Pointer to src metadata of a JPEG image
* */
* @param[in] metadata Pointer to information of a JPEG image
*/
void __attribute__((overloadable))
print_attr(const JPEG* src);
print_attr(const JPEG* metadata);
/**
* @brief Finds start of extension in image name
* @details Loops through the character array starting from the end to find the
* start of the extension.
* @param[in] str Char array to loop through
*/
size_t
find_extension(const char* n);
find_extension(const char* str);
#endif
+35 -32
View File
@@ -27,18 +27,16 @@ u32_swap_rb(const unsigned int clr)
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);
/*
* 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
* WHY: The colors use the REC 601 Standard: In layman's terms they are
* weighed in how the human eye perceives 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;
unsigned int gray = ((DECODE_R(clr) * 77) + (DECODE_G(clr) * 150) +
(DECODE_B(clr) * 29)) >>
SHIFT_8;
return (clr & BYTE_A) | (gray << SHIFT_16) | (gray << SHIFT_8) | gray;
}
@@ -56,6 +54,7 @@ u32_step(const unsigned int clr, const unsigned char max)
unsigned int
u32_brighten(const unsigned int clr, const float factor)
{
// NOTE: keep definition for readability
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);
@@ -65,6 +64,7 @@ u32_brighten(const unsigned int clr, const float factor)
unsigned int
u32_mix(const unsigned int clr_a, const unsigned int clr_b)
{
// NOTE: keep definition for readability
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;
@@ -78,9 +78,10 @@ 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
* because they are whole number pixels. So JPEG are better because the
* numbers result in floating point colors.
* */
// NOTE: keep definition for readability
unsigned char blue =
((DECODE_B(clr_a) * (1 - ratio)) + ((DECODE_B(clr_b) * ratio)));
unsigned char green =
@@ -112,9 +113,9 @@ u32_mask(const unsigned int clr, const char keep)
}
unsigned int
u32_rotl(const unsigned int clr, const int n)
u32_rotl(const unsigned int clr, const int dist)
{
if (n > 8)
if (dist > 8)
{
LOGERROR(ERR_INVALID_SIZE);
return ERR_INVALID_SIZE;
@@ -138,9 +139,9 @@ u32_rotl(const unsigned int clr, const int n)
* 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.
* This creates a pixilated looking image of random colors while still
* holding the original image's structure under. Almost a distorted,
* 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
@@ -148,15 +149,16 @@ u32_rotl(const unsigned int clr, const int n)
* 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;
red = ((red << dist) | (red >> (SHIFT_8 - dist))) & BYTE;
green = ((green << dist) | (green >> (SHIFT_8 - dist))) & BYTE;
blue = ((blue << dist) | (blue >> (SHIFT_8 - dist))) & BYTE;
return ENCODE_RGB(red, green, blue);
}
unsigned int
u32_contrast(const unsigned int clr, const float factor)
{
// NOTE: keep definition for readability
int red = CLAMP_MIN(CLAMP_MAX(
(int)(factor * (float)(int)(DECODE_R(clr) - HALF_MAX)) + HALF_MAX));
int green = CLAMP_MIN(CLAMP_MAX(
@@ -179,7 +181,7 @@ u32_avg_gb(const unsigned int clr)
* @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
* into their own int, 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
@@ -219,7 +221,7 @@ blur_col(const unsigned int* src, const int x, const int y, const PPM* img,
* @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
* own int, 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
@@ -255,37 +257,38 @@ blur_row(const unsigned int* src, const int x, const int y, const PPM* img,
}
unsigned int*
u32_box_blur(const unsigned int* src_buf, const PPM* img, const int radius)
u32_box_blur(const unsigned int* buf, const PPM* metadata, const int radius)
{
unsigned int* first_pass = malloc(img->len * sizeof(unsigned int));
if (!first_pass)
unsigned int* col_buf = malloc(metadata->len * sizeof(unsigned int));
if (!col_buf)
{
LOGERROR(ERR_BUF_NULL);
return NULL;
}
unsigned int* dst_buf = malloc(img->len * sizeof(unsigned int));
unsigned int* dst_buf = malloc(metadata->len * sizeof(unsigned int));
if (!dst_buf)
{
free(first_pass);
free(col_buf);
LOGERROR(ERR_BUF_NULL);
return NULL;
}
for (int y = 0; y < img->height; y++)
for (int y = 0; y < metadata->height; y++)
{
for (int x = 0; x < img->width; x++)
for (int x = 0; x < metadata->width; x++)
{
first_pass[y * img->width + x] =
blur_col(src_buf, x, y, img, radius);
col_buf[y * metadata->width + x] =
blur_col(buf, x, y, metadata, radius);
}
}
for (int y = 0; y < img->height; y++)
for (int y = 0; y < metadata->height; y++)
{
for (int x = 0; x < img->width; x++)
for (int x = 0; x < metadata->width; x++)
{
dst_buf[y * img->width + x] =
blur_row(first_pass, x, y, img, radius);
dst_buf[y * metadata->width + x] =
blur_row(col_buf, x, y, metadata, radius);
}
}
free(first_pass);
free(col_buf);
free((void*)buf);
return dst_buf;
}
+98 -104
View File
@@ -8,52 +8,52 @@
#include "image/utils.h"
unsigned int*
read_ppm(const char* file, PPM* m_attr)
read_ppm(const char* filename, PPM* metadata)
{
LOG("Decoding");
unsigned char red, green, blue;
char fmt[3];
int max_val;
FILE* fptr;
fptr = fopen(file, "r");
fptr = fopen(filename, "r");
if (!fptr)
{
LOGERROR(ERR_IMG_NAME_INVALID);
LOG(file);
LOG(filename);
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;
metadata->fmt = fmt[1];
fscanf(fptr, "%d %d", &metadata->width, &metadata->height);
metadata->len = metadata->width * metadata->height;
fscanf(fptr, "%d", &max_val);
fgetc(fptr);
unsigned char* img_buf = malloc(m_attr->len * 3);
unsigned char* img_buf = malloc(metadata->len * 3);
if (!img_buf)
{
LOGERROR(ERR_BUF_NULL);
return NULL;
}
unsigned int* clr_buf = malloc(m_attr->len * sizeof(unsigned int));
unsigned int* clr_buf = malloc(metadata->len * sizeof(unsigned int));
if (!clr_buf)
{
free(img_buf);
LOGERROR(ERR_BUF_NULL);
return NULL;
}
if (m_attr->fmt == '3')
if (metadata->fmt == '3')
{
for (int i = 0; i < m_attr->len; i++)
for (int i = 0; i < metadata->len; i++)
{
fscanf(fptr, "%hhu %hhu %hhu", &red, &green, &blue);
clr_buf[i] = ENCODE_RGB(red, green, blue);
}
}
else if (m_attr->fmt == '6')
else if (metadata->fmt == '6')
{
fread(img_buf, 3, m_attr->len, fptr);
fread(img_buf, 3, metadata->len, fptr);
for (int i = 0; i < m_attr->len; i++)
for (int i = 0; i < metadata->len; i++)
{
red = img_buf[i * 3];
green = img_buf[i * 3 + 1];
@@ -69,14 +69,14 @@ read_ppm(const char* file, PPM* m_attr)
}
int
write_ppm(const unsigned int* buf, const PPM img, const char* name)
write_ppm(const unsigned int* buf, const PPM metadata, const char* filename)
{
if (!buf)
{
LOGERROR(ERR_BUF_NULL);
return ERR_BUF_NULL;
}
unsigned char* dst_buf = malloc(img.len * 3);
unsigned char* dst_buf = malloc(metadata.len * 3);
if (!dst_buf)
{
LOGERROR(ERR_BUF_NULL);
@@ -85,14 +85,14 @@ write_ppm(const unsigned int* buf, const PPM img, const char* name)
LOG("Encoding");
FILE* fptr;
if (img.fmt == '3')
if (metadata.fmt == '3')
{
fptr = fopen(name, "w");
fptr = fopen(filename, "w");
}
if (img.fmt == '6')
if (metadata.fmt == '6')
{
fptr = fopen(name, "wb");
fptr = fopen(filename, "wb");
}
if (!fptr)
@@ -102,10 +102,11 @@ write_ppm(const unsigned int* buf, const PPM img, const char* name)
return ERR_FILE_NULL;
}
fprintf(fptr, "P%c\n%d %d\n255\n", img.fmt, img.width, img.height);
if (img.fmt == '3')
fprintf(fptr, "P%c\n%d %d\n255\n", metadata.fmt, metadata.width,
metadata.height);
if (metadata.fmt == '3')
{
for (int i = 0; i < img.len; i++)
for (int i = 0; i < metadata.len; i++)
{
fprintf(fptr, "%d %d %d ", DECODE_R(buf[i]),
DECODE_G(buf[i]), DECODE_B(buf[i]));
@@ -116,15 +117,15 @@ write_ppm(const unsigned int* buf, const PPM img, const char* name)
}
}
if (img.fmt == '6')
if (metadata.fmt == '6')
{
for (int i = 0; i < img.len; i++)
for (int i = 0; i < metadata.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);
fwrite(dst_buf, 1, metadata.len * 3, fptr);
}
fclose(fptr);
free(dst_buf);
@@ -133,41 +134,41 @@ write_ppm(const unsigned int* buf, const PPM img, const char* name)
}
unsigned int*
read_jpeg(JPEG* m_attr)
read_jpeg(JPEG* metadata)
{
LOG("Decoding");
tjhandle tjdecompress = tj3Init(TJINIT_DECOMPRESS);
unsigned char* jpeg_buf = NULL;
FILE* infile = fopen(m_attr->name, "rb");
FILE* infile = fopen(metadata->name, "rb");
if (!infile)
{
LOGERROR(ERR_FILE_NULL);
return NULL;
}
fseek(infile, 0, SEEK_END);
m_attr->len = ftell(infile);
metadata->len = ftell(infile);
fseek(infile, 0, SEEK_SET);
jpeg_buf = (unsigned char*)malloc(m_attr->len);
fread(jpeg_buf, 1, m_attr->len, infile);
jpeg_buf = (unsigned char*)malloc(metadata->len);
fread(jpeg_buf, 1, metadata->len, infile);
fclose(infile);
if (tj3DecompressHeader(tjdecompress, jpeg_buf, m_attr->len) < 0)
if (tj3DecompressHeader(tjdecompress, jpeg_buf, metadata->len) < 0)
{
LOGERROR(ERR_JHEADER);
LOG(tj3GetErrorStr(tjdecompress));
free(jpeg_buf);
return NULL;
}
m_attr->width = tj3Get(tjdecompress, TJPARAM_JPEGWIDTH);
m_attr->height = tj3Get(tjdecompress, TJPARAM_JPEGHEIGHT);
m_attr->len = m_attr->width * m_attr->height;
metadata->width = tj3Get(tjdecompress, TJPARAM_JPEGWIDTH);
metadata->height = tj3Get(tjdecompress, TJPARAM_JPEGHEIGHT);
metadata->len = metadata->width * metadata->height;
unsigned int* pixels =
malloc(m_attr->width * m_attr->height * sizeof(unsigned int));
malloc(metadata->width * metadata->height * sizeof(unsigned int));
if (!pixels)
{
LOGERROR(ERR_BUF_NULL);
return NULL;
}
tj3Decompress8(tjdecompress, jpeg_buf, m_attr->len,
tj3Decompress8(tjdecompress, jpeg_buf, metadata->len,
(unsigned char*)pixels, 0, TJPF_BGRA);
tj3Destroy(tjdecompress);
free(jpeg_buf);
@@ -176,7 +177,7 @@ read_jpeg(JPEG* m_attr)
}
int
write_jpeg(const unsigned int* src_buf, const JPEG* attr, const char* name)
write_jpeg(const unsigned int* buf, const JPEG* metadata, const char* filename)
{
LOG("Encoding");
tjhandle tjcompress = tj3Init(TJINIT_COMPRESS);
@@ -184,21 +185,21 @@ write_jpeg(const unsigned int* src_buf, const JPEG* attr, const char* name)
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)
if (tj3Compress8(tjcompress, (unsigned char*)buf, metadata->width, 0,
metadata->height, TJPF_BGRA, &dst_buf, &dst_size) < 0)
{
LOGERROR(ERR_JHEADER);
LOG(tj3GetErrorStr(tjcompress));
return ERR_JHEADER;
}
FILE* fptr = fopen(name, "wb");
FILE* fptr = fopen(filename, "wb");
if (!fptr)
{
tj3Free(dst_buf);
LOGERROR(ERR_FILE_NULL);
return ERR_FILE_NULL;
}
if (dst_buf == NULL)
if (!dst_buf)
{
tj3Free(dst_buf);
LOGERROR(ERR_BUF_NULL);
@@ -220,115 +221,108 @@ write_jpeg(const unsigned int* src_buf, const JPEG* attr, const char* name)
}
void
transcode_to_jpeg(PPM* in)
transcode_to_jpeg(const char* location)
{
size_t length = find_extension(in->name);
char str2[length];
JPEG dst;
PPM src;
if (!location)
{
LOGERROR(ERR_IMG_NAME_INVALID);
return;
}
size_t length = find_extension(location);
char stem[length];
for (int i = 0; i < length; i++)
{
str2[i] = in->name[i];
stem[i] = location[i];
}
char* str3 = ".jpg";
char* result = malloc(strlen(str2) + strlen(str3) + 1);
if (result != NULL)
{
strcat(result, str2);
strcat(result, str3);
}
else
char* ext = ".jpg";
char* result = malloc(strlen(stem) + strlen(ext) + 1);
if (!result)
{
LOGERROR(ERR_MALLOC_NULL);
return;
}
JPEG img;
if (!in)
{
LOGERROR(ERR_IMG_NULL);
return;
}
unsigned int* dst = read_ppm(in->name, in);
if (!dst)
strcat(result, stem);
strcat(result, ext);
unsigned int* dst_buf = read_ppm(location, &src);
if (!dst_buf)
{
LOGERROR(ERR_BUF_NULL);
return;
}
img.width = in->width;
img.height = in->height;
write_jpeg(dst, &img, result);
free(dst);
dst = inherit_from_ppm(&src);
write_jpeg(dst_buf, &dst, result);
free(dst_buf);
free(result);
}
void
transcode_to_ppm(JPEG* in)
transcode_to_ppm(const char* location)
{
size_t length = find_extension(in->name);
JPEG src;
PPM dst;
if (!location)
{
LOGERROR(ERR_IMG_NAME_INVALID);
return;
}
src.name = (void*)location;
size_t length = find_extension(location);
char str2[length];
for (int i = 0; i < length; i++)
{
str2[i] = in->name[i];
str2[i] = location[i];
}
char* str3 = ".ppm";
char* result = malloc(strlen(str2) + strlen(str3) + 1);
if (result != NULL)
{
strcat(result, str2);
strcat(result, str3);
}
else
if (!result)
{
LOGERROR(ERR_MALLOC_NULL);
return;
}
PPM img;
if (!in)
{
LOGERROR(ERR_IMG_NULL);
return;
}
unsigned int* dst = read_jpeg(in);
if (!dst)
strcat(result, str2);
strcat(result, str3);
unsigned int* dst_buf = read_jpeg(&src);
if (!dst_buf)
{
LOGERROR(ERR_BUF_NULL);
return;
}
img.width = in->width;
img.height = in->height;
img.len = img.width * img.height;
write_ppm(dst, img, result);
free(dst);
dst = inherit_from_jpeg(&src, '6');
write_ppm(dst_buf, dst, result);
free(result);
free(dst_buf);
}
PPM
inherit_from_jpeg(const JPEG* in, const char fmt)
inherit_from_jpeg(const JPEG* metadata, const char fmt)
{
PPM img;
if (!in)
PPM image;
if (!metadata)
{
LOGERROR(ERR_IMG_NULL);
return img;
return image;
}
PPM image = { .width = in->width,
.height = in->height,
.len = in->len,
.fmt = fmt };
image.width = metadata->width;
image.height = metadata->height;
image.len = metadata->len;
image.fmt = fmt;
return image;
}
JPEG
inherit_from_ppm(const PPM* in)
inherit_from_ppm(const PPM* metadata)
{
JPEG blank;
if (!in)
JPEG image;
if (!metadata)
{
LOGERROR(ERR_IMG_NULL);
return blank;
return image;
}
JPEG image = {
.width = in->width,
.height = in->height,
.len = in->len,
};
image.width = metadata->width;
image.height = metadata->height;
image.len = metadata->len;
return image;
}
+2 -3
View File
@@ -18,9 +18,8 @@
int
main()
{
PPM img;
img.name = "../assets/IMG_1649_new.ppm";
transcode_to_jpeg(&img);
char img[] = "../assets/IMG_1649_new.ppm";
transcode_to_jpeg(img);
return 0;
}
+23 -19
View File
@@ -6,25 +6,27 @@
#include "image/utils.h"
unsigned int*
u32_flop(const unsigned int* buf, const PPM img)
u32_flop(const unsigned int* buf, const int width, const int height)
{
int len, idx;
if (!buf)
{
LOGERROR(ERR_BUF_NULL);
return NULL;
}
unsigned int* flip = malloc(img.len * sizeof(unsigned int));
len = width * height;
unsigned int* flip = malloc(len * sizeof(unsigned int));
if (!flip)
{
LOGERROR(ERR_CREATION_FAIL);
return NULL;
}
int idx = 0;
for (int i = 0; i < img.height; i++)
idx = 0;
for (int i = 0; i < height; i++)
{
for (int k = img.width - 1; k >= 0; k--)
for (int k = width - 1; k >= 0; k--)
{
flip[idx] = buf[(i * img.width) + k];
flip[idx] = buf[(i * width) + k];
idx++;
}
}
@@ -34,26 +36,28 @@ u32_flop(const unsigned int* buf, const PPM img)
}
unsigned int*
u32_flip(const unsigned int* buf, const PPM img)
u32_flip(const unsigned int* buf, const int width, const int height)
{
int len, idx;
if (!buf)
{
LOGERROR(ERR_BUF_NULL);
return NULL;
}
unsigned int* flip = malloc(img.len * sizeof(unsigned int));
len = width * height;
unsigned int* flip = malloc(len * sizeof(unsigned int));
if (!flip)
{
LOGERROR(ERR_FILE_NULL);
return NULL;
}
int idx = 0;
for (int i = img.height - 1; i >= 0; i--)
idx = 0;
for (int i = height - 1; i >= 0; i--)
{
for (int k = 0; k < img.width; k++)
for (int k = 0; k < width; k++)
{
flip[idx] = buf[(i * img.width) + k];
flip[idx] = buf[(i * width) + k];
idx++;
}
}
@@ -63,26 +67,26 @@ u32_flip(const unsigned int* buf, const PPM img)
}
unsigned int*
u32_rotate(const unsigned int* buf, PPM* m_img)
u32_rotate(const unsigned int* buf, PPM* metadata)
{
if (!buf)
{
LOGERROR(ERR_BUF_NULL);
return NULL;
}
if (!m_img)
if (!metadata)
{
LOGERROR(ERR_IMG_NULL);
return NULL;
}
unsigned int* mask = malloc(m_img->len * sizeof(unsigned int));
unsigned int* mask = malloc(metadata->len * sizeof(unsigned int));
if (!mask)
{
LOGERROR(ERR_BUF_NULL);
return NULL;
}
int old_width = m_img->width;
int old_height = m_img->height;
int old_width = metadata->width;
int old_height = metadata->height;
for (int row = 0; row < old_height; row++)
{
for (int col = 0; col < old_width; col++)
@@ -94,8 +98,8 @@ u32_rotate(const unsigned int* buf, PPM* m_img)
buf[(row * old_width) + col];
}
}
m_img->width = old_height;
m_img->height = old_width;
metadata->width = old_height;
metadata->height = old_width;
free((void*)buf);
return mask;
}
+9 -8
View File
@@ -37,26 +37,27 @@ u64to_u32(const unsigned long clr, const unsigned long u64_div)
}
void __attribute__((overloadable))
print_attr(const PPM* src)
print_attr(const PPM* metadata)
{
printf("Name: %s\nFMT: %c\nWidth: %i, Height: %i\nLength: %i\n",
src->name, src->fmt, src->width, src->height, src->len);
metadata->name, metadata->fmt, metadata->width, metadata->height,
metadata->len);
}
void __attribute__((overloadable))
print_attr(const JPEG* src)
print_attr(const JPEG* metadata)
{
printf("Name: %s\nWidth: %i, Height: %i\nLength: %i\n", src->name,
src->width, src->height, src->len);
printf("Name: %s\nWidth: %i, Height: %i\nLength: %i\n", metadata->name,
metadata->width, metadata->height, metadata->len);
}
size_t
find_extension(const char* n)
find_extension(const char* str)
{
size_t length = strlen(n);
size_t length = strlen(str);
for (int i = length; i >= 0; i--)
{
if (n[i] == '.')
if (str[i] == '.')
{
return i;
}