int handle_decode(char **argv, Images &images, int quality, int scale) { char *ext = strrchr(argv[1],'.'); if (!check_compatible_extension(ext)) { e_printf("Error: expected \".png\", \".pnm\" or \".pam\" file name extension for output file\n"); return 1; } if (!decode_flif(argv, images, quality, scale)) return 3; if (scale>1) v_printf(3,"Downscaling output: %ux%u -> %ux%u\n",images[0].cols(),images[0].rows(),images[0].cols()/scale,images[0].rows()/scale); if (images.size() == 1) { if (!images[0].save(argv[1],scale)) return 2; } else { int counter=0; std::vector<char> vfilename(strlen(argv[1])+6); char *filename = &vfilename[0]; strcpy(filename,argv[1]); char *a_ext = strrchr(filename,'.'); for (Image& image : images) { sprintf(a_ext,"-%03d%s",counter++,ext); if (!image.save(filename,scale)) return 2; v_printf(2," (%i/%i) \r",counter,(int)images.size()); v_printf(4,"\n"); } } v_printf(2,"\n"); return -1; }
int handle_decode(int argc, char **argv, Images &images, int quality, int scale, int resize_width, int resize_height) { if (scale < 0) { // just identify the file(s), don't actually decode while (argc>0) { decode_flif(argv, images, quality, scale, resize_width, resize_height); argv++; argc--; } return 0; } char *ext = strrchr(argv[1],'.'); if (!check_compatible_extension(ext) && strcmp(argv[1],"null:")) { e_printf("Error: expected \".png\", \".pnm\" or \".pam\" file name extension for output file\n"); return 1; } if (!decode_flif(argv, images, quality, scale, resize_width, resize_height)) { e_printf("Error: could not decode FLIF file\n"); return 3; } if (!strcmp(argv[1],"null:")) return 0; // if (scale>1) // v_printf(3,"Downscaling output: %ux%u -> %ux%u\n",images[0].cols(),images[0].rows(),images[0].cols()/scale,images[0].rows()/scale); if (images.size() == 1) { if (!images[0].save(argv[1])) return 2; } else { int counter=0; std::vector<char> vfilename(strlen(argv[1])+6); char *filename = &vfilename[0]; strcpy(filename,argv[1]); char *a_ext = strrchr(filename,'.'); for (Image& image : images) { sprintf(a_ext,"-%03d%s",counter++,ext); if (!image.save(filename)) return 2; v_printf(2," (%i/%i) \r",counter,(int)images.size()); v_printf(4,"\n"); } } v_printf(2,"\n"); return 0; }
int main(int argc, char **argv) { Images images; int mode = 0; // 0 = encode, 1 = decode int method = 0; // 1=non-interlacing, 2=interlacing int quality = 100; // 100 = everything, positive value: partial decode, negative value: only rough data int learn_repeats = -1; int acb = -1; // try auto color buckets int scale = 1; int frame_delay = 100; int palette_size = 512; int lookback = 1; if (strcmp(argv[0],"flif") == 0) mode = 0; if (strcmp(argv[0],"dflif") == 0) mode = 1; if (strcmp(argv[0],"deflif") == 0) mode = 1; if (strcmp(argv[0],"decflif") == 0) mode = 1; static struct option optlist[] = { {"help", 0, NULL, 'h'}, {"encode", 0, NULL, 'e'}, {"decode", 0, NULL, 'd'}, {"first", 1, NULL, 'f'}, {"verbose", 0, NULL, 'v'}, {"interlace", 0, NULL, 'i'}, {"no-interlace", 0, NULL, 'n'}, {"acb", 0, NULL, 'a'}, {"no-acb", 0, NULL, 'b'}, {"quality", 1, NULL, 'q'}, {"scale", 1, NULL, 's'}, {"palette", 1, NULL, 'p'}, {"repeats", 1, NULL, 'r'}, {"frame-delay", 1, NULL, 'f'}, {"lookback", 1, NULL, 'l'}, {0, 0, 0, 0} }; int i,c; while ((c = getopt_long (argc, argv, "hedvinabq:s:p:r:f:l:", optlist, &i)) != -1) { switch (c) { case 'e': mode=0; break; case 'd': mode=1; break; case 'v': verbosity++; break; case 'i': if (method==0) method=2; break; case 'n': method=1; break; case 'a': acb=1; break; case 'b': acb=0; break; case 'p': palette_size=atoi(optarg); if (palette_size < -1 || palette_size > 30000) {fprintf(stderr,"Not a sensible number for option -p\n"); return 1; } if (palette_size == 0) {v_printf(2,"Palette disabled\n"); } break; case 'q': quality=atoi(optarg); if (quality < -1 || quality > 100) {fprintf(stderr,"Not a sensible number for option -q\n"); return 1; } break; case 's': scale=atoi(optarg); if (scale < 1 || scale > 128) {fprintf(stderr,"Not a sensible number for option -s\n"); return 1; } break; case 'r': learn_repeats=atoi(optarg); if (learn_repeats < 0 || learn_repeats > 1000) {fprintf(stderr,"Not a sensible number for option -r\n"); return 1; } break; case 'f': frame_delay=atoi(optarg); if (frame_delay < 0 || frame_delay > 60000) {fprintf(stderr,"Not a sensible number for option -f\n"); return 1; } break; case 'l': lookback=atoi(optarg); if (lookback < -1 || lookback > 256) {fprintf(stderr,"Not a sensible number for option -l\n"); return 1; } break; case 'h': default: show_help(); return 0; } } argc -= optind; argv += optind; v_printf(3," _____ __ (__) _____"); v_printf(3,"\n (___ || | | || ___) ");v_printf(2,"FLIF 0.1 [2 October 2015]"); v_printf(3,"\n (__ || |_|__|| __) Free Lossless Image Format"); v_printf(3,"\n (__||______) |__) (c) 2010-2015 J.Sneyers & P.Wuille, GNU GPL v3+\n"); v_printf(3,"\n"); if (argc == 0) { //fprintf(stderr,"Input file missing.\n"); if (verbosity == 1) show_help(); return 1; } if (argc == 1) { fprintf(stderr,"Output file missing.\n"); show_help(); return 1; } if (file_exists(argv[0])) { if (mode == 0 && file_is_flif(argv[0])) { v_printf(2,"Input file is a FLIF file, adding implicit -d\n"); mode = 1; } char *f = strrchr(argv[0],'/'); char *ext = f ? strrchr(f,'.') : strrchr(argv[0],'.'); if (mode == 0) { if (ext && ( !strcasecmp(ext,".png") || !strcasecmp(ext,".pnm") || !strcasecmp(ext,".ppm") || !strcasecmp(ext,".pgm") || !strcasecmp(ext,".pbm") || !strcasecmp(ext,".pam"))) { // ok } else { fprintf(stderr,"Warning: expected \".png\" or \".pnm\" file name extension for input file, trying anyway...\n"); } } else { if (ext && ( !strcasecmp(ext,".flif") || ( !strcasecmp(ext,".flf") ))) { // ok } else { fprintf(stderr,"Warning: expected file name extension \".flif\" for input file, trying anyway...\n"); } } } else if (argc>0) { fprintf(stderr,"Input file does not exist: %s\n",argv[0]); return 1; } if (mode == 0) { int nb_input_images = argc-1; while(argc>1) { Image image; v_printf(2,"\r"); if (!image.load(argv[0])) { fprintf(stderr,"Could not read input file: %s\n", argv[0]); return 2; }; images.push_back(image); if (image.rows() != images[0].rows() || image.cols() != images[0].cols() || image.numPlanes() != images[0].numPlanes()) { fprintf(stderr,"Dimensions of all input images should be the same!\n"); fprintf(stderr," First image is %ux%u, %i channels.\n",images[0].cols(),images[0].rows(),images[0].numPlanes()); fprintf(stderr," This image is %ux%u, %i channels: %s\n",image.cols(),image.rows(),image.numPlanes(),argv[0]); return 2; } argc--; argv++; if (nb_input_images>1) {v_printf(2," (%i/%i) ",(int)images.size(),nb_input_images); v_printf(4,"\n");} } v_printf(2,"\n"); bool flat=true; for (Image &image : images) if (image.uses_alpha()) flat=false; if (flat && images[0].numPlanes() == 4) { v_printf(2,"Alpha channel not actually used, dropping it.\n"); for (Image &image : images) image.drop_alpha(); } uint64_t nb_pixels = (uint64_t)images[0].rows() * images[0].cols(); std::vector<std::string> desc; desc.push_back("YIQ"); // convert RGB(A) to YIQ(A) desc.push_back("BND"); // get the bounds of the color spaces if (palette_size > 0) desc.push_back("PLA"); // try palette (including alpha) if (palette_size > 0) desc.push_back("PLT"); // try palette (without alpha) if (acb == -1) { // not specified if ACB should be used if (nb_pixels > 10000) desc.push_back("ACB"); // try auto color buckets on large images } else if (acb) desc.push_back("ACB"); // try auto color buckets if forced if (method == 0) { // no method specified, pick one heuristically if (nb_pixels < 10000) method=1; // if the image is small, not much point in doing interlacing else method=2; // default method: interlacing } if (images.size() > 1) { desc.push_back("DUP"); // find duplicate frames desc.push_back("FRS"); // get the shapes of the frames if (lookback != 0) desc.push_back("FRA"); // make a "deep" alpha channel (negative values are transparent to some previous frame) } if (learn_repeats < 0) { // no number of repeats specified, pick a number heuristically learn_repeats = TREE_LEARN_REPEATS; if (nb_pixels < 5000) learn_repeats--; // avoid large trees for small images if (learn_repeats < 0) learn_repeats=0; } encode(argv[0], images, desc, method, learn_repeats, acb, frame_delay, palette_size, lookback); } else { char *ext = strrchr(argv[1],'.'); if (ext && ( !strcasecmp(ext,".png") || !strcasecmp(ext,".pnm") || !strcasecmp(ext,".ppm") || !strcasecmp(ext,".pgm") || !strcasecmp(ext,".pbm") || !strcasecmp(ext,".pam"))) { // ok } else { fprintf(stderr,"Error: expected \".png\", \".pnm\" or \".pam\" file name extension for output file\n"); return 1; } if (!decode(argv[0], images, quality, scale)) return 3; if (scale>1) v_printf(3,"Downscaling output: %ux%u -> %ux%u\n",images[0].cols(),images[0].rows(),images[0].cols()/scale,images[0].rows()/scale); if (images.size() == 1) { if (!images[0].save(argv[1],scale)) return 2; } else { int counter=0; std::vector<char> vfilename(strlen(argv[1])+6); char *filename = &vfilename[0]; strcpy(filename,argv[1]); char *a_ext = strrchr(filename,'.'); for (Image& image : images) { sprintf(a_ext,"-%03d%s",counter++,ext); if (!image.save(filename,scale)) return 2; v_printf(2," (%i/%i) \r",counter,(int)images.size()); v_printf(4,"\n"); } } v_printf(2,"\n"); } for (Image &image : images) image.clear(); return 0; }
int handle_decode(int argc, char **argv, Images &images, flif_options &options) { if (options.scale < 0) { // just identify the file(s), don't actually decode while (argc>0) { decode_flif(argv, images, options); argv++; argc--; } return 0; } if (argc == 1 && options.show_breakpoints) { decode_flif(argv, images, options); return 0; } char *ext = strrchr(argv[1],'.'); if (check_metadata_extension(ext)) { // only requesting metadata, no need to actually decode the file options.scale = -2; decode_flif(argv, images, options); if (!images[0].save(argv[1])) return 2; v_printf(2,"\n"); return 0; } if (!check_compatible_extension(ext) && strcmp(argv[1],"null:") && strcmp(argv[1],"-")) { e_printf("Error: expected \".png\", \".pnm\" or \".pam\" file name extension for output file\n"); return 1; } if (!(ext && ( !strcasecmp(ext,".png")))) options.keep_palette = false; // don't try to make a palette PNM try { if (!decode_flif(argv, images, options)) { e_printf("Error: could not decode FLIF file\n"); return 3; } } catch (std::bad_alloc& ba) { e_printf("Error: memory allocation problem (unexpected)\n"); return 3; } if (!strcmp(argv[1],"null:")) return 0; // if (scale>1) // v_printf(3,"Downscaling output: %ux%u -> %ux%u\n",images[0].cols(),images[0].rows(),images[0].cols()/scale,images[0].rows()/scale); if (images.size() == 1) { if (!images[0].save(argv[1])) return 2; } else { bool to_stdout=false; if (!strcmp(argv[1],"-")) { to_stdout=true; v_printf(1,"Warning: writing animation to standard output as a concatenation of PAM files.\n"); } int counter=0; int maxlength = strlen(argv[1])+100; std::vector<char> vfilename(maxlength); char *filename = &vfilename[0]; bool use_custom_format = false; if (strchr(argv[1],'%')) use_custom_format = true; strcpy(filename,argv[1]); char *a_ext = strrchr(filename,'.'); if (!a_ext && !to_stdout) { e_printf("Problem saving animation to %s\n",filename); return 2; } for (Image& image : images) { if (!to_stdout) { if (use_custom_format) snprintf(filename,maxlength,argv[1],counter); else if (images.size() < 1000) sprintf(a_ext,"-%03d%s",counter,ext); else if (images.size() < 10000) sprintf(a_ext,"-%04d%s",counter,ext); else if (images.size() < 100000) sprintf(a_ext,"-%05d%s",counter,ext); else sprintf(a_ext,"-%08d%s",counter,ext); if (file_exists(filename) && !options.overwrite) { e_printf("Error: output file already exists: %s\nUse --overwrite to force overwrite.\n",filename); return 4; } if (!image.save(filename)) return 2; } else { if (!image.save(argv[1])) return 2; } counter++; v_printf(2," (%i/%i) \r",counter,(int)images.size()); v_printf(4,"\n"); } } // get rid of palette (should also do this in the non-standard/error paths, but being lazy here since the tool will exit anyway) images[0].clear(); v_printf(2,"\n"); return 0; }
bool encode_load_input_images(int argc, char **argv, Images &images, flif_options &options) { int nb_input_images = argc-1; int nb_actual_images = 0; metadata_options md; md.icc = options.color_profile; md.xmp = options.metadata; md.exif = options.metadata; while(argc>1) { int maxlength = strlen(argv[0])+100; std::vector<char> vfilename(maxlength); char *filename = argv[0]; int framecounter = 0; int stop_searching = 0; bool multiple=false; if (!file_exists(argv[0]) && strchr(argv[0],'%')) { multiple=true; filename = &vfilename[0]; } for ( ; framecounter < 0xFFFFFFF && stop_searching < 1000; framecounter++ ) { if (multiple) { snprintf(filename,maxlength,argv[0],framecounter); if (!file_exists(filename)) { stop_searching++; continue; } stop_searching = 0; } Image image; if (options.keep_palette) image.palette = true; // tells the PNG loader to keep palette intact v_printf_tty(2,"\r"); if (!image.load(filename,md)) { e_printf("Could not read input file: %s\n", argv[0]); return false; }; if (image.numPlanes() > 0) { nb_actual_images++; images.push_back(std::move(image)); Image& last_image = images.back(); if (last_image.rows() != images[0].rows() || last_image.cols() != images[0].cols()) { e_printf("Dimensions of all input images should be the same!\n"); e_printf(" First image is %ux%u\n",images[0].cols(),images[0].rows()); e_printf(" This image is %ux%u: %s\n",last_image.cols(),last_image.rows(),filename); return false; } if (last_image.numPlanes() < images[0].numPlanes()) { if (images[0].numPlanes() == 3) last_image.ensure_chroma(); else if (images[0].numPlanes() == 4) last_image.ensure_alpha(); else { e_printf("Problem while loading input images, please report this.\n"); return false; } } else if (last_image.numPlanes() > images[0].numPlanes()) { if (last_image.numPlanes() == 3) { for (Image& i : images) i.ensure_chroma(); } else if (last_image.numPlanes() == 4) { for (Image& i : images) i.ensure_alpha(); } else { e_printf("Problem while loading input images, please report this.\n"); return false; } } } else { if (images.size() == 0) { e_printf("First specify the actual image(s), then the metadata.\n"); return false; } for (size_t i=0; i<image.metadata.size(); i++) images[0].metadata.push_back(image.metadata[i]); } if (nb_input_images>1) {v_printf(2," (%i/%i) ",(int)images.size(),nb_input_images); v_printf(4,"\n");} if (!multiple) break; } argc--; argv++; } if (nb_actual_images > 0) return true; e_printf("Error: no actual input images to be encoded!\n"); return false; }