/** * \file ppm.c * \brief Fichier de déclaration des fonctions de manipulation d'images ppm * * Déclaration du corps des fonctions déclarées dans \ref ppm.h */ #include "ppm.h" #include #define RGB_COMPONENT_COLOR 255 #define CREATOR "CL" /** * \brief function description * * Fonction d’ouverture de fichier selon le mode demandé. Si la fonction ne * peut pas ouvrir le fichier, elle arrête le processus qui renverra la valeur * `1`. En cas de succès, la fonction renverra un pointeur de fichier vers le * fichier ouvert. * * \param[in] t_filename Nom du fichier à ouvrir * \param[in] t_mode Mode du fichier à ouvrir * \return Pointeur de fichier */ FILE *get_file(const char *t_filename, const char *t_mode) { FILE *fp = fopen(t_filename, t_mode); if (!fp) { fprintf(stderr, "Unable to open file '%s'\n", t_filename); exit(FILE_IO_ERROR); } return fp; } /** * Lit et vérifie le format du fichier passé en argument. Si le format n’est * pas correct, la fonction arrête le processus qui renverra la valeur `1`. * * \param[in] t_fp Fichier ppm où lire les données * \param[in] t_filename Nom du fichier ouvert */ void read_file_format(FILE *t_fp, const char *t_filename) { char buff[16]; if (!fgets(buff, sizeof(buff), t_fp)) { perror(t_filename); exit(FILE_IO_ERROR); } /* check file format */ if (buff[0] != 'P' || buff[1] != '6') { fprintf(stderr, "Invalid image format (must be 'P6')\n"); exit(FILE_FORMAT_ERROR); } } /** * Vérifie si le header contient des commentaires et les ignore le cas échéant. * * \param[in] t_fp Fichier ppm où lire les données */ void check_for_comments(FILE *t_fp) { char c; c = (char)getc(t_fp); while (c == '#') { while (getc(t_fp) != '\n') { } c = (char)getc(t_fp); } ungetc(c, t_fp); } /** * Lit la taille des données image et les écrit dans le conteneur d’images * passé en argument. * * \param[in] t_fp Fichier ppm où lire les données * \param[out] t_img Conteneur d’image où écrire les résultats * \param[in] t_filename Nom du fichier ouvert */ void read_file_size(FILE *t_fp, Image *t_img, const char *t_filename) { if (fscanf(t_fp, "%lu %lu", &t_img->sizeX, &t_img->sizeY) != 2) { fprintf(stderr, "Invalid image size (error loading '%s')\n", t_filename); exit(FILE_FORMAT_ERROR); } } /** * Vérifie le format RGB de l’image ppm. Si le format n’est pas correct, la * fonction arrête le processus qui renverra la valeur `1`. * * \param[in] t_fp Fichier ppm où lire les données * \param[in] t_filename Nom du fichier ouvert */ void read_rgb(FILE *t_fp, const char *t_filename) { char d; int rgb_comp_color; /* read rgb component */ if (fscanf(t_fp, "%d", &rgb_comp_color) != 1) { fprintf(stderr, "Invalid rgb component (error loading '%s')\n", t_filename); exit(FILE_FORMAT_ERROR); } fscanf(t_fp, "%c ", &d); /* check rgb component depth */ if (rgb_comp_color != RGB_COMPONENT_COLOR) { fprintf(stderr, "'%s' does not have 8-bits components\n", t_filename); exit(FILE_FORMAT_ERROR); } } /** * \brief function description * * Lit les données images brutes du fichier ppm ouvert et les stocke dans \p * t_data. * * \param[in] t_fp Fichier ppm ouvert source * \param[in] t_size Taille des données brutes * \param[out] t_data Pointeur vers le tableau de sortie des données brutes * \param[in] t_filename Nom du fichier d’entrée * \return Taille du tableau de données obtenu */ void read_data(FILE *t_fp, uint64_t t_size, unsigned char **t_data, const char *t_filename) { *t_data = (unsigned char *)malloc(t_size * sizeof(unsigned char)); assert(*t_data); /* read pixel data from file */ if (!fread(*t_data, (size_t)1, t_size, t_fp)) { fprintf(stderr, "Error loading image '%s'\n", t_filename); free(*t_data); exit(FILE_IO_ERROR); } } /** * Convertit vers un tableau de `unsigned char` les pixels contenus dans un * conteneur d’image. La taille du tableau de `unsigned char` est la taille du * tableau de pixels multipliée par trois du fait des trois emplacements séparés * par couleur. * * \param[out] t_img Image dont les pixels doivent être convertis * \param[in] t_data Données à convertir en structures \ref Pixel * \param[in] t_size Taille du tableau de `unsigned char` */ void dataToImage(Image *t_img, uint8_t *t_data, uint64_t t_size) { uint64_t i; t_img->pixels = darrayNew(sizeof(Pixel)); for (i = 0; i < t_size; i += 3) { darrayPushBack(t_img->pixels, newPixel(t_data[i], t_data[i + 1], t_data[i + 2])); } } /** * Convertit le vecteur de pixels d’un conteneur d’image en un tableau de * valeurs de type `uint8_t` afin de permettre l’écriture d’une image dans un * fichier. * * \param[in] t_img Conteneur d’image contenant les pixels à convertir * \return Tableau de pointeurs de `uint8_t` */ uint8_t *imageToData(Image *t_img) { Pixel *pixel; uint8_t *data, size; uint64_t i; size = (uint8_t)darraySize(t_img->pixels); data = (uint8_t *)malloc(3 * sizeof(uint8_t) * size); for (i = 0; i < size; i += 3) { pixel = darrayGet(t_img->pixels, i / 3); data[i] = pixel->red; data[i + 1] = pixel->green; data[i + 2] = pixel->blue; } return data; } /** * Ouvre le fichier image avec son nom de fichier passé par le paramètre * `filename` et charge ses informations et données dans l'objet `img` dans * lequel les données et l'image seront manipulables. Retourne la valeur 1 en * cas de succès. * * \param[in] t_filename Nom du fichier image à ouvrir * \param[out] t_img Objet \ref Image manipulable * \return Retourne 1 en cas de succès */ int imageLoadPPM(const char *t_filename, Image *t_img) { FILE *fp; uint64_t size; unsigned char *data = NULL; fp = get_file(t_filename, "rb"); /* open PPM file for reading */ read_file_format(fp, t_filename); /* read image format */ check_for_comments(fp); /* check for comments */ read_file_size(fp, t_img, t_filename); /* read image size information */ read_rgb(fp, t_filename); /* read rgb component */ size = t_img->sizeX * t_img->sizeY * 3; read_data(fp, size, &data, t_filename); /* read data from file */ dataToImage(t_img, data, size); free(data); fclose(fp); return 1; } /** * Ouvre le fichier image avec son nom de fichier passé par le paramètre * `filename` et y écrit les informations trouvées dans l’objet `img`. * * \param[in] t_filename Nom du fichier image à ouvrir * \param[in] t_img Objet \ref Image à écrire * \param[in] t_data Données décompressées de l’image au format natif ppm */ void imageSavePPM(const char *t_filename, Image *t_img, uint8_t *t_data) { FILE *fp; fp = get_file(t_filename, "wb"); /* open file for output */ /* write the header file */ fprintf(fp, "P6\n"); /* image format */ fprintf(fp, "# Created by %s\n", CREATOR); /* comments */ fprintf(fp, "%lu %lu\n", t_img->sizeX, t_img->sizeY); /* image size */ fprintf(fp, "%d\n", RGB_COMPONENT_COLOR); /* rgb component depth */ fwrite(t_data, (size_t)1, (size_t)(3 * t_img->sizeX * t_img->sizeY), fp); free(t_data); fclose(fp); }