/** * \file common.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, `true` si le pixel est éligible, `false` 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, int t_idx, Zone* t_zone) { Pixel *current_pixel = darrayGet(t_img->pixels, t_idx); int xd, xg, y = t_idx / (int)t_img->x; if (t_idx >= (int)darraySize(t_img->pixels) || 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); if(!sameColor(current_pixel, t_zone)) { break; } (*current_pixel).visited = true; } for(xg = t_idx; xg - y >= 0; --xg) { /* fetch right limit of segment */ current_pixel = darrayGet(t_img->pixels, xd); if(current_pixel->visited || !sameColor(current_pixel, t_zone)) { break; } (*current_pixel).visited = true; } /* Add segment to its zone */ darrayPushBack(t_zone->segments, newSegment(xd, xg)); for(; xg <= xd; ++xg) { /* 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, int t_idx, darray *zones) { Zone* current_zone; Pixel* pixel; size_t i; pixel = darrayGet(t_img->pixels, 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(zones); ++i) { current_zone = darrayGet(zones, i); /* if it does, add selected pixel and its neighbourging 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(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); size_t i; zones = darrayNew(sizeof(Zone)); /* for each pixel, try to create a new zone */ for (i = 0; i < 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; size_t i; img = newImage(); imageLoadPPM(t_input_file, img); zones = imgToZones(img); /* print segments for debug ************************************************/ DEBUG { printf("Detected %zu zones\n", darraySize(zones)); for (i = 0; i < darraySize(zones); ++i) { 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); } } printf("\n"); } deleteImage(img); for(i = 0; i < darraySize(zones); ++i) { deleteZoneContent(darrayGet(zones, i)); } darrayDelete(zones); }