├── .appveyor.yml ├── .github └── workflows │ └── tests.yml ├── .gitignore ├── .travis.yml ├── COPYING ├── INSTALL ├── Makefile.am ├── NEWS.md ├── README.md ├── bootstrap.sh ├── configure.ac ├── gifdiff.1 ├── gifsicle.1 ├── gifview.1 ├── include ├── lcdf │ ├── clp.h │ └── inttypes.h └── lcdfgif │ ├── gif.h │ └── gifx.h ├── logo.gif ├── logo1.gif ├── src ├── Makefile.am ├── Makefile.bcc ├── Makefile.mingw ├── Makefile.w32 ├── clp.c ├── fmalloc.c ├── gifdiff.c ├── giffunc.c ├── gifread.c ├── gifsicle.c ├── gifsicle.h ├── giftoc.c ├── gifunopt.c ├── gifview.c ├── gifwrite.c ├── gifx.c ├── kcolor.c ├── kcolor.h ├── merge.c ├── optimize.c ├── opttemplate.c ├── quantize.c ├── strerror.c ├── support.c ├── ungifwrt.c ├── win32cfg.h └── xform.c └── test ├── 001-transexpand.testie ├── 002-cropresize.testie ├── 003-bgopt.testie ├── 004-careful.testie ├── 005-resize.testie ├── 006-unoptdisposal.testie ├── 007-alltransp.testie ├── 008-resizemix.testie ├── 009-zerowidth.testie ├── 010-warnings.testie ├── 011-resizemix.testie ├── 012-framechange.testie └── testie /.appveyor.yml: -------------------------------------------------------------------------------- 1 | os: Visual Studio 2015 2 | 3 | build_script: 4 | - CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\vcvars32.bat" 5 | - cd "%APPVEYOR_BUILD_FOLDER%\src" 6 | - nmake -f Makefile.w32 gifsicle.exe gifdiff.exe 7 | - gifsicle.exe --version 8 | - gifdiff.exe --version 9 | 10 | test: off 11 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: [ master, github ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v3 16 | 17 | - name: Configure and build 18 | run: | 19 | autoreconf -i 20 | ./configure 21 | 22 | - name: Make 23 | run: make 24 | 25 | - name: Run tests 26 | run: make check 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.d 2 | *.o 3 | .deps 4 | Makefile 5 | Makefile.in 6 | aclocal.m4 7 | autom4te* 8 | config.cache 9 | config.h 10 | config.h.in 11 | config.h.in~ 12 | config.log 13 | config.status 14 | configure 15 | depcomp 16 | gifdiff 17 | gifsicle 18 | gifsicle-*.rpm 19 | gifsicle-*.tar.gz 20 | gifview 21 | include 22 | install-sh 23 | missing 24 | src/gifdiff 25 | src/gifsicle 26 | src/gifview 27 | stamp-h* 28 | ungifsicle-*.rpm 29 | ungifsicle-*.tar.gz 30 | compile 31 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: 3 | - gcc 4 | - clang 5 | env: 6 | global: 7 | # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created 8 | # via the "travis encrypt" command using the project repo's public key 9 | - secure: "NEQUeZwTTJ+1Pdy2CpNte1nJLYRY2H8+dpvnGS2HP6U/Ok6ZQm4Zxic+ufhabuymbCV7Jvwwy6Gaj8+ObIDahhzAsiMRXSymnvFHELAnq7DiikJ2HqBvFhPzPI2/vDmEP37msGW1dloipLhdKMeuYYP++BpApnC6KVRVJDXg5JI=" 10 | matrix: 11 | include: 12 | - compiler: gcc 13 | - compiler: gcc 14 | env: CCARG=-std=c89 15 | - compiler: gcc 16 | env: CCARG=-m32 17 | addons: 18 | apt: 19 | packages: 20 | - gcc-multilib 21 | - libc6:i386 22 | - compiler: clang 23 | script: ./configure || cat config.log && make check 24 | before_script: 25 | - export CC="$CC $CCARG" 26 | - ./bootstrap.sh 27 | sudo: false 28 | 29 | addons: 30 | coverity_scan: 31 | project: 32 | name: "kohler/gifsicle" 33 | description: "Build submitted via Travis CI" 34 | notification_email: ekohler@gmail.com 35 | build_command_prepend: "./configure; make clean" 36 | build_command: "make -j 4" 37 | branch_pattern: coverity_scan 38 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Basic Installation 2 | ================== 3 | 4 | These are generic installation instructions. 5 | 6 | The `configure' shell script attempts to guess correct values for 7 | various system-dependent variables used during compilation. It uses 8 | those values to create a `Makefile' in each directory of the package. 9 | It may also create one or more `.h' files containing system-dependent 10 | definitions. Finally, it creates a shell script `config.status' that 11 | you can run in the future to recreate the current configuration, a file 12 | `config.cache' that saves the results of its tests to speed up 13 | reconfiguring, and a file `config.log' containing compiler output 14 | (useful mainly for debugging `configure'). 15 | 16 | If you need to do unusual things to compile the package, please try 17 | to figure out how `configure' could check whether to do them, and mail 18 | diffs or instructions to the address given in the `README' so they can 19 | be considered for the next release. If at some point `config.cache' 20 | contains results you don't want to keep, you may remove or edit it. 21 | 22 | The file `configure.in' is used to create `configure' by a program 23 | called `autoconf'. You only need `configure.in' if you want to change 24 | it or regenerate `configure' using a newer version of `autoconf'. 25 | 26 | The simplest way to compile this package is: 27 | 28 | 1. `cd' to the directory containing the package's source code and type 29 | `./configure' to configure the package for your system. If you're 30 | using `csh' on an old version of System V, you might need to type 31 | `sh ./configure' instead to prevent `csh' from trying to execute 32 | `configure' itself. 33 | 34 | Running `configure' takes awhile. While running, it prints some 35 | messages telling which features it is checking for. 36 | 37 | 2. Type `make' to compile the package. 38 | 39 | 3. Optionally, type `make check' to run any self-tests that come with 40 | the package. 41 | 42 | 4. Type `make install' to install the programs and any data files and 43 | documentation. 44 | 45 | 5. You can remove the program binaries and object files from the 46 | source code directory by typing `make clean'. To also remove the 47 | files that `configure' created (so you can compile the package for 48 | a different kind of computer), type `make distclean'. There is 49 | also a `make maintainer-clean' target, but that is intended mainly 50 | for the package's developers. If you use it, you may have to get 51 | all sorts of other programs in order to regenerate files that came 52 | with the distribution. 53 | 54 | Compilers and Options 55 | ===================== 56 | 57 | Some systems require unusual options for compilation or linking that 58 | the `configure' script does not know about. You can give `configure' 59 | initial values for variables by setting them in the environment. Using 60 | a Bourne-compatible shell, you can do that on the command line like 61 | this: 62 | CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure 63 | 64 | Or on systems that have the `env' program, you can do it like this: 65 | env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure 66 | 67 | Compiling For Multiple Architectures 68 | ==================================== 69 | 70 | You can compile the package for more than one kind of computer at the 71 | same time, by placing the object files for each architecture in their 72 | own directory. To do this, you must use a version of `make' that 73 | supports the `VPATH' variable, such as GNU `make'. `cd' to the 74 | directory where you want the object files and executables to go and run 75 | the `configure' script. `configure' automatically checks for the 76 | source code in the directory that `configure' is in and in `..'. 77 | 78 | If you have to use a `make' that does not supports the `VPATH' 79 | variable, you have to compile the package for one architecture at a time 80 | in the source code directory. After you have installed the package for 81 | one architecture, use `make distclean' before reconfiguring for another 82 | architecture. 83 | 84 | Installation Names 85 | ================== 86 | 87 | By default, `make install' will install the package's files in 88 | `/usr/local/bin', `/usr/local/man', etc. You can specify an 89 | installation prefix other than `/usr/local' by giving `configure' the 90 | option `--prefix=PATH'. 91 | 92 | You can specify separate installation prefixes for 93 | architecture-specific files and architecture-independent files. If you 94 | give `configure' the option `--exec-prefix=PATH', the package will use 95 | PATH as the prefix for installing programs and libraries. 96 | Documentation and other data files will still use the regular prefix. 97 | 98 | If the package supports it, you can cause programs to be installed 99 | with an extra prefix or suffix on their names by giving `configure' the 100 | option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. 101 | 102 | Optional Features 103 | ================= 104 | 105 | Some packages pay attention to `--enable-FEATURE' options to 106 | `configure', where FEATURE indicates an optional part of the package. 107 | They may also pay attention to `--with-PACKAGE' options, where PACKAGE 108 | is something like `gnu-as' or `x' (for the X Window System). The 109 | `README' should mention any `--enable-' and `--with-' options that the 110 | package recognizes. 111 | 112 | For packages that use the X Window System, `configure' can usually 113 | find the X include and library files automatically, but if it doesn't, 114 | you can use the `configure' options `--x-includes=DIR' and 115 | `--x-libraries=DIR' to specify their locations. 116 | 117 | Specifying the System Type 118 | ========================== 119 | 120 | There may be some features `configure' can not figure out 121 | automatically, but needs to determine by the type of host the package 122 | will run on. Usually `configure' can figure that out, but if it prints 123 | a message saying it can not guess the host type, give it the 124 | `--host=TYPE' option. TYPE can either be a short name for the system 125 | type, such as `sun4', or a canonical name with three fields: 126 | CPU-COMPANY-SYSTEM 127 | 128 | See the file `config.sub' for the possible values of each field. If 129 | `config.sub' isn't included in this package, then this package doesn't 130 | need to know the host type. 131 | 132 | If you are building compiler tools for cross-compiling, you can also 133 | use the `--target=TYPE' option to select the type of system they will 134 | produce code for and the `--build=TYPE' option to select the type of 135 | system on which you are compiling the package. 136 | 137 | Sharing Defaults 138 | ================ 139 | 140 | If you want to set default values for `configure' scripts to share, 141 | you can create a site shell script called `config.site' that gives 142 | default values for variables like `CC', `cache_file', and `prefix'. 143 | `configure' looks for `PREFIX/share/config.site' if it exists, then 144 | `PREFIX/etc/config.site' if it exists. Or, you can set the 145 | `CONFIG_SITE' environment variable to the location of the site script. 146 | A warning: not all `configure' scripts look for a site script. 147 | 148 | Operation Controls 149 | ================== 150 | 151 | `configure' recognizes the following options to control how it 152 | operates. 153 | 154 | `--cache-file=FILE' 155 | Use and save the results of the tests in FILE instead of 156 | `./config.cache'. Set FILE to `/dev/null' to disable caching, for 157 | debugging `configure'. 158 | 159 | `--help' 160 | Print a summary of the options to `configure', and exit. 161 | 162 | `--quiet' 163 | `--silent' 164 | `-q' 165 | Do not print messages saying which checks are being made. 166 | 167 | `--srcdir=DIR' 168 | Look for the package's source code in directory DIR. Usually 169 | `configure' can determine that directory automatically. 170 | 171 | `--version' 172 | Print the version of Autoconf used to generate the `configure' 173 | script, and exit. 174 | 175 | `configure' also accepts some other, not widely useful, options. 176 | 177 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ## Process this file with automake to produce Makefile.in 2 | AUTOMAKE_OPTIONS = foreign 3 | 4 | SUBDIRS = src 5 | 6 | man_MANS = gifsicle.1 @OTHERMANS@ 7 | 8 | EXTRA_DIST = COPYING README.md NEWS.md \ 9 | include/lcdf/clp.h include/lcdf/inttypes.h \ 10 | include/lcdfgif/gif.h include/lcdfgif/gifx.h \ 11 | gifsicle.1 gifview.1 gifdiff.1 logo.gif logo1.gif \ 12 | test/testie \ 13 | test/001-transexpand.testie \ 14 | test/002-cropresize.testie \ 15 | test/003-bgopt.testie \ 16 | test/004-careful.testie \ 17 | test/005-resize.testie \ 18 | test/006-unoptdisposal.testie \ 19 | test/007-alltransp.testie \ 20 | test/008-resizemix.testie \ 21 | test/009-zerowidth.testie \ 22 | test/010-warnings.testie \ 23 | test/011-resizemix.testie 24 | 25 | gifsicle: config.h 26 | @cd src && $(MAKE) gifsicle 27 | gifdiff: config.h 28 | @cd src && $(MAKE) gifdiff 29 | gifview: config.h 30 | @cd src && $(MAKE) gifview 31 | 32 | srclinks: 33 | cd $(top_srcdir); sh ./sourcecheckout.sh 34 | 35 | versionize: 36 | perl -pi -e "s/^\\.ds V.*/.ds V $(VERSION)/;" $(top_srcdir)/gifsicle.1 $(top_srcdir)/gifview.1 $(top_srcdir)/gifdiff.1 37 | perl -pi -e "s/gifsicle [\d.]+/gifsicle $(VERSION)/; s/VERSION \"[\w.]+/VERSION \"$(VERSION)/;" $(top_srcdir)/src/win32cfg.h 38 | 39 | dist-ungif: dist 40 | $(AMTAR) xzf gifsicle-$(VERSION).tar.gz 41 | @rm gifsicle-$(VERSION)/src/gifwrite.c 42 | rm -rf ungifsicle-$(VERSION) 43 | mv gifsicle-$(VERSION) ungifsicle-$(VERSION) 44 | GZIP=$(GZIP_ENV) $(AMTAR) chozf ungifsicle-$(VERSION).tar.gz ungifsicle-$(VERSION) 45 | rm -rf ungifsicle-$(VERSION) 46 | 47 | check: all 48 | $(top_srcdir)/test/testie -p $(top_builddir)/src $(top_srcdir)/test 49 | 50 | .PHONY: srclinks versionize rpm dist-ungif rpm-ungif check 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![gifsicle-logo](https://raw.githubusercontent.com/kohler/gifsicle/master/logo.gif) 2 | 3 | Gifsicle [![Build Status](https://github.com/kohler/gifsicle/actions/workflows/tests.yml/badge.svg)](https://github.com/kohler/gifsicle/actions/workflows/tests.yml) 4 | ======== 5 | 6 | Gifsicle manipulates GIF image files. Depending on command line 7 | options, it can merge several GIFs into a GIF animation; explode an 8 | animation into its component frames; change individual frames in an 9 | animation; turn interlacing on and off; add transparency; add delays, 10 | disposals, and looping to animations; add and remove comments; flip 11 | and rotate; optimize animations for space; change images' colormaps; 12 | and other things. 13 | 14 | Gifview, a companion program, displays GIF images and animations on an 15 | X display. It can display multi-frame GIFs either as slideshows, 16 | displaying one frame at a time, or as real-time animations. 17 | 18 | Gifdiff, another companion program, checks two GIF files for identical 19 | visual appearance. This is probably most useful for testing 20 | GIF-manipulating software. 21 | 22 | Each of these programs has a manpage, `PROGRAMNAME.1`. 23 | 24 | The Gifsicle package comes with NO WARRANTY, express or implied, 25 | including, but not limited to, the implied warranties of 26 | merchantability and fitness for a particular purpose. 27 | 28 | See `NEWS.md` in this directory for changes in recent versions. The Gifsicle 29 | home page is: 30 | 31 | http://www.lcdf.org/gifsicle/ 32 | 33 | 34 | Building Gifsicle on UNIX 35 | ------------------------- 36 | 37 | Type `./configure`, then `make`. 38 | 39 | If `./configure` does not exist (you downloaded from Github), run 40 | `autoreconf -i` first. 41 | 42 | `./configure` accepts the usual options; see `INSTALL` for details. 43 | To build without gifview (for example, if you don't have X11), use 44 | `./configure --disable-gifview`. To build without gifdiff, 45 | use `./configure --disable-gifdiff`. 46 | 47 | `make install` will build and install Gifsicle and its manual page 48 | (under /usr/local by default). 49 | 50 | 51 | Building Gifsicle on Windows 52 | ---------------------------- 53 | 54 | To build Gifsicle on Windows using Visual C, change into the `src` 55 | directory and run 56 | 57 | nmake -f Makefile.w32 58 | 59 | Gifview will not be built. The makefile is from Emil Mikulic 60 | with updates by Steven Marthouse 61 | . 62 | 63 | To build Gifsicle on Windows using Borland C++, change into the `src` 64 | directory and run 65 | 66 | nmake -f Makefile.bcc 67 | 68 | Stephen Schnipsel provided `Makefile.bcc`. You 69 | will need to edit one of these Makefiles to use a different compiler. 70 | You can edit it with any text editor (like Notepad). See the file for 71 | more information. 72 | 73 | 74 | Contact 75 | ------- 76 | 77 | If you have trouble building or running Gifsicle, try GitHub issues: 78 | https://github.com/kohler/gifsicle 79 | 80 | Eddie Kohler, ekohler@gmail.com 81 | http://www.read.seas.harvard.edu/~kohler/ 82 | Please note that I do not provide support for Windows. 83 | 84 | 85 | Copyright/License 86 | ----------------- 87 | 88 | All source code is Copyright (C) 1997-2025 Eddie Kohler. 89 | 90 | IF YOU PLAN TO USE GIFSICLE ONLY TO CREATE OR MODIFY GIF IMAGES, DON'T 91 | WORRY ABOUT THE REST OF THIS SECTION. Anyone can use Gifsicle however 92 | they wish; the license applies only to those who plan to copy, 93 | distribute, or alter its code. If you use Gifsicle for an 94 | organizational or commercial Web site, I would appreciate a link to 95 | the Gifsicle home page on any 'About This Server' page, but it's not 96 | required. 97 | 98 | This code is distributed under the GNU General Public License, Version 99 | 2 (and only Version 2). The GNU General Public License is available 100 | via the Web at or in the 101 | 'COPYING' file in this directory. 102 | 103 | The following alternative license may be used at your discretion. 104 | 105 | Permission is granted to copy, distribute, or alter Gifsicle, whole or 106 | in part, as long as source code copyright notices are kept intact, 107 | with the following restriction: Developers or distributors who plan to 108 | use Gifsicle code, whole or in part, in a product whose source code 109 | will not be made available to the end user -- more precisely, in a 110 | context which would violate the GPL -- MUST contact the author and 111 | obtain permission before doing so. 112 | 113 | 114 | Authors 115 | ------- 116 | 117 | This program is dedicated to the memory of Anne Dudfield, who named 118 | it. Anne was an incredible friend and ridiculous person. ANNIED!!!!!! 119 | 120 | Eddie Kohler \ 121 | http://www.read.seas.harvard.edu/~kohler/ \ 122 | He wrote it. 123 | 124 | David Hedbor \ 125 | Many bug reports and constructive whining about the optimizer. 126 | 127 | Emil Mikulic \ 128 | Win32 port help. 129 | 130 | Hans Dinsen-Hansen \ 131 | http://www.danbbs.dk/~dino/ \ 132 | Adaptive tree method for GIF writing. 133 | 134 | Kornel Lesiński \ 135 | `--lossy` option. 136 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | autoreconf -i 3 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl Process this file with autoconf to produce a configure script. 2 | AC_INIT([gifsicle],[1.96]) 3 | AC_PREREQ([2.69]) 4 | AC_CONFIG_SRCDIR([src/gifsicle.h]) 5 | AC_CONFIG_HEADERS([config.h]) 6 | AC_CONFIG_FILES([Makefile src/Makefile]) 7 | AM_INIT_AUTOMAKE 8 | ac_user_cc=${CC+y} 9 | 10 | AC_PROG_MAKE_SET 11 | AC_PROG_CC 12 | AC_PROG_CPP 13 | AC_C_CONST 14 | AC_C_INLINE 15 | 16 | AC_CHECK_HEADERS([sys/select.h sys/stat.h inttypes.h unistd.h]) 17 | 18 | build_gifview=maybe 19 | AC_ARG_ENABLE([gifview], 20 | [AS_HELP_STRING([--disable-gifview], [do not build gifview X11 viewer])], 21 | [if test "x$enableval" != xyes; then build_gifview=no; fi]) 22 | 23 | build_gifdiff=yes 24 | AC_ARG_ENABLE([gifdiff], 25 | [AS_HELP_STRING([--disable-gifdiff], [do not build gifdiff comparison utility])], 26 | [if test "x$enableval" != xyes; then build_gifdiff=no; fi]) 27 | 28 | AC_ARG_ENABLE([warnings], 29 | [AS_HELP_STRING([--disable-warnings], [compile without -W -Wall])], 30 | [:], [enable_warnings=yes]) 31 | if test x$enable_warnings = xyes; then 32 | CC="$CC -W -Wall" 33 | fi 34 | 35 | WERROR= 36 | AC_ARG_ENABLE([werror], 37 | [AS_HELP_STRING([--enable-werror], [compile with -Werror])], 38 | [if test "x$enableval" = xyes; then WERROR="-Werror"; fi]) 39 | AC_SUBST([WERROR]) 40 | 41 | use_dmalloc= 42 | AC_ARG_ENABLE([dmalloc], 43 | [AS_HELP_STRING([--enable-dmalloc], [build with debugging malloc library])], 44 | [if test "x$enableval" = xyes; then use_dmalloc=yes; fi]) 45 | 46 | ungif= 47 | AC_ARG_ENABLE([ungif], 48 | [AS_HELP_STRING([--enable-ungif], [build without LZW compression])], 49 | [if test "x$enableval" = xyes; then ungif=yes; fi]) 50 | 51 | 52 | dnl 53 | dnl Choose programs to build. Always build gifsicle 54 | dnl 55 | 56 | OTHERPROGRAMS="" 57 | OTHERMANS="" 58 | 59 | if test "x$build_gifview" = xyes -o "x$build_gifview" = xmaybe; then 60 | AC_PATH_XTRA 61 | if test "x$no_x" = xyes; then 62 | if test "x$build_gifview" = xyes; then 63 | AC_MSG_ERROR([ 64 | ****************************************************************************** 65 | Cannot find X, but you explicitly requested that gifview be built. 66 | You may need to install an X11 development package to get the X headers, 67 | supply a '--with-x' option, or simply '--disable-gifview'.]) 68 | else 69 | AC_MSG_WARN([Not compiling gifview since X is not available.]) 70 | build_gifview=no 71 | fi 72 | else 73 | build_gifview=yes 74 | fi 75 | fi 76 | 77 | if test "x$build_gifview" = xyes ; then 78 | OTHERPROGRAMS="$OTHERPROGRAMS gifview"'$(EXEEXT)' 79 | OTHERMANS="$OTHERMANS gifview.1" 80 | AC_CHECK_HEADERS([time.h sys/time.h]) 81 | AC_CACHE_CHECK([for gettimeofday prototype], [ac_cv_gettimeofday], 82 | [AC_COMPILE_IFELSE([AC_LANG_PROGRAM( 83 | [[#include 84 | #include ]], 85 | [[gettimeofday((void*) 0, (void*) 0)]])], 86 | [ac_cv_gettimeofday=2], 87 | [AC_COMPILE_IFELSE([AC_LANG_PROGRAM( 88 | [[#include 89 | #include ]], 90 | [[gettimeofday((void*) 0)]])], 91 | [ac_cv_gettimeofday=1], [ac_cv_gettimeofday=0])])]) 92 | AC_DEFINE_UNQUOTED([GETTIMEOFDAY_PROTO], [$ac_cv_gettimeofday], [Define to the number of arguments to gettimeofday.]) 93 | else 94 | AC_DEFINE_UNQUOTED(X_DISPLAY_MISSING, 1, [Define if X is not available.]) 95 | fi 96 | 97 | if test "x$build_gifdiff" = xyes ; then 98 | OTHERPROGRAMS="$OTHERPROGRAMS gifdiff"'$(EXEEXT)' 99 | OTHERMANS="$OTHERMANS gifdiff.1" 100 | fi 101 | 102 | AC_SUBST(OTHERPROGRAMS) 103 | AC_SUBST(OTHERMANS) 104 | 105 | 106 | dnl 107 | dnl Set up ungif support 108 | dnl 109 | 110 | if test "x$ungif" = xyes -o "x$GIFSICLE_UNGIF" = xyes ; then 111 | GIFWRITE_O=ungifwrt.o 112 | AC_DEFINE(GIF_UNGIF, 1, [Define if GIF LZW compression is off.]) 113 | elif test -r $srcdir/src/gifwrite.c ; then 114 | GIFWRITE_O=gifwrite.o 115 | else 116 | AC_MSG_ERROR([This version of gifsicle cannot create compressed GIFs. 117 | *** Either reconfigure with the '--enable-ungif' option or download the 118 | *** version with compression from http://www.lcdf.org/gifsicle/]) 119 | fi 120 | AC_SUBST(GIFWRITE_O) 121 | 122 | dnl 123 | dnl random or rand, strerror, strtoul, mkstemp, snprintf 124 | dnl 125 | 126 | AC_CHECK_FUNC([random], [random_func=random], [random_func=rand]) 127 | AC_DEFINE_UNQUOTED(RANDOM, ${random_func}, [Define to a function that returns a random number.]) 128 | 129 | AC_REPLACE_FUNCS([strerror]) 130 | AC_CHECK_FUNCS([strtoul mkstemp snprintf]) 131 | AC_SEARCH_LIBS([pow], [m], [AC_DEFINE([HAVE_POW], [1], [Define to 1 if you have the `pow' function.])]) 132 | AC_SEARCH_LIBS([cbrtf], [m], [AC_DEFINE([HAVE_CBRTF], [1], [Define to 1 if you have the `cbrtf' function.])]) 133 | 134 | 135 | dnl 136 | dnl integer types 137 | dnl 138 | 139 | AC_CHECK_HEADERS(inttypes.h, have_inttypes_h=yes, have_inttypes_h=no) 140 | AC_CHECK_HEADERS(sys/types.h, have_sys_types_h=yes, have_sys_types_h=no) 141 | 142 | if test $have_inttypes_h = no -a $have_sys_types_h = yes; then 143 | AC_CACHE_CHECK([for uintXX_t typedefs], ac_cv_uint_t, 144 | [AC_EGREP_HEADER(dnl 145 | changequote(<<,>>)<<(^|[^a-zA-Z_0-9])uint32_t[^a-zA-Z_0-9]>>changequote([,]), 146 | sys/types.h, ac_cv_uint_t=yes, ac_cv_uint_t=no)]) 147 | fi 148 | if test $have_inttypes_h = no -a $have_sys_types_h = yes -a "$ac_cv_uint_t" = no; then 149 | AC_CACHE_CHECK([for u_intXX_t typedefs], ac_cv_u_int_t, 150 | [AC_EGREP_HEADER(dnl 151 | changequote(<<,>>)<<(^|[^a-zA-Z_0-9])u_int32_t[^a-zA-Z_0-9]>>changequote([,]), 152 | sys/types.h, ac_cv_u_int_t=yes, ac_cv_u_int_t=no)]) 153 | fi 154 | if test $have_inttypes_h = yes -o "$ac_cv_uint_t" = yes; then 155 | : 156 | elif test "$ac_cv_u_int_t" = yes; then 157 | AC_DEFINE([HAVE_U_INT_TYPES], [1], [Define if you have u_intXX_t types but not uintXX_t types.]) 158 | fi 159 | 160 | AC_CHECK_TYPES([uintptr_t, int64_t, uint64_t], [], [], 161 | [#if HAVE_INTTYPES_H 162 | # include 163 | #endif 164 | #if HAVE_SYS_TYPES_H 165 | # include 166 | #endif 167 | ]) 168 | 169 | AC_CHECK_SIZEOF([float]) 170 | AC_CHECK_SIZEOF([unsigned int]) 171 | AC_CHECK_SIZEOF([unsigned long]) 172 | AC_CHECK_SIZEOF([void *]) 173 | 174 | 175 | dnl 176 | dnl vector types 177 | dnl 178 | 179 | AC_MSG_CHECKING([for usable vector_size vector types]) 180 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM( 181 | [AC_INCLUDES_DEFAULT 182 | [typedef float float4 __attribute__((vector_size (4 * sizeof(float))));]], 183 | [[float4 a; 184 | a[0] = a[1] = a[2] = a[3] = (float) rand(); 185 | a *= (float) 3; 186 | printf("%f\n", a[0]);]])], 187 | [AC_DEFINE([HAVE_VECTOR_SIZE_VECTOR_TYPES], 1, [Define to 1 if `vector_size' vector types are usable.]) 188 | ac_cv_vector_size_vector_types=yes], [ac_cv_vector_size_vector_types=no]) 189 | AC_MSG_RESULT([$ac_cv_vector_size_vector_types]) 190 | 191 | AC_MSG_CHECKING([for usable ext_vector_type vector types]) 192 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM( 193 | [AC_INCLUDES_DEFAULT 194 | [typedef float float4 __attribute__((ext_vector_type (4)));]], 195 | [[float4 a; 196 | a[0] = a[1] = a[2] = a[3] = (float) rand(); 197 | a *= (float) 3; 198 | printf("%f\n", a[0]);]])], 199 | [AC_DEFINE([HAVE_EXT_VECTOR_TYPE_VECTOR_TYPES], 1, [Define to 1 if `ext_vector_type' vector types are usable.]) 200 | ac_cv_ext_vector_type_vector_types=yes], [ac_cv_ext_vector_type_vector_types=no]) 201 | AC_MSG_RESULT([$ac_cv_ext_vector_type_vector_types]) 202 | 203 | AC_MSG_CHECKING([for __builtin_shufflevector]) 204 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM( 205 | [AC_INCLUDES_DEFAULT 206 | [typedef float float4 __attribute__((ext_vector_type (4)));]], 207 | [[float4 a; 208 | a[0] = a[1] = a[2] = a[3] = (float) rand(); 209 | a = __builtin_shufflevector(a, a, 3, 2, 1, 0); 210 | printf("%f\n", a[0]);]])], 211 | [AC_DEFINE([HAVE___BUILTIN_SHUFFLEVECTOR], 1, [Define to 1 if you have the `__builtin_shufflevector' function.]) 212 | ac_cv___builtin_shufflevector=yes], [ac_cv___builtin_shufflevector=no]) 213 | AC_MSG_RESULT([$ac_cv___builtin_shufflevector]) 214 | 215 | AC_ARG_ENABLE([simd], 216 | [AS_HELP_STRING([--disable-simd], [do not use SIMD instructions])], 217 | [], [enable_simd=maybe]) 218 | 219 | if test $ac_cv_vector_size_vector_types = no -a $ac_cv_ext_vector_type_vector_types = no; then 220 | if test x$enable_simd = xmaybe; then 221 | AC_MSG_WARN([ 222 | Not using SIMD since your compiler's support for SIMD types is inadequate. 223 | ]) 224 | elif test x$enable_simd != xno; then 225 | AC_MSG_ERROR([ 226 | ****************************************************************************** 227 | `--enable-simd' was supplied, but this compiler does not support SIMD types.]) 228 | fi 229 | enable_simd=no 230 | fi 231 | if test x"$enable_simd" != xno; then 232 | AC_DEFINE([HAVE_SIMD], [1], [Define to 1 if SIMD types should be used.]) 233 | fi 234 | 235 | 236 | dnl 237 | dnl threads 238 | dnl 239 | 240 | AC_SEARCH_LIBS([pthread_create], [pthread], [have_threads=yes], [have_threads=no]) 241 | AC_MSG_CHECKING([for __sync_add_and_fetch]) 242 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM( 243 | [AC_INCLUDES_DEFAULT 244 | extern volatile int x;], 245 | [[printf("%d", __sync_add_and_fetch(&x, 1));]])], 246 | [AC_DEFINE([HAVE___SYNC_ADD_AND_FETCH], 1, [Define to 1 if you have the `__sync_add_and_fetch' function.]) 247 | result=yes], [result=no]) 248 | AC_MSG_RESULT([$result]) 249 | 250 | AC_ARG_ENABLE([threads], 251 | [AS_HELP_STRING([--disable-threads], [do not include multithreading])], 252 | [], [enable_threads=maybe]) 253 | 254 | if test "x$have_threads" = xno -a "x$enable_threads" = xyes; then 255 | AC_MSG_ERROR([ 256 | ****************************************************************************** 257 | Threading support is not available on this platform.]) 258 | elif test "x$have_threads" = xyes -a "x$enable_threads" != xno; then 259 | AC_DEFINE([ENABLE_THREADS], [1], [Define to 1 if multithreading support is available.]) 260 | fi 261 | 262 | 263 | dnl 264 | dnl verbatim portions of the header 265 | dnl 266 | 267 | AC_DEFINE_UNQUOTED(PATHNAME_SEPARATOR, ['/'], [Pathname separator character ('/' on Unix).]) 268 | if false; then 269 | AC_DEFINE(OUTPUT_GIF_TO_TERMINAL, 1, [Define to write GIFs to stdout even when stdout is a terminal.]) 270 | fi 271 | 272 | AH_TOP([#ifndef GIFSICLE_CONFIG_H 273 | #define GIFSICLE_CONFIG_H]) 274 | 275 | AH_BOTTOM([#include 276 | 277 | #ifdef __cplusplus 278 | extern "C" { 279 | #endif 280 | 281 | /* Use the clean-failing malloc library in fmalloc.c. */ 282 | #define GIF_ALLOCATOR_DEFINED 1 283 | #define Gif_Free free 284 | 285 | /* Prototype strerror if we don't have it. */ 286 | #ifndef HAVE_STRERROR 287 | char *strerror(int errno); 288 | #endif 289 | 290 | #ifdef __cplusplus 291 | } 292 | /* Get rid of a possible inline macro under C++. */ 293 | # define inline inline 294 | #endif 295 | 296 | /* Need _setmode under MS-DOS, to set stdin/stdout to binary mode */ 297 | /* Need _fsetmode under OS/2 for the same reason */ 298 | /* Windows has _isatty, not isatty */ 299 | /* Modern Windows has _snprintf, but we prefer snprintf */ 300 | #if defined(_MSDOS) || defined(_WIN32) || defined(__EMX__) || defined(__DJGPP__) 301 | # include 302 | # include 303 | # define isatty _isatty 304 | #endif 305 | 306 | #ifndef HAVE_SNPRINTF 307 | #define snprintf(s, n, ...) sprintf((s), __VA_ARGS__) 308 | #endif 309 | 310 | #endif /* GIFSICLE_CONFIG_H */]) 311 | 312 | 313 | dnl 314 | dnl Output 315 | dnl 316 | 317 | AC_OUTPUT 318 | -------------------------------------------------------------------------------- /gifdiff.1: -------------------------------------------------------------------------------- 1 | .\" -*- mode: nroff -*- 2 | .ds V 1.96 3 | .ds E " \-\- 4 | .if t .ds E \(em 5 | .de Op 6 | .BR "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" 7 | .. 8 | .de Sp 9 | .if n .sp 10 | .if t .sp 0.4 11 | .. 12 | .de Es 13 | .Sp 14 | .RS 5 15 | .nf 16 | .. 17 | .de Ee 18 | .fi 19 | .RE 20 | .PP 21 | .. 22 | .TH GIFDIFF 1 "11 July 2017" "Version \*V" 23 | .SH NAME 24 | gifdiff \- compares GIF images 25 | .SH SYNOPSIS 26 | .B gifdiff 27 | \%[options] 28 | GIF\-file\-1 29 | GIF\-file\-2 30 | ' 31 | .SH DESCRIPTION 32 | .B gifdiff 33 | compares two GIF files and determines if they appear identical. Differences 34 | that don't affect appearance (like colormap ordering or how much an 35 | animation is optimized) are not reported. 36 | .PP 37 | .B gifdiff 38 | prints details of any differences it finds. If the GIFs are the same, it 39 | prints nothing. It exits with status 0 if there were no differences, 1 if 40 | there were some differences, and 2 if there was trouble. 41 | ' 42 | .SH OPTIONS 43 | 44 | .PD 0 45 | .TP 5 46 | .Op \-\-brief ", " \-q 47 | ' 48 | Report only whether the GIFs differ, not the details of the differences. 49 | ' 50 | .Sp 51 | .TP 5 52 | .Op \-\-ignore\-redundancy ", " \-w 53 | ' 54 | Do not report differences in the numbers of redundant frames (frames which 55 | do not change the displayed image). 56 | ' 57 | .Sp 58 | .TP 5 59 | .Op \-\-ignore\-background ", " \-B 60 | ' 61 | Do not report differences in background colors. 62 | ' 63 | .Sp 64 | .TP 5 65 | .Op \-\-help 66 | ' 67 | Print usage information and exit. 68 | ' 69 | .Sp 70 | .TP 71 | .Op \-\-version 72 | ' 73 | Print the version number and some quickie warranty information and exit. 74 | ' 75 | .PD 76 | ' 77 | .SH SEE ALSO 78 | 79 | .BR gifsicle (1) 80 | ' 81 | .SH BUGS 82 | 83 | Please email suggestions, additions, patches and bugs to 84 | ekohler@gmail.com. 85 | ' 86 | .SH AUTHORS 87 | .na 88 | Eddie Kohler, ekohler@gmail.com 89 | .br 90 | http://www.read.seas.harvard.edu/~kohler/ 91 | .PP 92 | http://www.lcdf.org/gifsicle/ 93 | .br 94 | The 95 | .B gifsicle 96 | home page. 97 | ' 98 | -------------------------------------------------------------------------------- /gifview.1: -------------------------------------------------------------------------------- 1 | .\" -*- mode: nroff -*- 2 | .ds V 1.96 3 | .ds E " \-\- 4 | .if t .ds E \(em 5 | .de Op 6 | .BR "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" 7 | .. 8 | .de Oa 9 | .IR "\fB\\$1\& \|\fI\\$2" "\\$3" "\\$4" "\\$5" "\\$6" 10 | .. 11 | .de Qo 12 | .RB ` "\\$1" "'\\$2" 13 | .. 14 | .de Sp 15 | .if n .sp 16 | .if t .sp 0.4 17 | .. 18 | .de Es 19 | .Sp 20 | .RS 5 21 | .nf 22 | .. 23 | .de Ee 24 | .fi 25 | .RE 26 | .PP 27 | .. 28 | .TH GIFVIEW 1 "11 July 2017" "Version \*V" 29 | .SH NAME 30 | gifview \- displays GIF images and animations on the X window system 31 | .SH SYNOPSIS 32 | .B gifview 33 | \%[\fB--display\fP \fIdisplay\fP] 34 | \%[options] 35 | \%[filenames and frames].\|.\|. 36 | ' 37 | .SH DESCRIPTION 38 | .B gifview 39 | displays GIF image files on workstations and terminals running the X Window 40 | System. 41 | .B gifview 42 | understands multi-image GIFs, which can be displayed either as slideshows 43 | or as animations. 44 | ' 45 | .SH INTERACTION 46 | .B gifview 47 | windows recognize several keystrokes and button commands. Many of them are 48 | only useful for multi-image GIFs. 49 | .TP 12 50 | .RB "Space or " n 51 | ' 52 | Go to the next frame. 53 | .TP 54 | .BR b " or " p 55 | Go to the previous frame. 56 | .TP 57 | .BR r " or " < 58 | Go to the first frame. 59 | .TP 60 | .BR > 61 | Go to the last frame. 62 | .TP 63 | ESC 64 | Stop the animation. 65 | .TP 66 | .BR s " or " a 67 | Toggle between animation and slideshow mode. 68 | .TP 69 | .BR u 70 | Toggle between normal and unoptimized mode. 71 | .TP 72 | Backspace 73 | ' 74 | Delete this window. 75 | .TP 76 | .B q 77 | Quit 78 | .BR gifview . 79 | ' 80 | .PP 81 | Left-clicking on a window goes to the next frame; right-clicking on a 82 | window deletes that window. 83 | ' 84 | .SH COMMAND LINE 85 | 86 | .BR gifview 's 87 | command line consists of 88 | .IR "GIF input files" 89 | and 90 | .IR options . 91 | Most options start with a dash (\-) or plus (+); frame selections, a kind 92 | of option, start with a number sign (#). Anything else is a GIF input file. 93 | .PP 94 | .B gifview 95 | displays one window for each GIF input file you specify. If no GIF input 96 | file is given, or you give the special filename `\-', it reads from the 97 | standard input. 98 | ' 99 | .SH OPTIONS 100 | 101 | .PD 0 102 | .TP 5 103 | .Op \-\-animate ", " \-a 104 | ' 105 | Animate multi-image GIFs by default. Normally, multi-image GIFs first 106 | appear in slideshow mode. You can always use the 107 | .RB ` a ' 108 | keystroke to toggle between modes. This option has a converse, 109 | .Qo \-\-no\-animate 110 | or 111 | .Qo +a . 112 | ' 113 | .Sp 114 | .TP 5 115 | .Op \-\-unoptimize ", " \-U 116 | ' 117 | Display multi-image GIFs as ``unoptimized'', which shows a faithful 118 | representation of what a user will see at each frame of an animation. See 119 | .BR gifsicle (1) 120 | for a more detailed description of unoptimization. This option has a 121 | converse, 122 | .Qo \-\-no\-unoptimize 123 | or 124 | .Qo +U . 125 | GIFs are always displayed unoptimized in animation mode. 126 | ' 127 | .Sp 128 | .TP 5 129 | .Oa \-d display 130 | .TP 131 | .Oa \-\-display display 132 | ' 133 | Sets the X display to 134 | .IR display . 135 | This option must come before any GIF files. 136 | ' 137 | .Sp 138 | .TP 5 139 | .Oa \-\-name name 140 | ' 141 | Sets the application name under which resources are found, rather than 142 | the default of "gifview". Since 143 | .B gifview 144 | itself does not use the resource database, this is mostly useful for 145 | communication with your window manager. 146 | ' 147 | .Sp 148 | .TP 5 149 | .Oa \-\-geometry geometry 150 | ' 151 | Set the size and position of 152 | .BR gifview 's 153 | windows. This is a standard X option. At most one 154 | .Op \-\-geometry 155 | option can be given per window (that is, per input GIF file). 156 | ' 157 | .Sp 158 | .TP 5 159 | .Oa \-\-title title 160 | ' 161 | Sets the 162 | .B gifview 163 | window's title. The default is "gifview", followed by information about the 164 | currently displayed file and frame. 165 | ' 166 | .Sp 167 | .TP 5 168 | .Oa \-w window 169 | .TP 170 | .Oa \-\-window window 171 | ' 172 | Display the next GIF input in an existing X window, instead of making a new 173 | top-level window. This way, you can use 174 | .B gifview 175 | to display animated GIFs in a window you created with another program. The 176 | .I window 177 | argument should be an integer 178 | .RB ( gifview 179 | will use that window ID) 180 | or `root' 181 | .RB ( gifview 182 | will use the root window). 183 | ' 184 | .Sp 185 | .TP 5 186 | .Oa \-\-new\-window window 187 | ' 188 | Display the next GIF input in a new child of an existing X window. This 189 | child window will disappear when 190 | .B gifview 191 | exits. The 192 | .I window 193 | argument should be an integer 194 | .RB ( gifview 195 | will use that window ID) 196 | or `root' 197 | .RB ( gifview 198 | will use the root window). 199 | ' 200 | .Sp 201 | .TP 5 202 | .Op \-\-install\-colormap ", " \-i 203 | ' 204 | Use a private colormap for each window (if you are using a PseudoColor 205 | display). This avoids polluting the existing colormap, and may produce 206 | better results if your colormap is full, but causes annoying colormap 207 | flashing. 208 | ' 209 | .Sp 210 | .TP 5 211 | .Oa \-\-background color 212 | .TP 213 | .Oa \-\-bg color 214 | ' 215 | Set the background color, which is used 216 | for transparent pixels. 217 | ' 218 | .Sp 219 | .TP 5 220 | .Oa \-\-min\-delay delay 221 | ' 222 | Set the minimum delay between frames to 223 | .IR delay , 224 | which is measured in hundredths of a second. Default is 0. 225 | ' 226 | .Sp 227 | .TP 5 228 | .Oa \-\-fallback\-delay delay 229 | ' 230 | Set the frame delay of GIFs that do not specify a delay value 231 | or have a delay of 0. 232 | The final value is still subject to the value of \-\-min\-delay. 233 | Like \-\-min\-delay, 234 | .IR delay 235 | is measured in hundredths of a second. Default is 0. 236 | ' 237 | .Sp 238 | .TP 5 239 | .Op \-\-no\-interactive ", " \+e 240 | ' 241 | Don't pay attention to mouse buttons or keystrokes. 242 | ' 243 | .Sp 244 | .TP 5 245 | .Op \-\-memory\-limit lim 246 | ' 247 | Cache at most 248 | .I lim 249 | megabytes of images in memory when animating. Default is 40. 250 | ' 251 | .Sp 252 | .TP 5 253 | .Op \-\-help 254 | ' 255 | Print usage information and exit. 256 | ' 257 | .Sp 258 | .TP 259 | .Op \-\-version 260 | ' 261 | Print the version number and some quickie warranty information and exit. 262 | ' 263 | .PD 264 | ' 265 | .\" ----------------------------------------------------------------- 266 | .SS Frame Selections 267 | 268 | A frame selection tells 269 | .B gifview 270 | which frame to initially display from the current input file. They are 271 | useful only for animations, as non-animated GIFs only have one frame. Frame 272 | selections can only be displayed in slideshow mode. 273 | .Sp 274 | .PD 0 275 | .TP 13 276 | .BI # num 277 | ' 278 | Select frame \fInum\fR. (The first frame is 279 | .Qo #0 . 280 | Negative numbers count backwards from the last frame, which is 281 | .Qo #-1 .) 282 | ' 283 | .TP 13 284 | .BI # name 285 | ' 286 | Select the frame named \fIname\fR. 287 | .PD 288 | .PP 289 | If you give two or more frame selections, you will get one window per frame 290 | selection. 291 | ' 292 | .SH SEE ALSO 293 | 294 | .BR gifsicle (1) 295 | ' 296 | .SH BUGS 297 | 298 | Please email suggestions, additions, patches and bugs to 299 | ekohler@gmail.com. 300 | ' 301 | .SH AUTHORS 302 | .na 303 | Eddie Kohler, ekohler@gmail.com 304 | .br 305 | http://www.read.seas.harvard.edu/~kohler/ 306 | .PP 307 | http://www.lcdf.org/gifsicle/ 308 | .br 309 | The 310 | .B gifsicle 311 | home page. 312 | ' 313 | -------------------------------------------------------------------------------- /include/lcdf/clp.h: -------------------------------------------------------------------------------- 1 | /* -*- related-file-name: "../../liblcdf/clp.c" -*- */ 2 | #ifndef LCDF_CLP_H 3 | #define LCDF_CLP_H 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | /* clp.h - Public interface to CLP. 9 | * This file is part of CLP, the command line parser package. 10 | * 11 | * Copyright (c) 1997-2025 Eddie Kohler, ekohler@gmail.com 12 | * 13 | * CLP is free software. It is distributed under the GNU General Public 14 | * License, Version 2, or, alternatively and at your discretion, under the 15 | * more permissive (BSD-like) Click LICENSE file as described below. 16 | * 17 | * Permission is hereby granted, free of charge, to any person obtaining a 18 | * copy of this software and associated documentation files (the 19 | * "Software"), to deal in the Software without restriction, subject to the 20 | * conditions listed in the Click LICENSE file, which is available in full at 21 | * http://github.com/kohler/click/blob/master/LICENSE. The conditions 22 | * include: you must preserve this copyright notice, and you cannot mention 23 | * the copyright holders in advertising related to the Software without 24 | * their permission. The Software is provided WITHOUT ANY WARRANTY, EXPRESS 25 | * OR IMPLIED. This notice is a summary of the Click LICENSE file; the 26 | * license in that file is binding. */ 27 | 28 | #include 29 | #include 30 | 31 | typedef struct Clp_Option Clp_Option; 32 | typedef struct Clp_Parser Clp_Parser; 33 | typedef struct Clp_ParserState Clp_ParserState; 34 | 35 | 36 | /** @brief Option description. 37 | * 38 | * CLP users declare arrays of Clp_Option structures to specify what options 39 | * should be parsed. 40 | * @sa Clp_NewParser, Clp_SetOptions */ 41 | struct Clp_Option { 42 | const char *long_name; /**< Name of long option, or NULL if the option 43 | has no long name. */ 44 | int short_name; /**< Character defining short option, or 0 if 45 | the option has no short name. */ 46 | int option_id; /**< User-specified ID defining option, 47 | returned by Clp_Next. */ 48 | int val_type; /**< ID of option's value type, or 0 if option 49 | takes no value. */ 50 | int flags; /**< Option parsing flags. */ 51 | }; 52 | 53 | /** @name Value types 54 | * These values describe the type of an option's argument and are used in 55 | * the Clp_Option val_type field. For example, if an option took integers, its 56 | * Clp_Option structure would have val_type set to Clp_ValInt. */ 57 | /**@{*/ 58 | #define Clp_NoVal 0 /**< @brief Option takes no value. */ 59 | #define Clp_ValString 1 /**< @brief Option value is an 60 | arbitrary string. */ 61 | #define Clp_ValStringNotOption 2 /**< @brief Option value is a 62 | non-option string. 63 | 64 | See Clp_DisallowOptions. */ 65 | #define Clp_ValBool 3 /**< @brief Option value is a 66 | boolean. 67 | 68 | Accepts "true", "false", "yes", "no", "1", and "0", or any 69 | prefixes thereof. The match is case-insensitive. */ 70 | #define Clp_ValInt 4 /**< @brief Option value is a 71 | signed int. 72 | 73 | Accepts an optional "+" or "-" sign, followed by one or more 74 | digits. The digits may be include a "0x" or "0X" prefix, for 75 | a hexadecimal number, or a "0" prefix, for an octal number; 76 | otherwise it is decimal. */ 77 | #define Clp_ValUnsigned 5 /**< @brief Option value is an 78 | unsigned int. 79 | 80 | Accepts an optional "+" sign, followed by one or more 81 | digits. The digits may be include a "0x" or "0X" prefix, for 82 | a hexadecimal number, or a "0" prefix, for an octal number; 83 | otherwise it is decimal. */ 84 | #define Clp_ValLong 6 /**< @brief Option value is a 85 | signed long. */ 86 | #define Clp_ValUnsignedLong 7 /**< @brief Option value is an 87 | unsigned long. */ 88 | #define Clp_ValDouble 8 /**< @brief Option value is a 89 | double. 90 | Accepts a real number as defined by strtod(). */ 91 | #define Clp_ValFirstUser 10 /**< @brief Value types >= 92 | Clp_ValFirstUser are available 93 | for user types. */ 94 | /**@}*/ 95 | 96 | /** @name Option flags 97 | * These flags are used in the Clp_Option flags field. */ 98 | /**@{*/ 99 | #define Clp_Mandatory (1<<0) /**< @brief Option flag: value 100 | is mandatory. 101 | 102 | It is an error if the option has no value. This is the 103 | default if an option has arg_type != 0 and the Clp_Optional 104 | flag is not provided. */ 105 | #define Clp_Optional (1<<1) /**< @brief Option flag: value 106 | is optional. */ 107 | #define Clp_Negate (1<<2) /**< @brief Option flag: option 108 | may be negated. 109 | 110 | --no-[long_name] will be accepted in argument lists. */ 111 | #define Clp_OnlyNegated (1<<3) /**< @brief Option flag: option 112 | must be negated. 113 | 114 | --no-[long_name] will be accepted in argument lists, but 115 | --[long_name] will not. This is the default if long_name 116 | begins with "no-". */ 117 | #define Clp_PreferredMatch (1<<4) /**< @brief Option flag: prefer this 118 | option when matching. 119 | 120 | Prefixes of --[long_name] should map to this option, even if 121 | other options begin with --[long_name]. */ 122 | /**@}*/ 123 | 124 | /** @name Option character types 125 | * These flags are used in to define character types in Clp_SetOptionChar(). */ 126 | /**@{*/ 127 | /* Clp_NotOption 0 */ 128 | #define Clp_Short (1<<0) /**< @brief Option character begins 129 | a set of short options. */ 130 | #define Clp_Long (1<<1) /**< @brief Option character begins 131 | a long option. */ 132 | #define Clp_ShortNegated (1<<2) /**< @brief Option character begins 133 | a set of negated short options. */ 134 | #define Clp_LongNegated (1<<3) /**< @brief Option character begins 135 | a negated long option. */ 136 | #define Clp_LongImplicit (1<<4) /**< @brief Option character can begin 137 | a long option, and is part of that 138 | long option. */ 139 | /**@}*/ 140 | 141 | #define Clp_NotOption 0 /**< @brief Clp_Next value: argument 142 | was not an option. */ 143 | #define Clp_Done -1 /**< @brief Clp_Next value: there are 144 | no more arguments. */ 145 | #define Clp_BadOption -2 /**< @brief Clp_Next value: argument 146 | was an erroneous option. */ 147 | #define Clp_Error -3 /**< @brief Clp_Next value: internal 148 | CLP error. */ 149 | 150 | #define Clp_ValSize 40 /**< @brief Minimum size of the 151 | Clp_Parser val.cs field. */ 152 | #define Clp_ValIntSize 10 /**< @brief Minimum size of the 153 | Clp_Parser val.is field. */ 154 | 155 | 156 | /** @brief A value parsing function. 157 | * @param clp the parser 158 | * @param vstr the value to be parsed 159 | * @param complain if nonzero, report error messages via Clp_OptionError 160 | * @param user_data user data passed to Clp_AddType() 161 | * @return 1 if parsing succeeded, 0 otherwise 162 | */ 163 | typedef int (*Clp_ValParseFunc)(Clp_Parser *clp, const char *vstr, 164 | int complain, void *user_data); 165 | 166 | /** @brief A function for reporting option errors. 167 | * @param clp the parser 168 | * @param message error message 169 | */ 170 | typedef void (*Clp_ErrorHandler)(Clp_Parser *clp, const char *message); 171 | 172 | 173 | /** @brief Command line parser. 174 | * 175 | * A Clp_Parser object defines an instance of CLP, including allowed options, 176 | * value types, and current arguments. 177 | * @sa Clp_NewParser, Clp_SetOptions, Clp_SetArguments */ 178 | struct Clp_Parser { 179 | const Clp_Option *option; /**< The last option. */ 180 | 181 | int negated; /**< Whether the last option was negated. */ 182 | 183 | int have_val; /**< Whether the last option had a value. */ 184 | const char *vstr; /**< The string value provided with the last 185 | option. */ 186 | 187 | union { 188 | int i; 189 | unsigned u; 190 | long l; 191 | unsigned long ul; 192 | double d; 193 | const char *s; 194 | void *pv; 195 | #ifdef HAVE_INT64_TYPES 196 | int64_t i64; 197 | uint64_t u64; 198 | #endif 199 | char cs[Clp_ValSize]; 200 | unsigned char ucs[Clp_ValSize]; 201 | int is[Clp_ValIntSize]; 202 | unsigned us[Clp_ValIntSize]; 203 | } val; /**< The parsed value provided with the last 204 | option. */ 205 | 206 | void *user_data; /**< Uninterpreted by CLP; users can set 207 | arbitrarily. */ 208 | 209 | struct Clp_Internal *internal; 210 | }; 211 | 212 | /** @cond never */ 213 | #if __GNUC__ >= 4 214 | # define CLP_SENTINEL __attribute__((sentinel)) 215 | #else 216 | # define CLP_SENTINEL /* nothing */ 217 | #endif 218 | /** @endcond never */ 219 | 220 | 221 | /** @brief Create a new Clp_Parser. */ 222 | Clp_Parser *Clp_NewParser(int argc, const char * const *argv, 223 | int nopt, const Clp_Option *opt); 224 | 225 | /** @brief Destroy a Clp_Parser object. */ 226 | void Clp_DeleteParser(Clp_Parser *clp); 227 | 228 | 229 | /** @brief Return @a clp's program name. */ 230 | const char *Clp_ProgramName(Clp_Parser *clp); 231 | 232 | /** @brief Set @a clp's program name. */ 233 | const char *Clp_SetProgramName(Clp_Parser *clp, const char *name); 234 | 235 | 236 | /** @brief Set @a clp's error handler function. */ 237 | Clp_ErrorHandler Clp_SetErrorHandler(Clp_Parser *clp, Clp_ErrorHandler errh); 238 | 239 | /** @brief Set @a clp's UTF-8 mode. */ 240 | int Clp_SetUTF8(Clp_Parser *clp, int utf8); 241 | 242 | /** @brief Return @a clp's treatment of character @a c. */ 243 | int Clp_OptionChar(Clp_Parser *clp, int c); 244 | 245 | /** @brief Set @a clp's treatment of character @a c. */ 246 | int Clp_SetOptionChar(Clp_Parser *clp, int c, int type); 247 | 248 | /** @brief Set @a clp's option definitions. */ 249 | int Clp_SetOptions(Clp_Parser *clp, int nopt, const Clp_Option *opt); 250 | 251 | /** @brief Set @a clp's arguments. */ 252 | void Clp_SetArguments(Clp_Parser *clp, int argc, const char * const *argv); 253 | 254 | /** @brief Set whether @a clp is searching for options. */ 255 | int Clp_SetOptionProcessing(Clp_Parser *clp, int on); 256 | 257 | 258 | #define Clp_DisallowOptions (1<<0) /**< @brief Value type flag: value 259 | can't be an option string. 260 | 261 | See Clp_AddType(). */ 262 | 263 | /** @brief Define a new value type for @a clp. */ 264 | int Clp_AddType(Clp_Parser *clp, int val_type, int flags, 265 | Clp_ValParseFunc parser, void *user_data); 266 | 267 | 268 | #define Clp_AllowNumbers (1<<0) /**< @brief String list flag: allow 269 | explicit numbers. 270 | 271 | See Clp_AddStringListType() and Clp_AddStringListTypeVec(). */ 272 | #define Clp_StringListLong (1<<1) /**< @brief String list flag: values 273 | have long type. */ 274 | 275 | /** @brief Define a new string list value type for @a clp. */ 276 | int Clp_AddStringListTypeVec(Clp_Parser *clp, int val_type, int flags, 277 | int nstrs, const char * const *strs, 278 | const int *vals); 279 | 280 | /** @brief Define a new string list value type for @a clp. */ 281 | int Clp_AddStringListType(Clp_Parser *clp, int val_type, int flags, ...) 282 | CLP_SENTINEL; 283 | 284 | 285 | /** @brief Parse and return the next argument from @a clp. */ 286 | int Clp_Next(Clp_Parser *clp); 287 | 288 | /** @brief Return the next argument from @a clp without option parsing. */ 289 | const char *Clp_Shift(Clp_Parser *clp, int allow_options); 290 | 291 | 292 | /** @brief Create a new Clp_ParserState. */ 293 | Clp_ParserState *Clp_NewParserState(void); 294 | 295 | /** @brief Destroy a Clp_ParserState object. */ 296 | void Clp_DeleteParserState(Clp_ParserState *state); 297 | 298 | /** @brief Save @a clp's current state in @a state. */ 299 | void Clp_SaveParser(const Clp_Parser *clp, Clp_ParserState *state); 300 | 301 | /** @brief Restore parser state from @a state into @a clp. */ 302 | void Clp_RestoreParser(Clp_Parser *clp, const Clp_ParserState *state); 303 | 304 | 305 | /** @brief Report a parser error. */ 306 | int Clp_OptionError(Clp_Parser *clp, const char *format, ...); 307 | 308 | /** @brief Format a message. */ 309 | int Clp_vsnprintf(Clp_Parser* clp, char* str, size_t size, 310 | const char* format, va_list val); 311 | 312 | /** @brief Print a message. */ 313 | int Clp_fprintf(Clp_Parser* clp, FILE* f, const char* format, ...); 314 | 315 | /** @brief Print a message. */ 316 | int Clp_vfprintf(Clp_Parser* clp, FILE* f, const char* format, va_list val); 317 | 318 | /** @brief Extract the current option as a string. */ 319 | int Clp_CurOptionNameBuf(Clp_Parser *clp, char *buf, int len); 320 | 321 | /** @brief Extract the current option as a string. */ 322 | const char *Clp_CurOptionName(Clp_Parser *clp); 323 | 324 | /** @brief Test if the current option had long name @a name. */ 325 | int Clp_IsLong(Clp_Parser *clp, const char *long_name); 326 | 327 | /** @brief Test if the current option had short name @a name. */ 328 | int Clp_IsShort(Clp_Parser *clp, int short_name); 329 | 330 | /** @brief Return a UTF-8 or normal single quote. */ 331 | const char* Clp_QuoteChar(const Clp_Parser* clp, int isclose); 332 | 333 | #undef CLP_SENTINEL 334 | #ifdef __cplusplus 335 | } 336 | #endif 337 | #endif 338 | -------------------------------------------------------------------------------- /include/lcdf/inttypes.h: -------------------------------------------------------------------------------- 1 | #ifndef LCDF_INTTYPES_H 2 | #define LCDF_INTTYPES_H 3 | /* Define known-width integer types. */ 4 | 5 | #ifdef HAVE_INTTYPES_H 6 | # include 7 | #elif defined(HAVE_SYS_TYPES_H) 8 | # include 9 | # ifdef HAVE_U_INT_TYPES 10 | typedef u_int8_t uint8_t; 11 | typedef u_int16_t uint16_t; 12 | typedef u_int32_t uint32_t; 13 | # endif 14 | #elif defined(_WIN32) 15 | typedef __int8 int8_t; 16 | typedef unsigned __int8 uint8_t; 17 | typedef __int16 int16_t; 18 | typedef unsigned __int16 uint16_t; 19 | typedef __int32 int32_t; 20 | typedef unsigned __int32 uint32_t; 21 | #endif 22 | 23 | #ifndef HAVE_UINTPTR_T 24 | # if SIZEOF_VOID_P == SIZEOF_UNSIGNED_LONG 25 | typedef unsigned long uintptr_t; 26 | # elif SIZEOF_VOID_P == SIZEOF_UNSIGNED_INT 27 | typedef unsigned int uintptr_t; 28 | # endif 29 | #endif 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/lcdfgif/gif.h: -------------------------------------------------------------------------------- 1 | /* gif.h - Interface to the LCDF GIF library. 2 | Copyright (C) 1997-2025 Eddie Kohler, ekohler@gmail.com 3 | This file is part of the LCDF GIF library. 4 | 5 | The GIF library is free software. It is distributed under the GNU General 6 | Public License, version 2; you can copy, distribute, or alter it at will, 7 | as long as this notice is kept intact and this source code is made 8 | available. There is no warranty, express or implied. */ 9 | 10 | #ifndef LCDF_GIF_H /* -*- mode: c -*- */ 11 | #define LCDF_GIF_H 12 | #include 13 | #include 14 | #include 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | /* NOTE: You should define the types uint8_t, uint16_t and uint32_t before 20 | including this file, probably by #including . */ 21 | 22 | #define GIF_MAJOR_VERSION 2 23 | #define GIF_MINOR_VERSION 0 24 | #define GIF_VERSION "2.0" 25 | 26 | typedef struct Gif_Stream Gif_Stream; 27 | typedef struct Gif_Image Gif_Image; 28 | typedef struct Gif_Colormap Gif_Colormap; 29 | typedef struct Gif_Comment Gif_Comment; 30 | typedef struct Gif_Extension Gif_Extension; 31 | typedef struct Gif_Record Gif_Record; 32 | 33 | typedef uint16_t Gif_Code; 34 | #define GIF_MAX_CODE_BITS 12 35 | #define GIF_MAX_CODE 0x1000 36 | #define GIF_MAX_BLOCK 255 37 | 38 | 39 | /** GIF_STREAM **/ 40 | 41 | struct Gif_Stream { 42 | Gif_Image **images; 43 | int nimages; 44 | int imagescap; 45 | 46 | Gif_Colormap *global; 47 | uint16_t background; /* 256 means no background */ 48 | 49 | uint16_t screen_width; 50 | uint16_t screen_height; 51 | long loopcount; /* -1 means no loop count */ 52 | 53 | Gif_Comment* end_comment; 54 | Gif_Extension* end_extension_list; 55 | 56 | unsigned errors; 57 | uint32_t user_flags; 58 | 59 | const char* landmark; 60 | int refcount; 61 | }; 62 | 63 | Gif_Stream * Gif_NewStream(void); 64 | void Gif_DeleteStream(Gif_Stream *); 65 | 66 | Gif_Stream * Gif_CopyStreamSkeleton(Gif_Stream *); 67 | Gif_Stream * Gif_CopyStreamImages(Gif_Stream *); 68 | 69 | #define Gif_ScreenWidth(gfs) ((gfs)->screen_width) 70 | #define Gif_ScreenHeight(gfs) ((gfs)->screen_height) 71 | #define Gif_ImageCount(gfs) ((gfs)->nimages) 72 | 73 | #define GIF_UNOPTIMIZE_SIMPLEST_DISPOSAL 1 74 | #define GIF_MAX_SCREEN_WIDTH 65535 75 | #define GIF_MAX_SCREEN_HEIGHT 65535 76 | 77 | void Gif_CalculateScreenSize(Gif_Stream *, int force); 78 | int Gif_Unoptimize(Gif_Stream *); 79 | int Gif_FullUnoptimize(Gif_Stream *, int flags); 80 | 81 | 82 | /** GIF_IMAGE **/ 83 | 84 | struct Gif_Image { 85 | uint8_t **img; /* img[y][x] == image byte (x,y) */ 86 | uint8_t *image_data; 87 | 88 | uint16_t width; 89 | uint16_t height; 90 | uint16_t left; 91 | uint16_t top; 92 | uint16_t delay; 93 | uint8_t disposal; 94 | uint8_t interlace; 95 | 96 | short transparent; /* -1 means no transparent index */ 97 | Gif_Colormap *local; 98 | 99 | char* identifier; 100 | Gif_Comment* comment; 101 | Gif_Extension* extension_list; 102 | 103 | void (*free_image_data)(void *); 104 | 105 | uint32_t compressed_len; 106 | uint32_t compressed_errors; 107 | uint8_t* compressed; 108 | void (*free_compressed)(void *); 109 | 110 | uint32_t user_flags; 111 | void* user_data; 112 | void (*free_user_data)(void *); 113 | int refcount; 114 | 115 | }; 116 | 117 | #define GIF_DISPOSAL_NONE 0 118 | #define GIF_DISPOSAL_ASIS 1 119 | #define GIF_DISPOSAL_BACKGROUND 2 120 | #define GIF_DISPOSAL_PREVIOUS 3 121 | 122 | Gif_Image * Gif_NewImage(void); 123 | void Gif_DeleteImage(Gif_Image *gfi); 124 | 125 | int Gif_AddImage(Gif_Stream *gfs, Gif_Image *gfi); 126 | void Gif_RemoveImage(Gif_Stream *gfs, int i); 127 | Gif_Image * Gif_CopyImage(Gif_Image *gfi); 128 | void Gif_MakeImageEmpty(Gif_Image* gfi); 129 | 130 | Gif_Image * Gif_GetImage(Gif_Stream *gfs, int i); 131 | Gif_Image * Gif_GetNamedImage(Gif_Stream *gfs, const char *name); 132 | int Gif_ImageNumber(Gif_Stream *gfs, Gif_Image *gfi); 133 | 134 | #define Gif_ImageWidth(gfi) ((gfi)->width) 135 | #define Gif_ImageHeight(gfi) ((gfi)->height) 136 | #define Gif_ImageDelay(gfi) ((gfi)->delay) 137 | #define Gif_ImageUserData(gfi) ((gfi)->userdata) 138 | #define Gif_SetImageUserData(gfi, v) ((gfi)->userdata = v) 139 | int Gif_ImageColorBound(const Gif_Image* gfi); 140 | 141 | typedef void (*Gif_ReadErrorHandler)(Gif_Stream* gfs, 142 | Gif_Image* gfi, 143 | int is_error, 144 | const char* error_text); 145 | 146 | typedef struct { 147 | int flags; 148 | int loss; 149 | void *padding[7]; 150 | } Gif_CompressInfo; 151 | 152 | #define Gif_UncompressImage(gfs, gfi) Gif_FullUncompressImage((gfs),(gfi),0) 153 | int Gif_FullUncompressImage(Gif_Stream* gfs, Gif_Image* gfi, 154 | Gif_ReadErrorHandler handler); 155 | int Gif_CompressImage(Gif_Stream *gfs, Gif_Image *gfi); 156 | int Gif_FullCompressImage(Gif_Stream *gfs, Gif_Image *gfi, 157 | const Gif_CompressInfo *gcinfo); 158 | void Gif_ReleaseUncompressedImage(Gif_Image *gfi); 159 | void Gif_ReleaseCompressedImage(Gif_Image *gfi); 160 | int Gif_SetUncompressedImage(Gif_Image *gfi, uint8_t *data, 161 | void (*free_data)(void *), int data_interlaced); 162 | int Gif_CreateUncompressedImage(Gif_Image* gfi, int data_interlaced); 163 | 164 | int Gif_ClipImage(Gif_Image *gfi, int l, int t, int w, int h); 165 | 166 | void Gif_InitCompressInfo(Gif_CompressInfo *gcinfo); 167 | 168 | 169 | /** GIF_COLORMAP **/ 170 | 171 | typedef struct { 172 | uint8_t haspixel; /* semantics assigned by user */ 173 | uint8_t gfc_red; /* red component (0-255) */ 174 | uint8_t gfc_green; /* green component (0-255) */ 175 | uint8_t gfc_blue; /* blue component (0-255) */ 176 | uint32_t pixel; /* semantics assigned by user */ 177 | } Gif_Color; 178 | 179 | 180 | struct Gif_Colormap { 181 | int ncol; 182 | int capacity; 183 | uint32_t user_flags; 184 | int refcount; 185 | Gif_Color *col; 186 | }; 187 | 188 | Gif_Colormap * Gif_NewColormap(void); 189 | Gif_Colormap * Gif_NewFullColormap(int count, int capacity); 190 | void Gif_DeleteColormap(Gif_Colormap *); 191 | 192 | Gif_Colormap * Gif_CopyColormap(Gif_Colormap *); 193 | 194 | int Gif_ColorEq(Gif_Color *, Gif_Color *); 195 | #define GIF_COLOREQ(c1, c2) \ 196 | ((c1)->gfc_red==(c2)->gfc_red && (c1)->gfc_green==(c2)->gfc_green && \ 197 | (c1)->gfc_blue==(c2)->gfc_blue) 198 | #define GIF_SETCOLOR(c, r, g, b) \ 199 | ((c)->gfc_red = (r), (c)->gfc_green = (g), (c)->gfc_blue = (b)) 200 | 201 | int Gif_FindColor(Gif_Colormap *, Gif_Color *); 202 | int Gif_AddColor(Gif_Colormap *, Gif_Color *, int look_from); 203 | 204 | 205 | /** GIF_COMMENT **/ 206 | 207 | struct Gif_Comment { 208 | char **str; 209 | int *len; 210 | int count; 211 | int cap; 212 | }; 213 | 214 | Gif_Comment * Gif_NewComment(void); 215 | void Gif_DeleteComment(Gif_Comment *); 216 | int Gif_AddCommentTake(Gif_Comment *, char *, int); 217 | int Gif_AddComment(Gif_Comment *, const char *, int); 218 | 219 | 220 | /** GIF_EXTENSION **/ 221 | 222 | struct Gif_Extension { 223 | int kind; /* negative kinds are reserved */ 224 | char* appname; 225 | int applength; 226 | uint8_t* data; 227 | uint32_t length; 228 | int packetized; 229 | 230 | Gif_Stream *stream; 231 | Gif_Image *image; 232 | Gif_Extension *next; 233 | void (*free_data)(void *); 234 | }; 235 | 236 | 237 | Gif_Extension* Gif_NewExtension(int kind, const char* appname, int applength); 238 | void Gif_DeleteExtension(Gif_Extension* gfex); 239 | Gif_Extension* Gif_CopyExtension(Gif_Extension* gfex); 240 | int Gif_AddExtension(Gif_Stream* gfs, Gif_Image* gfi, 241 | Gif_Extension* gfex); 242 | 243 | 244 | /** READING AND WRITING **/ 245 | 246 | struct Gif_Record { 247 | const unsigned char *data; 248 | uint32_t length; 249 | }; 250 | 251 | #define GIF_READ_COMPRESSED 1 252 | #define GIF_READ_UNCOMPRESSED 2 253 | #define GIF_READ_CONST_RECORD 4 254 | #define GIF_READ_TRAILING_GARBAGE_OK 8 255 | #define GIF_WRITE_CAREFUL_MIN_CODE_SIZE 1 256 | #define GIF_WRITE_EAGER_CLEAR 2 257 | #define GIF_WRITE_OPTIMIZE 4 258 | #define GIF_WRITE_SHRINK 8 259 | 260 | void Gif_SetErrorHandler(Gif_ReadErrorHandler handler); 261 | Gif_Stream* Gif_ReadFile(FILE* f); 262 | Gif_Stream* Gif_FullReadFile(FILE* f, int flags, const char* landmark, 263 | Gif_ReadErrorHandler handler); 264 | Gif_Stream* Gif_ReadRecord(const Gif_Record* record); 265 | Gif_Stream* Gif_FullReadRecord(const Gif_Record* record, int flags, 266 | const char* landmark, 267 | Gif_ReadErrorHandler handler); 268 | int Gif_WriteFile(Gif_Stream *gfs, FILE *f); 269 | int Gif_FullWriteFile(Gif_Stream *gfs, 270 | const Gif_CompressInfo *gcinfo, FILE *f); 271 | 272 | #define Gif_ReadFile(f) Gif_FullReadFile((f),GIF_READ_UNCOMPRESSED,0,0) 273 | #define Gif_ReadRecord(r) Gif_FullReadRecord((r),GIF_READ_UNCOMPRESSED,0,0) 274 | #define Gif_CompressImage(s, i) Gif_FullCompressImage((s),(i),0) 275 | #define Gif_WriteFile(s, f) Gif_FullWriteFile((s),0,(f)) 276 | 277 | typedef struct Gif_Writer Gif_Writer; 278 | Gif_Writer* Gif_IncrementalWriteFileInit(Gif_Stream* gfs, const Gif_CompressInfo* gcinfo, FILE *f); 279 | int Gif_IncrementalWriteImage(Gif_Writer* grr, Gif_Stream* gfs, Gif_Image* gfi); 280 | int Gif_IncrementalWriteComplete(Gif_Writer* grr, Gif_Stream* gfs); 281 | 282 | 283 | /** HOOKS AND MISCELLANEOUS **/ 284 | 285 | int Gif_InterlaceLine(int y, int height); 286 | char * Gif_CopyString(const char *); 287 | 288 | #define GIF_T_STREAM (0) 289 | #define GIF_T_IMAGE (1) 290 | #define GIF_T_COLORMAP (2) 291 | typedef void (*Gif_DeletionHookFunc)(int, void *, void *); 292 | int Gif_AddDeletionHook(int, Gif_DeletionHookFunc, void *); 293 | void Gif_RemoveDeletionHook(int, Gif_DeletionHookFunc, void *); 294 | 295 | #ifdef GIF_DEBUGGING 296 | #define GIF_DEBUG(x) Gif_Debug x 297 | void Gif_Debug(char *x, ...); 298 | #else 299 | #define GIF_DEBUG(x) 300 | #endif 301 | 302 | void* Gif_Realloc(void* p, size_t s, size_t n, 303 | const char* file, int line); 304 | void Gif_Free(void* p); 305 | #if !GIF_ALLOCATOR_DEFINED 306 | # define Gif_Free free 307 | #endif 308 | 309 | #ifndef Gif_New 310 | # define Gif_New(t) ((t*) Gif_Realloc(0, sizeof(t), 1, __FILE__, __LINE__)) 311 | # define Gif_NewArray(t, n) ((t*) Gif_Realloc(0, sizeof(t), (n), __FILE__, __LINE__)) 312 | # define Gif_ReArray(p, t, n) ((p)=(t*) Gif_Realloc((void*) (p), sizeof(t), (n), __FILE__, __LINE__)) 313 | # define Gif_Delete(p) Gif_Free((void*) (p)) 314 | # define Gif_DeleteArray(p) Gif_Free((void*) (p)) 315 | #endif 316 | 317 | #ifdef __cplusplus 318 | } 319 | #endif 320 | #endif 321 | -------------------------------------------------------------------------------- /include/lcdfgif/gifx.h: -------------------------------------------------------------------------------- 1 | #ifndef LCDF_GIFX_H 2 | #define LCDF_GIFX_H 3 | #include 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | /* gifx.h - Functions to turn GIFs in memory into X Pixmaps. 9 | Copyright (C) 1997-2025 Eddie Kohler, ekohler@gmail.com 10 | This file is part of the LCDF GIF library. 11 | 12 | The LCDF GIF library is free software*. It is distributed under the GNU 13 | General Public License, version 2; you can copy, distribute, or 14 | alter it at will, as long as this notice is kept intact and this source 15 | code is made available. There is no warranty, express or implied. */ 16 | 17 | #include 18 | 19 | #define GIFX_COLORMAP_EXTENSION -107 20 | 21 | 22 | typedef struct Gif_XContext Gif_XContext; 23 | typedef struct Gif_XColormap Gif_XColormap; 24 | typedef struct Gif_XFrame Gif_XFrame; 25 | 26 | struct Gif_XContext { 27 | Display *display; 28 | int screen_number; 29 | Drawable drawable; 30 | Visual *visual; 31 | uint16_t depth; 32 | uint16_t ncolormap; 33 | Colormap colormap; 34 | 35 | uint16_t nclosest; 36 | Gif_Color *closest; 37 | 38 | int free_deleted_colormap_pixels; 39 | Gif_XColormap *xcolormap; 40 | 41 | GC image_gc; 42 | GC mask_gc; 43 | 44 | unsigned long transparent_pixel; 45 | unsigned long foreground_pixel; 46 | int refcount; 47 | }; 48 | 49 | struct Gif_XFrame { 50 | Pixmap pixmap; 51 | int postdisposal; 52 | int user_data; 53 | }; 54 | 55 | Gif_XContext * Gif_NewXContext(Display *display, Window window); 56 | Gif_XContext * Gif_NewXContextFromVisual(Display *display, int screen_number, 57 | Visual *visual, int depth, Colormap cmap); 58 | void Gif_DeleteXContext(Gif_XContext *gfx); 59 | 60 | Pixmap Gif_XImage(Gif_XContext *gfx, Gif_Stream *gfs, Gif_Image *gfi); 61 | Pixmap Gif_XImageColormap(Gif_XContext *gfx, Gif_Stream *gfs, 62 | Gif_Colormap *gfcm, Gif_Image *gfi); 63 | Pixmap Gif_XSubImage(Gif_XContext *gfx, Gif_Stream *gfs, 64 | Gif_Image *gfi, int l, int t, int w, int h); 65 | Pixmap Gif_XSubImageColormap(Gif_XContext *gfx, 66 | Gif_Stream* gfs, Gif_Image *gfi, 67 | Gif_Colormap* gfcm, int l, int t, int w, int h); 68 | 69 | Pixmap Gif_XMask(Gif_XContext *gfx, Gif_Stream *gfs, Gif_Image *gfi); 70 | Pixmap Gif_XSubMask(Gif_XContext* gfx, Gif_Stream* gfs, Gif_Image* gfi, 71 | int l, int t, int w, int h); 72 | 73 | Gif_XFrame * Gif_NewXFrames(Gif_Stream *gfs); 74 | void Gif_DeleteXFrames(Gif_XContext *gfx, Gif_Stream *gfs, 75 | Gif_XFrame *frames); 76 | Pixmap Gif_XNextImage(Gif_XContext *gfx, Gif_Stream *gfs, int i, 77 | Gif_XFrame *frames); 78 | 79 | int Gif_XAllocateColors(Gif_XContext *gfx, Gif_Colormap *gfcm); 80 | void Gif_XDeallocateColors(Gif_XContext *gfx, Gif_Colormap *gfcm); 81 | unsigned long * Gif_XClaimStreamColors(Gif_XContext *gfx, Gif_Stream *gfs, 82 | int *np_store); 83 | 84 | 85 | #ifdef __cplusplus 86 | } 87 | #endif 88 | #endif 89 | -------------------------------------------------------------------------------- /logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kohler/gifsicle/ed5b01816b42423e0ae6897cad0149cda561898c/logo.gif -------------------------------------------------------------------------------- /logo1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kohler/gifsicle/ed5b01816b42423e0ae6897cad0149cda561898c/logo1.gif -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | ## Process this file with automake to produce Makefile.in 2 | AUTOMAKE_OPTIONS = foreign check-news 3 | 4 | bin_PROGRAMS = gifsicle @OTHERPROGRAMS@ 5 | EXTRA_PROGRAMS = gifview gifdiff 6 | 7 | LDADD = @LIBOBJS@ 8 | gifsicle_LDADD = $(LDADD) @GIFWRITE_O@ 9 | gifview_LDADD = $(LDADD) @X_LIBS@ @X_PRE_LIBS@ -lX11 @X_EXTRA_LIBS@ 10 | 11 | gifsicle_DEPENDENCIES = @GIFWRITE_O@ @LIBOBJS@ 12 | gifview_DEPENDENCIES = @LIBOBJS@ 13 | gifdiff_DEPENDENCIES = @LIBOBJS@ 14 | 15 | WERROR = @WERROR@ 16 | 17 | gifsicle_SOURCES = clp.c fmalloc.c \ 18 | giffunc.c gifread.c gifunopt.c \ 19 | gifsicle.h kcolor.h \ 20 | kcolor.c merge.c optimize.c quantize.c support.c xform.c \ 21 | gifsicle.c 22 | 23 | gifview_SOURCES = clp.c fmalloc.c \ 24 | giffunc.c gifread.c gifx.c \ 25 | gifview.c 26 | 27 | gifdiff_SOURCES = clp.c fmalloc.c \ 28 | giffunc.c gifread.c \ 29 | gifdiff.c 30 | 31 | AM_CPPFLAGS = $(X_CFLAGS) $(WERROR) -I$(top_srcdir)/include 32 | 33 | EXTRA_gifsicle_SOURCES = gifwrite.c ungifwrt.c 34 | 35 | EXTRA_DIST = opttemplate.c Makefile.bcc Makefile.w32 win32cfg.h 36 | -------------------------------------------------------------------------------- /src/Makefile.bcc: -------------------------------------------------------------------------------- 1 | # Win32 Makefile originally by Emil Mikulic 2 | # Updates by Eddie Kohler and 3 | # Steven Marthouse 4 | 5 | # ---------------------------------------------------------- 6 | # Makefile adapted for Borland C++ 5.5 Compiler 7 | # Stephen Schnipsel 8 | # 9 | # set this to your bcc directory! 10 | MYBCCDIR = D:\BCC55 11 | # 12 | # ---------------------------------------------------------- 13 | 14 | 15 | # *** MAKING UNGIFSICLE *** 16 | # If `GIFWRITE_OBJ' is defined to `gifwrite.obj', Gifsicle will use 17 | # Unisys-patented LZW compression. If it is defined to `ungifwrt.obj', it 18 | # will use unpatented run-length compression, which creates larger GIFs but 19 | # is completely free software. If you downloaded the ungifsicle package, 20 | # which doesn't have `gifwrite.c', you MUST define `GIFWRITE_OBJ' to 21 | # `ungifwrt.obj' by commenting the first line below and uncommenting the 22 | # second. 23 | GIFWRITE_OBJ = gifwrite.obj 24 | #GIFWRITE_OBJ = ungifwrt.obj 25 | 26 | # *** SUPPORTING WILDCARD EXPANSION *** 27 | # Define `SETARGV_OBJ' to the filename for the `setargv.obj' object file. 28 | # The definition included here works for Microsoft compilers; you will 29 | # probably need to change it if you're using a different compiler. You can 30 | # define it to the empty string, in which case Gifsicle will compile fine, 31 | # but you won't be able to use wildcards in file name arguments. 32 | SETARGV_OBJ = $(MYBCCDIR)\LIB\WILDARGS.OBJ 33 | 34 | 35 | CC = bcc32 36 | CFLAGS = -I.. -I..\INCLUDE -DHAVE_CONFIG_H=1 -D_CONSOLE -O2 -D_setmode=setmode 37 | 38 | GIFSICLE_OBJS = clp.obj fmalloc.obj giffunc.obj gifread.obj gifunopt.obj \ 39 | $(GIFWRITE_OBJ) merge.obj optimize.obj quantize.obj support.obj \ 40 | xform.obj gifsicle.obj $(SETARGV_OBJ) 41 | 42 | GIFDIFF_OBJS = clp.obj fmalloc.obj giffunc.obj gifread.obj gifdiff.obj \ 43 | $(SETARGV_OBJ) 44 | 45 | .c.obj: 46 | $(CC) $(CFLAGS) -c $< 47 | 48 | all: gifsicle.exe gifdiff.exe 49 | 50 | gifsicle.exe: $(GIFSICLE_OBJS) 51 | $(CC) $(CFLAGS) -egifsicle $(GIFSICLE_OBJS) 52 | 53 | gifdiff.exe: $(GIFDIFF_OBJS) 54 | $(CC) $(CFLAGS) -egifdiff $(GIFDIFF_OBJS) 55 | 56 | clp.obj: ..\config.h ..\include\lcdf\clp.h clp.c 57 | 58 | fmalloc.obj: ..\config.h fmalloc.c 59 | 60 | giffunc.obj: ..\config.h giffunc.c ..\include\lcdfgif\gif.h 61 | gifread.obj: ..\config.h gifread.c ..\include\lcdfgif\gif.h 62 | gifwrite.obj: ..\config.h gifwrite.c ..\include\lcdfgif\gif.h 63 | ungifwrt.obj: ..\config.h ungifwrt.c ..\include\lcdfgif\gif.h 64 | gifunopt.obj: ..\config.h gifunopt.c ..\include\lcdfgif\gif.h 65 | 66 | merge.obj: ..\config.h gifsicle.h merge.c 67 | optimize.obj: ..\config.h gifsicle.h optimize.c 68 | quantize.obj: ..\config.h gifsicle.h quantize.c 69 | support.obj: ..\config.h gifsicle.h support.c 70 | xform.obj: ..\config.h gifsicle.h xform.c 71 | gifsicle.obj: ..\config.h gifsicle.h gifsicle.c 72 | 73 | ..\config.h: win32cfg.h 74 | copy win32cfg.h ..\config.h 75 | 76 | clean: 77 | del *.obj 78 | del *.exe 79 | del *.tds 80 | 81 | -------------------------------------------------------------------------------- /src/Makefile.mingw: -------------------------------------------------------------------------------- 1 | # Win32 Makefile originally by Emil Mikulic 2 | # Updates by Eddie Kohler and 3 | # Steven Marthouse 4 | 5 | # ---------------------------------------------------------- 6 | # Makefile adapted for MinGW GCC Compiler 7 | # José Manuel Muñoz 8 | # ---------------------------------------------------------- 9 | 10 | 11 | # *** MAKING UNGIFSICLE *** 12 | # If `GIFWRITE_OBJ' is defined to `gifwrite.o', Gifsicle will use 13 | # Unisys-patented LZW compression. If it is defined to `ungifwrt.o', it 14 | # will use unpatented run-length compression, which creates larger GIFs but 15 | # is completely free software. If you downloaded the ungifsicle package, 16 | # which doesn't have `gifwrite.c', you MUST define `GIFWRITE_OBJ' to 17 | # `ungifwrt.o' by commenting the first line below and uncommenting the 18 | # second. 19 | GIFWRITE_OBJ = gifwrite.o 20 | #GIFWRITE_OBJ = ungifwrt.o 21 | 22 | # *** SUPPORTING WILDCARD EXPANSION *** 23 | # MinGW seems to include support for this by itself. 24 | 25 | CC = gcc 26 | MINGWFLAGS = -DHAVE_UINTPTR_T -DHAVE_INTTYPES_H 27 | CFLAGS = -I.. -I..\include -DHAVE_CONFIG_H=1 -D_CONSOLE -O2 $(MINGWFLAGS) 28 | 29 | GIFSICLE_OBJS = clp.o fmalloc.o giffunc.o gifread.o gifunopt.o \ 30 | $(GIFWRITE_OBJ) merge.o optimize.o quantize.o support.o \ 31 | xform.o gifsicle.o 32 | 33 | GIFDIFF_OBJS = clp.o fmalloc.o giffunc.o gifread.o gifdiff.o 34 | 35 | 36 | all: gifsicle.exe gifdiff.exe 37 | 38 | gifsicle.exe: $(GIFSICLE_OBJS) 39 | $(CC) $(CFLAGS) -o $@ $(GIFSICLE_OBJS) 40 | 41 | gifdiff.exe: $(GIFDIFF_OBJS) 42 | $(CC) $(CFLAGS) -o $@ $(GIFDIFF_OBJS) 43 | 44 | clp.o: ..\config.h ..\include\lcdf\clp.h clp.c 45 | 46 | fmalloc.o: ..\config.h fmalloc.c 47 | 48 | giffunc.o: ..\config.h giffunc.c ..\include\lcdfgif\gif.h 49 | gifread.o: ..\config.h gifread.c ..\include\lcdfgif\gif.h 50 | gifwrite.o: ..\config.h gifwrite.c ..\include\lcdfgif\gif.h 51 | ungifwrt.o: ..\config.h ungifwrt.c ..\include\lcdfgif\gif.h 52 | gifunopt.o: ..\config.h gifunopt.c ..\include\lcdfgif\gif.h 53 | 54 | merge.o: ..\config.h gifsicle.h merge.c 55 | optimize.o: ..\config.h gifsicle.h optimize.c 56 | quantize.o: ..\config.h gifsicle.h quantize.c 57 | support.o: ..\config.h gifsicle.h support.c 58 | xform.o: ..\config.h gifsicle.h xform.c 59 | gifsicle.o: ..\config.h gifsicle.h gifsicle.c 60 | 61 | ..\config.h: win32cfg.h 62 | copy win32cfg.h ..\config.h 63 | 64 | clean: 65 | del *.o 66 | del *.exe 67 | -------------------------------------------------------------------------------- /src/Makefile.w32: -------------------------------------------------------------------------------- 1 | # Win32 Makefile originally by Emil Mikulic 2 | # Updates by Eddie Kohler and 3 | # Steven Marthouse 4 | 5 | # This makefile should work under Win32 (95/98/NT/whatever) or Win64. 6 | # It should work out-of-the-box with Visual C++ 5. 7 | # 8 | # C:\GIFSICLE> nmake -f Makefile.w32 9 | 10 | # *** MAKING UNGIFSICLE *** 11 | # If `GIFWRITE_OBJ' is defined to `gifwrite.obj', Gifsicle will use 12 | # Unisys-patented LZW compression. If it is defined to `ungifwrt.obj', it 13 | # will use unpatented run-length compression, which creates larger GIFs but 14 | # is completely free software. If you downloaded the ungifsicle package, 15 | # which doesn't have `gifwrite.c', you MUST define `GIFWRITE_OBJ' to 16 | # `ungifwrt.obj' by commenting the first line below and uncommenting the 17 | # second. 18 | GIFWRITE_OBJ = gifwrite.obj 19 | #GIFWRITE_OBJ = ungifwrt.obj 20 | 21 | CC = cl 22 | CFLAGS = -I.. -I..\include -DHAVE_CONFIG_H -D_CONSOLE /W2 /O2 23 | LDLIBS = setargv.obj 24 | 25 | GIFSICLE_OBJS = clp.obj fmalloc.obj giffunc.obj gifread.obj gifunopt.obj \ 26 | $(GIFWRITE_OBJ) merge.obj optimize.obj quantize.obj support.obj \ 27 | xform.obj gifsicle.obj 28 | 29 | GIFDIFF_OBJS = clp.obj fmalloc.obj giffunc.obj gifread.obj gifdiff.obj 30 | 31 | .c.obj: 32 | $(CC) $(CFLAGS) /c $< 33 | 34 | gifsicle.exe: $(GIFSICLE_OBJS) 35 | $(CC) $(CFLAGS) /Fegifsicle.exe $(GIFSICLE_OBJS) /link $(LDLIBS) 36 | 37 | gifdiff.exe: $(GIFDIFF_OBJS) 38 | $(CC) $(CFLAGS) /Fegifdiff.exe $(GIFDIFF_OBJS) /link $(LDLIBS) 39 | 40 | clp.obj: ..\config.h ..\include\lcdf\clp.h clp.c 41 | 42 | fmalloc.obj: ..\config.h fmalloc.c 43 | 44 | giffunc.obj: ..\config.h giffunc.c ..\include\lcdfgif\gif.h 45 | gifread.obj: ..\config.h gifread.c ..\include\lcdfgif\gif.h 46 | gifwrite.obj: ..\config.h gifwrite.c ..\include\lcdfgif\gif.h 47 | ungifwrt.obj: ..\config.h ungifwrt.c ..\include\lcdfgif\gif.h 48 | gifunopt.obj: ..\config.h gifunopt.c ..\include\lcdfgif\gif.h 49 | 50 | merge.obj: ..\config.h gifsicle.h merge.c 51 | optimize.obj: ..\config.h gifsicle.h optimize.c 52 | quantize.obj: ..\config.h gifsicle.h quantize.c 53 | support.obj: ..\config.h gifsicle.h support.c 54 | xform.obj: ..\config.h gifsicle.h xform.c 55 | gifsicle.obj: ..\config.h gifsicle.h gifsicle.c 56 | 57 | ..\config.h: win32cfg.h 58 | copy win32cfg.h ..\config.h 59 | 60 | clean: 61 | del *.obj 62 | del *.exe 63 | -------------------------------------------------------------------------------- /src/fmalloc.c: -------------------------------------------------------------------------------- 1 | #if HAVE_CONFIG_H 2 | # include 3 | #endif 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | extern const char* program_name; 12 | 13 | void* Gif_Realloc(void* p, size_t s, size_t n, const char* file, int line) { 14 | (void) file, (void) line; 15 | if (s == 0 || n == 0) { 16 | Gif_Free(p); 17 | return (void*) 0; 18 | } else if (s == 1 || n == 1 || s <= ((size_t) -1) / n) { 19 | p = realloc(p, s * n); 20 | if (!p) { 21 | fprintf(stderr, "%s: Out of memory, giving up\n", program_name); 22 | exit(1); 23 | } 24 | return p; 25 | } else { 26 | fprintf(stderr, "%s: Out of memory, giving up (huge allocation)\n", program_name); 27 | exit(1); 28 | return (void*) 0; 29 | } 30 | } 31 | 32 | #undef Gif_Free 33 | void Gif_Free(void* p) { 34 | free(p); 35 | } 36 | 37 | #ifdef __cplusplus 38 | } 39 | #endif 40 | -------------------------------------------------------------------------------- /src/gifdiff.c: -------------------------------------------------------------------------------- 1 | /* gifdiff.c - Gifdiff compares GIF images for identical appearance. 2 | Copyright (C) 1998-2025 Eddie Kohler, ekohler@gmail.com 3 | This file is part of gifdiff, in the gifsicle package. 4 | 5 | Gifdiff is free software. It is distributed under the GNU Public License, 6 | version 2; you can copy, distribute, or alter it at will, as long 7 | as this notice is kept intact and this source code is made available. There 8 | is no warranty, express or implied. */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #if HAVE_UNISTD_H 18 | # include 19 | #endif 20 | 21 | #define QUIET_OPT 300 22 | #define HELP_OPT 301 23 | #define VERSION_OPT 302 24 | #define IGNORE_REDUNDANCY_OPT 303 25 | #define REDUNDANCY_OPT 304 26 | #define IGNORE_BACKGROUND_OPT 305 27 | #define BACKGROUND_OPT 306 28 | 29 | const Clp_Option options[] = { 30 | { "help", 'h', HELP_OPT, 0, 0 }, 31 | { "brief", 'q', QUIET_OPT, 0, Clp_Negate }, 32 | { "redundancy", 0, REDUNDANCY_OPT, 0, Clp_Negate }, 33 | { "ignore-redundancy", 'w', IGNORE_REDUNDANCY_OPT, 0, Clp_Negate }, 34 | { "bg", 0, BACKGROUND_OPT, 0, Clp_Negate }, 35 | { "ignore-bg", 0, IGNORE_BACKGROUND_OPT, 0, Clp_Negate }, 36 | { "background", 0, BACKGROUND_OPT, 0, Clp_Negate }, 37 | { "ignore-background", 'B', IGNORE_BACKGROUND_OPT, 0, Clp_Negate }, 38 | { "version", 'v', VERSION_OPT, 0, 0 } 39 | }; 40 | 41 | const char *program_name; 42 | 43 | static const char *filename1; 44 | static const char *filename2; 45 | 46 | static unsigned screen_width, screen_height; 47 | #define TRANSP (0) 48 | 49 | static uint16_t *gdata[2]; 50 | static uint16_t *glast[2]; 51 | static uint16_t *scratch; 52 | static uint16_t *line; 53 | 54 | static int brief; 55 | static int ignore_redundancy; 56 | static int ignore_background; 57 | 58 | static Clp_Parser* clp; 59 | 60 | 61 | static void 62 | combine_colormaps(Gif_Colormap *gfcm, Gif_Colormap *newcm) 63 | { 64 | int i, gfcm_ncol = gfcm ? gfcm->ncol : 0; 65 | for (i = 0; i < gfcm_ncol; i++) { 66 | Gif_Color *c = &gfcm->col[i]; 67 | c->pixel = Gif_AddColor(newcm, c, 1); 68 | } 69 | } 70 | 71 | static void 72 | fill_area(uint16_t *data, int l, int t, int w, int h, uint16_t val) 73 | { 74 | int x; 75 | data += screen_width * t + l; 76 | for (; h > 0; --h) { 77 | for (x = w; x > 0; --x) 78 | *data++ = val; 79 | data += screen_width - w; 80 | } 81 | } 82 | 83 | static void 84 | copy_area(uint16_t *dst, const uint16_t *src, int l, int t, int w, int h) 85 | { 86 | dst += screen_width * t + l; 87 | src += screen_width * t + l; 88 | for (; h > 0; --h, dst += screen_width, src += screen_width) 89 | memcpy(dst, src, sizeof(uint16_t) * w); 90 | } 91 | 92 | static void 93 | expand_bounds(int *lf, int *tp, int *rt, int *bt, const Gif_Image *gfi) 94 | { 95 | int empty = (*lf >= *rt || *tp >= *bt); 96 | if (empty || gfi->left < *lf) 97 | *lf = gfi->left; 98 | if (empty || gfi->top < *tp) 99 | *tp = gfi->top; 100 | if (empty || gfi->left + gfi->width > *rt) 101 | *rt = gfi->left + gfi->width; 102 | if (empty || gfi->top + gfi->height > *bt) 103 | *bt = gfi->top + gfi->height; 104 | } 105 | 106 | 107 | static int 108 | apply_image(int is_second, Gif_Stream *gfs, int imageno, uint16_t background) 109 | { 110 | int i, x, y, any_change; 111 | Gif_Image *gfi = gfs->images[imageno]; 112 | Gif_Image *pgfi = imageno ? gfs->images[imageno - 1] : 0; 113 | int width = gfi->width; 114 | uint16_t map[256]; 115 | uint16_t *data = gdata[is_second]; 116 | uint16_t *last = glast[is_second]; 117 | Gif_Colormap *gfcm = gfi->local ? gfi->local : gfs->global; 118 | int gfcm_ncol = gfcm ? gfcm->ncol : 0; 119 | 120 | /* set up colormap */ 121 | for (i = 0; i < gfcm_ncol; ++i) 122 | map[i] = gfcm->col[i].pixel; 123 | for (i = gfcm_ncol; i < 256; ++i) 124 | map[i] = 1; 125 | if (gfi->transparent >= 0 && gfi->transparent < 256) 126 | map[gfi->transparent] = TRANSP; 127 | 128 | /* if this image's disposal is 'previous', save the post-disposal version in 129 | 'scratch' */ 130 | if (gfi->disposal == GIF_DISPOSAL_PREVIOUS) { 131 | copy_area(scratch, data, gfi->left, gfi->top, gfi->width, gfi->height); 132 | if (pgfi && pgfi->disposal == GIF_DISPOSAL_PREVIOUS) 133 | copy_area(scratch, last, pgfi->left, pgfi->top, pgfi->width, pgfi->height); 134 | else if (pgfi && pgfi->disposal == GIF_DISPOSAL_BACKGROUND) 135 | fill_area(scratch, pgfi->left, pgfi->top, pgfi->width, pgfi->height, background); 136 | } 137 | 138 | /* uncompress and clip */ 139 | Gif_UncompressImage(gfs, gfi); 140 | Gif_ClipImage(gfi, 0, 0, screen_width, screen_height); 141 | 142 | any_change = imageno == 0; 143 | { 144 | int lf = 0, tp = 0, rt = 0, bt = 0; 145 | expand_bounds(&lf, &tp, &rt, &bt, gfi); 146 | if (pgfi && pgfi->disposal == GIF_DISPOSAL_PREVIOUS) 147 | expand_bounds(&lf, &tp, &rt, &bt, pgfi); 148 | else if (pgfi && pgfi->disposal == GIF_DISPOSAL_BACKGROUND) { 149 | expand_bounds(&lf, &tp, &rt, &bt, pgfi); 150 | fill_area(last, pgfi->left, pgfi->top, pgfi->width, pgfi->height, background); 151 | } else 152 | pgfi = 0; 153 | for (y = tp; y < bt; ++y) { 154 | uint16_t *outd = data + screen_width * y + lf; 155 | if (!any_change) 156 | memcpy(line, outd, (rt - lf) * sizeof(uint16_t)); 157 | if (pgfi && y >= pgfi->top && y < pgfi->top + pgfi->height) 158 | memcpy(outd + pgfi->left - lf, 159 | last + screen_width * y + pgfi->left, 160 | pgfi->width * sizeof(uint16_t)); 161 | if (y >= gfi->top && y < gfi->top + gfi->height) { 162 | uint16_t *xoutd = outd + gfi->left - lf; 163 | const uint8_t *ind = gfi->img[y - gfi->top]; 164 | for (x = 0; x < width; ++x, ++ind, ++xoutd) 165 | if (map[*ind] != TRANSP) 166 | *xoutd = map[*ind]; 167 | } 168 | if (!any_change && memcmp(line, outd, (rt - lf) * sizeof(uint16_t)) != 0) 169 | any_change = 1; 170 | } 171 | } 172 | 173 | Gif_ReleaseUncompressedImage(gfi); 174 | Gif_ReleaseCompressedImage(gfi); 175 | 176 | /* switch 'glast' with 'scratch' if necessary */ 177 | if (gfi->disposal == GIF_DISPOSAL_PREVIOUS) { 178 | uint16_t *x = scratch; 179 | scratch = glast[is_second]; 180 | glast[is_second] = x; 181 | } 182 | 183 | return any_change; 184 | } 185 | 186 | 187 | #define SAME 0 188 | #define DIFFERENT 1 189 | static int was_different; 190 | 191 | static void 192 | different(const char *format, ...) 193 | { 194 | va_list val; 195 | va_start(val, format); 196 | if (!brief) { 197 | vfprintf(stdout, format, val); 198 | fputc('\n', stdout); 199 | } 200 | va_end(val); 201 | was_different = 1; 202 | } 203 | 204 | 205 | static void 206 | name_loopcount(int loopcount, char *buf, size_t bufsz) 207 | { 208 | if (loopcount < 0) 209 | strcpy(buf, "none"); 210 | else if (loopcount == 0) 211 | strcpy(buf, "forever"); 212 | else 213 | snprintf(buf, bufsz, "%d", loopcount); 214 | } 215 | 216 | static void 217 | name_delay(int delay, char *buf, size_t bufsz) 218 | { 219 | if (delay == 0) 220 | strcpy(buf, "none"); 221 | else 222 | snprintf(buf, bufsz, "%d.%02ds", delay / 100, delay % 100); 223 | } 224 | 225 | static void 226 | name_color(int color, Gif_Colormap *gfcm, char *buf, size_t bufsz) 227 | { 228 | if (color == TRANSP) 229 | strcpy(buf, "transparent"); 230 | else { 231 | Gif_Color *c = &gfcm->col[color]; 232 | snprintf(buf, bufsz, "#%02X%02X%02X", c->gfc_red, c->gfc_green, c->gfc_blue); 233 | } 234 | } 235 | 236 | 237 | int 238 | compare(Gif_Stream *s1, Gif_Stream *s2) 239 | { 240 | Gif_Colormap *newcm; 241 | int imageno1, imageno2, background1, background2; 242 | char buf1[256], buf2[256], fbuf[256]; 243 | 244 | was_different = 0; 245 | 246 | /* Compare image counts and screen sizes. If either of these differs, quit 247 | early. */ 248 | Gif_CalculateScreenSize(s1, 0); 249 | Gif_CalculateScreenSize(s2, 0); 250 | 251 | if (s1->screen_width != s2->screen_width 252 | || s1->screen_height != s2->screen_height) { 253 | different("screen sizes differ: <%dx%d >%dx%d", s1->screen_width, 254 | s1->screen_height, s2->screen_width, s2->screen_height); 255 | return DIFFERENT; 256 | } 257 | 258 | if (s1->screen_width == 0 || s1->screen_height == 0 259 | || s2->screen_width == 0 || s2->screen_height == 0) { 260 | /* paranoia -- don't think this can happen */ 261 | different("zero screen sizes"); 262 | return DIFFERENT; 263 | } 264 | 265 | if (s1->nimages == 0 || s2->nimages == 0) { 266 | if (s1->nimages != s2->nimages) { 267 | different("frame counts differ: <#%d >#%d", s1->nimages, s2->nimages); 268 | return DIFFERENT; 269 | } else 270 | return SAME; 271 | } 272 | 273 | /* Create arrays for the image data */ 274 | screen_width = s1->screen_width; 275 | screen_height = s1->screen_height; 276 | 277 | gdata[0] = Gif_NewArray(uint16_t, screen_width * screen_height); 278 | gdata[1] = Gif_NewArray(uint16_t, screen_width * screen_height); 279 | glast[0] = Gif_NewArray(uint16_t, screen_width * screen_height); 280 | glast[1] = Gif_NewArray(uint16_t, screen_width * screen_height); 281 | scratch = Gif_NewArray(uint16_t, screen_width * screen_height); 282 | line = Gif_NewArray(uint16_t, screen_width); 283 | 284 | /* Merge all distinct colors from the two images into one colormap, setting 285 | the 'pixel' slots in the images' colormaps to the corresponding values 286 | in the merged colormap. Don't forget transparency */ 287 | newcm = Gif_NewFullColormap(1, 256); 288 | combine_colormaps(s1->global, newcm); 289 | combine_colormaps(s2->global, newcm); 290 | for (imageno1 = 0; imageno1 < s1->nimages; ++imageno1) 291 | combine_colormaps(s1->images[imageno1]->local, newcm); 292 | for (imageno2 = 0; imageno2 < s2->nimages; ++imageno2) 293 | combine_colormaps(s2->images[imageno2]->local, newcm); 294 | 295 | /* Choose the background values */ 296 | background1 = background2 = TRANSP; 297 | if ((s1->nimages == 0 || s1->images[0]->transparent < 0) 298 | && s1->global && s1->background < s1->global->ncol) 299 | background1 = s1->global->col[ s1->background ].pixel; 300 | if ((s2->nimages == 0 || s2->images[0]->transparent < 0) 301 | && s2->global && s2->background < s2->global->ncol) 302 | background2 = s2->global->col[ s2->background ].pixel; 303 | 304 | /* Clear screens */ 305 | fill_area(gdata[0], 0, 0, screen_width, screen_height, TRANSP); 306 | fill_area(gdata[1], 0, 0, screen_width, screen_height, TRANSP); 307 | 308 | /* Loopcounts differ? */ 309 | if (s1->loopcount != s2->loopcount) { 310 | name_loopcount(s1->loopcount, buf1, sizeof(buf1)); 311 | name_loopcount(s2->loopcount, buf2, sizeof(buf2)); 312 | different("loop counts differ: <%s >%s", buf1, buf2); 313 | } 314 | 315 | /* Loop over frames, comparing image data and delays */ 316 | apply_image(0, s1, 0, background1); 317 | apply_image(1, s2, 0, background2); 318 | imageno1 = imageno2 = 0; 319 | while (imageno1 != s1->nimages && imageno2 != s2->nimages) { 320 | int fi1 = imageno1, fi2 = imageno2, 321 | delay1 = s1->images[fi1]->delay, delay2 = s2->images[fi2]->delay; 322 | 323 | /* get message right */ 324 | if (imageno1 == imageno2) 325 | snprintf(fbuf, sizeof(fbuf), "#%d", imageno1); 326 | else 327 | snprintf(fbuf, sizeof(fbuf), "<#%d >#%d", imageno1, imageno2); 328 | 329 | /* compare pixels */ 330 | if (memcmp(gdata[0], gdata[1], 331 | screen_width * screen_height * sizeof(uint16_t)) != 0) { 332 | unsigned d, c = screen_width * screen_height; 333 | uint16_t *d1 = gdata[0], *d2 = gdata[1]; 334 | for (d = 0; d < c; d++, d1++, d2++) 335 | if (*d1 != *d2) { 336 | name_color(*d1, newcm, buf1, sizeof(buf1)); 337 | name_color(*d2, newcm, buf2, sizeof(buf2)); 338 | different("frame %s pixels differ: %d,%d <%s >%s", 339 | fbuf, d % screen_width, d / screen_width, buf1, buf2); 340 | break; 341 | } 342 | } 343 | 344 | /* compare background */ 345 | if (!ignore_background && background1 != background2 346 | && (imageno1 == 0 || s1->images[imageno1 - 1]->disposal == GIF_DISPOSAL_BACKGROUND) 347 | && (imageno2 == 0 || s2->images[imageno2 - 1]->disposal == GIF_DISPOSAL_BACKGROUND)) { 348 | unsigned d, c = screen_width * screen_height; 349 | uint16_t *d1 = gdata[0], *d2 = gdata[1]; 350 | for (d = 0; d < c; ++d, ++d1, ++d2) 351 | if (*d1 == TRANSP || *d2 == TRANSP) { 352 | name_color(background1, newcm, buf1, sizeof(buf1)); 353 | name_color(background2, newcm, buf2, sizeof(buf2)); 354 | different("frame %s background pixels differ: %d,%d <%s >%s", 355 | fbuf, d % screen_width, d / screen_width, buf1, buf2); 356 | background1 = background2 = TRANSP; 357 | break; 358 | } 359 | } 360 | 361 | /* move to next images, skipping redundancy */ 362 | for (++imageno1; 363 | imageno1 < s1->nimages && !apply_image(0, s1, imageno1, background1); 364 | ++imageno1) 365 | delay1 += s1->images[imageno1]->delay; 366 | for (++imageno2; 367 | imageno2 < s2->nimages && !apply_image(1, s2, imageno2, background2); 368 | ++imageno2) 369 | delay2 += s2->images[imageno2]->delay; 370 | 371 | if (!ignore_redundancy) { 372 | fi1 = (imageno1 - fi1) - (imageno2 - fi2); 373 | for (; fi1 > 0; --fi1) 374 | different("extra redundant frame: <#%d", imageno1 - fi1); 375 | for (; fi1 < 0; ++fi1) 376 | different("extra redundant frame: >#%d", imageno2 + fi1); 377 | } 378 | 379 | if (delay1 != delay2) { 380 | name_delay(delay1, buf1, sizeof(buf1)); 381 | name_delay(delay2, buf2, sizeof(buf2)); 382 | different("frame %s delays differ: <%s >%s", fbuf, buf1, buf2); 383 | } 384 | } 385 | 386 | if (imageno1 != s1->nimages || imageno2 != s2->nimages) 387 | different("frame counts differ: <#%d >#%d", s1->nimages, s2->nimages); 388 | 389 | /* That's it! */ 390 | Gif_DeleteColormap(newcm); 391 | Gif_DeleteArray(gdata[0]); 392 | Gif_DeleteArray(gdata[1]); 393 | Gif_DeleteArray(glast[0]); 394 | Gif_DeleteArray(glast[1]); 395 | Gif_DeleteArray(scratch); 396 | Gif_DeleteArray(line); 397 | 398 | return was_different ? DIFFERENT : SAME; 399 | } 400 | 401 | 402 | void short_usage(void) { 403 | Clp_fprintf(clp, stderr, "Usage: %s [OPTION]... FILE1 FILE2\n\ 404 | Try %<%s --help%> for more information.\n", 405 | program_name, program_name); 406 | } 407 | 408 | void usage(void) { 409 | Clp_fprintf(clp, stdout, "\ 410 | % compares two GIF files (either images or animations) for identical\n\ 411 | visual appearance. An animation and an optimized version of the same animation\n\ 412 | should compare as the same. Gifdiff exits with status 0 if the images are\n\ 413 | the same, 1 if they%,re different, and 2 if there was some error.\n\ 414 | \n\ 415 | Usage: %s [OPTION]... FILE1 FILE2\n\n", program_name); 416 | Clp_fprintf(clp, stdout, "\ 417 | Options:\n\ 418 | -q, --brief Don%,t report detailed differences.\n\ 419 | -w, --ignore-redundancy Ignore differences in redundant frames.\n\ 420 | -B, --ignore-background Ignore differences in background colors.\n\ 421 | -h, --help Print this message and exit.\n\ 422 | -v, --version Print version number and exit.\n\ 423 | \n\ 424 | Report bugs to .\n"); 425 | } 426 | 427 | 428 | void fatal_error(const char* format, ...) { 429 | char buf[BUFSIZ]; 430 | int n = snprintf(buf, BUFSIZ, "%s: ", program_name); 431 | va_list val; 432 | va_start(val, format); 433 | Clp_vsnprintf(clp, buf + n, BUFSIZ - n, format, val); 434 | va_end(val); 435 | fputs(buf, stderr); 436 | exit(2); /* exit(2) for trouble */ 437 | } 438 | 439 | void error(const char* format, ...) { 440 | char buf[BUFSIZ]; 441 | int n = snprintf(buf, BUFSIZ, "%s: ", program_name); 442 | va_list val; 443 | va_start(val, format); 444 | Clp_vsnprintf(clp, buf + n, BUFSIZ - n, format, val); 445 | va_end(val); 446 | fputs(buf, stderr); 447 | } 448 | 449 | static int gifread_error_count; 450 | 451 | static void 452 | gifread_error(Gif_Stream* gfs, Gif_Image* gfi, 453 | int is_error, const char *message) 454 | { 455 | static int last_is_error = 0; 456 | static int last_which_image = 0; 457 | static char last_message[256]; 458 | static int different_error_count = 0; 459 | static int same_error_count = 0; 460 | int which_image = Gif_ImageNumber(gfs, gfi); 461 | const char *filename = gfs->landmark; 462 | if (which_image < 0) 463 | which_image = gfs->nimages; 464 | 465 | if (gifread_error_count == 0) { 466 | last_which_image = -1; 467 | last_message[0] = 0; 468 | different_error_count = 0; 469 | } 470 | 471 | gifread_error_count++; 472 | if (last_message[0] && different_error_count <= 10 473 | && (last_which_image != which_image || message == 0 474 | || strcmp(message, last_message) != 0)) { 475 | const char *etype = last_is_error ? "error" : "warning"; 476 | error("While reading %<%s%> frame #%d:\n", filename, last_which_image); 477 | if (same_error_count == 1) 478 | error(" %s: %s\n", etype, last_message); 479 | else if (same_error_count > 0) 480 | error(" %s: %s (%d times)\n", etype, last_message, same_error_count); 481 | same_error_count = 0; 482 | last_message[0] = 0; 483 | } 484 | 485 | if (message) { 486 | if (last_message[0] == 0) 487 | different_error_count++; 488 | same_error_count++; 489 | strcpy(last_message, message); 490 | last_which_image = which_image; 491 | last_is_error = is_error; 492 | } else 493 | last_message[0] = 0; 494 | 495 | if (different_error_count == 11 && message) { 496 | error("(more errors while reading %<%s%>)\n", filename); 497 | different_error_count++; 498 | } 499 | } 500 | 501 | static Gif_Stream * 502 | read_stream(const char **filename) 503 | { 504 | FILE *f; 505 | Gif_Stream *gfs; 506 | if (*filename == 0) { 507 | #if 0 508 | /* Since gifdiff always takes explicit filename arguments, 509 | allow explicit reads from terminal. */ 510 | #ifndef OUTPUT_GIF_TO_TERMINAL 511 | if (isatty(fileno(stdin))) { 512 | fatal_error(": is a terminal\n"); 513 | return NULL; 514 | } 515 | #endif 516 | #endif 517 | f = stdin; 518 | #if defined(_MSDOS) || defined(_WIN32) 519 | _setmode(_fileno(stdin), _O_BINARY); 520 | #elif defined(__DJGPP__) 521 | setmode(fileno(stdin), O_BINARY); 522 | #elif defined(__EMX__) 523 | _fsetmode(stdin, "b"); 524 | #endif 525 | *filename = ""; 526 | } else { 527 | f = fopen(*filename, "rb"); 528 | if (!f) 529 | fatal_error("%s: %s\n", *filename, strerror(errno)); 530 | } 531 | gifread_error_count = 0; 532 | gfs = Gif_FullReadFile(f, GIF_READ_COMPRESSED, *filename, gifread_error); 533 | if (!gfs) 534 | fatal_error("%s: file not in GIF format\n", *filename); 535 | return gfs; 536 | } 537 | 538 | int 539 | main(int argc, char *argv[]) 540 | { 541 | int how_many_inputs = 0; 542 | int status; 543 | const char **inputp; 544 | Gif_Stream *gfs1, *gfs2; 545 | 546 | clp = Clp_NewParser(argc, (const char * const *)argv, 547 | sizeof(options) / sizeof(options[0]), options); 548 | 549 | program_name = Clp_ProgramName(clp); 550 | brief = 0; 551 | 552 | while (1) { 553 | int opt = Clp_Next(clp); 554 | switch (opt) { 555 | 556 | case HELP_OPT: 557 | usage(); 558 | exit(0); 559 | break; 560 | 561 | case VERSION_OPT: 562 | printf("gifdiff (LCDF Gifsicle) %s\n", VERSION); 563 | printf("Copyright (C) 1998-2025 Eddie Kohler\n\ 564 | This is free software; see the source for copying conditions.\n\ 565 | There is NO warranty, not even for merchantability or fitness for a\n\ 566 | particular purpose.\n"); 567 | exit(0); 568 | break; 569 | 570 | case QUIET_OPT: 571 | brief = !clp->negated; 572 | break; 573 | 574 | case IGNORE_REDUNDANCY_OPT: 575 | ignore_redundancy = !clp->negated; 576 | break; 577 | 578 | case REDUNDANCY_OPT: 579 | ignore_redundancy = !!clp->negated; 580 | break; 581 | 582 | case IGNORE_BACKGROUND_OPT: 583 | ignore_background = !clp->negated; 584 | break; 585 | 586 | case BACKGROUND_OPT: 587 | ignore_background = !!clp->negated; 588 | break; 589 | 590 | case Clp_NotOption: 591 | if (how_many_inputs == 2) { 592 | error("too many file arguments\n"); 593 | goto bad_option; 594 | } 595 | inputp = (how_many_inputs == 0 ? &filename1 : &filename2); 596 | how_many_inputs++; 597 | if (strcmp(clp->vstr, "-") == 0) 598 | *inputp = 0; 599 | else 600 | *inputp = clp->vstr; 601 | break; 602 | 603 | bad_option: 604 | case Clp_BadOption: 605 | short_usage(); 606 | exit(1); 607 | break; 608 | 609 | case Clp_Done: 610 | goto done; 611 | 612 | } 613 | } 614 | 615 | done: 616 | 617 | if (how_many_inputs < 2) 618 | fatal_error("need exactly 2 file arguments\n"); 619 | if (filename1 == 0 && filename2 == 0) 620 | fatal_error("can%,t read both files from stdin\n"); 621 | 622 | gfs1 = read_stream(&filename1); 623 | gfs2 = read_stream(&filename2); 624 | 625 | status = (compare(gfs1, gfs2) == DIFFERENT); 626 | if (status == 1 && brief) 627 | printf("GIF files %s and %s differ\n", filename1, filename2); 628 | 629 | Gif_DeleteStream(gfs1); 630 | Gif_DeleteStream(gfs2); 631 | return status; 632 | } 633 | -------------------------------------------------------------------------------- /src/gifsicle.h: -------------------------------------------------------------------------------- 1 | /* gifsicle.h - Function declarations for gifsicle. 2 | Copyright (C) 1997-2025 Eddie Kohler, ekohler@gmail.com 3 | This file is part of gifsicle. 4 | 5 | Gifsicle is free software. It is distributed under the GNU Public License, 6 | version 2; you can copy, distribute, or alter it at will, as long 7 | as this notice is kept intact and this source code is made available. There 8 | is no warranty, express or implied. */ 9 | 10 | #ifndef GIFSICLE_H 11 | #define GIFSICLE_H 12 | #include 13 | #include 14 | #ifdef __GNUC__ 15 | #define NORETURN __attribute__ ((noreturn)) 16 | #define USED_ATTR __attribute__ ((used)) 17 | #else 18 | #define NORETURN 19 | #define USED_ATTR 20 | #endif 21 | 22 | typedef struct Gt_Frameset Gt_Frameset; 23 | typedef struct Gt_Crop Gt_Crop; 24 | typedef struct Gt_ColorTransform Gt_ColorTransform; 25 | 26 | #if ENABLE_THREADS 27 | #include 28 | extern pthread_mutex_t kd3_sort_lock; 29 | #endif 30 | 31 | typedef struct Gt_Frame { 32 | 33 | Gif_Stream *stream; 34 | Gif_Image *image; 35 | int use; 36 | 37 | const char *name; 38 | int no_name; 39 | Gif_Comment *comment; 40 | int no_comments; 41 | 42 | Gif_Color transparent; /* also background */ 43 | int interlacing; 44 | int left; 45 | int top; 46 | 47 | Gt_Crop *crop; 48 | int left_offset; 49 | int top_offset; 50 | 51 | int delay; 52 | int disposal; 53 | 54 | Gt_Frameset *nest; 55 | int explode_by_name; 56 | 57 | int no_extensions; 58 | int no_app_extensions; 59 | Gif_Extension *extensions; 60 | 61 | unsigned flip_horizontal: 1; 62 | unsigned flip_vertical: 1; 63 | unsigned info_flags: 3; 64 | unsigned position_is_offset: 1; 65 | unsigned total_crop: 1; 66 | unsigned rotation; 67 | 68 | const char *input_filename; 69 | 70 | } Gt_Frame; 71 | 72 | 73 | struct Gt_Frameset { 74 | int count; 75 | int cap; 76 | Gt_Frame *f; 77 | }; 78 | 79 | 80 | struct Gt_Crop { 81 | int ready; 82 | int transparent_edges; 83 | int spec_x; 84 | int spec_y; 85 | int spec_w; 86 | int spec_h; 87 | int x; 88 | int y; 89 | int w; 90 | int h; 91 | int left_offset; 92 | int top_offset; 93 | }; 94 | 95 | 96 | typedef void (*colormap_transform_func)(Gif_Colormap *, void *); 97 | 98 | struct Gt_ColorTransform { 99 | Gt_ColorTransform *prev; 100 | Gt_ColorTransform *next; 101 | colormap_transform_func func; 102 | void *data; 103 | }; 104 | 105 | 106 | typedef struct { 107 | 108 | const char *output_name; 109 | const char *active_output_name; 110 | 111 | int screen_mode; 112 | int screen_width; 113 | int screen_height; 114 | 115 | Gif_Color background; 116 | int loopcount; 117 | 118 | int colormap_size; 119 | Gif_Colormap *colormap_fixed; 120 | int colormap_fixed_exact; 121 | int colormap_algorithm; 122 | int colormap_needs_transparency; 123 | int dither_type; 124 | const uint8_t* dither_data; 125 | const char* dither_name; 126 | int colormap_gamma_type; 127 | double colormap_gamma; 128 | 129 | int optimizing; 130 | 131 | int scaling; 132 | int resize_width; 133 | int resize_height; 134 | int resize_flags; 135 | double scale_x; 136 | double scale_y; 137 | int scale_method; 138 | int scale_colors; 139 | 140 | int conserve_memory; 141 | 142 | } Gt_OutputData; 143 | 144 | extern Gt_OutputData active_output_data; 145 | extern Clp_Parser* clp; 146 | 147 | #define GT_SCALING_NONE 0 148 | #define GT_SCALING_RESIZE 1 149 | #define GT_SCALING_SCALE 2 150 | 151 | #define GT_RESIZE_FIT 1 152 | #define GT_RESIZE_FIT_DOWN 2 153 | #define GT_RESIZE_FIT_UP 4 154 | #define GT_RESIZE_MIN_DIMEN 8 155 | 156 | #define SCALE_METHOD_POINT 0 157 | #define SCALE_METHOD_BOX 1 158 | #define SCALE_METHOD_MIX 2 159 | #define SCALE_METHOD_CATROM 3 160 | #define SCALE_METHOD_LANCZOS2 4 161 | #define SCALE_METHOD_LANCZOS3 5 162 | #define SCALE_METHOD_MITCHELL 6 163 | 164 | #define GT_OPT_MASK 0xFFFF 165 | #define GT_OPT_KEEPEMPTY 0x10000 166 | 167 | 168 | /***** 169 | * helper 170 | **/ 171 | 172 | static inline int 173 | constrain(int low, int x, int high) 174 | { 175 | return x < low ? low : (x < high ? x : high); 176 | } 177 | 178 | 179 | /***** 180 | * error & verbose 181 | **/ 182 | extern const char *program_name; 183 | extern int verbosing; 184 | extern int error_count; 185 | extern int no_warnings; 186 | extern int thread_count; 187 | extern Gif_CompressInfo gif_write_info; 188 | 189 | void fatal_error(const char* format, ...) NORETURN; 190 | void warning(int need_file, const char* format, ...); 191 | void lwarning(const char* landmark, const char* format, ...); 192 | void error(int need_file, const char* format, ...); 193 | void lerror(const char* landmark, const char* format, ...); 194 | void clp_error_handler(Clp_Parser *clp, const char *clp_message); 195 | void usage(Clp_Parser* clp); 196 | void short_usage(Clp_Parser* clp); 197 | 198 | void verbose_open(char, const char *); 199 | void verbose_close(char); 200 | void verbose_endline(void); 201 | 202 | const char* debug_color_str(const Gif_Color* gfc); 203 | 204 | #define EXIT_OK 0 205 | #define EXIT_ERR 1 206 | #define EXIT_USER_ERR 1 207 | 208 | /***** 209 | * info &c 210 | **/ 211 | #define INFO_COLORMAPS 1 212 | #define INFO_EXTENSIONS 2 213 | #define INFO_SIZES 4 214 | void stream_info(FILE *f, Gif_Stream *gfs, const char *filename, int flags); 215 | void image_info(FILE *f, Gif_Stream *gfs, Gif_Image *gfi, int flags); 216 | 217 | char *explode_filename(const char *filename, int number, 218 | const char *name, int max_nimg); 219 | 220 | /***** 221 | * merging images 222 | **/ 223 | void unmark_colors(Gif_Colormap *); 224 | void unmark_colors_2(Gif_Colormap *); 225 | void mark_used_colors(Gif_Stream *gfs, Gif_Image *gfi, Gt_Crop *crop, 226 | int compress_immediately); 227 | int find_color_index(Gif_Color *c, int nc, Gif_Color *); 228 | int merge_colormap_if_possible(Gif_Colormap *, Gif_Colormap *); 229 | 230 | extern int warn_local_colormaps; 231 | void merge_stream(Gif_Stream *dest, Gif_Stream *src, int no_comments); 232 | void merge_comments(Gif_Comment *destc, Gif_Comment *srcc); 233 | Gif_Image* merge_image(Gif_Stream* dest, Gif_Stream* src, Gif_Image* srci, 234 | Gt_Frame* srcfr, int same_compressed_ok); 235 | 236 | void optimize_fragments(Gif_Stream *, int optimizeness, int huge_stream); 237 | 238 | /***** 239 | * image/colormap transformations 240 | **/ 241 | Gif_Colormap *read_colormap_file(const char *, FILE *); 242 | void apply_color_transforms(Gt_ColorTransform *, Gif_Stream *); 243 | 244 | typedef void (*color_transform_func)(Gif_Colormap *, void *); 245 | Gt_ColorTransform *append_color_transform 246 | (Gt_ColorTransform *list, color_transform_func, void *); 247 | Gt_ColorTransform *delete_color_transforms 248 | (Gt_ColorTransform *list, color_transform_func); 249 | 250 | void color_change_transformer(Gif_Colormap *, void *); 251 | Gt_ColorTransform *append_color_change 252 | (Gt_ColorTransform *list, Gif_Color, Gif_Color); 253 | 254 | void pipe_color_transformer(Gif_Colormap *, void *); 255 | 256 | void combine_crop(Gt_Crop *dstcrop, const Gt_Crop *srccrop, const Gif_Image *gfi); 257 | int crop_image(Gif_Image* gfi, Gt_Frame* fr, int preserve_total_crop); 258 | 259 | void flip_image(Gif_Image* gfi, Gt_Frame* fr, int is_vert); 260 | void rotate_image(Gif_Image* gfi, Gt_Frame* fr, int rotation); 261 | void resize_dimensions(int* w, int* h, double new_width, double new_height, 262 | int flags); 263 | void resize_stream(Gif_Stream* gfs, double new_width, double new_height, 264 | int flags, int method, int scale_colors); 265 | 266 | /***** 267 | * quantization 268 | **/ 269 | #define KC_GAMMA_SRGB 0 270 | #define KC_GAMMA_NUMERIC 1 271 | #define KC_GAMMA_OKLAB 2 272 | void kc_set_gamma(int type, double gamma); 273 | 274 | #define COLORMAP_DIVERSITY 0 275 | #define COLORMAP_BLEND_DIVERSITY 1 276 | #define COLORMAP_MEDIAN_CUT 2 277 | 278 | enum { 279 | dither_none = 0, dither_default, dither_floyd_steinberg, 280 | dither_ordered, dither_ordered_new, dither_atkinson 281 | }; 282 | int set_dither_type(Gt_OutputData* od, const char* name); 283 | void colormap_stream(Gif_Stream*, Gif_Colormap*, Gt_OutputData*, int allow_shrink); 284 | 285 | struct kchist; 286 | Gif_Colormap* colormap_blend_diversity(struct kchist* kch, Gt_OutputData* od); 287 | Gif_Colormap* colormap_flat_diversity(struct kchist* kch, Gt_OutputData* od); 288 | Gif_Colormap* colormap_median_cut(struct kchist* kch, Gt_OutputData* od); 289 | 290 | 291 | /***** 292 | * parsing stuff 293 | **/ 294 | extern int frame_spec_1; 295 | extern int frame_spec_2; 296 | extern char * frame_spec_name; 297 | extern int dimensions_x; 298 | extern int dimensions_y; 299 | extern int position_x; 300 | extern int position_y; 301 | extern Gif_Color parsed_color; 302 | extern Gif_Color parsed_color2; 303 | extern double parsed_scale_factor_x; 304 | extern double parsed_scale_factor_y; 305 | 306 | int parse_frame_spec(Clp_Parser*, const char*, int, void*); 307 | int parse_dimensions(Clp_Parser*, const char*, int, void*); 308 | int parse_position(Clp_Parser*, const char*, int, void*); 309 | int parse_scale_factor(Clp_Parser*, const char*, int, void*); 310 | int parse_color(Clp_Parser*, const char*, int, void*); 311 | int parse_rectangle(Clp_Parser*, const char*, int, void*); 312 | int parse_two_colors(Clp_Parser*, const char*, int, void*); 313 | 314 | extern Gif_Stream *input; 315 | extern const char *input_name; 316 | 317 | void input_stream(const char*); 318 | void input_done(void); 319 | void output_frames(void); 320 | 321 | /***** 322 | * stuff with frames 323 | **/ 324 | extern Gt_Frame def_frame; 325 | #define FRAME(fs, i) ((fs)->f[i]) 326 | 327 | Gt_Frameset * new_frameset(int initial_cap); 328 | Gt_Frame* add_frame(Gt_Frameset*, Gif_Stream*, Gif_Image*); 329 | void clear_def_frame_once_options(void); 330 | 331 | Gif_Stream * merge_frame_interval(Gt_Frameset*, int f1, int f2, 332 | Gt_OutputData*, int compress, int *huge); 333 | void clear_frameset(Gt_Frameset*, int from); 334 | void blank_frameset(Gt_Frameset*, int from, int to, int delete_ob); 335 | 336 | /***** 337 | * mode 338 | **/ 339 | #define BLANK_MODE 0 340 | #define MERGING 1 341 | #define BATCHING 2 342 | #define EXPLODING 3 343 | #define INFOING 4 344 | #define DELETING 5 345 | #define INSERTING 6 346 | #define FRAME_SELECTION_MODE_MASK 0x1F 347 | 348 | extern int mode; 349 | extern int nested_mode; 350 | 351 | #endif 352 | -------------------------------------------------------------------------------- /src/giftoc.c: -------------------------------------------------------------------------------- 1 | #if HAVE_CONFIG_H 2 | # include 3 | #endif 4 | #ifndef PATHNAME_SEPARATOR 5 | # define PATHNAME_SEPARATOR '/' 6 | #endif 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | int is_static = 1; 14 | int is_const = 1; 15 | 16 | static void * 17 | fmalloc(int size) 18 | { 19 | void *p = malloc(size); 20 | if (!p) { 21 | fputs("giftoc: Out of memory!\n", stderr); 22 | exit(1); 23 | } 24 | return p; 25 | } 26 | 27 | void 28 | print_reckless(FILE *f, char *gifrecname) 29 | { 30 | unsigned long size = 0; 31 | int c; 32 | int lasthex = 0; 33 | 34 | printf("\n%sGifRecord %s = { (unsigned char *)\"", 35 | is_static ? "static " : "", gifrecname); 36 | size = 0; 37 | c = getc(f); 38 | while (c != EOF) { 39 | 40 | if (size % 60 == 0) printf("\"\n \""); 41 | 42 | switch (c) { 43 | 44 | case '\\': 45 | fputs("\\\\", stdout); 46 | lasthex = 0; 47 | break; 48 | 49 | case '\"': 50 | fputs("\\\"", stdout); 51 | lasthex = 0; 52 | break; 53 | 54 | case '\b': 55 | fputs("\\b", stdout); 56 | lasthex = 0; 57 | break; 58 | 59 | case '\r': 60 | fputs("\\r", stdout); 61 | lasthex = 0; 62 | break; 63 | 64 | case '\n': 65 | fputs("\\n", stdout); 66 | lasthex = 0; 67 | break; 68 | 69 | case '\f': 70 | fputs("\\f", stdout); 71 | lasthex = 0; 72 | break; 73 | 74 | case '\t': 75 | fputs("\\t", stdout); 76 | lasthex = 0; 77 | break; 78 | 79 | case '0': case '1': case '2': case '3': case '4': case '5': case '6': 80 | case '7': 81 | if (lasthex) 82 | printf("\\%o", c); 83 | else 84 | putchar(c); 85 | break; 86 | 87 | default: 88 | if (isprint(c)) { 89 | putchar(c); 90 | lasthex = 0; 91 | } else { 92 | printf("\\%o", c); 93 | lasthex = 1; 94 | } 95 | break; 96 | 97 | } 98 | 99 | size++; 100 | c = getc(f); 101 | } 102 | 103 | printf("\",\n %lu\n};\n", size); 104 | } 105 | 106 | void 107 | print_unreckless(FILE *f, char *gifrecname) 108 | { 109 | unsigned long size = 0; 110 | int c; 111 | 112 | printf("\nstatic %sunsigned char %s_data[] = {", 113 | (is_const ? "const " : ""), gifrecname); 114 | size = 0; 115 | c = getc(f); 116 | while (c != EOF) { 117 | if (size % 20 == 0) printf("\n"); 118 | printf("%d,", c); 119 | size++; 120 | c = getc(f); 121 | } 122 | printf("};\n%s%sGif_Record %s = { %s_data, %lu };\n", 123 | (is_static ? "static " : ""), 124 | (is_const ? "const " : ""), 125 | gifrecname, gifrecname, size); 126 | } 127 | 128 | int 129 | main(int argc, char *argv[]) 130 | { 131 | int reckless = 0; 132 | int make_name = 0; 133 | const char *directory = ""; 134 | 135 | argc--, argv++; 136 | 137 | while (argv[0] && argv[0][0] == '-') { 138 | if (!strcmp(argv[0], "-reckless")) 139 | reckless = 1, argc--, argv++; 140 | else if (!strcmp(argv[0], "-static")) 141 | is_static = 1, argc--, argv++; 142 | else if (!strcmp(argv[0], "-extern")) 143 | is_static = 0, argc--, argv++; 144 | else if (!strcmp(argv[0], "-makename")) 145 | make_name = 1, argc--, argv++; 146 | else if (!strcmp(argv[0], "-nonconst")) 147 | is_const = 0, argc--, argv++; 148 | else if (!strcmp(argv[0], "-const")) 149 | is_const = 1, argc--, argv++; 150 | else if (!strcmp(argv[0], "-dir") && argc > 1) { 151 | directory = argv[1], argc -= 2, argv += 2; 152 | /* make sure directory is slash-terminated */ 153 | if (directory[ strlen(directory) - 1 ] != PATHNAME_SEPARATOR 154 | && directory[0]) { 155 | size_t len = strlen(directory) + 2; 156 | char *ndirectory = (char *)fmalloc(len); 157 | snprintf(ndirectory, len, "%s%c", directory, PATHNAME_SEPARATOR); 158 | directory = ndirectory; 159 | } 160 | } else 161 | break; 162 | } 163 | 164 | if ((!make_name && argc % 2 != 0) 165 | || argc < 1 166 | || (argv[0] && argv[0][0] == '-')) { 167 | fprintf(stderr, "\ 168 | usage: giftoc [OPTIONS] FILE NAME [FILE NAME...]\n\ 169 | or: giftoc -makename [OPTIONS] FILE [FILE...]\n\ 170 | OPTIONS are -reckless, -extern, -nonconst, -dir DIR\n"); 171 | exit(1); 172 | } 173 | 174 | if (!is_static) 175 | printf("#include \"config.h\"\n#include \n\n"); 176 | 177 | for (; argc > 0; argc--, argv++) { 178 | FILE *f; 179 | char *rec_name = 0; 180 | char *file_name = (char *)fmalloc(strlen(argv[0]) + strlen(directory) + 1); 181 | 182 | strcpy(file_name, directory); 183 | strcat(file_name, argv[0]); 184 | f = fopen(file_name, "rb"); 185 | 186 | if (!f) { 187 | fprintf(stderr, "%s: %s\n", file_name, strerror(errno)); 188 | goto done; 189 | } 190 | 191 | if (make_name) { 192 | char *sin, *sout; 193 | sin = strrchr(file_name, PATHNAME_SEPARATOR) + 1; 194 | if (!sin) sin = file_name; 195 | sout = rec_name = (char *)fmalloc(strlen(sin) + 2); 196 | if (isdigit(*sin)) *sout++ = 'N'; 197 | for (; *sin; sin++, sout++) 198 | if (isalnum(*sin)) 199 | *sout = *sin; 200 | else 201 | *sout = '_'; 202 | *sout = 0; 203 | 204 | } else { 205 | argv++, argc--; 206 | rec_name = argv[0]; 207 | } 208 | 209 | if (reckless) print_reckless(f, rec_name); 210 | else print_unreckless(f, rec_name); 211 | 212 | done: 213 | if (make_name) 214 | free(rec_name); 215 | free(file_name); 216 | if (f) 217 | fclose(f); 218 | } 219 | 220 | exit(0); 221 | 222 | } 223 | -------------------------------------------------------------------------------- /src/gifunopt.c: -------------------------------------------------------------------------------- 1 | /* gifunopt.c - Unoptimization function for the GIF library. 2 | Copyright (C) 1997-2025 Eddie Kohler, ekohler@gmail.com 3 | This file is part of the LCDF GIF library. 4 | 5 | The LCDF GIF library is free software. It is distributed under the GNU 6 | General Public License, version 2; you can copy, distribute, or alter it at 7 | will, as long as this notice is kept intact and this source code is made 8 | available. There is no warranty, express or implied. */ 9 | 10 | #if HAVE_CONFIG_H 11 | # include 12 | #endif 13 | #include 14 | #include 15 | #include 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | #define TRANSPARENT 256 21 | 22 | static void 23 | put_image_in_screen(Gif_Stream *gfs, Gif_Image *gfi, uint16_t *screen) 24 | { 25 | int transparent = gfi->transparent; 26 | unsigned x, y; 27 | unsigned w = gfi->width; 28 | unsigned h = gfi->height; 29 | if (gfi->left + w > gfs->screen_width) 30 | w = gfs->screen_width - gfi->left; 31 | if (gfi->top + h > gfs->screen_height) 32 | h = gfs->screen_height - gfi->top; 33 | 34 | for (y = 0; y < h; y++) { 35 | uint16_t *move = screen + (unsigned) gfs->screen_width * (y + gfi->top) + gfi->left; 36 | uint8_t *line = gfi->img[y]; 37 | for (x = 0; x < w; x++, move++, line++) 38 | if (*line != transparent) 39 | *move = *line; 40 | } 41 | } 42 | 43 | 44 | static void 45 | put_background_in_screen(Gif_Stream *gfs, Gif_Image *gfi, uint16_t *screen) 46 | { 47 | uint16_t solid; 48 | unsigned x, y; 49 | unsigned w = gfi->width; 50 | unsigned h = gfi->height; 51 | if (gfi->left + w > gfs->screen_width) w = gfs->screen_width - gfi->left; 52 | if (gfi->top + h > gfs->screen_height) h = gfs->screen_height - gfi->top; 53 | 54 | if (gfi->transparent < 0 && gfs->images[0]->transparent < 0 55 | && gfs->global && gfs->background < gfs->global->ncol) 56 | solid = gfs->background; 57 | else 58 | solid = TRANSPARENT; 59 | 60 | for (y = 0; y < h; y++) { 61 | uint16_t *move = screen + (unsigned) gfs->screen_width * (y + gfi->top) + gfi->left; 62 | for (x = 0; x < w; x++, move++) 63 | *move = solid; 64 | } 65 | } 66 | 67 | 68 | static int 69 | create_image_data(Gif_Stream *gfs, Gif_Image *gfi, uint16_t *screen, 70 | uint8_t *new_data, int *used_transparent) 71 | { 72 | int have[257]; 73 | int transparent = -1; 74 | unsigned pos, size = (unsigned) gfs->screen_width * (unsigned) gfs->screen_height; 75 | uint16_t *move; 76 | int i; 77 | 78 | /* mark colors used opaquely in the image */ 79 | assert(TRANSPARENT == 256); 80 | for (i = 0; i < 257; i++) 81 | have[i] = 0; 82 | for (pos = 0, move = screen; pos != size; ++pos, move++) 83 | have[*move] = 1; 84 | 85 | /* the new transparent color is a color unused in either */ 86 | if (have[TRANSPARENT]) { 87 | for (i = 0; i < 256 && transparent < 0; i++) 88 | if (!have[i]) 89 | transparent = i; 90 | if (transparent < 0) 91 | goto error; 92 | if (transparent >= gfs->global->ncol) { 93 | Gif_ReArray(gfs->global->col, Gif_Color, 256); 94 | if (!gfs->global->col) goto error; 95 | gfs->global->ncol = transparent + 1; 96 | } 97 | } 98 | 99 | /* map the wide image onto the new data */ 100 | *used_transparent = 0; 101 | for (pos = 0, move = screen; pos != size; ++pos, move++, new_data++) 102 | if (*move == TRANSPARENT) { 103 | *new_data = transparent; 104 | *used_transparent = 1; 105 | } else 106 | *new_data = *move; 107 | 108 | gfi->transparent = transparent; 109 | return 1; 110 | 111 | error: 112 | return 0; 113 | } 114 | 115 | 116 | static int 117 | unoptimize_image(Gif_Stream *gfs, Gif_Image *gfi, uint16_t *screen) 118 | { 119 | unsigned size = (unsigned) gfs->screen_width * (unsigned) gfs->screen_height; 120 | int used_transparent; 121 | uint8_t *new_data = Gif_NewArray(uint8_t, size); 122 | uint16_t *new_screen = screen; 123 | if (!new_data) return 0; 124 | 125 | /* Oops! May need to uncompress it */ 126 | Gif_UncompressImage(gfs, gfi); 127 | Gif_ReleaseCompressedImage(gfi); 128 | 129 | if (gfi->disposal == GIF_DISPOSAL_PREVIOUS) { 130 | new_screen = Gif_NewArray(uint16_t, size); 131 | if (!new_screen) return 0; 132 | memcpy(new_screen, screen, size * sizeof(uint16_t)); 133 | } 134 | 135 | put_image_in_screen(gfs, gfi, new_screen); 136 | if (!create_image_data(gfs, gfi, new_screen, new_data, &used_transparent)) { 137 | Gif_DeleteArray(new_data); 138 | return 0; 139 | } 140 | 141 | if (gfi->disposal == GIF_DISPOSAL_PREVIOUS) 142 | Gif_DeleteArray(new_screen); 143 | else if (gfi->disposal == GIF_DISPOSAL_BACKGROUND) 144 | put_background_in_screen(gfs, gfi, screen); 145 | 146 | gfi->left = 0; 147 | gfi->top = 0; 148 | gfi->width = gfs->screen_width; 149 | gfi->height = gfs->screen_height; 150 | gfi->disposal = used_transparent; 151 | Gif_SetUncompressedImage(gfi, new_data, Gif_Free, 0); 152 | 153 | return 1; 154 | } 155 | 156 | 157 | static int 158 | no_more_transparency(Gif_Image *gfi1, Gif_Image *gfi2) 159 | { 160 | int t1 = gfi1->transparent, t2 = gfi2->transparent, y; 161 | if (t1 < 0) 162 | return 1; 163 | for (y = 0; y < gfi1->height; ++y) { 164 | uint8_t *d1 = gfi1->img[y], *d2 = gfi2->img[y], *ed1 = d1 + gfi1->width; 165 | while (d1 < ed1) { 166 | if (*d1 == t1 && *d2 != t2) 167 | return 0; 168 | ++d1, ++d2; 169 | } 170 | } 171 | return 1; 172 | } 173 | 174 | 175 | int 176 | Gif_FullUnoptimize(Gif_Stream *gfs, int flags) 177 | { 178 | int ok = 1; 179 | int i; 180 | unsigned pos, size; 181 | uint16_t *screen; 182 | uint16_t background; 183 | Gif_Image *gfi; 184 | 185 | if (gfs->nimages < 1) return 1; 186 | for (i = 0; i < gfs->nimages; i++) 187 | if (gfs->images[i]->local) 188 | return 0; 189 | if (!gfs->global) 190 | return 0; 191 | 192 | Gif_CalculateScreenSize(gfs, 0); 193 | size = (unsigned) gfs->screen_width * (unsigned) gfs->screen_height; 194 | 195 | screen = Gif_NewArray(uint16_t, size); 196 | gfi = gfs->images[0]; 197 | if (gfi->transparent < 0 198 | && gfs->global && gfs->background < gfs->global->ncol) 199 | background = gfs->background; 200 | else 201 | background = TRANSPARENT; 202 | for (pos = 0; pos != size; ++pos) 203 | screen[pos] = background; 204 | 205 | for (i = 0; i < gfs->nimages; i++) 206 | if (!unoptimize_image(gfs, gfs->images[i], screen)) 207 | ok = 0; 208 | 209 | if (ok) { 210 | if (flags & GIF_UNOPTIMIZE_SIMPLEST_DISPOSAL) { 211 | /* set disposal based on use of transparency. 212 | If (every transparent pixel in frame i is also transparent in frame 213 | i - 1), then frame i - 1 gets disposal ASIS; otherwise, disposal 214 | BACKGROUND. */ 215 | for (i = 0; i < gfs->nimages; ++i) 216 | if (i == gfs->nimages - 1 217 | || no_more_transparency(gfs->images[i+1], gfs->images[i])) 218 | gfs->images[i]->disposal = GIF_DISPOSAL_NONE; 219 | else 220 | gfs->images[i]->disposal = GIF_DISPOSAL_BACKGROUND; 221 | } else 222 | for (i = 0; i < gfs->nimages; ++i) 223 | gfs->images[i]->disposal = GIF_DISPOSAL_BACKGROUND; 224 | } 225 | 226 | Gif_DeleteArray(screen); 227 | return ok; 228 | } 229 | 230 | int 231 | Gif_Unoptimize(Gif_Stream *gfs) 232 | { 233 | return Gif_FullUnoptimize(gfs, 0); 234 | } 235 | 236 | #ifdef __cplusplus 237 | } 238 | #endif 239 | -------------------------------------------------------------------------------- /src/kcolor.h: -------------------------------------------------------------------------------- 1 | /* kcolor.h - Color-oriented function declarations for gifsicle. 2 | Copyright (C) 2013-2021 Eddie Kohler, ekohler@gmail.com 3 | This file is part of gifsicle. 4 | 5 | Gifsicle is free software. It is distributed under the GNU Public License, 6 | version 2; you can copy, distribute, or alter it at will, as long 7 | as this notice is kept intact and this source code is made available. There 8 | is no warranty, express or implied. */ 9 | 10 | #ifndef GIFSICLE_KCOLOR_H 11 | #define GIFSICLE_KCOLOR_H 12 | #include 13 | #include 14 | 15 | /* kcolor: a 3D vector, each component has 15 bits of precision */ 16 | /* 15 bits means KC_MAX * KC_MAX always fits within a signed 32-bit 17 | integer, and a 3-D squared distance always fits within an unsigned 32-bit 18 | integer. */ 19 | #define KC_MAX 0x7FFF 20 | #define KC_WHOLE 0x8000 21 | #define KC_HALF 0x4000 22 | #define KC_QUARTER 0x2000 23 | #define KC_BITS 15 24 | typedef struct kcolor { 25 | int16_t a[3]; 26 | } kcolor; 27 | 28 | #undef min 29 | #undef max 30 | #define min(a, b) ((a) < (b) ? (a) : (b)) 31 | #define max(a, b) ((a) > (b) ? (a) : (b)) 32 | 33 | #define KC_CLAMPV(v) (max(0, min((v), KC_MAX))) 34 | 35 | typedef union kacolor { 36 | kcolor k; 37 | int16_t a[4]; 38 | #if HAVE_INT64_T 39 | int64_t q; /* to get better alignment */ 40 | #endif 41 | } kacolor; 42 | 43 | 44 | /* gamma_tables[0]: array of 256 gamma-conversion values 45 | gamma_tables[1]: array of 256 reverse gamma-conversion values */ 46 | extern const uint16_t* gamma_tables[2]; 47 | 48 | 49 | /* return the gamma transformation of `a0/a1/a2` [RGB] */ 50 | static inline kcolor kc_make8g(int a0, int a1, int a2) { 51 | kcolor kc; 52 | kc.a[0] = gamma_tables[0][a0]; 53 | kc.a[1] = gamma_tables[0][a1]; 54 | kc.a[2] = gamma_tables[0][a2]; 55 | return kc; 56 | } 57 | 58 | /* return the gamma transformation of `*gfc` */ 59 | static inline kcolor kc_makegfcg(const Gif_Color* gfc) { 60 | return kc_make8g(gfc->gfc_red, gfc->gfc_green, gfc->gfc_blue); 61 | } 62 | 63 | /* return the uncorrected representation of `a0/a1/a2` [RGB] */ 64 | static inline kcolor kc_make8ng(int a0, int a1, int a2) { 65 | kcolor kc; 66 | kc.a[0] = (a0 << 7) + (a0 >> 1); 67 | kc.a[1] = (a1 << 7) + (a1 >> 1); 68 | kc.a[2] = (a2 << 7) + (a2 >> 1); 69 | return kc; 70 | } 71 | 72 | /* return the kcolor representation of `*gfc` (no gamma transformation) */ 73 | static inline kcolor kc_makegfcng(const Gif_Color* gfc) { 74 | return kc_make8ng(gfc->gfc_red, gfc->gfc_green, gfc->gfc_blue); 75 | } 76 | 77 | /* return transparency */ 78 | static inline kacolor kac_transparent() { 79 | kacolor x; 80 | x.a[0] = x.a[1] = x.a[2] = x.a[3] = 0; 81 | return x; 82 | } 83 | 84 | /* return a hex color string definition for `x` */ 85 | const char* kc_debug_str(kcolor x); 86 | 87 | /* set `*x` to the reverse gamma transformation of `*x` */ 88 | kcolor kc_revgamma_transform(kcolor x); 89 | 90 | /* return the reverse gramma transformation of `*x` as a Gif_Color */ 91 | static inline Gif_Color kc_togfcg(kcolor x) { 92 | Gif_Color gfc; 93 | x = kc_revgamma_transform(x); 94 | gfc.gfc_red = (uint8_t) (x.a[0] >> 7); 95 | gfc.gfc_green = (uint8_t) (x.a[1] >> 7); 96 | gfc.gfc_blue = (uint8_t) (x.a[2] >> 7); 97 | gfc.haspixel = 0; 98 | return gfc; 99 | } 100 | 101 | 102 | /* return the squared Euclidean distance between `*x` and `*y` */ 103 | static inline uint32_t kc_distance(kcolor x, kcolor y) { 104 | /* It’s OK to use unsigned multiplication for this: the low 32 bits 105 | are the same either way. Unsigned avoids undefined behavior. */ 106 | uint32_t d0 = (uint32_t) x.a[0] - (uint32_t) y.a[0]; 107 | uint32_t d1 = (uint32_t) x.a[1] - (uint32_t) y.a[1]; 108 | uint32_t d2 = (uint32_t) x.a[2] - (uint32_t) y.a[2]; 109 | return d0 * d0 + d1 * d1 + d2 * d2; 110 | } 111 | 112 | /* return the luminance value for `*x`; result is between 0 and KC_MAX */ 113 | static inline int kc_luminance(kcolor kc) { 114 | return (55 * kc.a[0] + 183 * kc.a[1] + 19 * kc.a[2]) >> 8; 115 | } 116 | 117 | /* set `*x` to the grayscale version of `*x`, transformed by luminance */ 118 | static inline kcolor kc_luminance_transform(int a0, int a1, int a2) { 119 | /* For grayscale colormaps, use distance in luminance space instead of 120 | distance in RGB space. The weights for the R,G,B components in 121 | luminance space are 0.2126,0.7152,0.0722. (That's ITU primaries, which 122 | are compatible with sRGB; NTSC recommended our previous values, 123 | 0.299,0.587,0.114.) Using the proportional factors 55,183,19 we get a 124 | scaled gray value between 0 and 255 * 257; dividing by 256 gives us 125 | what we want. Thanks to Christian Kumpf, , for 126 | providing a patch. */ 127 | kcolor kc = kc_make8g(a0, a1, a2); 128 | kc.a[0] = kc.a[1] = kc.a[2] = kc_luminance(kc); 129 | return kc; 130 | } 131 | 132 | kcolor kc_oklab_transform(int a0, int a1, int a2); 133 | 134 | 135 | /* wkcolor: like kcolor, but components are 32 bits instead of 16 */ 136 | 137 | typedef struct wkcolor { 138 | int32_t a[3]; 139 | } wkcolor; 140 | 141 | static inline wkcolor wkc_zero(void) { 142 | wkcolor wkc; 143 | wkc.a[0] = wkc.a[1] = wkc.a[2] = 0; 144 | return wkc; 145 | } 146 | 147 | static inline int wkc_iszero(wkcolor wkc) { 148 | return wkc.a[0] == 0 && wkc.a[1] == 0 && wkc.a[2] == 0; 149 | } 150 | 151 | static inline kcolor kc_adjust(kcolor k, wkcolor delta) { 152 | k.a[0] = KC_CLAMPV((int32_t) k.a[0] + delta.a[0]); 153 | k.a[1] = KC_CLAMPV((int32_t) k.a[1] + delta.a[1]); 154 | k.a[2] = KC_CLAMPV((int32_t) k.a[2] + delta.a[2]); 155 | return k; 156 | } 157 | 158 | 159 | /* kd3_tree: kd-tree for 3 dimensions, indexing kcolors */ 160 | 161 | typedef struct kd3_tree kd3_tree; 162 | typedef struct kd3_treepos kd3_treepos; 163 | 164 | struct kd3_tree { 165 | kd3_treepos* tree; 166 | int ntree; 167 | int disabled; 168 | kcolor* ks; 169 | int nitems; 170 | int items_cap; 171 | int maxdepth; 172 | kcolor (*transform)(int, int, int); 173 | unsigned* xradius; 174 | }; 175 | 176 | /* initialize `kd3` with the given color `transform` (may be NULL) */ 177 | void kd3_init(kd3_tree* kd3, kcolor (*transform)(int, int, int)); 178 | 179 | /* free `kd3` */ 180 | void kd3_cleanup(kd3_tree* kd3); 181 | 182 | /* return the transformed color for 8-bit color `a0/a1/a2` (RGB) */ 183 | static inline kcolor kd3_make8g(kd3_tree* kd3, int a0, int a1, int a2) { 184 | return kd3->transform(a0, a1, a2); 185 | } 186 | 187 | /* return the transformed color for `*gfc` */ 188 | static inline kcolor kd3_makegfcg(kd3_tree* kd3, const Gif_Color* gfc) { 189 | return kd3_make8g(kd3, gfc->gfc_red, gfc->gfc_green, gfc->gfc_blue); 190 | } 191 | 192 | /* add the transformed color `k` to `*kd3` (do not apply `kd3->transform`). */ 193 | void kd3_add_transformed(kd3_tree* kd3, kcolor k); 194 | 195 | /* given 8-bit color `a0/a1/a2` (RGB), transform it by `kd3->transform` 196 | (e.g., apply gamma), and add it to `*kd3` */ 197 | static inline void kd3_add8g(kd3_tree* kd3, int a0, int a1, int a2) { 198 | kd3_add_transformed(kd3, kd3_make8g(kd3, a0, a1, a2)); 199 | } 200 | 201 | /* set `kd3->xradius`. given color `i`, `kd3->xradius[i]` is the square of the 202 | color's uniquely owned neighborhood. 203 | If `kc_distance(&kd3->ks[i], &k) < kd3->xradius[i]`, then 204 | `kd3_closest_transformed(kd3, &k) == i`. */ 205 | void kd3_build_xradius(kd3_tree* kd3); 206 | 207 | /* build the actual kd-tree for `kd3`. must be called before kd3_closest. */ 208 | void kd3_build(kd3_tree* kd3); 209 | 210 | /* kd3_init + kd3_add8g for all colors in `gfcm` + kd3_build */ 211 | void kd3_init_build(kd3_tree* kd3, kcolor (*transform)(int, int, int), 212 | const Gif_Colormap* gfcm); 213 | 214 | /* return the index of the color in `*kd3` closest to `k`. 215 | if `dist!=NULL`, store the distance from `k` to that index in `*dist`. */ 216 | int kd3_closest_transformed(kd3_tree* kd3, kcolor k, unsigned* dist); 217 | 218 | /* given 8-bit color `a0/a1/a2` (RGB), transform it by `kd3->transform` 219 | (e.g., apply gamma), and return the index of the color in `*kd3` 220 | closest to the result. */ 221 | static inline int kd3_closest8g(kd3_tree* kd3, int a0, int a1, int a2) { 222 | return kd3_closest_transformed(kd3, kd3_make8g(kd3, a0, a1, a2), NULL); 223 | } 224 | 225 | /* disable color index `i` in `*kd3`: it will never be returned by 226 | `kd3_closest*` */ 227 | static inline void kd3_disable(kd3_tree* kd3, int i) { 228 | assert((unsigned) i < (unsigned) kd3->nitems); 229 | assert(kd3->disabled < 0 || kd3->disabled == i); 230 | kd3->disabled = i; 231 | } 232 | 233 | /* enable all color indexes in `*kd3` */ 234 | static inline void kd3_enable_all(kd3_tree* kd3) { 235 | kd3->disabled = -1; 236 | } 237 | 238 | 239 | typedef uint32_t kchist_count_t; 240 | typedef struct kchistitem { 241 | kacolor ka; 242 | kchist_count_t count; 243 | } kchistitem; 244 | 245 | typedef struct kchist { 246 | kchistitem* h; 247 | int n; 248 | int capacity; 249 | } kchist; 250 | 251 | void kchist_init(kchist* kch); 252 | void kchist_cleanup(kchist* kch); 253 | void kchist_make(kchist* kch, Gif_Stream* gfs, uint32_t* ntransp); 254 | kchistitem* kchist_add(kchist* kch, kcolor color, kchist_count_t count); 255 | void kchist_compress(kchist* kch); 256 | 257 | static inline int kchistitem_compare_red(const void* va, const void* vb) { 258 | const kchistitem* a = (const kchistitem*) va; 259 | const kchistitem* b = (const kchistitem*) vb; 260 | return a->ka.a[0] - b->ka.a[0]; 261 | } 262 | 263 | static inline int kchistitem_compare_green(const void* va, const void* vb) { 264 | const kchistitem* a = (const kchistitem*) va; 265 | const kchistitem* b = (const kchistitem*) vb; 266 | return a->ka.a[1] - b->ka.a[1]; 267 | } 268 | 269 | static inline int kchistitem_compare_blue(const void* va, const void* vb) { 270 | const kchistitem* a = (const kchistitem*) va; 271 | const kchistitem* b = (const kchistitem*) vb; 272 | return a->ka.a[2] - b->ka.a[2]; 273 | } 274 | 275 | static inline int kchistitem_compare_popularity(const void* va, const void* vb) { 276 | const kchistitem* a = (const kchistitem*) va; 277 | const kchistitem* b = (const kchistitem*) vb; 278 | return a->count > b->count ? -1 : a->count != b->count; 279 | } 280 | 281 | 282 | typedef struct { 283 | kchist* kch; 284 | int* closest; 285 | uint32_t* min_dist; 286 | uint32_t* min_dither_dist; 287 | int* chosen; 288 | int nchosen; 289 | } kcdiversity; 290 | 291 | void kcdiversity_init(kcdiversity* div, kchist* kch, int dodither); 292 | void kcdiversity_cleanup(kcdiversity* div); 293 | int kcdiversity_find_popular(kcdiversity* div); 294 | int kcdiversity_find_diverse(kcdiversity* div, double ditherweight); 295 | int kcdiversity_choose(kcdiversity* div, int chosen, int dodither); 296 | 297 | 298 | #if HAVE_SIMD && HAVE_VECTOR_SIZE_VECTOR_TYPES 299 | typedef float float4 __attribute__((vector_size (sizeof(float) * 4))); 300 | typedef int int4 __attribute__((vector_size (sizeof(int) * 4))); 301 | #elif HAVE_SIMD && HAVE_EXT_VECTOR_TYPE_VECTOR_TYPES 302 | typedef float float4 __attribute__((ext_vector_type (4))); 303 | #else 304 | typedef float float4[4]; 305 | #endif 306 | 307 | typedef union scale_color { 308 | float4 a; 309 | } scale_color; 310 | 311 | static inline void sc_clear(scale_color* x) { 312 | x->a[0] = x->a[1] = x->a[2] = x->a[3] = 0; 313 | } 314 | 315 | static inline scale_color sc_makekc(kcolor k) { 316 | scale_color sc; 317 | sc.a[0] = k.a[0]; 318 | sc.a[1] = k.a[1]; 319 | sc.a[2] = k.a[2]; 320 | sc.a[3] = KC_MAX; 321 | return sc; 322 | } 323 | 324 | static inline scale_color sc_make(float a0, float a1, float a2, float a3) { 325 | scale_color sc; 326 | sc.a[0] = a0; 327 | sc.a[1] = a1; 328 | sc.a[2] = a2; 329 | sc.a[3] = a3; 330 | return sc; 331 | } 332 | 333 | #if HAVE_SIMD 334 | # define SCVEC_ADDV(sc, sc2) (sc).a += (sc2).a 335 | # define SCVEC_MULV(sc, sc2) (sc).a *= (sc2).a 336 | # define SCVEC_MULF(sc, f) (sc).a *= (f) 337 | # define SCVEC_DIVF(sc, f) (sc).a /= (f) 338 | # define SCVEC_ADDVxF(sc, sc2, f) (sc).a += (sc2).a * (f) 339 | # if HAVE___BUILTIN_SHUFFLEVECTOR 340 | # define SCVEC_ROT3(out, sc) do { (out).a = __builtin_shufflevector((sc).a, (sc).a, 1, 2, 0, 3); } while (0) 341 | # else 342 | # define SCVEC_ROT3(out, sc) do { int4 shufmask__ = {1, 2, 0, 3}; (out).a = __builtin_shuffle((sc).a, shufmask__); } while (0) 343 | # endif 344 | #else 345 | # define SCVEC_FOREACH(t) do { int k__; for (k__ = 0; k__ != 4; ++k__) { t; } } while (0) 346 | # define SCVEC_ADDV(sc, sc2) SCVEC_FOREACH((sc).a[k__] += (sc2).a[k__]) 347 | # define SCVEC_MULV(sc, sc2) SCVEC_FOREACH((sc).a[k__] *= (sc2).a[k__]) 348 | # define SCVEC_MULF(sc, f) SCVEC_FOREACH((sc).a[k__] *= (f)) 349 | # define SCVEC_DIVF(sc, f) SCVEC_FOREACH((sc).a[k__] /= (f)) 350 | # define SCVEC_ADDVxF(sc, sc2, f) SCVEC_FOREACH((sc).a[k__] += (sc2).a[k__] * (f)) 351 | # define SCVEC_ROT3(out, sc) do { float __a0 = (sc).a[0]; (out).a[0] = (sc).a[1]; (out).a[1] = (sc).a[2]; (out).a[2] = __a0; (out).a[3] = (sc).a[3]; } while (0) 352 | #endif 353 | 354 | #endif 355 | -------------------------------------------------------------------------------- /src/merge.c: -------------------------------------------------------------------------------- 1 | /* merge.c - Functions which actually combine and manipulate GIF image data. 2 | Copyright (C) 1997-2021 Eddie Kohler, ekohler@gmail.com 3 | This file is part of gifsicle. 4 | 5 | Gifsicle is free software. It is distributed under the GNU Public License, 6 | version 2; you can copy, distribute, or alter it at will, as long 7 | as this notice is kept intact and this source code is made available. There 8 | is no warranty, express or implied. */ 9 | 10 | #include 11 | #include "gifsicle.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | /* First merging stage: Mark the used colors in all colormaps. */ 20 | 21 | void 22 | unmark_colors(Gif_Colormap *gfcm) 23 | { 24 | int i; 25 | if (gfcm) 26 | for (i = 0; i < gfcm->ncol; i++) 27 | gfcm->col[i].haspixel = 0; 28 | } 29 | 30 | void 31 | unmark_colors_2(Gif_Colormap *gfcm) 32 | { 33 | int i; 34 | for (i = 0; i < gfcm->ncol; i++) { 35 | gfcm->col[i].pixel = 256; 36 | gfcm->col[i].haspixel = 0; 37 | } 38 | } 39 | 40 | 41 | void 42 | mark_used_colors(Gif_Stream *gfs, Gif_Image *gfi, Gt_Crop *crop, 43 | int compress_immediately) 44 | { 45 | Gif_Colormap *gfcm = gfi->local ? gfi->local : gfs->global; 46 | Gif_Color *col; 47 | int i, j, l, t, r, b, nleft, ncol, transp = gfi->transparent; 48 | 49 | /* There might not be a colormap. */ 50 | if (!gfcm) 51 | return; 52 | col = gfcm->col; 53 | ncol = gfcm->ncol; 54 | 55 | /* Mark color used for transparency. */ 56 | if (transp >= 0 && transp < ncol) 57 | col[transp].haspixel |= 2; 58 | 59 | /* Only mark colors until we've seen all of them. The left variable keeps 60 | track of how many are left. */ 61 | for (i = nleft = 0; i < ncol; ++i) 62 | if (!(col[i].haspixel & 1) && i != transp) 63 | ++nleft; 64 | if (nleft == 0) 65 | return; 66 | 67 | if (gfi->img || Gif_UncompressImage(gfs, gfi) == 2) 68 | compress_immediately = 0; 69 | 70 | /* Loop over every pixel (until we've seen all colors) */ 71 | if (crop) { 72 | Gt_Crop c; 73 | combine_crop(&c, crop, gfi); 74 | l = c.x; 75 | t = c.y; 76 | r = l + c.w; 77 | b = t + c.h; 78 | } else { 79 | l = t = 0; 80 | r = gfi->width; 81 | b = gfi->height; 82 | } 83 | 84 | for (j = t; j != b; ++j) { 85 | uint8_t *data = gfi->img[j] + l; 86 | for (i = l; i != r; ++i, ++data) 87 | if (*data < ncol && !(col[*data].haspixel & 1) && *data != transp) { 88 | col[*data].haspixel |= 1; 89 | --nleft; 90 | if (nleft == 0) 91 | goto done; 92 | } 93 | } 94 | 95 | done: 96 | if (compress_immediately > 0) 97 | Gif_ReleaseUncompressedImage(gfi); 98 | } 99 | 100 | 101 | int 102 | find_color_index(Gif_Color *c, int nc, Gif_Color *color) 103 | { 104 | int index; 105 | for (index = 0; index < nc; index++) 106 | if (GIF_COLOREQ(&c[index], color)) 107 | return index; 108 | return -1; 109 | } 110 | 111 | 112 | int 113 | merge_colormap_if_possible(Gif_Colormap *dest, Gif_Colormap *src) 114 | { 115 | Gif_Color *srccol; 116 | Gif_Color *destcol = dest->col; 117 | int destncol = dest->ncol; 118 | int dest_user_flags = dest->user_flags; 119 | int i, x; 120 | int trivial_map = 1; 121 | 122 | if (!src) 123 | return 1; 124 | 125 | srccol = src->col; 126 | for (i = 0; i < src->ncol; i++) { 127 | if (srccol[i].haspixel & 1) { 128 | /* Store an image color cell's mapping to the global colormap in 129 | its 'pixel' slot. This is useful caching: oftentimes many 130 | input frames will share a colormap */ 131 | int mapto = (srccol[i].pixel < 256 ? (int)srccol[i].pixel : -1); 132 | 133 | if (mapto == -1) 134 | mapto = find_color_index(destcol, destncol, &srccol[i]); 135 | 136 | if (mapto == -1 && destncol < 256) { 137 | /* add the color */ 138 | mapto = destncol; 139 | destcol[mapto] = srccol[i]; 140 | destncol++; 141 | } 142 | 143 | if (mapto == -1) 144 | /* check for a pure-transparent color */ 145 | for (x = 0; x < destncol; x++) 146 | if (destcol[x].haspixel == 2) { 147 | mapto = x; 148 | destcol[mapto] = srccol[i]; 149 | break; 150 | } 151 | 152 | if (mapto == -1) 153 | /* give up and require a local colormap */ 154 | goto local_colormap_required; 155 | 156 | assert(mapto >= 0 && mapto < destncol); 157 | assert(GIF_COLOREQ(&destcol[mapto], &srccol[i])); 158 | 159 | srccol[i].pixel = mapto; 160 | destcol[mapto].haspixel = 1; 161 | if (mapto != i) 162 | trivial_map = 0; 163 | 164 | } else if (srccol[i].haspixel & 2) { 165 | /* a dedicated transparent color; if trivial_map & at end of 166 | colormap insert it with haspixel == 2. (strictly not 167 | necessary; we do it to try to keep the map trivial.) */ 168 | if (trivial_map && i == destncol) { 169 | destcol[destncol] = srccol[i]; 170 | destncol++; 171 | } 172 | } 173 | } 174 | 175 | /* success! save new number of colors */ 176 | dest->ncol = destncol; 177 | dest->user_flags = dest_user_flags; 178 | return 1; 179 | 180 | /* failure: a local colormap is required */ 181 | local_colormap_required: 182 | if (warn_local_colormaps == 1) { 183 | static int context = 0; 184 | if (!context) { 185 | warning(1, "too many colors, using local colormaps\n" 186 | " (You may want to try %<--colors 256%>.)"); 187 | context = 1; 188 | } else 189 | warning(1, "too many colors, using local colormaps"); 190 | warn_local_colormaps = 2; 191 | } 192 | 193 | /* 9.Dec.1998 - This must have been a longstanding bug! We MUST clear 194 | the cached mappings of any pixels in the source colormap we 195 | assigned this time through, since we are throwing those colors 196 | away. We assigned it this time through if the cached mapping is >= 197 | dest->ncol. */ 198 | for (x = 0; x < i; x++) 199 | if ((srccol[x].haspixel & 1) && srccol[x].pixel >= (uint32_t)dest->ncol) 200 | srccol[x].pixel = 256; 201 | 202 | return 0; 203 | } 204 | 205 | 206 | void 207 | merge_stream(Gif_Stream *dest, Gif_Stream *src, int no_comments) 208 | { 209 | int i; 210 | assert(dest->global); 211 | 212 | /* unmark colors in global and local colormaps -- 12/9 */ 213 | if (src->global) 214 | unmark_colors_2(src->global); 215 | for (i = 0; i < src->nimages; i++) 216 | if (src->images[i]->local) 217 | unmark_colors_2(src->images[i]->local); 218 | 219 | if (dest->loopcount < 0) 220 | dest->loopcount = src->loopcount; 221 | 222 | if (src->end_comment && !no_comments) { 223 | if (!dest->end_comment) 224 | dest->end_comment = Gif_NewComment(); 225 | merge_comments(dest->end_comment, src->end_comment); 226 | } 227 | } 228 | 229 | 230 | void 231 | merge_comments(Gif_Comment *destc, Gif_Comment *srcc) 232 | { 233 | int i; 234 | for (i = 0; i < srcc->count; i++) 235 | Gif_AddComment(destc, srcc->str[i], srcc->len[i]); 236 | } 237 | 238 | 239 | static void merge_image_input_colors(uint8_t* inused, const Gif_Image* srci) { 240 | int i, x, y, nleft = Gif_ImageColorBound(srci); 241 | for (i = 0; i != 256; ++i) 242 | inused[i] = 0; 243 | for (y = 0; y != srci->height && nleft > 0; ++y) { 244 | const uint8_t* data = srci->img[y]; 245 | for (x = 0; x != srci->width; ++x, ++data) { 246 | nleft -= 1 - inused[*data]; 247 | inused[*data] = 1; 248 | } 249 | } 250 | if (srci->transparent >= 0) 251 | inused[srci->transparent] = 0; 252 | } 253 | 254 | 255 | Gif_Image * 256 | merge_image(Gif_Stream *dest, Gif_Stream *src, Gif_Image *srci, 257 | Gt_Frame* srcfr, int same_compressed_ok) 258 | { 259 | Gif_Colormap *imagecm; 260 | int imagecm_ncol; 261 | int i; 262 | Gif_Colormap *localcm = 0; 263 | Gif_Colormap *destcm = dest->global; 264 | 265 | uint8_t map[256]; /* map[input pixel value] == output pixval */ 266 | int trivial_map; /* does the map take input pixval --> the same 267 | pixel value for all colors in the image? */ 268 | uint8_t inused[256]; /* inused[input pival] == 1 iff used */ 269 | uint8_t used[256]; /* used[output pixval K] == 1 iff K was used 270 | in the image */ 271 | 272 | 273 | Gif_Image *desti; 274 | 275 | /* mark colors that were actually used in this image */ 276 | imagecm = srci->local ? srci->local : src->global; 277 | imagecm_ncol = imagecm ? imagecm->ncol : 0; 278 | merge_image_input_colors(inused, srci); 279 | for (i = imagecm_ncol; i != 256; ++i) 280 | if (inused[i]) { 281 | lwarning(srcfr->input_filename, "some colors undefined by colormap"); 282 | break; 283 | } 284 | 285 | /* map[old_pixel_value] == new_pixel_value */ 286 | for (i = 0; i < 256; i++) 287 | map[i] = used[i] = 0; 288 | 289 | /* Merge the colormap */ 290 | if (merge_colormap_if_possible(dest->global, imagecm)) { 291 | /* Create 'map' and 'used' for global colormap. */ 292 | for (i = 0; i != imagecm_ncol; ++i) 293 | if (inused[i]) 294 | map[i] = imagecm->col[i].pixel; 295 | 296 | } else { 297 | /* Need a local colormap. */ 298 | destcm = localcm = Gif_NewFullColormap(0, 256); 299 | for (i = 0; i != imagecm_ncol; ++i) 300 | if (inused[i]) { 301 | map[i] = localcm->ncol; 302 | localcm->col[localcm->ncol] = imagecm->col[i]; 303 | ++localcm->ncol; 304 | } 305 | } 306 | 307 | trivial_map = 1; 308 | for (i = 0; i != 256; ++i) 309 | if (inused[i]) { 310 | used[map[i]] = 1; 311 | trivial_map = trivial_map && map[i] == i; 312 | } 313 | 314 | /* Decide on a transparent index */ 315 | if (srci->transparent >= 0) { 316 | int found_transparent = -1; 317 | 318 | /* try to keep the map trivial -- prefer same transparent index */ 319 | if (trivial_map && !used[srci->transparent]) 320 | found_transparent = srci->transparent; 321 | else 322 | for (i = destcm->ncol - 1; i >= 0; i--) 323 | if (!used[i]) 324 | found_transparent = i; 325 | 326 | /* 1.Aug.1999 - Allow for the case that the transparent index is bigger 327 | than the number of colors we've created thus far. */ 328 | if (found_transparent < 0 || found_transparent >= destcm->ncol) { 329 | Gif_Color *c; 330 | found_transparent = destcm->ncol; 331 | /* 1.Aug.1999 - Don't update destcm->ncol -- we want the output colormap 332 | to be as small as possible. */ 333 | c = &destcm->col[found_transparent]; 334 | if (imagecm && srci->transparent < imagecm->ncol) 335 | *c = imagecm->col[srci->transparent]; 336 | c->haspixel = 2; 337 | assert(c->haspixel == 2 && found_transparent < 256); 338 | } 339 | 340 | map[srci->transparent] = found_transparent; 341 | if (srci->transparent != found_transparent) trivial_map = 0; 342 | } 343 | 344 | assert(destcm->ncol <= 256); 345 | /* Make the new image. */ 346 | desti = Gif_NewImage(); 347 | 348 | desti->identifier = Gif_CopyString(srci->identifier); 349 | if (srci->transparent > -1) 350 | desti->transparent = map[srci->transparent]; 351 | desti->delay = srci->delay; 352 | desti->disposal = srci->disposal; 353 | desti->left = srci->left; 354 | desti->top = srci->top; 355 | desti->interlace = srci->interlace; 356 | 357 | desti->width = srci->width; 358 | desti->height = srci->height; 359 | desti->local = localcm; 360 | 361 | if (trivial_map && same_compressed_ok && srci->compressed 362 | && !srci->compressed_errors) { 363 | desti->compressed_len = srci->compressed_len; 364 | desti->compressed = Gif_NewArray(uint8_t, srci->compressed_len); 365 | desti->free_compressed = Gif_Free; 366 | memcpy(desti->compressed, srci->compressed, srci->compressed_len); 367 | } else { 368 | int i, j; 369 | Gif_CreateUncompressedImage(desti, desti->interlace); 370 | 371 | if (trivial_map) 372 | for (j = 0; j < desti->height; j++) 373 | memcpy(desti->img[j], srci->img[j], desti->width); 374 | 375 | else 376 | for (j = 0; j < desti->height; j++) { 377 | uint8_t *srcdata = srci->img[j]; 378 | uint8_t *destdata = desti->img[j]; 379 | for (i = 0; i < desti->width; i++, srcdata++, destdata++) 380 | *destdata = map[*srcdata]; 381 | } 382 | } 383 | 384 | /* comments and extensions */ 385 | if (srci->comment) { 386 | desti->comment = Gif_NewComment(); 387 | merge_comments(desti->comment, srci->comment); 388 | } 389 | if (srci->extension_list && !srcfr->no_extensions) { 390 | Gif_Extension* gfex; 391 | for (gfex = srci->extension_list; gfex; gfex = gfex->next) 392 | if (gfex->kind != 255 || !srcfr->no_app_extensions) 393 | Gif_AddExtension(dest, desti, Gif_CopyExtension(gfex)); 394 | } 395 | while (srcfr->extensions) { 396 | Gif_Extension* next = srcfr->extensions->next; 397 | Gif_AddExtension(dest, desti, srcfr->extensions); 398 | srcfr->extensions = next; 399 | } 400 | 401 | Gif_AddImage(dest, desti); 402 | return desti; 403 | } 404 | -------------------------------------------------------------------------------- /src/optimize.c: -------------------------------------------------------------------------------- 1 | /* optimize.c - Functions to optimize animated GIFs. 2 | Copyright (C) 1997-2021 Eddie Kohler, ekohler@gmail.com 3 | This file is part of gifsicle. 4 | 5 | Gifsicle is free software. It is distributed under the GNU Public License, 6 | version 2; you can copy, distribute, or alter it at will, as long 7 | as this notice is kept intact and this source code is made available. There 8 | is no warranty, express or implied. */ 9 | 10 | #include 11 | #include "gifsicle.h" 12 | #include "kcolor.h" 13 | #include 14 | #include 15 | 16 | typedef int32_t penalty_type; 17 | 18 | typedef struct { 19 | int left; 20 | int top; 21 | int width; 22 | int height; 23 | } Gif_OptBounds; 24 | 25 | typedef struct { 26 | uint16_t left; 27 | uint16_t top; 28 | uint16_t width; 29 | uint16_t height; 30 | uint32_t size; 31 | uint8_t disposal; 32 | int transparent; 33 | uint8_t *needed_colors; 34 | unsigned required_color_count; 35 | int32_t active_penalty; 36 | int32_t global_penalty; 37 | int32_t colormap_penalty; 38 | Gif_Image *new_gfi; 39 | } Gif_OptData; 40 | 41 | /* Screen width and height */ 42 | static int screen_width; 43 | static int screen_height; 44 | 45 | /* Colormap containing all colors in the image. May have >256 colors */ 46 | static Gif_Colormap *all_colormap; 47 | /* Histogram so we can find colors quickly */ 48 | static kchist all_colormap_hist; 49 | 50 | /* The old global colormap, or a fake one we created if necessary */ 51 | static Gif_Colormap *in_global_map; 52 | 53 | /* The new global colormap */ 54 | static Gif_Colormap *out_global_map; 55 | 56 | #define TRANSP (0) 57 | #define NOT_IN_OUT_GLOBAL (256) 58 | static unsigned background; 59 | static int image_index; 60 | 61 | static penalty_type *permuting_sort_values; 62 | 63 | #define REQUIRED 2 64 | #define REPLACE_TRANSP 1 65 | 66 | 67 | /***** 68 | * SIMPLE HELPERS 69 | * new and delete optimize data; and colormap_combine; and sorting permutations 70 | **/ 71 | 72 | Gif_OptData * 73 | new_opt_data(void) 74 | { 75 | Gif_OptData *od = Gif_New(Gif_OptData); 76 | od->needed_colors = 0; 77 | od->global_penalty = 1; 78 | return od; 79 | } 80 | 81 | void 82 | delete_opt_data(Gif_OptData *od) 83 | { 84 | if (!od) return; 85 | Gif_DeleteArray(od->needed_colors); 86 | Gif_Delete(od); 87 | } 88 | 89 | 90 | /* all_colormap_add: Ensure that each color in 'src' is represented in 91 | 'all_colormap'. For each color 'i' in 'src', src->col[i].pixel == some j 92 | so that GIF_COLOREQ(&src->col[i], &all_colormap->col[j]). 93 | all_colormap->col[0] is reserved for transparency; no source color will 94 | be mapped to it. */ 95 | 96 | static void all_colormap_add(const Gif_Colormap* src) { 97 | int i; 98 | 99 | /* expand dst->col if necessary. This might change dst->col */ 100 | if (all_colormap->ncol + src->ncol >= all_colormap->capacity) { 101 | all_colormap->capacity *= 2; 102 | Gif_ReArray(all_colormap->col, Gif_Color, all_colormap->capacity); 103 | } 104 | 105 | for (i = 0; i < src->ncol; ++i) { 106 | kchistitem* khi = kchist_add(&all_colormap_hist, 107 | kc_makegfcng(&src->col[i]), 0); 108 | if (!khi->count) { 109 | all_colormap->col[all_colormap->ncol] = src->col[i]; 110 | all_colormap->col[all_colormap->ncol].pixel = 0; 111 | khi->count = all_colormap->ncol; 112 | ++all_colormap->ncol; 113 | } 114 | src->col[i].pixel = khi->count; 115 | } 116 | } 117 | 118 | 119 | /***** 120 | * MANIPULATING IMAGE AREAS 121 | **/ 122 | 123 | static Gif_OptBounds 124 | safe_bounds(Gif_Image *area) 125 | { 126 | /* Returns bounds constrained to lie within the screen. */ 127 | Gif_OptBounds b; 128 | b.left = constrain(0, area->left, screen_width); 129 | b.top = constrain(0, area->top, screen_height); 130 | b.width = constrain(0, area->left + area->width, screen_width) - b.left; 131 | b.height = constrain(0, area->top + area->height, screen_height) - b.top; 132 | return b; 133 | } 134 | 135 | 136 | /***** 137 | * FIND THE SMALLEST BOUNDING RECTANGLE ENCLOSING ALL CHANGES 138 | **/ 139 | 140 | /* fix_difference_bounds: make sure the image isn't 0x0. */ 141 | 142 | static void 143 | fix_difference_bounds(Gif_OptData *bounds) 144 | { 145 | if (bounds->width == 0 || bounds->height == 0) { 146 | bounds->top = 0; 147 | bounds->left = 0; 148 | bounds->width = 1; 149 | bounds->height = 1; 150 | } 151 | /* assert that image lies completely within screen */ 152 | assert(bounds->top < screen_height && bounds->left < screen_width 153 | && bounds->top + bounds->height <= screen_height 154 | && bounds->left + bounds->width <= screen_width); 155 | } 156 | 157 | 158 | /***** 159 | * CALCULATE OUTPUT GLOBAL COLORMAP 160 | **/ 161 | 162 | static void 163 | increment_penalties(Gif_OptData *opt, penalty_type *penalty, int32_t delta) 164 | { 165 | int i; 166 | int all_ncol = all_colormap->ncol; 167 | uint8_t *need = opt->needed_colors; 168 | for (i = 1; i < all_ncol; i++) 169 | if (need[i] == REQUIRED) 170 | penalty[i] += delta; 171 | } 172 | 173 | 174 | /***** 175 | * CREATE COLOR MAPPING FOR A PARTICULAR IMAGE 176 | **/ 177 | 178 | /* sort_colormap_permutation_rgb: for canonicalizing local colormaps by 179 | arranging them in RGB order */ 180 | 181 | static int 182 | colormap_rgb_permutation_sorter(const void *v1, const void *v2) 183 | { 184 | const Gif_Color *col1 = (const Gif_Color *)v1; 185 | const Gif_Color *col2 = (const Gif_Color *)v2; 186 | int value1 = (col1->gfc_red << 16) | (col1->gfc_green << 8) | col1->gfc_blue; 187 | int value2 = (col2->gfc_red << 16) | (col2->gfc_green << 8) | col2->gfc_blue; 188 | return value1 - value2; 189 | } 190 | 191 | 192 | /* prepare_colormap_map: Create and return an array of bytes mapping from 193 | global pixel values to pixel values for this image. It may add colormap 194 | cells to 'into'; if there isn't enough room in 'into', it will return 0. It 195 | sets the 'transparent' field of 'gfi->optdata', but otherwise doesn't 196 | change or read it at all. */ 197 | 198 | static uint8_t * 199 | prepare_colormap_map(Gif_Image *gfi, Gif_Colormap *into, uint8_t *need) 200 | { 201 | int i; 202 | int is_global = (into == out_global_map); 203 | 204 | int all_ncol = all_colormap->ncol; 205 | Gif_Color *all_col = all_colormap->col; 206 | 207 | int ncol = into->ncol; 208 | Gif_Color *col = into->col; 209 | 210 | uint8_t *map = Gif_NewArray(uint8_t, all_ncol); 211 | uint8_t into_used[256]; 212 | 213 | /* keep track of which pixel indices in 'into' have been used; initially, 214 | all unused */ 215 | for (i = 0; i < 256; i++) 216 | into_used[i] = 0; 217 | 218 | /* go over all non-transparent global pixels which MUST appear 219 | (need[P]==REQUIRED) and place them in 'into' */ 220 | for (i = 1; i < all_ncol; i++) { 221 | int val; 222 | if (need[i] != REQUIRED) 223 | continue; 224 | 225 | /* fail if a needed pixel isn't in the global map */ 226 | if (is_global) { 227 | val = all_col[i].pixel; 228 | if (val >= ncol) 229 | goto error; 230 | } else { 231 | /* always place colors in a local colormap */ 232 | if (ncol == 256) 233 | goto error; 234 | val = ncol; 235 | col[val] = all_col[i]; 236 | col[val].pixel = i; 237 | ncol++; 238 | } 239 | 240 | map[i] = val; 241 | into_used[val] = 1; 242 | } 243 | 244 | if (!is_global) { 245 | qsort(col, ncol, sizeof(Gif_Color), colormap_rgb_permutation_sorter); 246 | for (i = 0; i < ncol; ++i) 247 | map[col[i].pixel] = i; 248 | } 249 | 250 | /* now check for transparency */ 251 | gfi->transparent = -1; 252 | if (need[TRANSP]) { 253 | int transparent = -1; 254 | 255 | /* first, look for an unused index in 'into'. Pick the lowest one: the 256 | lower transparent index we get, the more likely we can shave a bit off 257 | min_code_bits later, thus saving space */ 258 | for (i = 0; i < ncol; i++) 259 | if (!into_used[i]) { 260 | transparent = i; 261 | break; 262 | } 263 | 264 | /* otherwise, [1.Aug.1999] use a fake slot for the purely transparent 265 | color. Don't actually enter the transparent color into the colormap -- 266 | we might be able to output a smaller colormap! If there's no room for 267 | it, give up */ 268 | if (transparent < 0) { 269 | if (ncol < 256) { 270 | transparent = ncol; 271 | /* 1.Aug.1999 - don't increase ncol */ 272 | col[ncol] = all_col[TRANSP]; 273 | } else 274 | goto error; 275 | } 276 | 277 | /* change mapping */ 278 | map[TRANSP] = transparent; 279 | for (i = 1; i < all_ncol; i++) 280 | if (need[i] == REPLACE_TRANSP) 281 | map[i] = transparent; 282 | 283 | gfi->transparent = transparent; 284 | } 285 | 286 | /* If we get here, it worked! Commit state changes (the number of color 287 | cells in 'into') and return the map. */ 288 | into->ncol = ncol; 289 | return map; 290 | 291 | error: 292 | /* If we get here, it failed! Return 0 and don't change global state. */ 293 | Gif_DeleteArray(map); 294 | return 0; 295 | } 296 | 297 | 298 | /* prepare_colormap: make a colormap up from the image data by fitting any 299 | used colors into a colormap. Returns a map from global color index to index 300 | in this image's colormap. May set a local colormap on 'gfi'. */ 301 | 302 | static uint8_t * 303 | prepare_colormap(Gif_Image *gfi, uint8_t *need) 304 | { 305 | uint8_t *map; 306 | 307 | /* try to map pixel values into the global colormap */ 308 | Gif_DeleteColormap(gfi->local); 309 | gfi->local = 0; 310 | map = prepare_colormap_map(gfi, out_global_map, need); 311 | 312 | if (!map) { 313 | /* that didn't work; add a local colormap. */ 314 | gfi->local = Gif_NewFullColormap(0, 256); 315 | map = prepare_colormap_map(gfi, gfi->local, need); 316 | } 317 | 318 | return map; 319 | } 320 | 321 | 322 | /***** 323 | * INITIALIZATION AND FINALIZATION 324 | **/ 325 | 326 | static int 327 | initialize_optimizer(Gif_Stream *gfs) 328 | { 329 | int i; 330 | 331 | if (gfs->nimages < 1) 332 | return 0; 333 | 334 | /* combine colormaps */ 335 | all_colormap = Gif_NewFullColormap(1, 384); 336 | all_colormap->col[0].gfc_red = 255; 337 | all_colormap->col[0].gfc_green = 255; 338 | all_colormap->col[0].gfc_blue = 255; 339 | 340 | in_global_map = gfs->global; 341 | if (!in_global_map) { 342 | Gif_Color *col; 343 | in_global_map = Gif_NewFullColormap(256, 256); 344 | col = in_global_map->col; 345 | for (i = 0; i < 256; i++, col++) 346 | col->gfc_red = col->gfc_green = col->gfc_blue = i; 347 | } 348 | 349 | { 350 | int any_globals = 0; 351 | int first_transparent = -1; 352 | 353 | kchist_init(&all_colormap_hist); 354 | for (i = 0; i < gfs->nimages; i++) { 355 | Gif_Image *gfi = gfs->images[i]; 356 | if (gfi->local) 357 | all_colormap_add(gfi->local); 358 | else 359 | any_globals = 1; 360 | if (gfi->transparent >= 0 && first_transparent < 0) 361 | first_transparent = i; 362 | } 363 | if (any_globals) 364 | all_colormap_add(in_global_map); 365 | kchist_cleanup(&all_colormap_hist); 366 | 367 | /* try and maintain transparency's pixel value */ 368 | if (first_transparent >= 0) { 369 | Gif_Image *gfi = gfs->images[first_transparent]; 370 | Gif_Colormap *gfcm = gfi->local ? gfi->local : gfs->global; 371 | all_colormap->col[TRANSP] = gfcm->col[gfi->transparent]; 372 | } 373 | } 374 | 375 | /* find screen_width and screen_height, and clip all images to screen */ 376 | Gif_CalculateScreenSize(gfs, 0); 377 | screen_width = gfs->screen_width; 378 | screen_height = gfs->screen_height; 379 | for (i = 0; i < gfs->nimages; i++) 380 | Gif_ClipImage(gfs->images[i], 0, 0, screen_width, screen_height); 381 | 382 | /* choose background */ 383 | if (gfs->images[0]->transparent < 0 384 | && gfs->global && gfs->background < in_global_map->ncol) 385 | background = in_global_map->col[gfs->background].pixel; 386 | else 387 | background = TRANSP; 388 | 389 | return 1; 390 | } 391 | 392 | static void 393 | finalize_optimizer(Gif_Stream *gfs, int optimize_flags) 394 | { 395 | int i; 396 | 397 | if (background == TRANSP) 398 | gfs->background = (uint8_t)gfs->images[0]->transparent; 399 | 400 | /* 11.Mar.2010 - remove entirely transparent frames. */ 401 | for (i = 1; i < gfs->nimages && !(optimize_flags & GT_OPT_KEEPEMPTY); ++i) { 402 | Gif_Image *gfi = gfs->images[i]; 403 | if (gfi->width == 1 && gfi->height == 1 && gfi->transparent >= 0 404 | && !gfi->identifier && !gfi->comment 405 | && (gfi->disposal == GIF_DISPOSAL_ASIS 406 | || gfi->disposal == GIF_DISPOSAL_NONE 407 | || gfi->disposal == GIF_DISPOSAL_PREVIOUS) 408 | && gfi->delay && gfs->images[i-1]->delay) { 409 | Gif_UncompressImage(gfs, gfi); 410 | if (gfi->img[0][0] == gfi->transparent 411 | && (gfs->images[i-1]->disposal == GIF_DISPOSAL_ASIS 412 | || gfs->images[i-1]->disposal == GIF_DISPOSAL_NONE)) { 413 | gfs->images[i-1]->delay += gfi->delay; 414 | Gif_DeleteImage(gfi); 415 | memmove(&gfs->images[i], &gfs->images[i+1], sizeof(Gif_Image *) * (gfs->nimages - i - 1)); 416 | --gfs->nimages; 417 | --i; 418 | } 419 | } 420 | } 421 | 422 | /* 10.Dec.1998 - prefer GIF_DISPOSAL_NONE to GIF_DISPOSAL_ASIS. This is 423 | semantically "wrong" -- it's better to set the disposal explicitly than 424 | rely on default behavior -- but will result in smaller GIF files, since 425 | the graphic control extension can be left off in many cases. */ 426 | for (i = 0; i < gfs->nimages; i++) 427 | if (gfs->images[i]->disposal == GIF_DISPOSAL_ASIS 428 | && gfs->images[i]->delay == 0 429 | && gfs->images[i]->transparent < 0) 430 | gfs->images[i]->disposal = GIF_DISPOSAL_NONE; 431 | 432 | Gif_DeleteColormap(in_global_map); 433 | Gif_DeleteColormap(all_colormap); 434 | } 435 | 436 | 437 | /* two versions of the optimization template */ 438 | #define palindex_type uint16_t 439 | #define X(t) t ## 16 440 | #include "opttemplate.c" 441 | #undef palindex_type 442 | #undef X 443 | 444 | #define palindex_type uint32_t 445 | #define X(t) t ## 32 446 | #include "opttemplate.c" 447 | 448 | /* the interface function! */ 449 | 450 | void 451 | optimize_fragments(Gif_Stream *gfs, int optimize_flags, int huge_stream) 452 | { 453 | if (!initialize_optimizer(gfs)) 454 | return; 455 | if ((unsigned) all_colormap->ncol >= 0xFFFF) { 456 | create_subimages32(gfs, optimize_flags, !huge_stream); 457 | create_out_global_map32(gfs); 458 | create_new_image_data32(gfs, optimize_flags); 459 | finalize_optimizer_data32(); 460 | } else { 461 | create_subimages16(gfs, optimize_flags, !huge_stream); 462 | create_out_global_map16(gfs); 463 | create_new_image_data16(gfs, optimize_flags); 464 | finalize_optimizer_data16(); 465 | } 466 | finalize_optimizer(gfs, optimize_flags); 467 | } 468 | -------------------------------------------------------------------------------- /src/strerror.c: -------------------------------------------------------------------------------- 1 | /* Some operating systems don't have strerror. 2 | This file provides a definition which David Mazieres 3 | assures me works. */ 4 | 5 | #if HAVE_CONFIG_H 6 | # include 7 | #endif 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | char * 13 | strerror(int errno) 14 | { 15 | extern int sys_nerr; 16 | extern char *sys_errlist[]; 17 | if (errno < 0 || errno >= sys_nerr) 18 | return (char *)"bad error number"; 19 | else 20 | return sys_errlist[errno]; 21 | } 22 | 23 | #ifdef __cplusplus 24 | } 25 | #endif 26 | -------------------------------------------------------------------------------- /src/win32cfg.h: -------------------------------------------------------------------------------- 1 | /* Hand-edited file based on config.h.in */ 2 | /* config.h.in. Generated from configure.ac by autoheader. */ 3 | 4 | #ifndef GIFSICLE_CONFIG_H 5 | #define GIFSICLE_CONFIG_H 6 | 7 | /* Define to 1 if multithreading support is available. */ 8 | /* #undef ENABLE_THREADS */ 9 | 10 | /* Define to the number of arguments to gettimeofday (gifview only). */ 11 | /* #undef GETTIMEOFDAY_PROTO */ 12 | 13 | /* Define if GIF LZW compression is off. */ 14 | /* #undef GIF_UNGIF */ 15 | 16 | /* Define to 1 if you have the `cbrtf' function. */ 17 | #define HAVE_CBRTF 1 18 | 19 | /* Define to 1 if `ext_vector_type' vector types are usable. */ 20 | /* #undef HAVE_EXT_VECTOR_TYPE_VECTOR_TYPES */ 21 | 22 | /* Define to 1 if the system has the type `int64_t'. */ 23 | /* #undef HAVE_INT64_T */ 24 | 25 | /* Define to 1 if you have the header file. */ 26 | #if defined(_MSC_VER) && _MSC_VER >= 1900 27 | # define HAVE_INTTYPES_H 1 28 | #endif 29 | 30 | /* Define to 1 if you have the header file. */ 31 | /* #undef HAVE_MEMORY_H */ 32 | 33 | /* Define to 1 if you have the `mkstemp' function. */ 34 | /* #undef HAVE_MKSTEMP */ 35 | 36 | /* Define to 1 if you have the `pow' function. */ 37 | #define HAVE_POW 1 38 | 39 | /* Define to 1 if SIMD types should be used. */ 40 | /* #undef HAVE_SIMD */ 41 | 42 | /* Define to 1 if you have the header file. */ 43 | #if defined(_MSC_VER) && _MSC_VER >= 1900 44 | # define HAVE_STDINT_H 1 45 | #endif 46 | 47 | /* Define to 1 if you have the header file. */ 48 | #define HAVE_STDLIB_H 1 49 | 50 | /* Define to 1 if you have the `strerror' function. */ 51 | #define HAVE_STRERROR 1 52 | 53 | /* Define to 1 if you have the header file. */ 54 | /* #undef HAVE_STRINGS_H */ 55 | 56 | /* Define to 1 if you have the header file. */ 57 | #define HAVE_STRING_H 1 58 | 59 | /* Define to 1 if you have the `strtoul' function. */ 60 | #define HAVE_STRTOUL 1 61 | 62 | /* Define to 1 if you have the header file. */ 63 | /* #undef HAVE_SYS_SELECT_H */ 64 | 65 | /* Define to 1 if you have the header file. */ 66 | /* #undef HAVE_SYS_STAT_H */ 67 | 68 | /* Define to 1 if you have the header file. */ 69 | /* #undef HAVE_SYS_TIME_H */ 70 | 71 | /* Define to 1 if you have the header file. */ 72 | /* #undef HAVE_SYS_TYPES_H */ 73 | 74 | /* Define to 1 if you have the header file. */ 75 | /* #undef HAVE_TIME_H */ 76 | 77 | /* Define to 1 if the system has the type `uint64_t'. */ 78 | /* #undef HAVE_UINT64_T */ 79 | 80 | /* Define to 1 if the system has the type `uintptr_t'. */ 81 | #if defined(_MSC_VER) && _MSC_VER >= 1900 82 | # define HAVE_UINTPTR_T 1 83 | #endif 84 | 85 | /* Define to 1 if you have the header file. */ 86 | /* #undef HAVE_UNISTD_H */ 87 | 88 | /* Define if you have u_intXX_t types but not uintXX_t types. */ 89 | /* #undef HAVE_U_INT_TYPES */ 90 | 91 | /* Define to 1 if `vector_size' vector types are usable. */ 92 | /* #undef HAVE_VECTOR_SIZE_VECTOR_TYPES */ 93 | 94 | /* Define to 1 if you have the `__builtin_shufflevector' function. */ 95 | /* #undef HAVE___BUILTIN_SHUFFLEVECTOR */ 96 | 97 | /* Define to 1 if you have the `__sync_add_and_fetch' function. */ 98 | /* #undef HAVE___SYNC_ADD_AND_FETCH */ 99 | 100 | /* Define to write GIFs to stdout even when stdout is a terminal. */ 101 | /* #undef OUTPUT_GIF_TO_TERMINAL */ 102 | 103 | /* Name of package */ 104 | #define PACKAGE "gifsicle" 105 | 106 | /* Define to the address where bug reports for this package should be sent. */ 107 | #define PACKAGE_BUGREPORT "" 108 | 109 | /* Define to the full name of this package. */ 110 | #define PACKAGE_NAME "gifsicle" 111 | 112 | /* Define to the full name and version of this package. */ 113 | #define PACKAGE_STRING "gifsicle 1.96" 114 | 115 | /* Define to the one symbol short name of this package. */ 116 | #define PACKAGE_TARNAME "gifsicle" 117 | 118 | /* Define to the home page for this package. */ 119 | #define PACKAGE_URL "" 120 | 121 | /* Define to the version of this package. */ 122 | #define PACKAGE_VERSION "1.96" 123 | 124 | /* Pathname separator character ('/' on Unix). */ 125 | #define PATHNAME_SEPARATOR '\\' 126 | 127 | /* Define to a function that returns a random number. */ 128 | #define RANDOM rand 129 | 130 | /* The size of `float', as computed by sizeof. */ 131 | #define SIZEOF_FLOAT 4 132 | 133 | /* The size of `unsigned int', as computed by sizeof. */ 134 | #define SIZEOF_UNSIGNED_INT 4 135 | 136 | /* The size of `unsigned long', as computed by sizeof. */ 137 | #define SIZEOF_UNSIGNED_LONG 4 138 | 139 | /* The size of `void *', as computed by sizeof. */ 140 | #ifdef _WIN64 141 | #define SIZEOF_VOID_P 8 142 | #else 143 | #define SIZEOF_VOID_P 4 144 | #endif 145 | 146 | /* Define to 1 if you have the ANSI C header files. */ 147 | #define STDC_HEADERS 1 148 | 149 | /* Version number of package */ 150 | #define VERSION "1.96 (Windows)" 151 | 152 | /* Define if X is not available. */ 153 | #define X_DISPLAY_MISSING 1 154 | 155 | /* Define to empty if `const' does not conform to ANSI C. */ 156 | /* #undef const */ 157 | 158 | /* Define to `__inline__' or `__inline' if that's what the C compiler 159 | calls it, or to nothing if 'inline' is not supported under any name. */ 160 | #ifndef __cplusplus 161 | # ifndef inline 162 | # define inline __inline 163 | # endif 164 | #endif 165 | 166 | /* Windows doesn't have popen, but it does have _popen. */ 167 | #define popen _popen 168 | #define pclose _pclose 169 | 170 | #include 171 | 172 | #ifdef __cplusplus 173 | extern "C" { 174 | #endif 175 | 176 | /* Use the clean-failing malloc library in fmalloc.c. */ 177 | #define GIF_ALLOCATOR_DEFINED 1 178 | #define Gif_Free free 179 | 180 | /* Prototype strerror if we don't have it. */ 181 | #ifndef HAVE_STRERROR 182 | char *strerror(int errno); 183 | #endif 184 | 185 | #ifdef __cplusplus 186 | } 187 | /* Get rid of a possible inline macro under C++. */ 188 | # define inline inline 189 | #endif 190 | 191 | /* Need _setmode under MS-DOS, to set stdin/stdout to binary mode */ 192 | /* Need _fsetmode under OS/2 for the same reason */ 193 | /* Windows has _isatty and _snprintf, not the normal versions */ 194 | #if defined(_MSDOS) || defined(_WIN32) || defined(__EMX__) || defined(__DJGPP__) 195 | # include 196 | # include 197 | # define isatty _isatty 198 | # if defined(_MSC_VER) && _MSC_VER < 1900 199 | # define snprintf _snprintf 200 | # endif 201 | #endif 202 | 203 | #endif /* GIFSICLE_CONFIG_H */ 204 | -------------------------------------------------------------------------------- /test/001-transexpand.testie: -------------------------------------------------------------------------------- 1 | %script 2 | for i in 0 1 2 3; do 3 | gifsicle -O$i x.gif > y.gif 4 | gifdiff x.gif y.gif 5 | done 6 | 7 | %file -e x.gif 8 | R0lGODlhCgBeAPEAAN2i6oaz/Haeq7yPvSH/C05FVFNDQVBFMi4wAwEAAAAh+QQFBQAEACwAAAAA 9 | CgBeAAADX0i63P4wykmrvTjrzbv/YCiOZGme6CRAQtAIcAAssQzMRBvYd77zN93uRhQCAUZiL0n0 10 | /XiK2hDn/FGru9dT++NmGUavi/YcR5/XJBjNGLAX7i283Ih/213HYJ/q+zUJACH5BAkFAAAALAAA 11 | EwAKAEAAAAIXhI+py+0Po5y02ouz3rz7D4biSJbm6RQAIfkEBQUABAAsAAATAAoAQAAAA1dIutz+ 12 | MMpJq7046827/2AICpAQOIIJMGkAvIvpvish07A80/r+9jhAz2cLGI00xRCmPCJZzhP02OhVnVfq 13 | 1JjlbmuxI7j5ZAzEjbN2ofaa12+pYzCQJAAAIfkECQUAAAAsAAAUAAoAPwAAAheEj6nL7Q+jnLTa 14 | i7PevPsPhuJIluZZAAAh+QQFBQAEACwAABQACgA/AAADWEi63P4wykmrvTjrvUX0EOgIQVg+pKmG 15 | X8u+6DqejEAC9Q3gChnsu17gB8T5iMAjcqdcNotNJCEanA6vv0W0hqUJr7Nh2PslW8WN4/gxQDfa 16 | ZgIc0o4MOAkAIfkECQUAAAAsBAAUAAYAPwAAAhGEj6nL7Q+jnLTai7PevPtfAAAh+QQFBQAEACwB 17 | ABQACQA/AAADVEi63P4wykmrvTjrLJ4IHth8IkM65xiUS6qGMMq2s/LFr8oKAuDbH9+PJPSRAkXA 18 | apVcIovOpy9aPDKN0SfBygRGTU5dzUWrbc032WNgJgwg7w0mAQAh+QQJBQAAACwIAB0AAgA2AAAC 19 | CYSPqcvtD6M8BQAh+QQFBQAEACwAABYACgA9AAADU0i63P4wykmrvThrLaAIHvgEokNCZ/OlzFou 20 | rhOr5AuzLX7byvr4NJ0iAABAikfjA7lUOpiLorQhLbpI1abxilUKuE4CNyicmXg9tFj9iQw28HgC 21 | ACH5BAkFAAAALAAAHgAKADQAAAIUhI+py+0Po5y02ouz3rz7D4biyBUAIfkEBQUAAwAsAAAUAAoA 22 | PwAAAlOcj6nL7Q+jnLTai5tgIqzuKaAYlKSZgCGinuvRpuULozJtjOdns31eAsAEAoBROAAdjcnZ 23 | EqAKPKPTmXQZvRqz2qb1Cvwik9lU7MbBwTSZtltSAAAh+QQJBQAAACwAAB4ACgA1AAACFYSPqcvt 24 | D6OctNqLs968+w+G4kgaBQAh+QQFBQADACwAABcACgA8AAACTpyPqcvtD6OctNrbBBNh8Z58ICKG 25 | wTkaYjqsJvqeShnPcI2Ttx4AsX/gAIZAgZDoEyEBymUT+SSunKgTslXtDVVZmWrFMn42Goz5jI4U 26 | AAAh+QQJBQAAACwAAB4ACgA1AAACFYSPqcvtD6OctNqLs968+w+G4kgaBQAh+QQFBQADACwAABUA 27 | CgA+AAACUJyPqcvtD6OctNqLr1hCBOUFXxKOR2kOKICsrSiyJxzIc0zSObyLIN/7+YIvoCEESM6S 28 | zGONqURCAaUpFWatTldQVCxKwx47YVKnk0mr144CACH5BAkFAAAALAAAFwAKADwAAAIXhI+py+0P 29 | o5y02ouz3rz7D4biSJZmUgAAIfkEBQUAAwAsAAAUAAoAPwAAAlOcj6nL7Q+jnLTaizMTS4igfAGI 30 | iGM5jgCarocJuAYsD2ZQ33l6vjzpSyVuQ15RyOrNfkFczCaKSWlSAHXaqlqz1Wvs5nx6fs+ZZwwc 31 | cjTstntSAAAh+QQJBQAAACwAABYACgA9AAACF4SPqcvtD6OctNqLs968+w+G4kiW5lQAACH5BAUF 32 | AAMALAAAEwAKAEAAAAJTnI+py+0Po5y02ouzpkKJHyRCQALISAamgZbr0KpvDMypfNB2Wue3erq9 33 | YELRzZhCkpA9n2z1qUlNqGmtagVgrdtpV9rKxp7U8ZL1SXc27LZ7UAAAIfkECQUAAAAsAAAWAAoA 34 | PQAAAheEj6nL7Q+jnLTai7PevPsPhuJIluZUAAAh+QQFBQADACwAABYACgA9AAACUJyPqcvtD6Oc 35 | tNqLs0aipx4EABeKoyGU5pGWwDm04cuqNGrD8ky6PV9TfYS/wBAYvOFMJ9DrGWM+Aa3ps2qlzrJY 36 | q4yrkkbDIVTn7Nmo15UCACH5BAkFAAAALAAAFgAKAD0AAAIXhI+py+0Po5y02ouz3rz7D4biSJbm 37 | VAAAIfkEBQUAAwAsAAAWAAoAPQAAAlCcj6nL7Q+jnLTai7PeWXjkCQEAHGKAkiY6qsOZunBbvmy8 38 | sq5918bs++kSsCBvRMTlSD4Bk/lyPkmnKZU2FVmvW0DVWszesNHx5xcScNaRAgAh+QQJBQAAACwA 39 | AB0ACgA2AAACFYSPqcvtD6OctNqLs968+w+G4khCBQAh+QQFBQADACwAABgACgA7AAACTpyPqcvt 40 | D6OctNqLs95D+OQFAHCE4mgIwTqi6nq6cEy+cJvOZ66T/I2wiUDAYLHE8vFwS+arJaONntBpskrF 41 | XqFZaGcb1QlSnvKYg34UAAAh+QQJBQAAACwAABQACgA/AAACF4SPqcvtD6OctNqLs968+w+G4kiW 42 | 5lkAACH5BAUFAAMALAEAFAAJAD8AAAJPnI+py+0Po5y02ouz3lgkAQIHGACmIQSlKaYqC7gvK69m 43 | Ddf2oJ+8OhsBS0KgCDU8/ozFV9O3ZCFnRxdsaWsZpbIr7tSrDolTlWcEOnMMBQAh+QQJBQAAACwC 44 | ABMACAA9AAACFISPqcvtD6OctNqLs968+w+G4lYAACH5BAUKAAQALAIAEwAIAD8AAANKSLrc/jDK 45 | Sau9OOvNu9cCI4yAMgZASggBmgJs+wKt/NZuis94Hteqn8u0IwKNMiRKWVoBm7GX01aKvn4z7PWZ 46 | Ekq90KKSMSgPMAkAIfkEBQoABAAsAgATAAgAQAAAAzNIutz+MMpJq7046827/0vQBAHAkGCqrko5 47 | uidghuXc1idOy6MskEDXLygiDIiLgVIJSgAAOw== 48 | -------------------------------------------------------------------------------- /test/005-resize.testie: -------------------------------------------------------------------------------- 1 | %script 2 | gifsicle --resize 30x30 x.gif > y.gif 3 | 4 | %file -e x.gif 5 | R0lGODlhZABkAPUAABxJWFgAZnQAiXWjspYAAIIAM4ozEKcAALcBAbwAS6EAbcgBAtcCAuUEAuUK 6 | EOgXFfQFAvMLE/QaFvkkB/gkGPwxC/o1F/InN/o2Jcs5RPs9UM1APtNEOfxAG/9RO9h5VPxIRvxI 7 | VPxSSPxTVY8AqMsAi/EXxqmIA/iFfeXJCP/+FrLP1bLY6fWMjPysjfm4yvvKr/bGy+f2+/zt6P7+ 8 | /gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/gNH 9 | NDQAIfkEBQoANgAsAAAAAGQAZAAABv9Am3BILBqPyKRyyWw6n9CodEqtWq/YrHbL7Xq/4LB4TC6b 10 | z+i0es1uu9/wuHwurdnv+HYtfS8kEheBF38Fd3wXZn2AgoyMCYV2ZTWBkgWNl5iDhWOTlGJ2maGX 11 | CZFfnZ5gNZairIybXaqPr6attY57XHgnKam2voGkW5M1KSkqKrS/v8FYqhcFJ8fIsMrVzFY1gKTS 12 | sKvVv7NUnaTG08Lf1rhDeOylRM6B0Me56N+vdgV+i4OyeTbZgraZa+atnq0/ilr1s7NP4DmDB/EU 13 | PPgIHyBiA6+cgigK30RlpNpF0wKPY6ZCH9PtkpbixMgsG026AmVSnrRjLTNWiakQUqj/iiU5YrzJ 14 | EmbKn7EKNQIatGY0osfUYVOWbxEpP5Cayhw09OYJqTuVAQzYZyvSlS0ZwlzG02wtpn5QTfVFyq2o 15 | o5nW0m1rVxDeS3ojxg2F9y+mR7YC24q1r1HhTH8aM4qFqSBMycAia37G+JLWQGOXovxzaTRmb0aX 16 | al5NiDFpzidTkjLwoXOjO68DriKZmbXvrIMvfCaFmRSMGTBc2BZUw4WBbJKdkfRNveLptiFHNZ/B 17 | PTllVx9m1M7NOQFJfdWZYhb+kZTypdu5d1ce/Fn4GcrJqxKWHlI+vqEB0xwMG5Fyn3zzfWeffATm 18 | Fo5G1PmH0ESLxGQgDA0yZwCCHHpn/1UBByIX2jUErSZhZAFaFVMNBmAIgwGrFHIchwh6h1UNIeIX 19 | GlgQouhaa46oONMHLo5nHI00Jvcciy7QmB9osFSEnmYlESLgPiy66MIHSyLpJYZbfiDmBy7MKF4n 20 | BXQDnWZZWSWLgKtcCAOXd3jppXLt2GFAkzPAOIgXa8JVV2ahKcWZC86xSGZtZtqpI5mIRrrlc/dl 21 | kyagwFFJ6IgAMamopM45Kh9tLpaqpQF79snjeb6NA0lDqhTCJZFaItrol6bmemp3YQQK5IRVolSD 22 | rhiK2mGxxnL3CXqvWmcoP3qamuy0onISK3SqMDZWa7QiS22Skt7KoSRrmnZVbwOaiv/ot9wlegep 23 | XiZikYmTUFnmqXrymWxtNQAwwAAA6EkkjXx4hOKUw2ppR8BMJovnACvIsAI7qSLIRp5UlvocABNH 24 | a+xzA8gg8h0QD9CwfHIwhFCpdoRcA8Q4JhsyDTSw0DLNNExc8Qx0XHuvcgCwMLEMNJicLNE0yGBy 25 | DSvgXHSWyvZsR5l0hjx00jUki/PEAdSANM0yTB01Hf90y3TNZ4etNc02k+C102HnS7YQWWJoANET 26 | D8CC0cZuXYPbAzjdcdY8z/3PvTCwzc7aaJPQddNJL71h4WTb0S3Ng7vN+MQkuP3yCiZ3zafhOLaI 27 | oeI1BKB534p3zo7qw47ds3IuYp7/OgkmEC6q3513bYfqq1PeM4G1N4677o7i3LYJjgegOvO6k078 28 | 6TnXYIIJJSCffNIAlHB957hjDwDKczeXcOJYY6/A+IybfP37179MfuWMUk8DAAooIP/m+MNvwvo0 29 | mF/lzrc1APiLWjhTWg3yx0COWcxw/3DRDBIosgDKh2Z2SuAKGNYvFowLggnjzgecZkH7fYmEIqvg 30 | rSAYQQShijY1mqCdMMSlF74wR8Kr3HHmxK5vhQiCDdjOwHAoHjMlh07xQdJ9fki2BjSAAUIUIZLy 31 | ZIAWoKAFMVMikujwRAZ4MYuOuoMVW/CCMpYRBUk0lhy8yMYFMGABLRKVHaxYRhi8/8COL4jBBiZn 32 | LBjEoY0LCGQgweilGtDxjmZEJBrF5aRVoUGQgkSAICEAgTTSqAYoSCQiyxiDF1iSRrWBgBsguQAE 33 | SBIBlEzlJ+VjSETi0Y54XGV3nkNJNpjylrhMpS4rScQZYFKTnazjIu9UA12uAZcIOEAyUblLXUKN 34 | QwbYwCZhaUYO8JFDtXEiBBogyjSY8gDgDGczxxkBHDVKOWTcpCI/mSgnurMBaQinPA8wzmZGIJlM 35 | MlNtMplHRGIxR+18pzvPMM8DEOAAE5hAPXUZgXl+SoQ14EAmYYCCDIBRSTUQqEDNIE8CePQAFUjo 36 | QlPZUAKAwKMEeNeilpQv5EzqDv9OZIBG4UkGcB70owSwAAVEOtIIoBSlCyyB/vRkQxveIQAC8J0d 37 | 2PjOmhrUoDc9gAVCqtCRSsCkBwVBUPXXNf2NSUxbWmDqkhpUodaAjTJlwBhs+tOnTpWnC/XoSUEA 38 | AgGUIHtdNWuZXspVAbhNAfDTH1rVGgaootSmB8CABRJa1Xo2QK4g0MBWayCAGtzVrHYQAGD159eu 39 | +S93gw3DT0drUAu8tbHNbMBV6RqCEAS1spX13l39h1fXAdZ/gmUjGJ5K2oMeoANTDelCG/AAk2oA 40 | BK41K+xuO9v36a9zsf0saHXrBcOilK4gCCdwK1CB4aqWACGIbEpLUFnoWvaz2fMxKwmi+9ncEpYL 41 | 8zzoCEIgAhF4AJwdoCpqddkAB8hVAx4VK/Act8D3KRd87P3sWb0YBAAh+QQFCgACACw6ADgAKgAk 42 | AAAG/0CBcEgsGouzpPJ4VM6YUKcUKoRNqUSp9nmEWa/Y7bb7BUfFWqN3jBUkYa6PXO4qc6teON3a 43 | drsMNTVCgTUGdUsCeXGCgR8wfS6BAACCk5SFX0QwgAACAwM1H301AJ8CAZ4yKwOUBmVCkaUCKzQr 44 | fQIAKzJEu0IrnQZ3AoADtgMsA7e9QzUDNEI0LFVDsdLMt0aktDQyoMOPH8REgthFqKWrNaiFLkIG 45 | toMBJOVF5ALq7lCo9EQkAYTypjEhQcLErWezaszzFyAgCXtUIPbpVEJAiYsWmXCx16lcMgEKQoaU 46 | OGSGqEuy2sAIV6oToUAGPrQzYtKATVe39NzcKVNYFnAzYdAkoeKknFCf/JImHSqEjVImjpTovDnz 47 | qRED7nZuaIEiA06rRbDW2CrgBYwXQlDUqApWyIexal5scIX06aYMKNDGSJuhRqa2Td9uQLEXxQZ2 48 | iAC/AUQoph3ATaU+ssMU8tHElo9CRoJmM02n2IIAACH5BA0KAAAALDoAPAAqABwAAAapQIBwJiwa 49 | j8ikEklcOoezqDTqnEqfRqu2ydxiAVtvV/sNi4uwcNlsPabPVXZbCHuTsXJy3T5/5u91cEt/V0Z8 50 | hX6EXHSCSopGLh8GH28wLgZfMDWTLnyWkjWbH0WXNQChAJhfBqGtrqahlIalQpNlMKeoR6K4THtU 51 | a3vCe1/FxsfIycrLzM2JRWrOR5RhMB/A0nSYlKytBm/Z0H/hUHLk4mbnWdHq69jJQQAh+QQNCgAA 52 | ACxHAFAADgAIAAAGJMDZDEAUGo2AD+x4hH2EMEMyWqvWDMysdnsscodEADdM7pIBQQAh+QQFCgAC 53 | ACw6ADgAKgAWAAAG/0CBcEgsGoswl9IFOx6TTKfUZahZa4ZPUyqgWg0ubvFjBQwGgK/W6Sp/w+Ku 54 | dbCSra7vYxVw/265VQMyg1d0A1hwRHMyNXQ1H3ECgjQ0LHOUNHdgRG0ALHcyNIeRoTQyA0IrmKJY 55 | Wx+BdgKhjJGYdwGyq4w1iQI1qiwCqrRxmMEkuZi7iEIGoSuSLKi1lETTApm+NUQwlMFXkdhFAb9C 56 | p81DMN00RMjhRSRzK4e4LjMC6t1EuO/w2lYBkMGYcQ8fOyEBTfQbNySgkIJH3C0cYoJEgIskFAqA 57 | aMSEiRLhCsoAALIiiYwCSgCItLIfKoUeYTo52FLSQmgCYnpUMJPdCjIAZmREmqEMlYKjR/kcLKJs 58 | 0FIxRCnZSVOGxVMiH1Zd5bKuqdM/egxk6afuldizY4sEAQAh+QQNCgAAACxHAE8ADwANAAAGasAP 59 | DECEuT6GWs1FBCifSkML1ap9iDXitPXqdlFLJ2D6AsBeQ0BsY3BOm030CwyroboxQFn/ir2WNVxn 60 | Q3Jnfy52XopdMHQGG3JlkS8cbYhck2dgTAAfiX1yVVdNnhx3jRlWcERIUAacREEAIfkEBQoANgAs 61 | AAAAAAEAAQAABgNAWxAAIfkEDQoAAAAsRwBQAA4ACAAABiTA2QxAFBqNgA/seIR9hDBDMlqr1gzM 62 | rHZ7LHKHRAA3TO6SAUEAIfkEBQoANgAsAAAAAAEAAQAABgNAWxAAOw== 63 | -------------------------------------------------------------------------------- /test/006-unoptdisposal.testie: -------------------------------------------------------------------------------- 1 | %script 2 | for i in 0 1 2 3; do 3 | gifsicle -O$i x.gif > y.gif 4 | gifdiff x.gif y.gif 5 | done 6 | 7 | %file -e x.gif 8 | R0lGODlhKAAUAPEAAP/////TAP8AAAAAACH5BAEUAAAALAAAAAAoABQAAAIahI+py+0Po5y02ouz 9 | 3rz7D4biSJbmiabqihUAIfkEARQAAAAsAAAAACgAFAAAAiiEj6nL7Q+jnLTai7PevPu/BeIYgAiJ 10 | mgaamiypvmMsl25Ny/m7t2YBACH5BAkUAAAALAAAAAAoABQAAAIohI+py+0Po5y02ouz3rz7vwni 11 | KIAIiZoGmposqb5jLJduTcv5u7dmAQAh+QQBFAAAACwAAAAAKAAUAAACGoSPqcvtD6OctNqLs968 12 | +w+G4kiW5omm6ooVADs= 13 | -------------------------------------------------------------------------------- /test/007-alltransp.testie: -------------------------------------------------------------------------------- 1 | %script 2 | gifsicle -I < x.gif 3 | gifsicle x.gif | gifsicle -I 4 | gifsicle --scale=2 x.gif | gifsicle -I 5 | 6 | %file -e x.gif 7 | R0lGODlhAgACAHAAACH5BAEBAAAALAAAAAACAAIAAAIChFEAOw== 8 | 9 | %expect stdout 10 | * 1 image 11 | logical screen 2x2 12 | + image #0 2x2 transparent 0 13 | delay 0.01s 14 | * 1 image 15 | logical screen 2x2 16 | global color table [2] 17 | background 0 18 | + image #0 2x2 transparent 0 19 | delay 0.01s 20 | * 1 image 21 | logical screen 4x4 22 | global color table [2] 23 | background 0 24 | + image #0 4x4 transparent 0 25 | delay 0.01s 26 | -------------------------------------------------------------------------------- /test/008-resizemix.testie: -------------------------------------------------------------------------------- 1 | %script 2 | gifsicle --resize=250x_ strip.gif > stripx.gif 3 | gifsicle greenmini.gif stripx.gif > stripy.gif 4 | gifsicle -UO2 --crop=0,0+10x7 stripy.gif '#1' > overlay.gif 5 | gifsicle --crop=0,0+10x7 greenmini.gif > minibit.gif 6 | gifdiff overlay.gif minibit.gif 7 | 8 | %file -e greenmini.gif 9 | R0lGODdh+gAHAPAAAD3kxzzixSwAAAAA+gAHAAACP4SPqcvtD6OctL4AsN1V8w+G4kiW5omm6sq2 10 | 7gvH8kzX9o1vXs73fhPYBYfDn/FITCqXjCIumGBmPrtc9dgoAAA7 11 | 12 | %file -e strip.gif 13 | R0lGODlhbgEKAPYAAAgEBQYIDQoJDgwLEwwMGhINFRkLFhIOGgsSFg0RHRUTHBkUGBoVHRsZHhQO 14 | IhQUIxoWIhYYJhwaIxwcKyIcJCkZJSIdKxwhKycjJyQiLCsjLDclKyMjMyslMiwqMysrPDMrNCsz 15 | Njk0Oy5IOjg3RkhESlZLVkxTVFhVW2VbZmhka3JqbGpodHd1e3l4hIeEiomIlZaUmaWbp6Kroail 16 | q7elqam2rqmotrartrm0u8e2usrJzNra3eXc5Ofm6PX27fDv8gAAAAAAAAAAAAAAAAAAAAAAAAAA 17 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 18 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 19 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH+ 20 | KEVkaXRlZCB3aXRoIEx1bmFQaWM6IGh0dHA6Ly9sdW5hcGljLmNvbS8AIfkEAQAAQQAsAAAAAG4B 21 | CgAAB/+AQYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKz 22 | tLW2t7iOP0G7vYK7vMHCi8DAhMbExkCFQMi5z9DRtkA8Ojk42DU2NNjc2dg4Njg1NTfaODc32NfX 23 | 4eDi69jw8uDoNznp1zQ0NjL7MzRy5NjhQ5rBgwhN9ZihYgQGDRgoSMyQoUFEDBcxdHiIIcNDiBo6 24 | dAgJooOHDRo8hNTAkqWFDBU8XrhAwYKEmxIUUGBwoMEABSQwBBjQQEACDA1QvNjhLKHTp1Ad8Ygh 25 | ggIGEFg9eMBacqRJEFpbilQpcsNIDRk0xIQZcScFCHDDIShgoKCuggMHENxFgCDBTwIBQrg4kcGC 26 | hgEIGgAA0EDFjqiQI0sWBARGhwkePojswIEDSw4XPFBoUEIDAxAROAiQcKFBAwkTIEhgUADChAkS 27 | HuiGEEFBbwUJ6iZoYBe4gr4KHhxwcPdABBgvVLxAgeFC9RARWxSczL17NBwkOEzgIJv1hZsRYCuQ 28 | YCLGCxAGJMDlIGFBegcRbDKQP+Cm7AgA/hcBb74VZ+AAEQzg3HorsJDCdNhdgIAIITQgAg3eRRUI 29 | ADs= 30 | -------------------------------------------------------------------------------- /test/009-zerowidth.testie: -------------------------------------------------------------------------------- 1 | %script 2 | gifsicle zerowidth-ok.gif | gifsicle -I 3 | 4 | %file -e zerowidth-ok.gif 5 | R0lGODlhgAKQAZECAAAAAKqqqgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFDwADACwAAAAA 6 | AAAAAQAC/oSPqcvtD6OctNqLs968+w+G4kiW5omm6sq27gvH8kzX9o3n+s73/g8MCofEovGITCqX 7 | zKbzCY1Kp9Sq9YrNarfcrvcLDovH5LL5jE6r1+y2+w2Py+f0uv2Oz+v3/L7/DxgoOEhYaHiImFgT 8 | wNjIqAgZKenl6Dh5iZmpucnZ6fkJGio6SlpqeoqaqrrK2ur6ChsrO0tba3uLm6u7y9vr+wscLDxM 9 | XGx8jJysvMzc7PwMHS09TV1tfY2drb3N3e39DR4uPk5ebn6Onq6+zt7u/g4fLz9PX29/j5+vv8/f 10 | 7/8PMKDAgQQLGjyIMKHChQwbOnwIMaLEiRQrWryIMaPG/o0cO3r8CDKkyJEkS5o8iTKlypUsW7p8 11 | CTOmzJk0a9q8iTOnzp08e/r8CTSo0KFEixo9ijSp0qVMmzp9CjWq1KlUq1q9ijWr1q1cu3r9Cjas 12 | 2LFky5o9izat2rVs27p9Czeu3Ll069q9izev3r18+/r9Cziw4MGECxs+jDix4sWMGzt+DDmy5MmU 13 | K1u+jDmz5s2cO3v+DDq06NGkS5s+jTq16tWsW7t+DTu27Nm0a9u+jTu37t28e/v+DTy48OHEixs/ 14 | jjy58uXMmzt/Dj269OnUq1u/jj279u3cu3v/Dj68+PHky5s/jz69+vXs27t/Dz++/Pn069u/jz+/ 15 | /v38/vv7/w9ggAIOSGCBBh6IYIIKLshggw4+CGGEEk5IYYUWXohhhhpuyGGHHn4IYogijkhiiSae 16 | iGKKKq7IYosuvghjjDLOSGONNt6IY4467shjjz7+CGSQQg5JZJFGHolkkkouyWSTTj4JZZRSTkll 17 | lVZeiWWWWm7JZZdefglmmGKOSWaZZp6JZppqrslmm26+CWeccs5JZ5123olnnnruyWeffv4JaKCC 18 | DkpooYYeimiiii7KaKOOPgpppJJOSmmlll6Kaaaabsppp55+Cmqooo5Kaqmmnopqqqquymqrrr4K 19 | a6yyzkprrbbeimuuuu7Ka6++/gpssMIOS2yxxh6LFmyyyi7LbLPOPgtttNJOS2211mpZAAAAIfkE 20 | BQ8AAwAsAAAOAAgAAgAAAgSEjwkFADs= 21 | 22 | %file -e zerowidth-bad.gif 23 | R0lGODdhQgFCAfcAAAAAAFUAAIAAAKoAANUAAP8AAAArAFUrAIArAKorANUrAP8rAABVAFVVAIBV 24 | AKpVANVVAP9VAACAAFWAAICAAKqAANWAAP+AAACqAFWqAICqAKqqANWqAP+qAADVAFXVAIDVAKrV 25 | ANXVAP/VAAD/AFX/AID/AKr/ANX/AP//AAAAVVUAVYAAVaoAVdUAVf8AVQArVVUrVYArVaorVdUr 26 | Vf8rVQBVVVVVVYBVVapVVdVVVf9VVQCAVVWAVYCAVaqAVdWAVf+AVQCqVVWqVYCqVaqqVdWqVf+q 27 | VQDVVVXVVYDVVarVVdXVVf/VVQD/VVX/VYD/Var/VdX/Vf//VQAAgFUAgIAAgKoAgNUAgP8AgAAr 28 | gFUrgIArgKorgNUrgP8rgABVgFVVgIBVgKpVgNVVgP9VgACAgFWAgICAgKqAgNWAgP+AgACqgFWq 29 | gICqgKqqgNWqgP+qgADVgFXVgIDVgKrVgNXVgP/VgAD/gFX/gID/gKr/gNX/gP//gAAAqlUAqoAA 30 | qqoAqtUAqv8AqgArqlUrqoArqqorqtUrqv8rqgBVqlVVqoBVqqpVqtVVqv9VqgCAqlWAqoCAqqqA 31 | qtWAqv+AqgCqqlWqqoCqqqqqqtWqqv+qqgDVqlXVqoDVqqrVqtXVqv/VqgD/qlX/qoD/qqr/qtX/ 32 | qv//qgAA1VUA1YAA1aoA1dUA1f8A1QAr1VUr1YAr1aor1dUr1f8r1QBV1VVV1YBV1apV1dVV1f9V 33 | 1QCA1VWA1YCA1aqA1dWA1f+A1QCq1VWq1YCq1aqq1dWq1f+q1QDV1VXV1YDV1arV1dXV1f/V1QD/ 34 | 1VX/1YD/1ar/1dX/1f//1QAA/1UA/4AA/6oA/9UA//8A/wAr/1Ur/4Ar/6or/9Ur//8r/wBV/1VV 35 | /4BV/6pV/9VV//9V/wCA/1WA/4CA/6qA/9WA//+A/wCq/1Wq/4Cq/6qq/9Wq//+q/wDV/1XV/4DV 36 | /6rV/9XV///V/wD//1X//4D//6r//9X//z8/P2tra5WVlcPDw////ywAAAAAAABCAQcI/wABCBxI 37 | sKDBgwgTKlzIsKHDhxAjSpxIsaLFixgt/vuXsaPHjyBDihxJsuTFjQA4mlzJsqXLlzBjJkSpUqbN 38 | mzhz6pS5sebOn0CDCh16sKdPokiTKl0K0ijTp1CjSkXodKrVq1iBVs3KtavXklu/ih1LNmLYsmjT 39 | qj2rtq1brmzfyp3LNC7du3h/2s3Lt+/LvX4DCw4JeLDhwxQLI17MWKHixpAjP45MGfHkypgDX87M 40 | Ge/mzqDffg5NGu3o0qi/nk7NGuvq1rCjvo5NW+ns2riF3s7NW+fu3sB59gxOXPbw4shtH0/OXPfy 41 | 5tB9P49OXTjK6thj/s7O3eH27uBnTv8PT77j9/Loz6Mnr349+PbuucOPj30+fer270PPr585//7I 42 | /QcgcQIOCFyBBvKGYIK4LcggbQ4+CFuEErJGYYWoXYghaRpuCFqHHnIGYoiYjUgiZSaeCFmKKjLG 43 | YouWjQfjezLOKF+NNtaHY4747cjjfj7+6F+QQgZIZJEEHonkgUouqWCTTjYIZZQQTknlhFZeaWGW 44 | WmbIZZccfgnmh2KOKWKZZpaIZpoorsnmim6+6WKccsZ4XZ25vYinV3ruCRedfmoGaKB99UmoVYYe 45 | KlWiikLFaKN1DQrpXI9OmlSllhKFaabO3clpZ5t+qpekoppGaqlkhYoqTqqualOrrmr/d2qsXcFK 46 | q0u23spSrrqaxGuvJP0KrEjCDtvUrMYa52mygi7LbKHIPrtUsdKeFG21SFGLbWLXbtvpUd5S2m24 47 | ozpLrlvanuvduOqyym67r74Lr6zmzpuqvPbiim++u+7Lr6/+/htswAITS3DBx9aLsGsHL2xeww5j 48 | lK7DEy9cMcIXF5yxwBv/2zG/H+cbsr0jz1syvCe3m7K6K5/bMrkvhxuztzNvWzO2N1ebs7Q786xw 49 | xFNBDLRGPw/taNFGRwpu0osizfSlTj+tadRSd1o1olRfXa7WTS/NdbZZf+2u12JbXfa0YZ9Nr9rK 50 | kc22dG6/PbbcU8dNt3V3m5331nvz/9033H/vJHTeg99dON2Hy53424uz3bjaj58dedmTi13515dz 51 | nbnWm1/dedWfSx3606MzXXrSpxud+tCrA916xK9TnHbghM1O+0exW2z77Q/bzfvAvv9ucPDCJ1z8 52 | 2sf/tXvyE+WO8fLMmwV99A85r/H01DdkPcfYZ7/Q9h537714xI/fvPjmGwQ+yOinT9D6IrfvvkDw 53 | kyz//PWbfL/7+aO8f/r9U9n/zBdAlg1wfAV02QG9l0CYLTB7DZTZA6kXQZpNMHoVtNkFmZdBnG0w 54 | eR3U2QePF0KflW9+6zohChlSwme1kFkvTFYMjTXDYdUQWDfsVQ51tcNb9ZBWP4xVEKBdNcRVFRFV 55 | RyxVEkW1xE81kVNPzFQULTXFSVURUldsVBYVtcVDdZFQXwxUGP00xj2VEU9nrFMa5bTGN7WRTW9M 56 | UxzNNMcx1RFMd+xSHrW0xyv1kUp/jFIgnTTIJRUSSYcsUiKFtMgfNZJHj8xRJG00yRlVEkaXbFEm 57 | VaTCFbKQJkYJpShHScpSmvKUqEylKlfJyla68pWwjKUsZ0lLWgakADs= 58 | 59 | %stdout 60 | * 2 images 61 | logical screen 640x400 62 | global color table [2] 63 | background 0 64 | loop forever 65 | + image #0 640x256 transparent 2 66 | disposal asis delay 0.15s 67 | + image #1 8x2 at 0,14 transparent 2 68 | disposal asis delay 0.15s 69 | -------------------------------------------------------------------------------- /test/010-warnings.testie: -------------------------------------------------------------------------------- 1 | %script 2 | export LANG=C 3 | gifsicle blank.gif -o /dev/null 2>&1 && echo blank worked 4 | gifsicle -w blank.gif -o /dev/null 2>&1 && echo blank worked 5 | gifsicle blank-garbage.gif -o /dev/null 2>&1 && echo blank-garbage worked 6 | gifsicle -w blank-garbage.gif -o /dev/null 2>&1 && echo blank-garbage worked 7 | 8 | %file -e blank.gif 9 | R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw== 10 | 11 | %file -e blank-garbage.gif 12 | R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAO2Rhc2prZm5zZGtqYW5m 13 | a2pkbnNha2YK 14 | 15 | %expect stdout 16 | blank worked 17 | blank worked 18 | gifsicle:blank-garbage.gif: warning: trailing garbage after GIF ignored 19 | blank-garbage worked 20 | blank-garbage worked 21 | -------------------------------------------------------------------------------- /test/011-resizemix.testie: -------------------------------------------------------------------------------- 1 | %script 2 | gifsicle -O2 --careful --resize-method=mix --resize=200x20 in.gif | gifdiff - want.gif 3 | 4 | %file -e in.gif 5 | R0lGODdhlgAPAPMOADhbmB89fxRBgSRCfilGgi1KhzFNiipQjDBUkDBWjDRXlC5dmDZckz1imSA9eAAAACwAAAAAlgAPAAAE/5DJSStLMmmtukcKIo7icSBmaqxsaxTwC8PEXBB4Tgz8kPe8gHBILBqPSIdyiRQul5aoZcPxWEmklPbg6hZeLNtMR8YBfbtBc802PpXrt6MhrU8S1nwHWzqZUFtdgitiYmWHZ2pti25vcW8NkZJ0dhgXenp8fFtcgl+fhWM0NYc/Z4yoQ3KPTwsLknUAABIKsphXmpudLLthYKFjpKVppolAqXJPjHKudhKytLcfIbl+1i69XsA3wcPFxkGoA8lwi8yuC9ATsuzt0nt7mlqAKoNgMsDe+sTgPUZnyZC9cdWOgq2Dtt6ByAVohAl7hH6FqjFKWBkfaHTwSHNGgL8iAKWVjCu3bCDBdigBvPvAcEufhwYefvEEats+RP2MJVkyLlzJVulSolxJjSEKeoEgisGXz1tGMjk7IhnpCJUcoUKlLTSKhVO2iExjbKt481tUAQJSqS2CNeFKrvI4eSI0dmJZqFGPrd3bVqUHV5jgdvU6U2ldQ3fN5tzLt62VBVoFv0wKke5hsjecgvOYKC1jtX07oIsseTJMF4XDXC6TuVTezp/VRgAAOw== 6 | 7 | %file -e want.gif 8 | R0lGODdhyAAUAPMOAD1imR89fxRBgSRCfilGgi1KhzFNiipQjDBUkDBWjDRXlC5dmDZckyA9eDhbmAAAACwAAAAAyAAUAAAE/5DJSatNSeKNlf8eEiJkWR4ogq6o4b5wXMyFQd/EXRB87w9A4C8YDBiPyKRyyWweG9Ao1PmURi3YLIWzAXlHJhNr3IqZX7UZWofzuYdEIaFIrdup1qg9D9X6LQgcJF9eYWFkY2eKNWZsNG+QPHEDPXR3l5gBfA17fAB/oFsihF+GJykrKoiKrGuOOpGxk0CZtUybnXkAu7y8oRoTCaOkhabGiAetNi5pr7CPOTuxlbO21ki4dZsLC72ffw4OEyLhw8RgxqYrZ8lnjM3O0DPT1HCzk9eam1a129y84sAFZOAh3Lli6dSVidGuETMb8eRJoyeH2j181vZJseVvwS8J4f8mHCyE7piqAyfZKWMWcV4bem8uVrM2QOOUfny46Qw3ECTPnz9HfhiW8CQqMishsmwJsymcOTKJMJmk8VpHoBV4KtAaVKiCUUVPnmq4shlEZ9Hm9ZgYidKctkKiSl1CdZ/VnNywUuBqcGtfoWFJpBqb9OGyiGnZsoXk1m3MuHJp0Y0jBUiUAXfz6FwAtHNXr1/DIhq7sAwjVmrOInZKMfI9PJVrWsKpOa/n26BDB1ZhdFVhNktXN3UM1zURAVRq2s3I57bzv8TA7j6EjGwrR8HjsW5tPIgA5PnCJ3mOe6SI6Qp9u3vXUvh2xt29gxdPnzx0r+iPIUPNvj3a97LENxtaffnY58AX3JyTH3XVnZaUf/8B6IaAkhEYnoEImregYNVZ9yCEbEhoEYXzWXgNhiAkeNCGpCFVGAwgwuKDWjBReJyJF9r3wWYassihi+stEmMkNE5j4404XhMBADs= 9 | -------------------------------------------------------------------------------- /test/012-framechange.testie: -------------------------------------------------------------------------------- 1 | %script 2 | gifsicle -e rainbow.gif 3 | gifsicle rainbow.gif "#1" "#1" "#2-5" "#5" -o x.gif 4 | gifsicle rainbow.gif --delete "#0" --insert-before "#2" rainbow.gif.001 --append rainbow.gif.005 -o y.gif 5 | gifsicle rainbow.gif --delete "#0" --insert-before "#2" rainbow.gif "#1" --append rainbow.gif "#5" -o z.gif 6 | cmp x.gif y.gif 7 | cmp x.gif z.gif 8 | 9 | %file -e rainbow.gif 10 | R0lGODdhBAAEAPIAAP8AAP+AAP//AAD/AAAA/4AA/wAAAAAAACwAAAAABAAEAAACBISPCQUALAAAAAAEAAQAAAIEjI8ZBQAsAAAAAAQABAAAAgSUjykFACwAAAAABAAEAAACBJyPOQUALAAAAAAEAAQAAAMESLrclAAsAAAAAAQABAAAAwRYutyVADs= 11 | --------------------------------------------------------------------------------