diff --git a/img/asterix5p.png b/img/asterix5p.png new file mode 100644 index 0000000..6183f55 Binary files /dev/null and b/img/asterix5p.png differ diff --git a/src/compress.c b/src/compress.c index 191265a..d86a78a 100644 --- a/src/compress.c +++ b/src/compress.c @@ -5,19 +5,39 @@ #include "compress.h" +/** + * \var uint32_t tolerance + * \brief Color tolerance + * + * Cette variable est la valeur du pourcentage de tolérance couleur lors de la + * création de nouvelles zones. Cette variable contient une valeur située entre + * 0 et 100 inclus. + */ +int32_t tolerance; + /** * Cette fonction permet d’évaluer si le pixel passé en argument est éligible à - * la zone passée également en argument. + * la zone passée également en argument. Si la \ref tolerance a pour valeur 0, + * alors les couleurs doivent être strictements identiques. Sinon, leur + * différence doit être inférieure à la tolérance de couleur. * * \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 */ int32_t sameColor(Pixel *t_pixel, Zone *t_zone) { - return (t_pixel->red == t_zone->red && t_pixel->green == t_zone->green && - t_pixel->blue == t_zone->blue) - ? 1 - : 0; + int diff_red, diff_green, diff_blue; + if (tolerance == 0) { + return (t_pixel->red == t_zone->red && t_pixel->green == t_zone->green && + t_pixel->blue == t_zone->blue) + ? 1 + : 0; + } + diff_red = (abs((int32_t)t_zone->red - (int32_t)t_pixel->red) * 100) / 255; + diff_green = + (abs((int32_t)t_zone->green - (int32_t)t_pixel->green) * 100) / 255; + diff_blue = (abs((int32_t)t_zone->blue - (int32_t)t_pixel->blue) * 100) / 255; + return ((diff_red + diff_green + diff_blue) / 3) <= tolerance; } /** @@ -172,14 +192,17 @@ void write_compressed_file(Image *t_img, FILE *t_output, darray *t_zones) { * * \param[in] t_input_file Nom/chemin du fichier `.ppm` d'entrée * \param[in] t_output_file Nom/chemin du fichier `.su` de sortie + * \param[in] t_tolerance Pourcentage de tolérance de couleur */ -void compress(const char *t_input_file, const char *t_output_file) { +void compress(const char *t_input_file, const char *t_output_file, + int32_t t_tolerance) { Image *img; darray *zones; FILE *output_file; if (!t_output_file) { t_output_file = DEFAULT_COMPRESSED_NAME; } + tolerance = t_tolerance; img = newImage(); imageLoadPPM(t_input_file, img); output_file = get_file(t_output_file, "wb"); diff --git a/src/compress.h b/src/compress.h index 9b2f832..50f7c25 100644 --- a/src/compress.h +++ b/src/compress.h @@ -23,6 +23,7 @@ void write_segments(FILE *t_output, darray *t_segments); /// Écrit les données compressées dans le fichier de sortie void write_compressed_file(Image *t_img, FILE *t_output, darray *t_zones); /// Compresse l'image d'entrée -void compress(const char *t_input_file, const char *t_output_file); +void compress(const char *t_input_file, const char *t_output_file, + int32_t tolerance); #endif /* SRC_COMPRESS_H_ */ diff --git a/src/main.c b/src/main.c index 8ba5bee..4676ca7 100644 --- a/src/main.c +++ b/src/main.c @@ -24,18 +24,21 @@ */ void help(int t_exit_code) { puts("Usage:\n" - "surfaces-unies -i path [-o path] [-options]\n\n" + "surfaces-unies -i path [-o path] [--options] [-t 0-100]\n\n" "The default action is to compress the mandatory input image to a .su\n" "file saved in the current directory.\n" "The input image MUST be saved in the ppm format.\n" "Options available:\n" - "-h --help\n\tdisplay the current message\n" - "-i --input\n\tpath to the input file (MANDATORY)\n" + "-h --help\n\tDisplay the current message\n" + "-i --input\n\tPath to the input file (MANDATORY)\n" "-o --output\n" - "\tpath to the output file (if the file already exists, it will be\n" + "\tPath to the output file (if the file already exists, it will be\n" "\toverwritten)\n" - "-c --compress\n\tcompress the input file\n" - "-u --uncompress\n\tuncompresses the input file to the output file."); + "-t --tolerance\n" + "\tColor tolerance for lossy compression. By default at 0 for lossless\n" + "\tcompression, at 100 will consider every color to be the same.\n" + "-c --compress\n\tCompress the input file\n" + "-u --uncompress\n\tUncompresses the input file to the output file."); exit(t_exit_code); } @@ -49,9 +52,10 @@ void help(int t_exit_code) { * supplémentaires aux fonctions. */ struct Argres { - char *input; /*!< Nom du fichier d'entrée */ - char *output; /*!< Nom du fichier de sortie */ - char compress; /*!< Le fichier d'entrée doit-il être compressé ? */ + char *input; /*!< Nom du fichier d'entrée */ + char *output; /*!< Nom du fichier de sortie */ + char compress; /*!< Le fichier d'entrée doit-il être compressé ? */ + int32_t tolerance; /*!< Tolérance en pourcentage des différences de couleur */ }; typedef struct Argres Argres; @@ -72,6 +76,12 @@ void get_args(Argres *t_args, const int *const t_c) { case 'o': (*t_args).output = optarg; break; case 'c': (*t_args).compress = 1; break; case 'u': (*t_args).compress = 0; break; + case 't': + (*t_args).tolerance = atoi(optarg); + if(t_args->tolerance < 0 || t_args->tolerance > 100) { + help(ARGERROR); + } + break; case '?': default: help(ARGERROR); } @@ -95,16 +105,18 @@ Argres process_args(const int t_argc, char *t_argv[]) { res.input = NULL; res.compress = 1; res.output = NULL; + res.tolerance = 0; while (1) { int option_index = 0; static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"input", required_argument, NULL, 'i'}, {"output", required_argument, NULL, 'o'}, + {"tolerance", required_argument, NULL, 't'}, {"compress", no_argument, NULL, 'c'}, {"uncompress", no_argument, NULL, 'u'}, {NULL, 0, NULL, 0}}; - int c = getopt_long(t_argc, t_argv, "hi:o:cu", long_options, &option_index); + int c = getopt_long(t_argc, t_argv, "hi:o:t:cu", long_options, &option_index); if (c == -1) break; get_args(&res, &c); @@ -129,8 +141,8 @@ int main(int argc, char **argv) { fprintf(stderr, "ERROR: no input file."); help(ARGERROR); } - if(argresults.compress) { - compress(argresults.input, argresults.output); + if (argresults.compress) { + compress(argresults.input, argresults.output, argresults.tolerance); } else { uncompress(argresults.input, argresults.output); }