2018-11-12 13:25:24 +00:00
|
|
|
|
/**
|
|
|
|
|
* \file common.c
|
|
|
|
|
* \brief Implémentation de la (dé)compression d’images
|
|
|
|
|
*/
|
|
|
|
|
|
2018-11-24 15:35:33 +00:00
|
|
|
|
#include "compress.h"
|
2018-11-06 15:08:20 +00:00
|
|
|
|
|
2018-11-12 13:25:24 +00:00
|
|
|
|
/**
|
|
|
|
|
* 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, `true` si le pixel est éligible, `false` sinon
|
|
|
|
|
*/
|
2018-11-21 09:46:01 +00:00
|
|
|
|
bool sameColor(Pixel* t_pixel, Zone* t_zone) {
|
2018-11-06 15:08:20 +00:00
|
|
|
|
return t_pixel->r == t_zone->r && t_pixel->g == t_zone->g &&
|
|
|
|
|
t_pixel->b == t_zone->b;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-12 13:25:24 +00:00
|
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
2018-11-21 09:46:01 +00:00
|
|
|
|
void addPixelToSelectedZone(Image* t_img, int t_idx, Zone* t_zone) {
|
2018-11-23 09:32:49 +00:00
|
|
|
|
Pixel *current_pixel = darrayGet(t_img->pixels, t_idx);
|
2018-11-07 00:03:55 +00:00
|
|
|
|
int xd, xg, y = t_idx / (int)t_img->x;
|
2018-11-24 15:35:33 +00:00
|
|
|
|
if (t_idx >= (int)darraySize(t_img->pixels) ||
|
2018-11-06 15:08:20 +00:00
|
|
|
|
t_idx < 0 || !sameColor(current_pixel, t_zone)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
(*current_pixel).visited = true;
|
|
|
|
|
for(xd = t_idx; xd % (int)t_img->x != 0; ++xd) { /* fetch right limit of segment */
|
|
|
|
|
current_pixel = darrayGet(t_img->pixels, xd);
|
2018-11-24 15:35:33 +00:00
|
|
|
|
if(!sameColor(current_pixel, t_zone)) {
|
2018-11-06 15:08:20 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
(*current_pixel).visited = true;
|
|
|
|
|
}
|
2018-11-07 00:03:55 +00:00
|
|
|
|
for(xg = t_idx; xg - y >= 0; --xg) { /* fetch right limit of segment */
|
2018-11-06 15:08:20 +00:00
|
|
|
|
current_pixel = darrayGet(t_img->pixels, xd);
|
2018-11-23 09:32:49 +00:00
|
|
|
|
if(current_pixel->visited || !sameColor(current_pixel, t_zone)) {
|
2018-11-06 15:08:20 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
(*current_pixel).visited = true;
|
|
|
|
|
}
|
|
|
|
|
/* Add segment to its zone */
|
2018-11-23 09:32:49 +00:00
|
|
|
|
darrayPushBack(t_zone->segments, newSegment(xd, xg));
|
2018-11-06 15:08:20 +00:00
|
|
|
|
for(; xg <= xd; ++xg) { /* process every pixel up and down the segment */
|
|
|
|
|
addPixelToSelectedZone(t_img, t_idx + t_img->x, t_zone);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-24 15:35:33 +00:00
|
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
2018-11-21 09:46:01 +00:00
|
|
|
|
void chooseZoneForPixel(Image* t_img, int t_idx, darray *zones) {
|
|
|
|
|
Zone* current_zone;
|
|
|
|
|
Pixel* pixel;
|
2018-11-06 15:08:20 +00:00
|
|
|
|
size_t i;
|
2018-11-22 13:05:11 +00:00
|
|
|
|
|
2018-11-06 15:08:20 +00:00
|
|
|
|
pixel = darrayGet(t_img->pixels, t_idx);
|
2018-11-22 13:05:11 +00:00
|
|
|
|
/* if the pixel has already been visited, no need to continue */
|
|
|
|
|
if (pixel->visited) {
|
2018-11-06 15:08:20 +00:00
|
|
|
|
return;
|
2018-11-22 13:05:11 +00:00
|
|
|
|
}
|
|
|
|
|
/* for each known zone, see if it matches the current pixel's color */
|
2018-11-06 15:08:20 +00:00
|
|
|
|
for (i = 0; i < darraySize(zones); ++i) {
|
|
|
|
|
current_zone = darrayGet(zones, i);
|
2018-11-22 13:05:11 +00:00
|
|
|
|
/* if it does, add selected pixel and its neighbourging pixels of the same
|
|
|
|
|
* color */
|
2018-11-06 15:08:20 +00:00
|
|
|
|
if (sameColor(pixel, current_zone)) {
|
|
|
|
|
addPixelToSelectedZone(t_img, t_idx, current_zone);
|
2018-11-22 13:05:11 +00:00
|
|
|
|
return;
|
2018-11-06 15:08:20 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-11-22 13:05:11 +00:00
|
|
|
|
/* 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(zones, current_zone);
|
|
|
|
|
addPixelToSelectedZone(t_img, t_idx, current_zone);
|
2018-11-06 15:08:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-24 15:35:33 +00:00
|
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
2018-11-21 09:46:01 +00:00
|
|
|
|
darray* imgToZones(Image* t_img) {
|
|
|
|
|
darray *zones;
|
2018-11-06 15:08:20 +00:00
|
|
|
|
const size_t nb_pixels = darraySize(t_img->pixels);
|
|
|
|
|
size_t i;
|
|
|
|
|
zones = darrayNew(sizeof(Zone));
|
|
|
|
|
|
2018-11-22 13:05:11 +00:00
|
|
|
|
/* for each pixel, try to create a new zone */
|
2018-11-06 15:08:20 +00:00
|
|
|
|
for (i = 0; i < nb_pixels; ++i) {
|
|
|
|
|
chooseZoneForPixel(t_img, i, zones);
|
|
|
|
|
}
|
|
|
|
|
return zones;
|
|
|
|
|
}
|
2018-11-07 00:03:55 +00:00
|
|
|
|
|
2018-11-24 15:35:33 +00:00
|
|
|
|
/**
|
|
|
|
|
* 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) {
|
2018-11-23 09:31:47 +00:00
|
|
|
|
Image *img;
|
2018-11-21 09:46:01 +00:00
|
|
|
|
darray *zones;
|
2018-11-24 15:35:33 +00:00
|
|
|
|
size_t i;
|
2018-11-07 00:03:55 +00:00
|
|
|
|
img = newImage();
|
2018-11-24 15:35:33 +00:00
|
|
|
|
imageLoadPPM(t_input_file, img);
|
2018-11-07 00:03:55 +00:00
|
|
|
|
zones = imgToZones(img);
|
2018-11-11 15:32:27 +00:00
|
|
|
|
|
2018-11-23 09:31:47 +00:00
|
|
|
|
/* print segments for debug ************************************************/
|
|
|
|
|
DEBUG {
|
|
|
|
|
printf("Detected %zu zones\n", darraySize(zones));
|
2018-11-24 15:35:33 +00:00
|
|
|
|
for (i = 0; i < darraySize(zones); ++i) {
|
2018-11-23 09:31:47 +00:00
|
|
|
|
Zone *zone = darrayGet(zones, i);
|
|
|
|
|
printf("\n=== Zone %zu (%d %d %d) ===\n", i, zone->r, zone->g, zone->b);
|
|
|
|
|
for (size_t j = 0; j < darraySize(zone->segments); ++j) {
|
|
|
|
|
Segment *segm = darrayGet(zone->segments, j);
|
|
|
|
|
printf("[%zu: %d, %d]\t", segm->xg / img->x, segm->xg, segm->xd);
|
|
|
|
|
}
|
2018-11-22 13:05:11 +00:00
|
|
|
|
}
|
2018-11-23 09:31:47 +00:00
|
|
|
|
printf("\n");
|
2018-11-22 13:05:11 +00:00
|
|
|
|
}
|
2018-11-11 15:32:27 +00:00
|
|
|
|
|
2018-11-07 00:03:55 +00:00
|
|
|
|
deleteImage(img);
|
2018-11-24 15:35:33 +00:00
|
|
|
|
for(i = 0; i < darraySize(zones); ++i) {
|
|
|
|
|
deleteZoneContent(darrayGet(zones, i));
|
|
|
|
|
}
|
|
|
|
|
darrayDelete(zones);
|
2018-11-07 00:03:55 +00:00
|
|
|
|
}
|