diff -Nru gifsicle-1.91/configure.ac gifsicle-1.92/configure.ac --- gifsicle-1.91/configure.ac 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/configure.ac 2019-04-18 20:09:42.000000000 +0000 @@ -1,5 +1,5 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT([gifsicle], [1.91]) +AC_INIT([gifsicle], [1.92]) AC_CONFIG_SRCDIR([src/gifsicle.h]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE diff -Nru gifsicle-1.91/debian/changelog gifsicle-1.92/debian/changelog --- gifsicle-1.91/debian/changelog 2018-05-31 21:53:01.000000000 +0000 +++ gifsicle-1.92/debian/changelog 2020-05-12 05:01:39.000000000 +0000 @@ -1,4 +1,10 @@ -gifsicle (1.91-2sergeyd1~xenial1) xenial; urgency=medium +gifsicle (1.92-2sergeyd1~xenial1) xenial; urgency=medium + + * New upstream version + + -- Sergey Dryabzhinsky Tue, 12 May 2020 06:54:30 +0300 + +gifsicle (1.91-2sergeyd1~trusty1) trusty; urgency=medium * Rebuild for SergeyD * Respect hardening env diff -Nru gifsicle-1.91/debian/patches/new_path_to_exec_in_tests.patch gifsicle-1.92/debian/patches/new_path_to_exec_in_tests.patch --- gifsicle-1.91/debian/patches/new_path_to_exec_in_tests.patch 1970-01-01 00:00:00.000000000 +0000 +++ gifsicle-1.92/debian/patches/new_path_to_exec_in_tests.patch 2019-07-23 14:12:23.000000000 +0000 @@ -0,0 +1,104 @@ +Description: Fix path to exec in test files +Author: Herbert Parentes Fortes Neto +Last-Update: 2018-06-30 +Index: gifsicle/test/001-transexpand.testie +=================================================================== +--- gifsicle.orig/test/001-transexpand.testie ++++ gifsicle/test/001-transexpand.testie +@@ -1,7 +1,7 @@ + %script + for i in 0 1 2 3; do +- gifsicle -O$i x.gif > y.gif +- gifdiff x.gif y.gif ++ ./src/gifsicle -O$i x.gif > y.gif ++ ./src/gifdiff x.gif y.gif + done + + %file -e x.gif +Index: gifsicle/test/002-cropresize.testie +=================================================================== +--- gifsicle.orig/test/002-cropresize.testie ++++ gifsicle/test/002-cropresize.testie +@@ -1,5 +1,5 @@ + %script +-gifsicle -O2 --crop 25,0+36x36 --resize 100x100 x.gif > y.gif ++./src/gifsicle -O2 --crop 25,0+36x36 --resize 100x100 x.gif > y.gif + + %file -e x.gif + R0lGODlhVgAkAPcFAEAUEAAAAIEAAOIAAP/mIP///1VACLoAAPbKAPraEL4AAP/aFP/iHPK2APrS +Index: gifsicle/test/003-bgopt.testie +=================================================================== +--- gifsicle.orig/test/003-bgopt.testie ++++ gifsicle/test/003-bgopt.testie +@@ -1,7 +1,7 @@ + %script + for i in 0 1 2 3; do +- gifsicle -O$i x.gif > y.gif +- gifdiff x.gif y.gif ++ ./src/gifsicle -O$i x.gif > y.gif ++ ./src/gifdiff x.gif y.gif + done + + %file -e x.gif +Index: gifsicle/test/004-careful.testie +=================================================================== +--- gifsicle.orig/test/004-careful.testie ++++ gifsicle/test/004-careful.testie +@@ -1,9 +1,9 @@ + %script + for i in 0 1 2 3; do +- gifsicle -O$i x.gif > y.gif +- gifdiff x.gif y.gif +- gifsicle -O$i --careful x.gif > y.gif +- gifdiff x.gif y.gif ++ ./src/gifsicle -O$i x.gif > y.gif ++ ./src/gifdiff x.gif y.gif ++ ./src/gifsicle -O$i --careful x.gif > y.gif ++ ./src/gifdiff x.gif y.gif + done + + %file -e x.gif +Index: gifsicle/test/005-resize.testie +=================================================================== +--- gifsicle.orig/test/005-resize.testie ++++ gifsicle/test/005-resize.testie +@@ -1,5 +1,5 @@ + %script +-gifsicle --resize 30x30 x.gif > y.gif ++./src/gifsicle --resize 30x30 x.gif > y.gif + + %file -e x.gif + R0lGODlhZABkAPUAABxJWFgAZnQAiXWjspYAAIIAM4ozEKcAALcBAbwAS6EAbcgBAtcCAuUEAuUK +Index: gifsicle/test/006-unoptdisposal.testie +=================================================================== +--- gifsicle.orig/test/006-unoptdisposal.testie ++++ gifsicle/test/006-unoptdisposal.testie +@@ -1,7 +1,7 @@ + %script + for i in 0 1 2 3; do +- gifsicle -O$i x.gif > y.gif +- gifdiff x.gif y.gif ++ ./src/gifsicle -O$i x.gif > y.gif ++ ./src/gifdiff x.gif y.gif + done + + %file -e x.gif +Index: gifsicle/test/008-resizemix.testie +=================================================================== +--- gifsicle.orig/test/008-resizemix.testie ++++ gifsicle/test/008-resizemix.testie +@@ -1,9 +1,9 @@ + %script +-gifsicle --resize=250x_ strip.gif > stripx.gif +-gifsicle greenmini.gif stripx.gif > stripy.gif +-gifsicle -UO2 --crop=0,0+10x7 stripy.gif '#1' > overlay.gif +-gifsicle --crop=0,0+10x7 greenmini.gif > minibit.gif +-gifdiff overlay.gif minibit.gif ++./src/gifsicle --resize=250x_ strip.gif > stripx.gif ++./src/gifsicle greenmini.gif stripx.gif > stripy.gif ++./src/gifsicle -UO2 --crop=0,0+10x7 stripy.gif '#1' > overlay.gif ++./src/gifsicle --crop=0,0+10x7 greenmini.gif > minibit.gif ++./src/gifdiff overlay.gif minibit.gif + + %file -e greenmini.gif + R0lGODdh+gAHAPAAAD3kxzzixSwAAAAA+gAHAAACP4SPqcvtD6OctL4AsN1V8w+G4kiW5omm6sq2 diff -Nru gifsicle-1.91/debian/patches/series gifsicle-1.92/debian/patches/series --- gifsicle-1.91/debian/patches/series 2018-01-08 16:48:24.000000000 +0000 +++ gifsicle-1.92/debian/patches/series 2019-07-23 14:12:23.000000000 +0000 @@ -0,0 +1,2 @@ +typo_manpage.patch +new_path_to_exec_in_tests.patch diff -Nru gifsicle-1.91/debian/patches/typo_manpage.patch gifsicle-1.92/debian/patches/typo_manpage.patch --- gifsicle-1.91/debian/patches/typo_manpage.patch 1970-01-01 00:00:00.000000000 +0000 +++ gifsicle-1.92/debian/patches/typo_manpage.patch 2019-07-23 14:12:23.000000000 +0000 @@ -0,0 +1,24 @@ +Description: Remove undefined macro 'AM' + Fix Kornel Lipiński name - 'n'. + (from NEWS.md file) +Author: Herbert Parentes Fortes Neto +Last-Update: 2019-05-30 +Index: gifsicle/gifsicle.1 +=================================================================== +--- gifsicle.orig/gifsicle.1 ++++ gifsicle/gifsicle.1 +@@ -1,5 +1,4 @@ + .\" -*- mode: nroff -*- +-.AM + .ds V 1.92 + .ds E " \-\- + .if t .ds E \(em +@@ -1324,7 +1323,7 @@ http://www.danbbs.dk/~dino/ + .br + Adaptive tree method for GIF writing. + .PP +-Kornel Lesin\*[']ski ++Kornel Lipinski + .br + .Op \-\-lossy + option. diff -Nru gifsicle-1.91/gifdiff.1 gifsicle-1.92/gifdiff.1 --- gifsicle-1.91/gifdiff.1 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/gifdiff.1 2019-04-18 20:09:42.000000000 +0000 @@ -1,5 +1,5 @@ .\" -*- mode: nroff -*- -.ds V 1.91 +.ds V 1.92 .ds E " \-\- .if t .ds E \(em .de Op diff -Nru gifsicle-1.91/gifsicle.1 gifsicle-1.92/gifsicle.1 --- gifsicle-1.91/gifsicle.1 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/gifsicle.1 2019-04-18 20:09:42.000000000 +0000 @@ -1,5 +1,6 @@ .\" -*- mode: nroff -*- -.ds V 1.91 +.AM +.ds V 1.92 .ds E " \-\- .if t .ds E \(em .de Op @@ -103,7 +104,7 @@ .Ix "\ \ \ disposal" "\fB\-\-disposal\fP" .Ix "\ \ \ looping" "\fB\-\-loopcount\fP" .Ix "\ \ \ portions of" "frame selections" -.Ix "\ \ \ smaller" "\fB\-\-optimize\fP, \fB\-\-colors\fP" +.Ix "\ \ \ smaller" "\fB\-\-optimize\fP, \fB\-\-colors\fP, \fB\-\-lossy\fP" .Ix "\ \ \ speed" "\fB\-\-delay\fP" .Ix "Bad output" "\fB\-\-careful\fP" .Ix "Background color" "\fB\-\-background\fP" @@ -111,7 +112,7 @@ .Ix "\ \ \ reducing number" "\fB\-\-colors\fP, \fB\-\-dither\fP, \fB\-\-gamma\fP" .Ix "Comments" "\fB\-\-comment\fP" .Ix "Extensions" "\fB\-\-extension\fP, \fB\-\-app\-extension\fP, \fB\-\-extension\-info\fP" -.Ix "File size" "\fB\-\-optimize\fP, \fB\-\-unoptimize\fP, \fB\-\-colors\fP" +.Ix "File size" "\fB\-\-optimize\fP, \fB\-\-unoptimize\fP, \fB\-\-colors\fP, \fB\-\-lossy\fP" .TP 30 Image transformations .Ix "\ \ \ cropping" "\fB\-\-crop\fP, \fB\-\-crop\-transparency\fP" @@ -824,7 +825,8 @@ .Sp .PP You may also be interested in other options for shrinking GIFs, such as -.Op \-k +.Op \-k , +.Op \-\-lossy , and .Op \-\-no\-extensions . .RE @@ -1092,6 +1094,18 @@ (\fB\-\-dither\fP). ' .Sp +.TP +.Op \-\-lossy "[=\fIlossiness\fR]" +' +Alter image colors to shrink output file size at the cost of artifacts +and noise. +.I Lossiness +determines how many artifacts are allowed; higher values can result +in smaller file sizes, but cause more artifacts. The default +.I lossiness +is 20. +' +.Sp .PD 0 .TP .Oa \-\-change\-color color1 " " color2 @@ -1310,6 +1324,11 @@ .br Adaptive tree method for GIF writing. .PP +Kornel Lesin\*[']ski +.br +.Op \-\-lossy +option. +.PP http://www.lcdf.org/gifsicle/ .br The diff -Nru gifsicle-1.91/gifview.1 gifsicle-1.92/gifview.1 --- gifsicle-1.91/gifview.1 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/gifview.1 2019-04-18 20:09:42.000000000 +0000 @@ -1,5 +1,5 @@ .\" -*- mode: nroff -*- -.ds V 1.91 +.ds V 1.92 .ds E " \-\- .if t .ds E \(em .de Op diff -Nru gifsicle-1.91/include/lcdfgif/gif.h gifsicle-1.92/include/lcdfgif/gif.h --- gifsicle-1.91/include/lcdfgif/gif.h 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/include/lcdfgif/gif.h 2019-04-18 20:09:42.000000000 +0000 @@ -145,6 +145,7 @@ typedef struct { int flags; + int loss; void *padding[7]; } Gif_CompressInfo; diff -Nru gifsicle-1.91/NEWS.md gifsicle-1.92/NEWS.md --- gifsicle-1.91/NEWS.md 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/NEWS.md 2019-04-18 20:09:42.000000000 +0000 @@ -1,6 +1,14 @@ Gifsicle NEWS ============= +## Version 1.92 – 18.Apr.2019 + +* Add `--lossy` option from Kornel Lipiński. + +* Remove an assertion failure possible with `--conserve-memory` + `--colors` + + `--careful`. + + ## Version 1.91 – 5.Jan.2018 * Several security bug fixes with malicious GIFs. diff -Nru gifsicle-1.91/README.md gifsicle-1.92/README.md --- gifsicle-1.91/README.md 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/README.md 2019-04-18 20:09:42.000000000 +0000 @@ -58,8 +58,8 @@ nmake -f Makefile.w32 Gifview will not be built. The makefile is from Emil Mikulic - with updates by Eddie Kohler and Steven -Marthouse . + with updates by Steven Marthouse +. To build Gifsicle on Windows using Borland C++, change into the `src` directory and run @@ -75,17 +75,18 @@ Contact ------- -Please write me if you have trouble building or running Gifsicle, or -if you have suggestions or patches. +If you have trouble building or running Gifsicle, try GitHub issues: +https://github.com/kohler/gifsicle -Eddie Kohler, ekohler@gmail.com -http://www.read.seas.harvard.edu/~kohler/ +Eddie Kohler, ekohler@gmail.com +http://www.read.seas.harvard.edu/~kohler/ +Please note that I do not provide support for Windows. Copyright/License ----------------- -All source code is Copyright (C) 1997-2018 Eddie Kohler. +All source code is Copyright (C) 1997-2019 Eddie Kohler. IF YOU PLAN TO USE GIFSICLE ONLY TO CREATE OR MODIFY GIF IMAGES, DON'T WORRY ABOUT THE REST OF THIS SECTION. Anyone can use Gifsicle however @@ -114,20 +115,23 @@ Authors ------- -Eddie Kohler -http://www.read.seas.harvard.edu/~kohler/ +Eddie Kohler \ +http://www.read.seas.harvard.edu/~kohler/ \ He wrote it. -Anne Dudfield -http://www.frii.com/~annied/ +Anne Dudfield \ +http://www.frii.com/~annied/ \ She named it. -David Hedbor +David Hedbor \ Many bug reports and constructive whining about the optimizer. -Emil Mikulic +Emil Mikulic \ Win32 port help. -Hans Dinsen-Hansen -http://www.danbbs.dk/~dino/ +Hans Dinsen-Hansen \ +http://www.danbbs.dk/~dino/ \ Adaptive tree method for GIF writing. + +Kornel Lesiński \ +`--lossy` option. diff -Nru gifsicle-1.91/src/clp.c gifsicle-1.92/src/clp.c --- gifsicle-1.91/src/clp.c 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/src/clp.c 2019-04-18 20:09:42.000000000 +0000 @@ -2,7 +2,7 @@ /* clp.c - Complete source code for CLP. * This file is part of CLP, the command line parser package. * - * Copyright (c) 1997-2018 Eddie Kohler, ekohler@gmail.com + * Copyright (c) 1997-2019 Eddie Kohler, ekohler@gmail.com * * CLP is free software. It is distributed under the GNU General Public * License, Version 2, or, alternatively and at your discretion, under the diff -Nru gifsicle-1.91/src/gifdiff.c gifsicle-1.92/src/gifdiff.c --- gifsicle-1.91/src/gifdiff.c 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/src/gifdiff.c 2019-04-18 20:09:42.000000000 +0000 @@ -1,5 +1,5 @@ /* gifdiff.c - Gifdiff compares GIF images for identical appearance. - Copyright (C) 1998-2018 Eddie Kohler, ekohler@gmail.com + Copyright (C) 1998-2019 Eddie Kohler, ekohler@gmail.com This file is part of gifdiff, in the gifsicle package. Gifdiff is free software. It is distributed under the GNU Public License, @@ -61,9 +61,8 @@ static void combine_colormaps(Gif_Colormap *gfcm, Gif_Colormap *newcm) { - int i; - if (!gfcm) return; - for (i = 0; i < gfcm->ncol; i++) { + int i, gfcm_ncol = gfcm ? gfcm->ncol : 0; + for (i = 0; i < gfcm_ncol; i++) { Gif_Color *c = &gfcm->col[i]; c->pixel = Gif_AddColor(newcm, c, 1); } @@ -116,11 +115,12 @@ uint16_t *data = gdata[is_second]; uint16_t *last = glast[is_second]; Gif_Colormap *gfcm = gfi->local ? gfi->local : gfs->global; + int gfcm_ncol = gfcm ? gfcm->ncol : 0; /* set up colormap */ - for (i = 0; i < gfcm->ncol; ++i) + for (i = 0; i < gfcm_ncol; ++i) map[i] = gfcm->col[i].pixel; - for (i = gfcm->ncol; i < 256; ++i) + for (i = gfcm_ncol; i < 256; ++i) map[i] = 1; if (gfi->transparent >= 0 && gfi->transparent < 256) map[gfi->transparent] = TRANSP; @@ -560,7 +560,7 @@ case VERSION_OPT: printf("gifdiff (LCDF Gifsicle) %s\n", VERSION); - printf("Copyright (C) 1998-2018 Eddie Kohler\n\ + printf("Copyright (C) 1998-2019 Eddie Kohler\n\ This is free software; see the source for copying conditions.\n\ There is NO warranty, not even for merchantability or fitness for a\n\ particular purpose.\n"); diff -Nru gifsicle-1.91/src/giffunc.c gifsicle-1.92/src/giffunc.c --- gifsicle-1.91/src/giffunc.c 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/src/giffunc.c 2019-04-18 20:09:42.000000000 +0000 @@ -1,5 +1,5 @@ /* giffunc.c - General functions for the GIF library. - Copyright (C) 1997-2018 Eddie Kohler, ekohler@gmail.com + Copyright (C) 1997-2019 Eddie Kohler, ekohler@gmail.com This file is part of the LCDF GIF library. The LCDF GIF library is free software. It is distributed under the GNU @@ -821,6 +821,7 @@ Gif_InitCompressInfo(Gif_CompressInfo *gcinfo) { gcinfo->flags = 0; + gcinfo->loss = 0; } diff -Nru gifsicle-1.91/src/gifread.c gifsicle-1.92/src/gifread.c --- gifsicle-1.91/src/gifread.c 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/src/gifread.c 2019-04-18 20:09:42.000000000 +0000 @@ -1,5 +1,5 @@ /* gifread.c - Functions to read GIFs. - Copyright (C) 1997-2018 Eddie Kohler, ekohler@gmail.com + Copyright (C) 1997-2019 Eddie Kohler, ekohler@gmail.com This file is part of the LCDF GIF library. The LCDF GIF library is free software. It is distributed under the GNU @@ -59,10 +59,10 @@ static Gif_ReadErrorHandler default_error_handler = 0; -#define gifgetc(grr) ((char)(*grr->byte_getter)(grr)) +#define gifgetc(grr) ((char)(*grr->byte_getter)(grr)) #define gifgetbyte(grr) ((*grr->byte_getter)(grr)) #define gifgetblock(ptr, size, grr) ((*grr->block_getter)(ptr, size, grr)) -#define gifeof(grr) ((*grr->eofer)(grr)) +#define gifeof(grr) ((*grr->eofer)(grr)) static inline uint16_t gifgetunsigned(Gif_Reader *grr) @@ -185,7 +185,7 @@ static int read_image_block(Gif_Reader *grr, uint8_t *buffer, int *bit_pos_store, - int *bit_len_store, int bits_needed) + int *bit_len_store, int bits_needed) { int bit_position = *bit_pos_store; int bit_length = *bit_len_store; @@ -281,8 +281,8 @@ if (bit_position + bits_needed > bit_length) /* Read in the next data block. */ if (!read_image_block(grr, buffer, &bit_position, &bit_length, - bits_needed)) - goto zero_length_block; + bits_needed)) + goto zero_length_block; i = bit_position / 8; accum = buffer[i] + (buffer[i+1] << 8); @@ -307,14 +307,14 @@ else if (code > next_code && next_code && next_code != clear_code) { /* code > next_code: a (hopefully recoverable) error. - Bug fix, 5/27: Do this even if old_code == clear_code, and set code - to 0 to prevent errors later. (If we didn't zero code, we'd later set - old_code = code; then we had old_code >= next_code; so the prefixes - array got all screwed up!) - - Bug fix, 4/12/2010: It is not an error if next_code == clear_code. - This happens at the end of a large GIF: see the next comment ("If no - meaningful next code should be defined...."). */ + Bug fix, 5/27: Do this even if old_code == clear_code, and set code + to 0 to prevent errors later. (If we didn't zero code, we'd later set + old_code = code; then we had old_code >= next_code; so the prefixes + array got all screwed up!) + + Bug fix, 4/12/2010: It is not an error if next_code == clear_code. + This happens at the end of a large GIF: see the next comment ("If no + meaningful next code should be defined...."). */ if (gfc->errors[1] < 20) gif_read_error(gfc, 1, "image corrupted, code out of range"); else if (gfc->errors[1] == 20) @@ -347,10 +347,10 @@ if (next_code != clear_code) { next_code++; if (next_code == CUR_BUMP_CODE) { - if (bits_needed < GIF_MAX_CODE_BITS) - bits_needed++; - else - next_code = clear_code; + if (bits_needed < GIF_MAX_CODE_BITS) + bits_needed++; + else + next_code = clear_code; } } @@ -476,11 +476,11 @@ i = gifgetbyte(grr); while (i > 0) { /* add 2 before check so we don't have to check after loop when appending - 0 block */ + 0 block */ if (comp_len + i + 2 > comp_cap) { - comp_cap *= 2; - Gif_ReArray(comp, uint8_t, comp_cap); - if (!comp) return 0; + comp_cap *= 2; + Gif_ReArray(comp, uint8_t, comp_cap); + if (!comp) return 0; } comp[comp_len] = i; gifgetblock(comp + comp_len + 1, i, grr); @@ -604,7 +604,7 @@ Gif_Reader new_grr; make_data_reader(&new_grr, gfi->compressed, gfi->compressed_len); if (!uncompress_image(gfc, gfi, &new_grr)) - return 0; + return 0; } } else if (read_flags & GIF_READ_UNCOMPRESSED) { @@ -627,7 +627,7 @@ static void read_graphic_control_extension(Gif_Context *gfc, Gif_Image *gfi, - Gif_Reader *grr) + Gif_Reader *grr) { uint8_t len; uint8_t crap[GIF_MAX_BLOCK]; @@ -744,7 +744,7 @@ gfs->loopcount = gifgetunsigned(grr); len = gifgetbyte(grr); if (len) - gif_read_error(gfc, 1, "bad loop extension"); + gif_read_error(gfc, 1, "bad loop extension"); } else gif_read_error(gfc, 1, "bad loop extension"); @@ -777,7 +777,7 @@ static Gif_Stream * read_gif(Gif_Reader *grr, int read_flags, - const char* landmark, Gif_ReadErrorHandler handler) + const char* landmark, Gif_ReadErrorHandler handler) { Gif_Stream *gfs; Gif_Image *gfi; @@ -846,35 +846,36 @@ switch (block) { case 0xF9: - read_graphic_control_extension(&gfc, gfi, grr); - break; + read_graphic_control_extension(&gfc, gfi, grr); + break; case 0xCE: - last_name = suck_data(last_name, 0, grr); - break; + last_name = suck_data(last_name, 0, grr); + break; case 0xFE: - if (!read_comment_extension(gfi, grr)) goto done; - break; + if (!read_comment_extension(gfi, grr)) goto done; + break; case 0xFF: - read_application_extension(&gfc, grr); - break; + read_application_extension(&gfc, grr); + break; default: read_unknown_extension(&gfc, grr, block, 0, 0); - break; + break; } break; default: if (!unknown_block_type) { - char buf[256]; - sprintf(buf, "unknown block type %d at file offset %u", block, grr->pos - 1); - gif_read_error(&gfc, 1, buf); - unknown_block_type = 1; + char buf[256]; + sprintf(buf, "unknown block type %d at file offset %u", block, grr->pos - 1); + gif_read_error(&gfc, 1, buf); } + if (++unknown_block_type > 20) + goto done; break; } @@ -917,7 +918,7 @@ Gif_Stream * Gif_FullReadFile(FILE *f, int read_flags, - const char* landmark, Gif_ReadErrorHandler h) + const char* landmark, Gif_ReadErrorHandler h) { Gif_Reader grr; if (!f) return 0; @@ -932,7 +933,7 @@ Gif_Stream * Gif_FullReadRecord(const Gif_Record *gifrec, int read_flags, - const char* landmark, Gif_ReadErrorHandler h) + const char* landmark, Gif_ReadErrorHandler h) { Gif_Reader grr; if (!gifrec) return 0; diff -Nru gifsicle-1.91/src/gifsicle.c gifsicle-1.92/src/gifsicle.c --- gifsicle-1.91/src/gifsicle.c 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/src/gifsicle.c 2019-04-18 20:09:42.000000000 +0000 @@ -1,6 +1,6 @@ /* -*- c-basic-offset: 2 -*- */ /* gifsicle.c - gifsicle's main loop. - Copyright (C) 1997-2018 Eddie Kohler, ekohler@gmail.com + Copyright (C) 1997-2019 Eddie Kohler, ekohler@gmail.com This file is part of gifsicle. Gifsicle is free software. It is distributed under the GNU Public License, @@ -201,6 +201,7 @@ #define RESIZE_TOUCH_OPT 377 #define RESIZE_TOUCH_WIDTH_OPT 378 #define RESIZE_TOUCH_HEIGHT_OPT 379 +#define LOSSY_OPT 380 #define LOOP_TYPE (Clp_ValFirstUser) #define DISPOSAL_TYPE (Clp_ValFirstUser + 1) @@ -268,6 +269,7 @@ { "logical-screen", 'S', LOGICAL_SCREEN_OPT, DIMENSIONS_TYPE, Clp_Negate }, { "loopcount", 'l', 'l', LOOP_TYPE, Clp_Optional | Clp_Negate }, + { "lossy", 0, LOSSY_OPT, Clp_ValInt, Clp_Optional }, { "merge", 'm', 'm', 0, 0 }, { "method", 0, COLORMAP_ALGORITHM_OPT, COLORMAP_ALG_TYPE, 0 }, @@ -2073,6 +2075,13 @@ } break; + case LOSSY_OPT: + if (clp->have_val) + gif_write_info.loss = clp->val.i; + else + gif_write_info.loss = 20; + break; + /* RANDOM OPTIONS */ case NO_WARNINGS_OPT: @@ -2116,7 +2125,7 @@ #else printf("LCDF Gifsicle %s\n", VERSION); #endif - printf("Copyright (C) 1997-2018 Eddie Kohler\n\ + printf("Copyright (C) 1997-2019 Eddie Kohler\n\ This is free software; see the source for copying conditions.\n\ There is NO warranty, not even for merchantability or fitness for a\n\ particular purpose.\n"); diff -Nru gifsicle-1.91/src/gifunopt.c gifsicle-1.92/src/gifunopt.c --- gifsicle-1.91/src/gifunopt.c 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/src/gifunopt.c 2019-04-18 20:09:42.000000000 +0000 @@ -1,5 +1,5 @@ /* gifunopt.c - Unoptimization function for the GIF library. - Copyright (C) 1997-2018 Eddie Kohler, ekohler@gmail.com + Copyright (C) 1997-2019 Eddie Kohler, ekohler@gmail.com This file is part of the LCDF GIF library. The LCDF GIF library is free software. It is distributed under the GNU diff -Nru gifsicle-1.91/src/gifview.c gifsicle-1.92/src/gifview.c --- gifsicle-1.91/src/gifview.c 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/src/gifview.c 2019-04-18 20:09:42.000000000 +0000 @@ -1,5 +1,5 @@ /* gifview.c - gifview's main loop. - Copyright (C) 1997-2018 Eddie Kohler, ekohler@gmail.com + Copyright (C) 1997-2019 Eddie Kohler, ekohler@gmail.com This file is part of gifview, in the gifsicle package. Gifview is free software. It is distributed under the GNU Public License, @@ -1356,7 +1356,7 @@ case VERSION_OPT: printf("gifview (LCDF Gifsicle) %s\n", VERSION); - printf("Copyright (C) 1997-2018 Eddie Kohler\n\ + printf("Copyright (C) 1997-2019 Eddie Kohler\n\ This is free software; see the source for copying conditions.\n\ There is NO warranty, not even for merchantability or fitness for a\n\ particular purpose.\n"); diff -Nru gifsicle-1.91/src/gifwrite.c gifsicle-1.92/src/gifwrite.c --- gifsicle-1.91/src/gifwrite.c 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/src/gifwrite.c 2019-04-18 20:09:42.000000000 +0000 @@ -1,6 +1,6 @@ /* -*- mode: c; c-basic-offset: 2 -*- */ /* gifwrite.c - Functions to write GIFs. - Copyright (C) 1997-2018 Eddie Kohler, ekohler@gmail.com + Copyright (C) 1997-2019 Eddie Kohler, ekohler@gmail.com This file is part of the LCDF GIF library. The LCDF GIF library is free software. It is distributed under the GNU @@ -23,9 +23,9 @@ extern "C" { #endif -#define WRITE_BUFFER_SIZE 255 -#define NODES_SIZE GIF_MAX_CODE -#define LINKS_SIZE GIF_MAX_CODE +#define WRITE_BUFFER_SIZE 255 +#define NODES_SIZE GIF_MAX_CODE +#define LINKS_SIZE GIF_MAX_CODE /* 1.Aug.1999 - Removed code hashing in favor of an adaptive tree strategy based on Whirlgif-3.04, written by Hans Dinsen-Hansen . Mr. @@ -46,9 +46,9 @@ very small numbers of colors (2, 4, 8) won't get the speed benefits of TABLE nodes. */ -#define TABLE_TYPE 0 -#define LINKS_TYPE 1 -#define MAX_LINKS_TYPE 5 +#define TABLE_TYPE 0 +#define LINKS_TYPE 1 +#define MAX_LINKS_TYPE 5 typedef struct Gif_Node { Gif_Code code; uint8_t type; @@ -86,8 +86,8 @@ }; -#define gifputbyte(b, grr) ((*grr->byte_putter)(b, grr)) -#define gifputblock(b, l, grr) ((*grr->block_putter)(b, l, grr)) +#define gifputbyte(b, grr) ((*grr->byte_putter)(b, grr)) +#define gifputblock(b, l, grr) ((*grr->block_putter)(b, l, grr)) static inline void gifputunsigned(uint16_t uns, Gif_Writer *grr) @@ -204,6 +204,48 @@ } } +/* Used to hold accumulated error for the current candidate match */ +typedef struct gfc_rgbdiff {signed short r, g, b;} gfc_rgbdiff; + +/* Difference (MSE) between given color indexes + dithering error */ +static inline unsigned int color_diff(Gif_Color a, Gif_Color b, int a_transparent, int b_transparent, gfc_rgbdiff dither) +{ + /* if one is transparent and the other is not, then return maximum difference */ + /* TODO: figure out what color is in the canvas under the transparent pixel and match against that */ + if (a_transparent != b_transparent) return 1<<25; + + /* Two transparent colors are identical */ + if (a_transparent) return 0; + + /* squared error with or without dithering. */ + unsigned int dith = (a.gfc_red-b.gfc_red+dither.r)*(a.gfc_red-b.gfc_red+dither.r) + + (a.gfc_green-b.gfc_green+dither.g)*(a.gfc_green-b.gfc_green+dither.g) + + (a.gfc_blue-b.gfc_blue+dither.b)*(a.gfc_blue-b.gfc_blue+dither.b); + + unsigned int undith = (a.gfc_red-b.gfc_red+dither.r/2)*(a.gfc_red-b.gfc_red+dither.r/2) + + (a.gfc_green-b.gfc_green+dither.g/2)*(a.gfc_green-b.gfc_green+dither.g/2) + + (a.gfc_blue-b.gfc_blue+dither.b/2)*(a.gfc_blue-b.gfc_blue+dither.b/2); + + /* Smaller error always wins, under assumption that dithering is not required and it's only done opportunistically */ + return dith < undith ? dith : undith; +} + +/* difference between expected color a+dither and color b (used to calculate dithering required) */ +static inline gfc_rgbdiff diffused_difference(Gif_Color a, Gif_Color b, int a_transparent, int b_transparent, gfc_rgbdiff dither) +{ + gfc_rgbdiff d; + if (a_transparent || b_transparent) { + d.r = d.g = d.b = 0; + } else { + d.r = a.gfc_red - b.gfc_red + dither.r * 3 / 4; + d.g = a.gfc_green - b.gfc_green + dither.g * 3 / 4; + d.b = a.gfc_blue - b.gfc_blue + dither.b * 3 / 4; + } + return d; +} + +static inline uint8_t gif_pixel_at_pos(Gif_Image *gfi, unsigned pos); + static void gfc_change_node_to_table(Gif_CodeTable *gfc, Gif_Node *work_node, Gif_Node *next_node) @@ -273,9 +315,97 @@ return (y + 1) * gfi->width; } +struct selected_node { + Gif_Node *node; /* which node has been chosen by gfc_lookup_lossy */ + unsigned long pos, /* where the node ends */ + diff; /* what is the overall quality loss for that node */ +}; + +static inline void +gfc_lookup_lossy_try_node(Gif_CodeTable *gfc, const Gif_Colormap *gfcm, Gif_Image *gfi, + unsigned pos, Gif_Node *node, uint8_t suffix, uint8_t next_suffix, + gfc_rgbdiff dither, unsigned long base_diff, const unsigned int max_diff, struct selected_node *best_t); + +/* Recursive loop + * Find node that is descendant of node (or start new search if work_node is null) that best matches pixels starting at pos + * base_diff and dither are distortion from search made so far */ +static struct selected_node +gfc_lookup_lossy(Gif_CodeTable *gfc, const Gif_Colormap *gfcm, Gif_Image *gfi, + unsigned pos, Gif_Node *node, unsigned long base_diff, gfc_rgbdiff dither, const unsigned int max_diff) +{ + unsigned image_endpos = gfi->width * gfi->height; + + struct selected_node best_t = {node, pos, base_diff}; + if (pos >= image_endpos) return best_t; + + uint8_t suffix = gif_pixel_at_pos(gfi, pos); + assert(!node || (node >= gfc->nodes && node < gfc->nodes + NODES_SIZE)); + assert(suffix < gfc->clear_code); + if (!node) { + gfc_rgbdiff zero_diff = {0, 0, 0}; + /* prefix of the new node must be same as suffix of previously added node */ + return gfc_lookup_lossy(gfc, gfcm, gfi, pos+1, &gfc->nodes[suffix], base_diff, zero_diff, max_diff); + } + + /* search all nodes that are less than max_diff different from the desired pixel */ + if (node->type == TABLE_TYPE) { + int i; + for(i=0; i < gfc->clear_code; i++) { + if (!node->child.m[i]) continue; + gfc_lookup_lossy_try_node(gfc, gfcm, gfi, pos, node->child.m[i], suffix, i, dither, base_diff, max_diff, &best_t); + } + } + else { + for (node = node->child.s; node; node = node->sibling) { + gfc_lookup_lossy_try_node(gfc, gfcm, gfi, pos, node, suffix, node->suffix, dither, base_diff, max_diff, &best_t); + } + } + + return best_t; +} + +/** + * Replaces best_t with a new node if it's better + * + * @param node Current node to search + * @param suffix Previous pixel + * @param next_suffix Next pixel to evaluate (must correspond to the node given) + * @param dither Desired dithering + * @param base_diff Difference accumulated in the search so far + * @param max_diff Maximum allowed pixel difference + * @param best_t Current best candidate (input/output argument) + */ +static inline void +gfc_lookup_lossy_try_node(Gif_CodeTable *gfc, const Gif_Colormap *gfcm, Gif_Image *gfi, + unsigned pos, Gif_Node *node, uint8_t suffix, uint8_t next_suffix, + gfc_rgbdiff dither, unsigned long base_diff, const unsigned int max_diff, struct selected_node *best_t) +{ + unsigned int diff = suffix == next_suffix ? 0 : color_diff(gfcm->col[suffix], gfcm->col[next_suffix], suffix == gfi->transparent, next_suffix == gfi->transparent, dither); + if (diff <= max_diff) { + gfc_rgbdiff new_dither = diffused_difference(gfcm->col[suffix], gfcm->col[next_suffix], suffix == gfi->transparent, next_suffix == gfi->transparent, dither); + /* if the candidate pixel is good enough, check all possible continuations of that dictionary string */ + struct selected_node t = gfc_lookup_lossy(gfc, gfcm, gfi, pos+1, node, base_diff + diff, new_dither, max_diff); + + /* search is biased towards finding longest candidate that is below treshold rather than a match with minimum average error */ + if (t.pos > best_t->pos || (t.pos == best_t->pos && t.diff < best_t->diff)) { + *best_t = t; + } + } +} + +static inline uint8_t +gif_pixel_at_pos(Gif_Image *gfi, unsigned pos) +{ + unsigned y = pos / gfi->width, x = pos - y * gfi->width; + if (!gfi->interlace) + return gfi->img[y][x]; + else + return gfi->img[Gif_InterlaceLine(y, gfi->height)][x]; +} + static int -write_compressed_data(Gif_Image *gfi, - int min_code_bits, Gif_Writer *grr) +write_compressed_data(Gif_Stream *gfs, Gif_Image *gfi, + int min_code_bits, Gif_Writer *grr) { Gif_CodeTable* gfc = &grr->code_table; uint8_t stack_buffer[512 - 24]; @@ -286,6 +416,7 @@ unsigned pos; unsigned clear_bufpos, clear_pos; unsigned line_endpos; + unsigned image_endpos; const uint8_t *imageline; unsigned run = 0; @@ -298,6 +429,7 @@ Gif_Code next_code = 0; Gif_Code output_code; uint8_t suffix; + Gif_Colormap *gfcm; int cur_code_bits; @@ -312,14 +444,19 @@ /* next_code set by first runthrough of output clear_code */ GIF_DEBUG(("clear(%d) eoi(%d) bits(%d) ", CLEAR_CODE, EOI_CODE, cur_code_bits)); - work_node = 0; + work_node = NULL; output_code = CLEAR_CODE; /* Because output_code is clear_code, we'll initialize next_code, et al. below. */ pos = clear_pos = clear_bufpos = 0; - line_endpos = gfi->width; - imageline = gif_imageline(gfi, pos); + if (grr->gcinfo.loss) { + image_endpos = gfi->height * gfi->width; + gfcm = (gfi->local ? gfi->local : gfs->global); + } else { + line_endpos = gfi->width; + imageline = gif_imageline(gfi, pos); + } while (1) { @@ -391,81 +528,143 @@ /***** * Find the next code to output. */ + if (grr->gcinfo.loss) { + gfc_rgbdiff zero_diff = {0, 0, 0}; + struct selected_node t = gfc_lookup_lossy(gfc, gfcm, gfi, pos, NULL, 0, zero_diff, grr->gcinfo.loss * 10); + + work_node = t.node; + run = t.pos - pos; + pos = t.pos; + + if (pos < image_endpos) { + /* Output the current code. */ + if (next_code < GIF_MAX_CODE) { + gfc_define(gfc, work_node, gif_pixel_at_pos(gfi, pos), next_code); + next_code++; + } else + next_code = GIF_MAX_CODE + 1; /* to match "> CUR_BUMP_CODE" above */ + + /* Check whether to clear table. */ + if (next_code > 4094) { + int do_clear = grr->gcinfo.flags & GIF_WRITE_EAGER_CLEAR; + + if (!do_clear) { + unsigned pixels_left = image_endpos - pos - 1; + if (pixels_left) { + /* Always clear if run_ewma gets small relative to + min_code_bits. Otherwise, clear if #images/run is smaller + than an empirical threshold, meaning it will take more than + 3000 or so average runs to complete the image. */ + if (run_ewma < ((36U << RUN_EWMA_SCALE) / min_code_bits) + || pixels_left > UINT_MAX / RUN_INV_THRESH + || run_ewma < pixels_left * RUN_INV_THRESH) + do_clear = 1; + } + } - /* If height is 0 -- no more pixels to write -- we output work_node next - time around. */ - while (imageline) { - suffix = *imageline; - next_node = gfc_lookup(gfc, work_node, suffix); - - imageline++; - pos++; - if (pos == line_endpos) { - imageline = gif_imageline(gfi, pos); - line_endpos += gfi->width; - } + if ((do_clear || run < 7) && !clear_pos) { + clear_pos = pos - run; + clear_bufpos = bufpos; + } else if (!do_clear && run > 50) + clear_pos = clear_bufpos = 0; + + if (do_clear) { + GIF_DEBUG(("rewind %u pixels/%d bits", pos + 1 - clear_pos, bufpos + cur_code_bits - clear_bufpos)); + output_code = CLEAR_CODE; + pos = clear_pos; + + bufpos = clear_bufpos; + buf[bufpos >> 3] &= (1 << (bufpos & 7)) - 1; + grr->cleared = 1; + continue; + } + } - if (next_node) { - work_node = next_node; - ++run; - continue; + /* Adjust current run length average. */ + run = (run << RUN_EWMA_SCALE) + (1 << (RUN_EWMA_SHIFT - 1)); + if (run < run_ewma) + run_ewma -= (run_ewma - run) >> RUN_EWMA_SHIFT; + else + run_ewma += (run - run_ewma) >> RUN_EWMA_SHIFT; } - /* Output the current code. */ - if (next_code < GIF_MAX_CODE) { - gfc_define(gfc, work_node, suffix, next_code); - next_code++; - } else - next_code = GIF_MAX_CODE + 1; /* to match "> CUR_BUMP_CODE" above */ - - /* Check whether to clear table. */ - if (next_code > 4094) { - int do_clear = grr->gcinfo.flags & GIF_WRITE_EAGER_CLEAR; - - if (!do_clear) { - unsigned pixels_left = gfi->width * gfi->height - pos; - if (pixels_left) { - /* Always clear if run_ewma gets small relative to - min_code_bits. Otherwise, clear if #images/run is smaller - than an empirical threshold, meaning it will take more than - 3000 or so average runs to complete the image. */ - if (run_ewma < ((36U << RUN_EWMA_SCALE) / min_code_bits) - || pixels_left > UINT_MAX / RUN_INV_THRESH - || run_ewma < pixels_left * RUN_INV_THRESH) - do_clear = 1; - } + output_code = (work_node ? work_node->code : EOI_CODE); + } else { + /* If height is 0 -- no more pixels to write -- we output work_node next + time around. */ + while (imageline) { + suffix = *imageline; + next_node = gfc_lookup(gfc, work_node, suffix); + + imageline++; + pos++; + if (pos == line_endpos) { + imageline = gif_imageline(gfi, pos); + line_endpos += gfi->width; } - if ((do_clear || run < 7) && !clear_pos) { - clear_pos = pos - (run + 1); - clear_bufpos = bufpos; - } else if (!do_clear && run > 50) - clear_pos = clear_bufpos = 0; - - if (do_clear) { - GIF_DEBUG(("rewind %u pixels/%d bits ", pos - clear_pos, bufpos + cur_code_bits - clear_bufpos)); - output_code = CLEAR_CODE; - pos = clear_pos; - imageline = gif_imageline(gfi, pos); - line_endpos = gif_line_endpos(gfi, pos); - bufpos = clear_bufpos; - buf[bufpos >> 3] &= (1 << (bufpos & 7)) - 1; - work_node = 0; - grr->cleared = 1; - goto found_output_code; + if (next_node) { + work_node = next_node; + ++run; + continue; } - } - output_code = work_node->code; - work_node = &gfc->nodes[suffix]; - goto found_output_code; - } + /* Output the current code. */ + if (next_code < GIF_MAX_CODE) { + gfc_define(gfc, work_node, suffix, next_code); + next_code++; + } else + next_code = GIF_MAX_CODE + 1; /* to match "> CUR_BUMP_CODE" above */ + + /* Check whether to clear table. */ + if (next_code > 4094) { + int do_clear = grr->gcinfo.flags & GIF_WRITE_EAGER_CLEAR; + + if (!do_clear) { + unsigned pixels_left = gfi->width * gfi->height - pos; + if (pixels_left) { + /* Always clear if run_ewma gets small relative to + min_code_bits. Otherwise, clear if #images/run is smaller + than an empirical threshold, meaning it will take more than + 3000 or so average runs to complete the image. */ + if (run_ewma < ((36U << RUN_EWMA_SCALE) / min_code_bits) + || pixels_left > UINT_MAX / RUN_INV_THRESH + || run_ewma < pixels_left * RUN_INV_THRESH) + do_clear = 1; + } + } - /* Ran out of data if we get here. */ - output_code = (work_node ? work_node->code : EOI_CODE); - work_node = 0; + if ((do_clear || run < 7) && !clear_pos) { + clear_pos = pos - (run + 1); + clear_bufpos = bufpos; + } else if (!do_clear && run > 50) + clear_pos = clear_bufpos = 0; + + if (do_clear) { + GIF_DEBUG(("rewind %u pixels/%d bits ", pos - clear_pos, bufpos + cur_code_bits - clear_bufpos)); + output_code = CLEAR_CODE; + pos = clear_pos; + imageline = gif_imageline(gfi, pos); + line_endpos = gif_line_endpos(gfi, pos); + bufpos = clear_bufpos; + buf[bufpos >> 3] &= (1 << (bufpos & 7)) - 1; + work_node = NULL; + grr->cleared = 1; + goto found_output_code; + } + } - found_output_code: ; + output_code = work_node->code; + work_node = &gfc->nodes[suffix]; + goto found_output_code; + } + + /* Ran out of data if we get here. */ + output_code = (work_node ? work_node->code : EOI_CODE); + work_node = NULL; + + found_output_code: ; + } } /* Output memory buffer to stream. */ @@ -504,8 +703,8 @@ for (y = 0; y < height && colors_used < 128; y++) { uint8_t *data = gfi->img[y]; for (x = width; x > 0; x--, data++) - if (*data > colors_used) - colors_used = *data; + if (*data > colors_used) + colors_used = *data; } colors_used++; @@ -518,7 +717,7 @@ colors_used = 256; } - min_code_bits = 2; /* min_code_bits of 1 isn't allowed */ + min_code_bits = 2; /* min_code_bits of 1 isn't allowed */ i = 4; while (i < colors_used) { min_code_bits++; @@ -530,7 +729,7 @@ static int get_color_table_size(const Gif_Stream *gfs, Gif_Image *gfi, - Gif_Writer *grr); + Gif_Writer *grr); static void save_compression_result(Gif_Image *gfi, Gif_Writer *grr, int ok) @@ -554,7 +753,7 @@ int Gif_FullCompressImage(Gif_Stream *gfs, Gif_Image *gfi, - const Gif_CompressInfo *gcinfo) + const Gif_CompressInfo *gcinfo) { int ok = 0; uint8_t min_code_bits; @@ -570,14 +769,14 @@ grr.local_size = get_color_table_size(gfs, gfi, &grr); min_code_bits = calculate_min_code_bits(gfi, &grr); - ok = write_compressed_data(gfi, min_code_bits, &grr); + ok = write_compressed_data(gfs, gfi, min_code_bits, &grr); save_compression_result(gfi, &grr, ok); if ((grr.gcinfo.flags & (GIF_WRITE_OPTIMIZE | GIF_WRITE_EAGER_CLEAR)) == GIF_WRITE_OPTIMIZE && grr.cleared && ok) { grr.gcinfo.flags |= GIF_WRITE_EAGER_CLEAR | GIF_WRITE_SHRINK; - if (write_compressed_data(gfi, min_code_bits, &grr)) + if (write_compressed_data(gfs, gfi, min_code_bits, &grr)) save_compression_result(gfi, &grr, 1); } @@ -606,8 +805,8 @@ ncol = gfi->transparent + 1; else if (!gfi) for (i = 0; i < gfs->nimages; i++) - if (gfs->images[i]->transparent >= ncol) - ncol = gfs->images[i]->transparent + 1; + if (gfs->images[i]->transparent >= ncol) + ncol = gfs->images[i]->transparent + 1; } /* Make sure the colormap is a power of two entries! */ @@ -687,11 +886,11 @@ } else if (!gfi->img) { Gif_UncompressImage(gfs, gfi); - write_compressed_data(gfi, min_code_bits, grr); + write_compressed_data(gfs, gfi, min_code_bits, grr); Gif_ReleaseUncompressedImage(gfi); } else - write_compressed_data(gfi, min_code_bits, grr); + write_compressed_data(gfs, gfi, min_code_bits, grr); return 1; } @@ -700,7 +899,7 @@ static void write_logical_screen_descriptor(Gif_Stream *gfs, Gif_Writer *grr) { - uint8_t packed = 0x70; /* high resolution colors */ + uint8_t packed = 0x70; /* high resolution colors */ grr->global_size = get_color_table_size(gfs, 0, grr); Gif_CalculateScreenSize(gfs, 0); @@ -719,7 +918,7 @@ gifputbyte(gfs->background, grr); else gifputbyte(255, grr); - gifputbyte(0, grr); /* no aspect ratio information */ + gifputbyte(0, grr); /* no aspect ratio information */ if (grr->global_size > 0) write_color_table(gfs->global, grr->global_size, grr); @@ -798,11 +997,11 @@ write_generic_extension(Gif_Extension *gfex, Gif_Writer *grr) { uint32_t pos = 0; - if (gfex->kind < 0) return; /* ignore our private extensions */ + if (gfex->kind < 0) return; /* ignore our private extensions */ gifputbyte('!', grr); gifputbyte(gfex->kind, grr); - if (gfex->kind == 255) { /* an application extension */ + if (gfex->kind == 255) { /* an application extension */ if (gfex->applength) { gifputbyte(gfex->applength, grr); gifputblock((const uint8_t*) gfex->appname, gfex->applength, grr); @@ -872,7 +1071,7 @@ int Gif_FullWriteFile(Gif_Stream *gfs, const Gif_CompressInfo *gcinfo, - FILE *f) + FILE *f) { Gif_Writer grr; int ok = gif_writer_init(&grr, f, gcinfo) diff -Nru gifsicle-1.91/src/gifx.c gifsicle-1.92/src/gifx.c --- gifsicle-1.91/src/gifx.c 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/src/gifx.c 2019-04-18 20:09:42.000000000 +0000 @@ -1,5 +1,5 @@ /* gifx.c - Functions to turn GIFs in memory into X Pixmaps. - Copyright (C) 1997-2018 Eddie Kohler, ekohler@gmail.com + Copyright (C) 1997-2019 Eddie Kohler, ekohler@gmail.com This file is part of the LCDF GIF library. The LCDF GIF library is free software. It is distributed under the GNU diff -Nru gifsicle-1.91/src/Makefile.am gifsicle-1.92/src/Makefile.am --- gifsicle-1.91/src/Makefile.am 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/src/Makefile.am 2019-04-18 20:09:42.000000000 +0000 @@ -30,5 +30,6 @@ AM_CPPFLAGS = $(X_CFLAGS) $(WERROR) -I$(top_srcdir)/include -EXTRA_DIST = gifwrite.c ungifwrt.c opttemplate.c \ - Makefile.bcc Makefile.w32 win32cfg.h +EXTRA_gifsicle_SOURCES = gifwrite.c ungifwrt.c + +EXTRA_DIST = opttemplate.c Makefile.bcc Makefile.w32 win32cfg.h diff -Nru gifsicle-1.91/src/merge.c gifsicle-1.92/src/merge.c --- gifsicle-1.91/src/merge.c 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/src/merge.c 2019-04-18 20:09:42.000000000 +0000 @@ -1,5 +1,5 @@ /* merge.c - Functions which actually combine and manipulate GIF image data. - Copyright (C) 1997-2018 Eddie Kohler, ekohler@gmail.com + Copyright (C) 1997-2019 Eddie Kohler, ekohler@gmail.com This file is part of gifsicle. Gifsicle is free software. It is distributed under the GNU Public License, diff -Nru gifsicle-1.91/src/optimize.c gifsicle-1.92/src/optimize.c --- gifsicle-1.91/src/optimize.c 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/src/optimize.c 2019-04-18 20:09:42.000000000 +0000 @@ -1,5 +1,5 @@ /* optimize.c - Functions to optimize animated GIFs. - Copyright (C) 1997-2018 Eddie Kohler, ekohler@gmail.com + Copyright (C) 1997-2019 Eddie Kohler, ekohler@gmail.com This file is part of gifsicle. Gifsicle is free software. It is distributed under the GNU Public License, diff -Nru gifsicle-1.91/src/opttemplate.c gifsicle-1.92/src/opttemplate.c --- gifsicle-1.91/src/opttemplate.c 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/src/opttemplate.c 2019-04-18 20:09:42.000000000 +0000 @@ -1,5 +1,5 @@ /* opttemplate.c - Functions to optimize animated GIFs. - Copyright (C) 1997-2018 Eddie Kohler, ekohler@gmail.com + Copyright (C) 1997-2019 Eddie Kohler, ekohler@gmail.com This file is part of gifsicle. Gifsicle is free software. It is distributed under the GNU Public License, diff -Nru gifsicle-1.91/src/quantize.c gifsicle-1.92/src/quantize.c --- gifsicle-1.91/src/quantize.c 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/src/quantize.c 2019-04-18 20:09:42.000000000 +0000 @@ -1,5 +1,5 @@ /* quantize.c - Histograms and quantization for gifsicle. - Copyright (C) 1997-2018 Eddie Kohler, ekohler@gmail.com + Copyright (C) 1997-2019 Eddie Kohler, ekohler@gmail.com This file is part of gifsicle. Gifsicle is free software. It is distributed under the GNU Public License, @@ -1620,11 +1620,6 @@ Gif_ReleaseCompressedImage(gfi); Gif_SetUncompressedImage(gfi, new_data, Gif_Free, 0); - if (only_compressed) { - Gif_FullCompressImage(gfs, gfi, &gif_write_info); - Gif_ReleaseUncompressedImage(gfi); - } - /* update count of used colors */ for (j = 0; j < 256; j++) new_col[j].pixel += histogram[j]; @@ -1643,6 +1638,12 @@ Gif_DeleteColormap(gfi->local); gfi->local = 0; } + + /* 1.92: recompress *after* deleting the local colormap */ + if (gfcm && only_compressed) { + Gif_FullCompressImage(gfs, gfi, &gif_write_info); + Gif_ReleaseUncompressedImage(gfi); + } } /* Set new_cm->ncol from new_ncol. We didn't update new_cm->ncol before so diff -Nru gifsicle-1.91/src/support.c gifsicle-1.92/src/support.c --- gifsicle-1.91/src/support.c 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/src/support.c 2019-04-18 20:09:42.000000000 +0000 @@ -1,5 +1,5 @@ /* support.c - Support functions for gifsicle. - Copyright (C) 1997-2018 Eddie Kohler, ekohler@gmail.com + Copyright (C) 1997-2019 Eddie Kohler, ekohler@gmail.com This file is part of gifsicle. Gifsicle is free software. It is distributed under the GNU Public License, @@ -220,6 +220,8 @@ --gamma G Set gamma for color reduction [2.2].\n"); #endif printf("\ + --lossy[=LOSSINESS] Alter image colors to shrink output file size\n\ + at the cost of artifacts and noise.\n\ --resize WxH Resize the output GIF to WxH.\n\ --resize-width W Resize to width W and proportional height.\n\ --resize-height H Resize to height H and proportional width.\n\ @@ -1586,8 +1588,6 @@ Gif_UncompressImage(fr->stream, srci); } - /* It was pretty stupid to remove this code, which I did between 1.2b6 and - 1.2 */ old_transp = apply_frame_transparent(srci, fr); /* Is it ok to use the old image's compressed version? */ diff -Nru gifsicle-1.91/src/ungifwrt.c gifsicle-1.92/src/ungifwrt.c --- gifsicle-1.91/src/ungifwrt.c 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/src/ungifwrt.c 2019-04-18 20:09:42.000000000 +0000 @@ -2,7 +2,7 @@ /* ungifwrt.c - Functions to write unGIFs -- GIFs with run-length compression, not LZW compression. Idea due to Hutchinson Avenue Software Corporation . - Copyright (C) 1997-2018 Eddie Kohler, ekohler@gmail.com + Copyright (C) 1997-2019 Eddie Kohler, ekohler@gmail.com This file is part of the LCDF GIF library. The LCDF GIF library is free software. It is distributed under the GNU diff -Nru gifsicle-1.91/src/win32cfg.h gifsicle-1.92/src/win32cfg.h --- gifsicle-1.91/src/win32cfg.h 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/src/win32cfg.h 2019-04-18 20:09:42.000000000 +0000 @@ -4,12 +4,21 @@ #ifndef GIFSICLE_CONFIG_H #define GIFSICLE_CONFIG_H +/* Define to 1 if multithreading support is available. */ +/* #undef ENABLE_THREADS */ + /* Define to the number of arguments to gettimeofday (gifview only). */ /* #undef GETTIMEOFDAY_PROTO */ /* Define if GIF LZW compression is off. */ /* #undef GIF_UNGIF */ +/* Define to 1 if `ext_vector_type' vector types are usable. */ +/* #undef HAVE_EXT_VECTOR_TYPE_VECTOR_TYPES */ + +/* Define to 1 if the system has the type `int64_t'. */ +/* #undef HAVE_INT64_T */ + /* Define to 1 if you have the header file. */ #if defined(_MSC_VER) && _MSC_VER >= 1900 # define HAVE_INTTYPES_H 1 @@ -24,6 +33,9 @@ /* Define to 1 if you have the `pow' function. */ #define HAVE_POW 1 +/* Define to 1 if SIMD types should be used. */ +/* #undef HAVE_SIMD */ + /* Define to 1 if you have the header file. */ #if defined(_MSC_VER) && _MSC_VER >= 1900 # define HAVE_STDINT_H 1 @@ -50,9 +62,18 @@ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_STAT_H */ +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_TIME_H */ + /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_TYPES_H */ +/* Define to 1 if you have the header file. */ +/* #undef HAVE_TIME_H */ + +/* Define to 1 if the system has the type `uint64_t'. */ +/* #undef HAVE_UINT64_T */ + /* Define to 1 if the system has the type `uintptr_t'. */ #if defined(_MSC_VER) && _MSC_VER >= 1900 # define HAVE_UINTPTR_T 1 @@ -64,6 +85,15 @@ /* Define if you have u_intXX_t types but not uintXX_t types. */ /* #undef HAVE_U_INT_TYPES */ +/* Define to 1 if `vector_size' vector types are usable. */ +/* #undef HAVE_VECTOR_SIZE_VECTOR_TYPES */ + +/* Define to 1 if you have the `__builtin_shufflevector' function. */ +/* #undef HAVE___BUILTIN_SHUFFLEVECTOR */ + +/* Define to 1 if you have the `__sync_add_and_fetch' function. */ +/* #undef HAVE___SYNC_ADD_AND_FETCH */ + /* Define to write GIFs to stdout even when stdout is a terminal. */ /* #undef OUTPUT_GIF_TO_TERMINAL */ @@ -77,7 +107,7 @@ #define PACKAGE_NAME "gifsicle" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "gifsicle 1.91" +#define PACKAGE_STRING "gifsicle 1.92" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "gifsicle" @@ -86,7 +116,7 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "1.91" +#define PACKAGE_VERSION "1.92" /* Pathname separator character ('/' on Unix). */ #define PATHNAME_SEPARATOR '\\' @@ -94,6 +124,9 @@ /* Define to a function that returns a random number. */ #define RANDOM rand +/* The size of `float', as computed by sizeof. */ +#define SIZEOF_FLOAT 4 + /* The size of `unsigned int', as computed by sizeof. */ #define SIZEOF_UNSIGNED_INT 4 @@ -111,7 +144,7 @@ #define STDC_HEADERS 1 /* Version number of package */ -#define VERSION "1.91 (Windows)" +#define VERSION "1.92 (Windows)" /* Define if X is not available. */ #define X_DISPLAY_MISSING 1 diff -Nru gifsicle-1.91/src/xform.c gifsicle-1.92/src/xform.c --- gifsicle-1.91/src/xform.c 2018-01-05 19:09:14.000000000 +0000 +++ gifsicle-1.92/src/xform.c 2019-04-18 20:09:42.000000000 +0000 @@ -1,5 +1,5 @@ /* xform.c - Image transformation functions for gifsicle. - Copyright (C) 1997-2018 Eddie Kohler, ekohler@gmail.com + Copyright (C) 1997-2019 Eddie Kohler, ekohler@gmail.com This file is part of gifsicle. Gifsicle is free software. It is distributed under the GNU Public License,