152 lines
5.1 KiB
C
152 lines
5.1 KiB
C
/**
|
||
* \file compress.c
|
||
* \brief Implémentation de la (dé)compression d’images
|
||
*/
|
||
|
||
#include "compress.h"
|
||
|
||
/**
|
||
* Cette fonction permet d’évaluer si le pixel passé en argument est éligible à
|
||
* la zone passée également en argument.
|
||
*
|
||
* \param[in] t_pixel Pointeur vers le pixel dont l’éligibilité est testée
|
||
* \param[in] t_zone Zone à laquelle le pixel est éligible ou non
|
||
* \return Valeur booléenne, `1` si le pixel est éligible, `0` sinon
|
||
*/
|
||
bool sameColor(Pixel *t_pixel, Zone *t_zone) {
|
||
return t_pixel->r == t_zone->r && t_pixel->g == t_zone->g &&
|
||
t_pixel->b == t_zone->b;
|
||
}
|
||
|
||
/**
|
||
* Ajoute un pixel à la zone passé en argument si le pixel à l’index passé en
|
||
* argument est éligible à la zone. Si un pixel n’a pas encore été visité, cela
|
||
* veut dire également qu’il ne fait partie d’aucun segment, il sera donc
|
||
* ajouté à un nouveau segment auquel seront rajoutés tous les pixels connexes
|
||
* éligibles à la zone. Ensuite, le segment est ajouté à la zone, et la
|
||
* fonction actuelle est appelée sur tous les pixels supérieurs et inférieurs
|
||
* aux pixels du segment.
|
||
*
|
||
* \param[in] t_img Image contenant les pixels explorés
|
||
* \param[in] t_idx Index du pixel actuel dans l’image `t_img`
|
||
* \param[out] t_zone Zone à laquelle sera potentiellement ajouté le pixel
|
||
*/
|
||
void addPixelToSelectedZone(Image *t_img, int64_t t_idx, Zone *t_zone) {
|
||
const size_t img_size = darraySize(t_img->pixels);
|
||
Pixel *current_pixel;
|
||
const uint32_t y = (uint32_t)(t_idx / t_img->x);
|
||
int64_t left_limit, right_limit;
|
||
const int64_t xd_limit = (int64_t)t_img->x * (y + 1);
|
||
|
||
if (t_idx >= (int64_t)img_size || t_idx < 0) {
|
||
return;
|
||
}
|
||
current_pixel = darrayGet(t_img->pixels, (size_t)t_idx);
|
||
if (current_pixel->visited || !sameColor(current_pixel, t_zone)) {
|
||
return;
|
||
}
|
||
(*current_pixel).visited = 1;
|
||
for (right_limit = t_idx; right_limit < xd_limit; ++right_limit) {
|
||
current_pixel = darrayGet(t_img->pixels, (size_t)right_limit);
|
||
if (!sameColor(current_pixel, t_zone)) {
|
||
break;
|
||
}
|
||
current_pixel->visited = 1;
|
||
}
|
||
|
||
for (left_limit = t_idx; left_limit - (y - 1) * (int64_t)t_img->x >= 0;
|
||
--left_limit) { /* fetch right limit of segment */
|
||
current_pixel = darrayGet(t_img->pixels, (size_t)left_limit);
|
||
if (current_pixel->visited || !sameColor(current_pixel, t_zone)) {
|
||
break;
|
||
}
|
||
(*current_pixel).visited = 1;
|
||
}
|
||
/* Add segment to its zone */
|
||
darrayPushBack(t_zone->segments,
|
||
newSegment((uint32_t)right_limit, (uint32_t)left_limit));
|
||
for (; left_limit <= right_limit;
|
||
++left_limit) { /* process every pixel up and down the segment */
|
||
addPixelToSelectedZone(t_img, t_idx + t_img->x, t_zone);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Sélectionne la zone correspondant à la couleur du pixel. Si aucune zone
|
||
* existante ne correspond, une nouvelle est créée et est ajoutée à l'image.
|
||
* Chaque pixel est itéré, et ignoré si le pixel a déjà été visité auparavant.
|
||
*
|
||
* \param[out] t_img L’image contenant les pixels à tester
|
||
* \param[in] t_idx Index du pixel à tester
|
||
* \param[out] t_zones Liste des zones de l’image
|
||
*/
|
||
void chooseZoneForPixel(Image *t_img, int64_t t_idx, darray *t_zones) {
|
||
Zone *current_zone;
|
||
Pixel *pixel;
|
||
size_t i;
|
||
|
||
pixel = darrayGet(t_img->pixels, (size_t)t_idx);
|
||
/* if the pixel has already been visited, no need to continue */
|
||
if (pixel->visited) {
|
||
return;
|
||
}
|
||
/* for each known zone, see if it matches the current pixel's color */
|
||
for (i = 0; i < darraySize(t_zones); ++i) {
|
||
current_zone = darrayGet(t_zones, i);
|
||
/* if it does, add selected pixel and its neighbouring pixels of the same
|
||
* color */
|
||
if (sameColor(pixel, current_zone)) {
|
||
addPixelToSelectedZone(t_img, t_idx, current_zone);
|
||
return;
|
||
}
|
||
}
|
||
/* if none of the same color was found, create a new one, add it to the image
|
||
* and add the selected pixel and its neighbours of the same color to the zone
|
||
*/
|
||
current_zone = newZone(pixel->r, pixel->g, pixel->b);
|
||
darrayPushBack(t_zones, current_zone);
|
||
addPixelToSelectedZone(t_img, t_idx, current_zone);
|
||
}
|
||
|
||
/**
|
||
* Génère les zones de l’image en titérant chaque pixel de l’image.
|
||
*
|
||
* \param t_img Image à convertir en zones
|
||
* \return Pointeur vers un \ref darray de structures \ref Zone
|
||
*/
|
||
darray *imgToZones(Image *t_img) {
|
||
darray *zones;
|
||
const size_t nb_pixels = darraySize(t_img->pixels);
|
||
int64_t i;
|
||
zones = darrayNew(sizeof(Zone));
|
||
|
||
/* for each pixel, try to create a new zone */
|
||
for (i = 0; i < (int64_t)nb_pixels; ++i) {
|
||
chooseZoneForPixel(t_img, i, zones);
|
||
}
|
||
return zones;
|
||
}
|
||
|
||
/**
|
||
* Convertit une image en zones puis écrit ces zones dans un fichier,
|
||
* compressant ainsi l'image passée en argument.
|
||
*
|
||
* \param[in] t_input_file Nom/chemin du fichier ppm d'entrée
|
||
*/
|
||
void compress(const char *t_input_file) {
|
||
Image *img;
|
||
darray *zones;
|
||
Zone *current_zone;
|
||
size_t i;
|
||
img = newImage();
|
||
imageLoadPPM(t_input_file, img);
|
||
zones = imgToZones(img);
|
||
|
||
deleteImage(img);
|
||
for (i = 0; i < darraySize(zones); ++i) {
|
||
current_zone = darrayGet(zones, i);
|
||
darrayDelete(current_zone->segments);
|
||
}
|
||
darrayDelete(zones);
|
||
}
|