bool encode_flif(int argc, char **argv, Images &images, int palette_size, int acb, flifEncodingOptional method, int lookback, int learn_repeats, int frame_delay, int divisor=CONTEXT_TREE_COUNT_DIV, int min_size=CONTEXT_TREE_MIN_SUBTREE_SIZE, int split_threshold=CONTEXT_TREE_SPLIT_THRESHOLD, int yiq=1, int plc=1) { 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(); } bool grayscale=true; for (Image &image : images) if (image.uses_color()) grayscale=false; if (grayscale && images[0].numPlanes() == 3) { v_printf(2,"Chroma not actually used, dropping it.\n"); for (Image &image : images) image.drop_color(); } uint64_t nb_pixels = (uint64_t)images[0].rows() * images[0].cols(); std::vector<std::string> desc; if (nb_pixels > 2) { // no point in doing anything for 1- or 2-pixel images if (plc) desc.push_back("PLC"); // compactify channels if (yiq) 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) { palette_size = 1024; if (nb_pixels * images.size() / 2 < 1024) palette_size = nb_pixels * images.size() / 2; } 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 * images.size() > 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.o == Optional::undefined) { // no method specified, pick one heuristically if (nb_pixels * images.size() < 10000) method.encoding=flifEncoding::nonInterlaced; // if the image is small, not much point in doing interlacing else method.encoding=flifEncoding::interlaced; // 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 * images.size() < 5000) learn_repeats--; // avoid large trees for small images if (learn_repeats < 0) learn_repeats=0; } FILE *file = fopen(argv[0],"wb"); if (!file) return false; FileIO fio(file, argv[0]); return flif_encode(fio, images, desc, method.encoding, learn_repeats, acb, frame_delay, palette_size, lookback, divisor, min_size, split_threshold); }
bool encode_flif(int argc, char **argv, Images &images, flif_options &options) { bool flat=true; unsigned int framenb=0; for (Image& i : images) { i.frame_delay = options.frame_delay[framenb]; if (framenb+1 < options.frame_delay.size()) framenb++; } 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(); } bool grayscale=true; for (Image &image : images) if (image.uses_color()) grayscale=false; if (grayscale && images[0].numPlanes() == 3) { v_printf(2,"Chroma not actually used, dropping it.\n"); for (Image &image : images) image.drop_color(); } uint64_t nb_pixels = (uint64_t)images[0].rows() * images[0].cols(); std::vector<std::string> desc; if (nb_pixels > 2) { // no point in doing anything for 1- or 2-pixel images if (options.plc && (images[0].getDepth() > 8 || !options.loss)) { desc.push_back("Channel_Compact"); // compactify channels (not if lossy, because then loss gets magnified!) } if (options.ycocg) { desc.push_back("YCoCg"); // convert RGB(A) to YCoCg(A) } desc.push_back("PermutePlanes"); // permute RGB to GRB desc.push_back("Bounds"); // get the bounds of the color spaces } // only use palette/CB if we're lossless, because lossy and palette don't go well together... if (options.palette_size == -1) { options.palette_size = DEFAULT_MAX_PALETTE_SIZE; if (nb_pixels * images.size() / 3 < DEFAULT_MAX_PALETTE_SIZE) { options.palette_size = nb_pixels * images.size() / 3; } } if (!options.loss && options.palette_size != 0) { desc.push_back("Palette_Alpha"); // try palette (including alpha) desc.push_back("Palette"); // try palette (without alpha) } if (!options.loss) { if (options.acb == -1) { // not specified if ACB should be used if (nb_pixels * images.size() > 10000) { desc.push_back("Color_Buckets"); // try auto color buckets on large images } } else if (options.acb) { desc.push_back("Color_Buckets"); // try auto color buckets if forced } } if (options.method.o == Optional::undefined) { // no method specified, pick one heuristically if (nb_pixels * images.size() < 10000) options.method.encoding=flifEncoding::nonInterlaced; // if the image is small, not much point in doing interlacing else options.method.encoding=flifEncoding::interlaced; // default method: interlacing } if (images.size() > 1) { desc.push_back("Duplicate_Frame"); // find duplicate frames if (!options.loss) { // only if lossless if (options.frs) desc.push_back("Frame_Shape"); // get the shapes of the frames if (options.lookback) desc.push_back("Frame_Lookback"); // make a "deep" alpha channel (negative values are transparent to some previous frame) } } if (options.learn_repeats < 0) { // no number of repeats specified, pick a number heuristically options.learn_repeats = TREE_LEARN_REPEATS; //if (nb_pixels * images.size() < 5000) learn_repeats--; // avoid large trees for small images if (options.learn_repeats < 0) options.learn_repeats=0; } bool result = true; if (!options.just_add_loss) { FILE *file = NULL; if (!strcmp(argv[0],"-")) file = stdout; else file = fopen(argv[0],"wb"); if (!file) return false; FileIO fio(file, (file == stdout? "to standard output" : argv[0])); if (!flif_encode(fio, images, desc, options)) result = false; } else { BlobIO bio; // will just contain some unneeded FLIF header stuff if (!flif_encode(bio, images, desc, options)) result = false; else if (!images[0].save(argv[0])) result = false; } // get rid of palette images[0].clear(); return result; }
bool encode_flif(int argc, char **argv, Images &images, int palette_size, int acb, flifEncodingOptional method, int lookback, int learn_repeats, std::vector<int> &frame_delay, int divisor=CONTEXT_TREE_COUNT_DIV, int min_size=CONTEXT_TREE_MIN_SUBTREE_SIZE, int split_threshold=CONTEXT_TREE_SPLIT_THRESHOLD, int yiq=1, int plc=1, int frs=1, int cutoff=2, int alpha=19, int crc_check=-1, int loss=0) { bool flat=true; unsigned int framenb=0; for (Image& i : images) { i.frame_delay = frame_delay[framenb]; if (framenb+1 < frame_delay.size()) framenb++; } 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(); } bool grayscale=true; for (Image &image : images) if (image.uses_color()) grayscale=false; if (grayscale && images[0].numPlanes() == 3) { v_printf(2,"Chroma not actually used, dropping it.\n"); for (Image &image : images) image.drop_color(); } uint64_t nb_pixels = (uint64_t)images[0].rows() * images[0].cols(); std::vector<std::string> desc; if (nb_pixels > 2) { // no point in doing anything for 1- or 2-pixel images if (plc && !loss) { desc.push_back("Channel_Compact"); // compactify channels (not if lossy, because then loss gets magnified!) } if (yiq) { desc.push_back("YCoCg"); // convert RGB(A) to YCoCg(A) } desc.push_back("Bounds"); // get the bounds of the color spaces } if (!loss) { // only use palette/CB if we're lossless, because lossy and palette don't go well together... if (palette_size == -1) { palette_size = 1024; if (nb_pixels * images.size() / 2 < 1024) { palette_size = nb_pixels * images.size() / 2; } } if (palette_size != 0) { desc.push_back("Palette_Alpha"); // try palette (including alpha) } if (palette_size != 0) { desc.push_back("Palette"); // try palette (without alpha) } if (acb == -1) { // not specified if ACB should be used if (nb_pixels * images.size() > 10000) { desc.push_back("Color_Buckets"); // try auto color buckets on large images } } else if (acb) { desc.push_back("Color_Buckets"); // try auto color buckets if forced } } if (method.o == Optional::undefined) { // no method specified, pick one heuristically if (nb_pixels * images.size() < 10000) method.encoding=flifEncoding::nonInterlaced; // if the image is small, not much point in doing interlacing else method.encoding=flifEncoding::interlaced; // default method: interlacing } if (images.size() > 1) { desc.push_back("Duplicate_Frame"); // find duplicate frames if (!loss) { // only if lossless if (frs) desc.push_back("Frame_Shape"); // get the shapes of the frames if (lookback) desc.push_back("Frame_Lookback"); // 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 * images.size() < 5000) learn_repeats--; // avoid large trees for small images if (learn_repeats < 0) learn_repeats=0; } FILE *file = fopen(argv[0],"wb"); if (!file) return false; FileIO fio(file, argv[0]); return flif_encode(fio, images, desc, method.encoding, learn_repeats, acb, palette_size, lookback, divisor, min_size, split_threshold, cutoff, alpha, crc_check, loss); }
bool handle_encode_arguments(int argc, char **argv, Images &images, int palette_size, int acb, flifEncodingOptional method, int lookback, int learn_repeats, int frame_delay) { int nb_input_images = argc-1; while(argc>1) { Image image; v_printf(2,"\r"); if (!image.load(argv[0])) { e_printf("Could not read input file: %s\n", argv[0]); return 2; }; images.push_back(std::move(image)); const Image& last_image = images.back(); if (last_image.rows() != images[0].rows() || last_image.cols() != images[0].cols() || last_image.numPlanes() != images[0].numPlanes()) { e_printf("Dimensions of all input images should be the same!\n"); e_printf(" First image is %ux%u, %i channels.\n",images[0].cols(),images[0].rows(),images[0].numPlanes()); e_printf(" This image is %ux%u, %i channels: %s\n",last_image.cols(),last_image.rows(),last_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.o == Optional::undefined) { // no method specified, pick one heuristically if (nb_pixels < 10000) method.encoding=flifEncoding::nonInterlaced; // if the image is small, not much point in doing interlacing else method.encoding=flifEncoding::interlaced; // 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; } FILE *file = fopen(argv[0],"wb"); if (!file) return false; FileIO fio(file, argv[0]); return flif_encode(fio, images, desc, method.encoding, learn_repeats, acb, frame_delay, palette_size, lookback); }