diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 224e7f0..0000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.pc/ diff --git a/Makefile b/Makefile index e4ded69..87966a9 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ MANDIR = $(PREFIX)/share/man VERSION:=$(shell ./getversion) LIBMAJOR=7 -LIBMINOR=1 +LIBMINOR=2 LIBPOINT=0 LIBVER=$(LIBMAJOR).$(LIBMINOR).$(LIBPOINT) @@ -37,17 +37,15 @@ USOURCES = qprintf.c quantize.c getarg.c UHEADERS = getarg.h UOBJECTS = $(USOURCES:.c=.o) +UNAME:=$(shell uname) + # Some utilities are installed INSTALLABLE = \ gif2rgb \ gifbuild \ - gifecho \ - giffilter \ giffix \ - gifinto \ giftext \ giftool \ - gifsponge \ gifclrmp # Some utilities are only used internally for testing. @@ -56,41 +54,87 @@ INSTALLABLE = \ UTILS = $(INSTALLABLE) \ gifbg \ gifcolor \ + gifecho \ + giffilter \ gifhisto \ + gifinto \ gifsponge \ gifwedge LDLIBS=libgif.a -lm -all: libgif.so libgif.a libutil.so libutil.a $(UTILS) +MANUAL_PAGES = \ + doc/gif2rgb.xml \ + doc/gifbuild.xml \ + doc/gifclrmp.xml \ + doc/giffix.xml \ + doc/giflib.xml \ + doc/giftext.xml \ + doc/giftool.xml + +SOEXTENSION = so +LIBGIFSO = libgif.$(SOEXTENSION) +LIBGIFSOMAJOR = libgif.$(SOEXTENSION).$(LIBMAJOR) +LIBGIFSOVER = libgif.$(SOEXTENSION).$(LIBVER) +LIBUTILSO = libutil.$(SOEXTENSION) +LIBUTILSOMAJOR = libutil.$(SOEXTENSION).$(LIBMAJOR) +ifeq ($(UNAME), Darwin) +SOEXTENSION = dylib +LIBGIFSO = libgif.$(SOEXTENSION) +LIBGIFSOMAJOR = libgif.$(LIBMAJOR).$(SOEXTENSION) +LIBGIFSOVER = libgif.$(LIBVER).$(SOEXTENSION) +LIBUTILSO = libutil.$(SOEXTENSION) +LIBUTILSOMAJOR = libutil.$(LIBMAJOR).$(SOEXTENSION) +endif + +all: $(LIBGIFSO) libgif.a $(LIBUTILSO) libutil.a $(UTILS) +ifeq ($(UNAME), Darwin) +else $(MAKE) -C doc +endif $(UTILS):: libgif.a libutil.a -libgif.so: $(OBJECTS) $(HEADERS) - $(CC) $(CFLAGS) -shared $(LDFLAGS) -Wl,-soname -Wl,libgif.so.$(LIBMAJOR) -o libgif.so $(OBJECTS) +$(LIBGIFSO): $(OBJECTS) $(HEADERS) +ifeq ($(UNAME), Darwin) + $(CC) $(CFLAGS) -dynamiclib -current_version $(LIBVER) $(OBJECTS) -o $(LIBGIFSO) +else + $(CC) $(CFLAGS) -shared $(LDFLAGS) -Wl,-soname -Wl,$(LIBGIFSOMAJOR) -o $(LIBGIFSO) $(OBJECTS) +endif libgif.a: $(OBJECTS) $(HEADERS) $(AR) rcs libgif.a $(OBJECTS) -libutil.so: $(UOBJECTS) $(UHEADERS) - $(CC) $(CFLAGS) -shared $(LDFLAGS) -Wl,-soname -Wl,libutil.so.$(LIBMAJOR) -o libutil.so $(UOBJECTS) +$(LIBUTILSO): $(UOBJECTS) $(UHEADERS) +ifeq ($(UNAME), Darwin) + $(CC) $(CFLAGS) -dynamiclib -current_version $(LIBVER) $(OBJECTS) -o $(LIBUTILSO) +else + $(CC) $(CFLAGS) -shared $(LDFLAGS) -Wl,-soname -Wl,$(LIBUTILMAJOR) -o $(LIBUTILSO) $(UOBJECTS) +endif libutil.a: $(UOBJECTS) $(UHEADERS) $(AR) rcs libutil.a $(UOBJECTS) clean: - rm -f $(UTILS) $(TARGET) libgetarg.a libgif.a libgif.so libutil.a libutil.so *.o - rm -f libgif.so.$(LIBMAJOR).$(LIBMINOR).$(LIBPOINT) - rm -f libgif.so.$(LIBMAJOR) + rm -f $(UTILS) $(TARGET) libgetarg.a libgif.a $(LIBGIFSO) libutil.a $(LIBUTILSO) *.o + rm -f $(LIBGIFSOVER) + rm -f $(LIBGIFSOMAJOR) rm -fr doc/*.1 *.html doc/staging check: all $(MAKE) -C tests +reflow: + @clang-format --style="{IndentWidth: 8, UseTab: ForIndentation}" -i $$(find . -name "*.[ch]") + # Installation/uninstallation +ifeq ($(UNAME), Darwin) +install: all install-bin install-include install-lib +else install: all install-bin install-include install-lib install-man +endif + install-bin: $(INSTALLABLE) $(INSTALL) -d "$(DESTDIR)$(BINDIR)" $(INSTALL) $^ "$(DESTDIR)$(BINDIR)" @@ -100,12 +144,12 @@ install-include: install-lib: $(INSTALL) -d "$(DESTDIR)$(LIBDIR)" $(INSTALL) -m 644 libgif.a "$(DESTDIR)$(LIBDIR)/libgif.a" - $(INSTALL) -m 755 libgif.so "$(DESTDIR)$(LIBDIR)/libgif.so.$(LIBVER)" - ln -sf libgif.so.$(LIBVER) "$(DESTDIR)$(LIBDIR)/libgif.so.$(LIBMAJOR)" - ln -sf libgif.so.$(LIBMAJOR) "$(DESTDIR)$(LIBDIR)/libgif.so" + $(INSTALL) -m 755 $(LIBGIFSO) "$(DESTDIR)$(LIBDIR)/$(LIBGIFSOVER)" + ln -sf $(LIBGIFSOVER) "$(DESTDIR)$(LIBDIR)/$(LIBGIFSOMAJOR)" + ln -sf $(LIBGIFSOMAJOR) "$(DESTDIR)$(LIBDIR)/$(LIBGIFSO)" install-man: $(INSTALL) -d "$(DESTDIR)$(MANDIR)/man1" - $(INSTALL) -m 644 doc/*.1 "$(DESTDIR)$(MANDIR)/man1" + $(INSTALL) -m 644 $(MANUAL_PAGES) "$(DESTDIR)$(MANDIR)/man1" uninstall: uninstall-man uninstall-include uninstall-lib uninstall-bin uninstall-bin: cd "$(DESTDIR)$(BINDIR)" && rm -f $(INSTALLABLE) @@ -113,9 +157,10 @@ uninstall-include: rm -f "$(DESTDIR)$(INCDIR)/gif_lib.h" uninstall-lib: cd "$(DESTDIR)$(LIBDIR)" && \ - rm -f libgif.a libgif.so libgif.so.$(LIBMAJOR) libgif.so.$(LIBVER) + rm -f libgif.a $(LIBGIFSO) $(LIBGIFSOMAJOR) $(LIBGIFSOVER) uninstall-man: cd "$(DESTDIR)$(MANDIR)/man1" && rm -f $(shell cd doc >/dev/null && echo *.1) + cd "$(DESTDIR)$(MANDIR)/man7" && rm -f $(shell cd doc >/dev/null && echo *.7) # Make distribution tarball # @@ -132,9 +177,10 @@ EXTRAS = README \ history.adoc \ control \ doc/whatsinagif \ + doc/gifstandard \ DSOURCES = Makefile *.[ch] -DOCS = doc/*.xml doc/*.1 doc/*.html doc/index.html.in doc/00README doc/Makefile +DOCS = doc/*.xml doc/*.1 doc/*.7 doc/*.html doc/index.html.in doc/00README doc/Makefile ALL = $(DSOURCES) $(DOCS) tests pic $(EXTRAS) giflib-$(VERSION).tar.gz: $(ALL) $(TAR) --transform='s:^:giflib-$(VERSION)/:' -czf giflib-$(VERSION).tar.gz $(ALL) @@ -151,7 +197,7 @@ version: # cppcheck should run clean cppcheck: - cppcheck --inline-suppr --template gcc --enable=all --suppress=unusedFunction --force *.[ch] + cppcheck --quiet --inline-suppr --template gcc --enable=all --suppress=unusedFunction --suppress=missingInclude --force *.[ch] # Verify the build distcheck: all diff --git a/NEWS b/NEWS index 30a460b..1cf09b9 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,97 @@ GIFLIB NEWS +Repository head +=============== + +This is a point release intended to clear up a couple of CVEs and +apply point fixes that have been accumulating since 5.2.1 + +There are a few unresolved (but minor) memory leaks related to design +issues in the API that still need to be resolved. Expect those fixes +in the next release. + +Code Fixes +---------- + +* Fixes for CVE-2023-48161, CVE-2022-28506, + +* Address SF issue #138 Documentation for obsolete utilities still installed + +* Address SF issue #139: Typo in "LZW image data" page ("110_2 = 4_10") + +* Address SF issue #140: Typo in "LZW image data" page ("LWZ") + +* Address SF issue #141: Typo in "Bits and bytes" page ("filed") + +* Note as already fixed SF issue #143: cannot compile under mingw + +* Address SF issue #144: giflib-5.2.1 cannot be build on windows and other platforms using c89 + +* Address SF issue #145: Remove manual pages installation for binaries that are not installed too + +* Address SF issue #146: [PATCH] Limit installed man pages to binaries, move giflib to section 7 + +* Address SF issue #147 [PATCH] Fixes to doc/whatsinagif/ content + +* Address SF issue #148: heap Out of Bound Read in gif2rgb.c:298 DumpScreen2RGB + +* Declared no-info on SF issue #150: There is a denial of service vulnerability in GIFLIB 5.2.1 + +* Declared Won't-fix on SF issue 149: Out of source builds no longer possible + +* Address SF issue #151: A heap-buffer-overflow in gif2rgb.c:294:45 + +* Address SF issue #152: Fix some typos on the html documentation and man pages + +* Address SF issue #153: Fix segmentation faults due to non correct checking for args + +* Address SF issue #154: Recover the giffilter manual page + +* Address SF issue #155: Add gifsponge docs + +* Address SF issue #157: An OutofMemory-Exception or Memory Leak in gif2rgb + +* Address SF issue #158: There is a null pointer problem in gif2rgb + +* Address SF issue #159 A heap-buffer-overflow in GIFLIB5.2.1 DumpScreen2RGB() in gif2rgb.c:298:45 + +* Address SF issue #163: detected memory leaks in openbsd_reallocarray giflib/openbsd-reallocarray.c + +* Address SF issue #164: detected memory leaks in GifMakeMapObject giflib/gifalloc.c + +* Address SF issue #166: a read zero page leads segment fault in getarg.c and memory leaks in gif2rgb.c and gifmalloc.c + +* Address SF issue #167: Heap-Buffer Overflow during Image Saving in DumpScreen2RGB Function at Line 321 of gif2rgb.c + +Version 5.2.1 +============== + +This is the "Maybe I shouldn't have done a release while in surgical recovery" release. + +* In gifbuild.c, avoid a core dump on no color map. + +* Restore inadvertently removed library version numbers in Makefile. + +Version 5.2.0 +============= + +The undocumented and deprecated GifQuantizeBuffer() entry point +has been moved to the util library to reduce libgif size and attack +surface. Applications needing this function are couraged to link the +util library or make their own copy. + +The following obsolete utility programs are no longer installed: +gifecho, giffilter, gifinto, gifsponge. These were either installed in +error or have been obsolesced by modern image-transformation tools +like ImageMagick convert. They may be removed entirely in a future +release. + +* Address SourceForge issue #136: Stack-buffer-overflow in gifcolor.c:84 + +* Address SF bug #134: Giflib fails to slurp significant number of gifs + +* Apply SPDX convention for license tagging. + Version 5.1.9 ============= @@ -14,7 +106,7 @@ is applied to GIFs. Version 5.1.8 ============= -* Address SF bug #119: MemorySanitizer: FPE on unknown address +* Address SF bug #119: MemorySanitizer: FPE on unknown address. This is CVE-2019-15133 * Address SF bug #125: 5.1.7: xmlto is still required for tarball @@ -50,19 +142,19 @@ Code Fixes * Fix SF bug #113: Heap Buffer Overflow-2 in function DGifDecompressLine() in cgif.c. This had been assigned CVE-2018-11490. -# Fix SF bug #111: segmentation fault in PrintCodeBlock +* Fix SF bug #111: segmentation fault in PrintCodeBlock * Fix SF bug #109: Segmentation fault of giftool reading a crafted file * Fix SF bug #107: Floating point exception in giftext utility -* Fix SF bug: #105 heap buffer overflow in DumpScreen2RGB in gif2rgb.c:317 +* Fix SF bug #105: heap buffer overflow in DumpScreen2RGB in gif2rgb.c:317 * Fix SF bug #104: Ineffective bounds check in DGifSlurp ^ Fix SF bug #103: GIFLIB 5.1.4: DGifSlurp fails on empty comment -* Fix SF bug #87 Heap buffer overflow in 5.1.2 (gif2rgb). +* Fix SF bug #87: Heap buffer overflow in 5.1.2 (gif2rgb). Build Fixes ----------- diff --git a/build.adoc b/build.adoc index f92adaa..5527b8e 100644 --- a/build.adoc +++ b/build.adoc @@ -33,7 +33,7 @@ Microsoft's toolchains are unlikely be looked on favorably. 1. Check the SourceForge tracker for bugs and patches. -2. Bump the version number in the gif_lib.h. Do "make version" +2. Bump the version number in gif_lib.h. Do "make version" to confirm that it looks sane when extracted to the Makefile. 3. Version-stamp the top entry in the NEWS file. diff --git a/debian/changelog b/debian/changelog index 58e4934..25dd7a0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,94 @@ +giflib (5.2.2-1deepin1) unstable; urgency=medium + + * Fix CVE-2025-31344 + + -- Tianyu Chen Sun, 28 Sep 2025 16:33:36 +0800 + +giflib (5.2.2-1) unstable; urgency=medium + + [ Debian Janitor] + * Update standards version to 4.6.1, no changes needed. + * Remove obsolete fields Contact, Name from debian/upstream/metadata + (already present in machine-readable debian/copyright). + * Use secure URI in Homepage field. + + [ Andreas Metzler ] + * debian/patches: + + Drop patches applied upstream (fix-get-args-segment-violation.patch + fix-spelling-errors-on-doc-pages.patch recover-giffilter-docs.patch + add-gifsponge-docs.patch) + + Drop superseded patch (install-only-distributed-binaries-manuals.patch). + + Unfuzzz patches + + Features fixes for CVE-2023-48161, CVE-2022-28506. + * Cherry-pick Correct-document-page-install.patch to install manpages + instead of xml source. + * Install giflib.7 manpage. + * Update symbol file (added DGifDecreaseImageCounter@Base). + * Cherry-pick Clean-up-memory-better-at-end-of-run-CVE-2021-40633.patch to + fix CVE-2021-40633. + * Add lintian overrides for html files built from xml (source-is-missing). + + [ David Suárez ] + * New upstream version; + Closes: #1024988, #988151, #1014586, #1045040, 1049653 + * Update email to debian domain. + * Wrap and sort. + * Add 'dont-build-html-pages-images' patch. + * Acknowledges NMU's uploads. + * Add 'salsa-ci.yml'. + + -- David Suárez Sun, 25 Feb 2024 17:44:51 +0000 + +giflib (5.2.1-2.5) unstable; urgency=medium + + * Non-maintainer upload + * Move declaration of GifQuantizeBuffer() back to gif_lib.h + (Closes: #1011705) + + -- Graham Inggs Sun, 12 Jun 2022 16:32:15 +0000 + +giflib (5.2.1-2.4) unstable; urgency=medium + + * Non-maintainer upload. + * Upload to unstable. + + -- Mattia Rizzolo Wed, 27 Apr 2022 20:17:29 +0200 + +giflib (5.2.1-2.3) experimental; urgency=medium + + * Non-maintainer upload. + * Merge experimental git branch into master + * tests: do not spoil test result files with stderr + + -- Vasyl Gello Fri, 22 Apr 2022 04:06:59 +0000 + +giflib (5.2.1-2.2) experimental; urgency=medium + + * Non-maintainer upload. + * Add patch from Fedora (giflib_quantize.patch) to reinstate + GifQuantizeBuffer into giflib.so. Closes: #935088 + * Re-add the symbols in libgif7.symbols. + + -- Mattia Rizzolo Thu, 14 Apr 2022 16:06:15 +0200 + +giflib (5.1.9-2.1) unstable; urgency=medium + + * Non-maintainer upload. + * Ship pkg-config information. + * Mark libgif-dev as Multi-Arch:same. (Closes: #998128) + * Bump standards version to 4.6.0, no changes required. + + -- Vasyl Gello Thu, 14 Apr 2022 15:59:53 +0200 + +giflib (5.2.1-2.1) experimental; urgency=medium + + * Non-maintainer upload. + * Set Maintainer to David Suárez , to match + unstable and fix a QA check that is warning about orphaned package but + missing O bug. + + -- Mattia Rizzolo Mon, 07 Jun 2021 14:41:34 +0200 + giflib (5.1.9-2) unstable; urgency=medium * New Debian version. @@ -71,6 +162,77 @@ giflib (5.1.9-1) unstable; urgency=medium -- David Suárez Sun, 08 Dec 2019 21:18:23 +0100 +giflib (5.2.1-2) experimental; urgency=low + + * QA upload. + * Run "wrap-and-sort --max-line-length=72 --short-indent"; also wrap package + descriptions. + * Pass -c4 to dpkg-gensymbols. + + -- Andreas Metzler Sun, 25 Aug 2019 18:13:08 +0200 + +giflib (5.2.1-1) experimental; urgency=low + + * QA upload. + * New upstream version. + + Drop 03-spelling_fixes.patch and 40_fixtestsuite.diff. + + Unfuzz and extend (for libutil addition) + 30_link_utils_dynamically.diff. + + Do not ship gif standards in txt format with giflib-tools. + + Update symbol file. Symbols which were only accidentally exported were + removed. For consumption by giflib-tools a small (static) helper library + (libutil) still provides the symbols. + + Pass CC=$(CC) to dh_auto_build + + -- Andreas Metzler Sun, 18 Aug 2019 14:32:47 +0200 + +giflib (5.1.8-1) experimental; urgency=low + + * QA upload. + * New upstream version: + + Drop 20_giflib_soname.patch and 25_giflib_mandir.patch. + + Unfuzz 30_link_utils_dynamically.diff. + + Fixes CVE-2019-15133 - SF bug #119: MemorySanitizer: FPE on unknown + address + * 40_fixtestsuite.diff: Do not use temporary files in /tmp with a predefined + name in testsuite. This also fixes a testsuite error with parallel builds. + * debian/rules: Remove prebuilt manpages before build instead of before + install. + + -- Andreas Metzler Sun, 17 Mar 2019 19:08:37 +0100 + +giflib (5.1.7-1) experimental; urgency=low + + * QA upload. + + [ Ondřej Nový ] + * d/watch: Use https protocol + + [ Andreas Metzler ] + * New upstream version: + + Refresh 03-spelling_fixes.patch. + + Drop CVE-2016-3977.patch, included upstream. + + AUTHORS file not shipped anymore, update debian/*.docs. + + Uses straight make instead of autotools, adapt debian/rules accordingly. + + 20_giflib_soname.patch from Fedora: Set soname on built library. + + Update symbol file. + + Fixes heap-based buffer overflow in DGifDecompressLine function. + CVE-2018-11490 sf#113 Closes: #904114 + + Update debian/copyright, add Format specifier. + * [lintian] Add Build-Depends-Package info to symbol file. + * Use dh 12 compat level. + * Add 25_giflib_mandir.patch to fix the manpage install path and ship the + installed manpages. + * Set Rules-Requires-Root: no. + * 30_link_utils_dynamically.diff: Link giflib-tools dynamically instead of + statically against giflib. + * Delete debian/libgif7.shlibs (Unused, we have a symbol file) and + debian/giflib-dbg.docs. + * debian/copyright: More maintainer history/copyright, assuming NMUs did not + feature big copyrightable changes. + + -- Andreas Metzler Sun, 17 Mar 2019 13:40:22 +0100 + giflib (5.1.4-3) unstable; urgency=medium * QA upload. diff --git a/debian/control b/debian/control index ff06eaa..7418750 100644 --- a/debian/control +++ b/debian/control @@ -1,12 +1,12 @@ Source: giflib Section: libs Priority: optional -Maintainer: David Suárez +Maintainer: David Suárez Build-Depends: debhelper-compat (= 13), xmlto -Standards-Version: 4.5.0 -Homepage: http://giflib.sourceforge.net/ -Vcs-Git: https://salsa.debian.org/deiv-guest/giflib.git -Vcs-Browser: https://salsa.debian.org/deiv-guest/giflib +Standards-Version: 4.6.1 +Homepage: https://giflib.sourceforge.net/ +Vcs-Git: https://salsa.debian.org/debian/giflib.git +Vcs-Browser: https://salsa.debian.org/debian/giflib Rules-Requires-Root: no Package: giflib-tools @@ -17,8 +17,8 @@ Depends: libperl4-corelibs-perl | perl (<< 5.12.3-7), ${shlibs:Depends} Provides: libungif-bin Description: library for GIF images (utilities) - GIFLIB is a package of portable tools and library routines for working with GIF - images. + GIFLIB is a package of portable tools and library routines for working with + GIF images. . This package contains additional utilities. @@ -28,17 +28,18 @@ Pre-Depends: ${misc:Pre-Depends} Depends: ${misc:Depends}, ${shlibs:Depends} Multi-Arch: same Description: library for GIF images (library) - GIFLIB is a package of portable tools and library routines for working with GIF - images. + GIFLIB is a package of portable tools and library routines for working with + GIF images. . This package contains the library. Package: libgif-dev Section: libdevel Architecture: any +Multi-Arch: same Depends: libgif7 (= ${binary:Version}), ${misc:Depends} Description: library for GIF images (development) - GIFLIB is a package of portable tools and library routines for working with GIF - images. + GIFLIB is a package of portable tools and library routines for working with + GIF images. . This package contains the development files. diff --git a/debian/gbp.conf b/debian/gbp.conf new file mode 100644 index 0000000..b4d264a --- /dev/null +++ b/debian/gbp.conf @@ -0,0 +1,10 @@ +[DEFAULT] +debian-branch = debian/latest +upstream-branch = upstream/latest +pristine-tar=True + +[buildpackage] +export-dir = ../build-area/ + +[import-orig] +filter=[ '.gitignore', '.travis.yml', '.git*' ] diff --git a/debian/giflib-tools.manpages b/debian/giflib-tools.manpages index 20b9957..512e183 100644 --- a/debian/giflib-tools.manpages +++ b/debian/giflib-tools.manpages @@ -1 +1,2 @@ usr/share/man/man1/*.1* +usr/share/man/man7/*.7* diff --git a/debian/libgif-dev.install b/debian/libgif-dev.install index 14671d6..1e11df4 100644 --- a/debian/libgif-dev.install +++ b/debian/libgif-dev.install @@ -1,3 +1,4 @@ /usr/include/* /usr/lib/*/*.a /usr/lib/*/*.so +/usr/lib/*/pkgconfig/* diff --git a/debian/libgif7.symbols b/debian/libgif7.symbols index b847338..09c1e47 100644 --- a/debian/libgif7.symbols +++ b/debian/libgif7.symbols @@ -1,6 +1,7 @@ libgif.so.7 libgif7 #MINVER# * Build-Depends-Package: libgif-dev DGifCloseFile@Base 5.1 + DGifDecreaseImageCounter@Base 5.2.2 DGifExtensionToGCB@Base 5.1 DGifGetCode@Base 5.1 DGifGetCodeNext@Base 5.1 diff --git a/debian/patches/0008-Fix-CVE-2025-31344.patch b/debian/patches/0008-Fix-CVE-2025-31344.patch new file mode 100644 index 0000000..c967e45 --- /dev/null +++ b/debian/patches/0008-Fix-CVE-2025-31344.patch @@ -0,0 +1,23 @@ +From: =?UTF-8?q?Bernhard=20Rosenkr=C3=A4nzer?= +Date: Mon, 7 Apr 2025 21:47:30 +0200 +Subject: Fix CVE-2025-31344 + +--- + gif2rgb.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/gif2rgb.c b/gif2rgb.c +index fc2e683..3345085 100644 +--- a/gif2rgb.c ++++ b/gif2rgb.c +@@ -329,6 +329,10 @@ static void DumpScreen2RGB(char *FileName, int OneFileFlag, + GifRow = ScreenBuffer[i]; + GifQprintf("\b\b\b\b%-4d", ScreenHeight - i); + for (j = 0; j < ScreenWidth; j++) { ++ /* Check if color is within color palete */ ++ if (GifRow[j] >= ColorMap->ColorCount) { ++ GIF_EXIT(GifErrorString(D_GIF_ERR_IMAGE_DEFECT)); ++ } + ColorMapEntry = &ColorMap->Colors[GifRow[j]]; + Buffers[0][j] = ColorMapEntry->Red; + Buffers[1][j] = ColorMapEntry->Green; diff --git a/debian/patches/30_link_utils_dynamically.diff b/debian/patches/30_link_utils_dynamically.diff new file mode 100644 index 0000000..bc7991f --- /dev/null +++ b/debian/patches/30_link_utils_dynamically.diff @@ -0,0 +1,60 @@ +Description: Link giflib-tools dynamically against giflib +Author: Andreas Metzler +Origin: vendor +Bug: https://sourceforge.net/p/giflib/bugs/123/ +Last-Update: 2024-02-24 + +--- a/Makefile ++++ b/Makefile +@@ -59,11 +59,11 @@ UTILS = $(INSTALLABLE) \ + gifhisto \ + gifinto \ + gifsponge \ + gifwedge + +-LDLIBS=libgif.a -lm ++LDLIBS=-lm + + MANUAL_PAGES_1 = \ + doc/gif2rgb.xml \ + doc/gifbuild.xml \ + doc/gifclrmp.xml \ +@@ -95,19 +95,22 @@ all: $(LIBGIFSO) libgif.a $(LIBUTILSO) l + ifeq ($(UNAME), Darwin) + else + $(MAKE) -C doc + endif + +-$(UTILS):: libgif.a libutil.a ++$(UTILS):: $(LIBGIFSOMAJOR) libutil.a + + $(LIBGIFSO): $(OBJECTS) $(HEADERS) + ifeq ($(UNAME), Darwin) + $(CC) $(CFLAGS) -dynamiclib -current_version $(LIBVER) $(OBJECTS) -o $(LIBGIFSO) + else + $(CC) $(CFLAGS) -shared $(LDFLAGS) -Wl,-soname -Wl,$(LIBGIFSOMAJOR) -o $(LIBGIFSO) $(OBJECTS) + endif + ++$(LIBGIFSOMAJOR): $(LIBGIFSO) ++ ln -s $< $@ ++ + libgif.a: $(OBJECTS) $(HEADERS) + $(AR) rcs libgif.a $(OBJECTS) + + $(LIBUTILSO): $(UOBJECTS) $(UHEADERS) + ifeq ($(UNAME), Darwin) +--- a/tests/makefile ++++ b/tests/makefile +@@ -1,10 +1,12 @@ + # Regression-test suite for the giflib library and tools + # All utilities have tests except gifbg, gifcolor, and gifhisto. + + .SUFFIXES: .gif .rgb + ++export LD_LIBRARY_PATH=$(CURDIR)/.. ++ + # This is what to do by default + test: render-regress \ + gifbuild-regress \ + gifclrmp-regress \ + gifecho-regress \ diff --git a/debian/patches/Clean-up-memory-better-at-end-of-run-CVE-2021-40633.patch b/debian/patches/Clean-up-memory-better-at-end-of-run-CVE-2021-40633.patch new file mode 100644 index 0000000..9002088 --- /dev/null +++ b/debian/patches/Clean-up-memory-better-at-end-of-run-CVE-2021-40633.patch @@ -0,0 +1,30 @@ +From ccbc956432650734c91acb3fc88837f7b81267ff Mon Sep 17 00:00:00 2001 +From: "Eric S. Raymond" +Date: Wed, 21 Feb 2024 18:55:00 -0500 +Subject: [PATCH] Clean up memory better at end of run (CVE-2021-40633) + +--- + gif2rgb.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/gif2rgb.c b/gif2rgb.c +index d51226d..fc2e683 100644 +--- a/gif2rgb.c ++++ b/gif2rgb.c +@@ -515,10 +515,13 @@ static void GIF2RGB(int NumFiles, char *FileName, bool OneFileFlag, + } + + DumpScreen2RGB(OutFileName, OneFileFlag, ColorMap, ScreenBuffer, + GifFile->SWidth, GifFile->SHeight); + ++ for (i = 0; i < GifFile->SHeight; i++) { ++ (void)free(ScreenBuffer[i]); ++ } + (void)free(ScreenBuffer); + + { + int Error; + if (DGifCloseFile(GifFile, &Error) == GIF_ERROR) { +-- +2.43.0 + diff --git a/debian/patches/Correct-document-page-install.patch b/debian/patches/Correct-document-page-install.patch new file mode 100644 index 0000000..4e10d86 --- /dev/null +++ b/debian/patches/Correct-document-page-install.patch @@ -0,0 +1,58 @@ +From 61f375082c80ee479eb8ff03189aea691a6a06aa Mon Sep 17 00:00:00 2001 +From: "Eric S. Raymond" +Date: Wed, 21 Feb 2024 08:33:51 -0500 +Subject: [PATCH] Correct document page install. + +--- + Makefile | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/Makefile b/Makefile +index 87966a9..f4ecb24 100644 +--- a/Makefile ++++ b/Makefile +@@ -61,19 +61,23 @@ UTILS = $(INSTALLABLE) \ + gifsponge \ + gifwedge + + LDLIBS=libgif.a -lm + +-MANUAL_PAGES = \ ++MANUAL_PAGES_1 = \ + doc/gif2rgb.xml \ + doc/gifbuild.xml \ + doc/gifclrmp.xml \ + doc/giffix.xml \ +- doc/giflib.xml \ + doc/giftext.xml \ + doc/giftool.xml + ++MANUAL_PAGES_7 = \ ++ doc/giflib.xml ++ ++MANUAL_PAGES = $(MANUAL_PAGES_1) $(MANUAL_PAGES_7) ++ + SOEXTENSION = so + LIBGIFSO = libgif.$(SOEXTENSION) + LIBGIFSOMAJOR = libgif.$(SOEXTENSION).$(LIBMAJOR) + LIBGIFSOVER = libgif.$(SOEXTENSION).$(LIBVER) + LIBUTILSO = libutil.$(SOEXTENSION) +@@ -146,12 +150,13 @@ install-lib: + $(INSTALL) -m 644 libgif.a "$(DESTDIR)$(LIBDIR)/libgif.a" + $(INSTALL) -m 755 $(LIBGIFSO) "$(DESTDIR)$(LIBDIR)/$(LIBGIFSOVER)" + ln -sf $(LIBGIFSOVER) "$(DESTDIR)$(LIBDIR)/$(LIBGIFSOMAJOR)" + ln -sf $(LIBGIFSOMAJOR) "$(DESTDIR)$(LIBDIR)/$(LIBGIFSO)" + install-man: +- $(INSTALL) -d "$(DESTDIR)$(MANDIR)/man1" +- $(INSTALL) -m 644 $(MANUAL_PAGES) "$(DESTDIR)$(MANDIR)/man1" ++ $(INSTALL) -d "$(DESTDIR)$(MANDIR)/man1" "$(DESTDIR)$(MANDIR)/man7" ++ $(INSTALL) -m 644 $(MANUAL_PAGES_1:xml=1) "$(DESTDIR)$(MANDIR)/man1" ++ $(INSTALL) -m 644 $(MANUAL_PAGES_7:xml=7) "$(DESTDIR)$(MANDIR)/man7" + uninstall: uninstall-man uninstall-include uninstall-lib uninstall-bin + uninstall-bin: + cd "$(DESTDIR)$(BINDIR)" && rm -f $(INSTALLABLE) + uninstall-include: + rm -f "$(DESTDIR)$(INCDIR)/gif_lib.h" +-- +2.43.0 + diff --git a/debian/patches/add-gifsponge-docs.patch b/debian/patches/add-gifsponge-docs.patch deleted file mode 100644 index d29fee0..0000000 --- a/debian/patches/add-gifsponge-docs.patch +++ /dev/null @@ -1,69 +0,0 @@ -Description: Add gifsponge docs -Author: David Suárez -Origin: vendor -Bug: https://sourceforge.net/p/giflib/bugs/155/ -Last-Update: 2020-12-20 - ---- a/Makefile -+++ b/Makefile -@@ -71,6 +71,7 @@ - doc/giffix.1 \ - doc/gifinto.1 \ - doc/giflib.1 \ -+ doc/gifsponge.1 \ - doc/giftext.1 \ - doc/giftool.1 - ---- a/doc/Makefile -+++ b/doc/Makefile -@@ -26,6 +26,7 @@ - giffix.xml \ - gifinto.xml \ - giflib.xml \ -+ gifsponge.xml \ - giftext.xml \ - giftool.xml - XMLINTERNAL = \ ---- /dev/null -+++ b/doc/gifsponge.xml -@@ -0,0 +1,40 @@ -+ -+ -+]> -+ -+20 Dec 2020 -+ -+gifsponge -+1 -+GIFLIB -+GIFLIB Documentation -+ -+ -+gifsponge -+expensive GIF copy, a model for slurp utilities -+ -+ -+ -+ -+ -+ gifsponge -+ -+ -+ -+Description -+ -+Slurp a GIF into core, operate on it, spew it out again. -+This is an expensive way to copy a GIF. The source is included -+as a skeleton for more sophisticated slurp utilities. See the source in the -+util directory for details. -+ -+ -+Author -+ -+David Suárez &email; -+ -+ -+ diff --git a/debian/patches/dont-build-html-pages-images.diff b/debian/patches/dont-build-html-pages-images.diff new file mode 100644 index 0000000..7d4fe35 --- /dev/null +++ b/debian/patches/dont-build-html-pages-images.diff @@ -0,0 +1,18 @@ +Description: Don't build the site HTML pages images. + It saves us to have ImageMagick as a b-depend. +Author: David Suárez +Origin: vendor +Last-Update: 2024-03-24 +Forwarded: not-needed + +--- a/doc/Makefile ++++ b/doc/Makefile +@@ -46,7 +46,7 @@ + convert $^ -resize 50x50 $@ + + # Philosophical choice: the website gets the internal manual pages +-allhtml: $(XMLALL:.xml=.html) giflib-logo.gif ++allhtml: $(XMLALL:.xml=.html) + + manpages: $(XMLMAN1:.xml=.1) $(XMLMAN7:.xml=.7) $(XMLINTERNAL:.xml=.1) + diff --git a/debian/patches/dont-spoil-tests-with-stderr.patch b/debian/patches/dont-spoil-tests-with-stderr.patch new file mode 100644 index 0000000..6200abe --- /dev/null +++ b/debian/patches/dont-spoil-tests-with-stderr.patch @@ -0,0 +1,44 @@ +Description: Do not spoil tests with stderror output +Author: Vasyl Gello +Last-Update: 2022-04-22 +Origin: vendor +Forwarded: not-needed + +--- a/tests/makefile ++++ b/tests/makefile +@@ -74,7 +74,7 @@ + do \ + stem=`basename $${test} | sed -e "s/.gif$$//"`; \ + if echo "gifclrmap: Checking colormap of $${test}" >&2; \ +- $(UTILS)/gifclrmp <$${test} > $@.$${stem}.regress 2>&1; \ ++ $(UTILS)/gifclrmp <$${test} > $@.$${stem}.regress; \ + then diff -u $${stem}.map $@.$${stem}.regress; \ + else echo "*** Nonzero return status on $${test}!"; exit 1; fi; \ + done +@@ -98,7 +98,7 @@ + do \ + stem=`basename $${test} | sed -e "s/.gif$$//"`; \ + if echo "giffiltr: Testing copy of $${test}" >&2; \ +- $(UTILS)/giffilter <$${test} | $(UTILS)/gif2rgb > $@.$${stem}.regress 2>&1; \ ++ $(UTILS)/giffilter <$${test} | $(UTILS)/gif2rgb > $@.$${stem}.regress; \ + then cmp $${stem}.rgb $@.$${stem}.regress; \ + else echo "*** Nonzero return status on $${test}!"; exit 1; fi; \ + done +@@ -126,7 +126,7 @@ + do \ + stem=`basename $${test} | sed -e "s/.gif$$//"`; \ + if echo "gifsponge: Testing copy of $${test}" >&2; \ +- $(UTILS)/gifsponge <$${test} | $(UTILS)/gif2rgb > $@.$${stem}.regress 2>&1; \ ++ $(UTILS)/gifsponge <$${test} | $(UTILS)/gif2rgb > $@.$${stem}.regress; \ + then cmp $${stem}.rgb $@.$${stem}.regress; \ + else echo "*** Nonzero return status on $${test}!"; exit 1; fi; \ + done +@@ -137,7 +137,7 @@ + do \ + stem=`basename $${test} | sed -e "s/.gif$$//"`; \ + if echo "giftext: Checking text dump of $${test}" >&2; \ +- $(UTILS)/giftext <$${test} > $@.$${stem}.regress 2>&1; \ ++ $(UTILS)/giftext <$${test} > $@.$${stem}.regress; \ + then diff -u $${stem}.dmp $@.$${stem}.regress; \ + else echo "*** Nonzero return status on $${test}!"; exit 1; fi; \ + done diff --git a/debian/patches/fix-get-args-segment-violation.patch b/debian/patches/fix-get-args-segment-violation.patch deleted file mode 100644 index 1595450..0000000 --- a/debian/patches/fix-get-args-segment-violation.patch +++ /dev/null @@ -1,24 +0,0 @@ -Description: Fix segmentation faults due to non correct checking for args -Author: David Suárez -Origin: vendor -Bug: https://sourceforge.net/p/giflib/bugs/153/ -Bug-Debian: https://bugs.debian.org/715963 -Bug-Debian: https://bugs.debian.org/715964 -Bug-Debian: https://bugs.debian.org/715967 -Last-Update: 2020-12-20 - ---- a/getarg.c -+++ b/getarg.c -@@ -305,6 +305,12 @@ - int i = 0, ScanRes; - - while (!(ISSPACE(CtrlStrCopy[i]))) { -+ -+ if ((*argv) == argv_end) { -+ GAErrorToken = Option; -+ return CMD_ERR_NumRead; -+ } -+ - switch (CtrlStrCopy[i + 1]) { - case 'd': /* Get signed integers. */ - ScanRes = sscanf(*((*argv)++), "%d", diff --git a/debian/patches/fix-spelling-errors-on-doc-pages.patch b/debian/patches/fix-spelling-errors-on-doc-pages.patch deleted file mode 100644 index 61b32de..0000000 --- a/debian/patches/fix-spelling-errors-on-doc-pages.patch +++ /dev/null @@ -1,29 +0,0 @@ -Description: Fix some typos on the html documentation and man pages -Author: David Suárez -Origin: vendor -Bug: https://sourceforge.net/p/giflib/bugs/152/ -Bug-Debian: https://bugs.debian.org/857609 -Last-Update: 2020-12-20 - ---- a/doc/gif_lib.xml -+++ b/doc/gif_lib.xml -@@ -280,7 +280,7 @@ - Create the union of two given color maps and return it. If the result - won't fit into 256 colors, NULL is returned, the allocated union - otherwise. ColorIn1 is copied as it to ColorUnion, while colors from --ColorIn2 are copied iff they didn't exist before. ColorTransIn2 maps -+ColorIn2 are copied if they didn't exist before. ColorTransIn2 maps - the old ColorIn2 into ColorUnion color map table. - - ---- a/doc/gifinto.xml -+++ b/doc/gifinto.xml -@@ -28,7 +28,7 @@ - - Description - --A program to save stdin into a file with given name, iff the -+A program to save stdin into a file with given name, if the - result file has size bigger than specified (see below). This can be - used to save a result under the same filename we started with in a - chain of pipes. diff --git a/debian/patches/giflib_quantize-header.patch b/debian/patches/giflib_quantize-header.patch new file mode 100644 index 0000000..4434109 --- /dev/null +++ b/debian/patches/giflib_quantize-header.patch @@ -0,0 +1,48 @@ +Description: Move declaration of GifQuantizeBuffer() back to gif_lib.h +Bug-Debian: https://bugs.debian.org/1011705 +Author: Graham Inggs , Andreas Metzler +Last-Update: 2024-02-24 + +--- a/gif_lib.h ++++ b/gif_lib.h +@@ -210,10 +210,19 @@ int DGifGetCode(GifFileType *GifFile, in + int DGifGetCodeNext(GifFileType *GifFile, GifByteType **GifCodeBlock); + int DGifGetLZCodes(GifFileType *GifFile, int *GifCode); + const char *DGifGetGifVersion(GifFileType *GifFile); + + /****************************************************************************** ++ Color table quantization ++******************************************************************************/ ++int GifQuantizeBuffer(unsigned int Width, unsigned int Height, ++ int *ColorMapSize, const GifByteType *RedInput, ++ const GifByteType *GreenInput, ++ const GifByteType *BlueInput, ++ GifByteType *OutputBuffer, GifColorType *OutputColorMap); ++ ++/****************************************************************************** + Error handling and reporting. + ******************************************************************************/ + extern const char *GifErrorString(int ErrorCode); /* new in 2012 - ESR */ + + /***************************************************************************** +--- a/getarg.h ++++ b/getarg.h +@@ -31,18 +31,10 @@ void GAPrintHowTo(char *CtrlStr); + From qprintf.c + ******************************************************************************/ + extern void GifQprintf(char *Format, ...); + extern void PrintGifError(int ErrorCode); + +-/****************************************************************************** +- Color table quantization +-******************************************************************************/ +-int GifQuantizeBuffer(unsigned int Width, unsigned int Height, +- int *ColorMapSize, GifByteType *RedInput, +- GifByteType *GreenInput, GifByteType *BlueInput, +- GifByteType *OutputBuffer, GifColorType *OutputColorMap); +- + /* These used to live in the library header */ + #define GIF_MESSAGE(Msg) fprintf(stderr, "\n%s: %s\n", PROGRAM_NAME, Msg) + #define GIF_EXIT(Msg) \ + { \ + GIF_MESSAGE(Msg); \ diff --git a/debian/patches/install-only-distributed-binaries-manuals.patch b/debian/patches/install-only-distributed-binaries-manuals.patch deleted file mode 100644 index 58588fe..0000000 --- a/debian/patches/install-only-distributed-binaries-manuals.patch +++ /dev/null @@ -1,35 +0,0 @@ -Description: Only install the manuals for installed binaries -Author: David Suárez -Origin: vendor -Bug: https://sourceforge.net/p/giflib/bugs/145/ -Last-Update: 2019-11-25 - ---- a/Makefile -+++ b/Makefile -@@ -62,6 +62,17 @@ - - LDLIBS=libgif.a -lm - -+MANUAL_PAGES = \ -+ doc/gif2rgb.1 \ -+ doc/gifbuild.1 \ -+ doc/gifclrmp.1 \ -+ doc/gifecho.1 \ -+ doc/giffix.1 \ -+ doc/gifinto.1 \ -+ doc/giflib.1 \ -+ doc/giftext.1 \ -+ doc/giftool.1 -+ - all: libgif.so libgif.a libutil.so libutil.a $(UTILS) - $(MAKE) -C doc - -@@ -105,7 +116,7 @@ - ln -sf libgif.so.$(LIBMAJOR) "$(DESTDIR)$(LIBDIR)/libgif.so" - install-man: - $(INSTALL) -d "$(DESTDIR)$(MANDIR)/man1" -- $(INSTALL) -m 644 doc/*.1 "$(DESTDIR)$(MANDIR)/man1" -+ $(INSTALL) -m 644 $(MANUAL_PAGES) "$(DESTDIR)$(MANDIR)/man1" - uninstall: uninstall-man uninstall-include uninstall-lib uninstall-bin - uninstall-bin: - cd "$(DESTDIR)$(BINDIR)" && rm -f $(INSTALLABLE) diff --git a/debian/patches/recover-giffilter-docs.patch b/debian/patches/recover-giffilter-docs.patch deleted file mode 100644 index 871ca42..0000000 --- a/debian/patches/recover-giffilter-docs.patch +++ /dev/null @@ -1,71 +0,0 @@ -Description: Recover the giffilter manual page -Author: David Suárez -Origin: vendor -Bug: https://sourceforge.net/p/giflib/bugs/154/ -Last-Update: 2020-12-20 - ---- /dev/null -+++ b/doc/giffilter.xml -@@ -0,0 +1,42 @@ -+ -+ -+]> -+ -+2 May 2012 -+ -+giffilter -+1 -+GIFLIB -+GIFLIB Documentation -+ -+ -+giffilter -+expensive GIF copy, a model for filter utilities -+ -+ -+ -+ -+ -+ giffilter -+ -+ -+ -+Description -+ -+This is an expensive way to copy a GIF. The source is included -+as a skeleton for more sophisticated filters. See the source in the -+util directory for details. -+ -+Also has some utility as a test of the sequential GIF record I/O -+routines. The output should be bytewise identical to the input. -+ -+ -+Author -+ -+Eric S. Raymond &email; -+ -+ -+ ---- a/Makefile -+++ b/Makefile -@@ -67,6 +67,7 @@ - doc/gifbuild.1 \ - doc/gifclrmp.1 \ - doc/gifecho.1 \ -+ doc/giffilter.1 \ - doc/giffix.1 \ - doc/gifinto.1 \ - doc/giflib.1 \ ---- a/doc/Makefile -+++ b/doc/Makefile -@@ -22,6 +22,7 @@ - gifbuild.xml \ - gifclrmp.xml \ - gifecho.xml \ -+ giffilter.xml \ - giffix.xml \ - gifinto.xml \ - giflib.xml \ diff --git a/debian/patches/revert-GifQuantizeBuffer-remove-from-lib.patch b/debian/patches/revert-GifQuantizeBuffer-remove-from-lib.patch index e8ed7b3..022961b 100644 --- a/debian/patches/revert-GifQuantizeBuffer-remove-from-lib.patch +++ b/debian/patches/revert-GifQuantizeBuffer-remove-from-lib.patch @@ -6,7 +6,9 @@ Description: Revert the remove of GifQuantizeBuffer symbol from the main lib. need to links against it. Author: David Suárez Origin: vendor -Last-Update: 2019-12-07 +Last-Update: 2024-03-24 +Bug: https://sourceforge.net/p/giflib/bugs/142/ +Bug-Debian: https://bugs.debian.org/935088 --- a/Makefile +++ b/Makefile diff --git a/debian/patches/series b/debian/patches/series index 279e5bd..58f1980 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,6 +1,8 @@ -fix-get-args-segment-violation.patch -install-only-distributed-binaries-manuals.patch +dont-build-html-pages-images.diff +Correct-document-page-install.patch revert-GifQuantizeBuffer-remove-from-lib.patch -fix-spelling-errors-on-doc-pages.patch -recover-giffilter-docs.patch -add-gifsponge-docs.patch +30_link_utils_dynamically.diff +dont-spoil-tests-with-stderr.patch +giflib_quantize-header.patch +Clean-up-memory-better-at-end-of-run-CVE-2021-40633.patch +0008-Fix-CVE-2025-31344.patch diff --git a/debian/pkgconfig/libgif7.pc.in b/debian/pkgconfig/libgif7.pc.in new file mode 100644 index 0000000..05e6841 --- /dev/null +++ b/debian/pkgconfig/libgif7.pc.in @@ -0,0 +1,10 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${prefix}/lib/@DEB_TARGET_MULTIARCH@ +includedir=${prefix}/include + +Name: libgif +Description: Loads and saves GIF files +Version: @VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -lgif diff --git a/debian/rules b/debian/rules index 47070db..40661d2 100755 --- a/debian/rules +++ b/debian/rules @@ -1,6 +1,14 @@ #!/usr/bin/make -f -export DEB_BUILD_MAINT_OPTIONS := hardening=+bindnow +export DH_ALWAYS_EXCLUDE=CVS:.idea + +# Include /usr/share/dpkg/pkg-info.mk to get DEB_{SOURCE,VERSION} +include /usr/share/dpkg/pkg-info.mk + +# Include /usr/share/dpkg/architecture.mk to get DEB_HOST_{MULTI,}ARCH +include /usr/share/dpkg/architecture.mk + +export DEB_BUILD_MAINT_OPTIONS := hardening=+all export DPKG_GENSYMBOLS_CHECK_LEVEL := 4 doc_html = \ @@ -27,6 +35,16 @@ override_dh_auto_build: LIBDIR=/usr/lib/$(DEB_TARGET_MULTIARCH) override_dh_auto_install: + # Installing pkgconfig information + mkdir -p debian/tmp/usr/lib/$(DEB_TARGET_MULTIARCH)/pkgconfig + sed -e "s/@DEB_TARGET_MULTIARCH@/$(DEB_TARGET_MULTIARCH)/" \ + -e "s/@VERSION@/$(DEB_VERSION_UPSTREAM)/" \ + debian/pkgconfig/libgif7.pc.in \ + 1>debian/tmp/usr/lib/$(DEB_TARGET_MULTIARCH)/pkgconfig/libgif7.pc + ln -sf /usr/lib/$(DEB_TARGET_MULTIARCH)/pkgconfig/libgif7.pc \ + debian/tmp/usr/lib/$(DEB_TARGET_MULTIARCH)/pkgconfig/libgif.pc + + # Performing an installation step dh_auto_install -- PREFIX=/usr \ LIBDIR=/usr/lib/$(DEB_TARGET_MULTIARCH) diff --git a/debian/salsa-ci.yml b/debian/salsa-ci.yml new file mode 100644 index 0000000..33c3a64 --- /dev/null +++ b/debian/salsa-ci.yml @@ -0,0 +1,4 @@ +--- +include: + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml diff --git a/debian/source/lintian-overrides b/debian/source/lintian-overrides index 0bbe693..292e080 100644 --- a/debian/source/lintian-overrides +++ b/debian/source/lintian-overrides @@ -1,2 +1,20 @@ # don't use the redirector giflib source: debian-watch-lacks-sourceforge-redirector +# all of these are built from xml files in the same directory. +giflib source: source-is-missing [doc/gif2rgb.html] +giflib source: source-is-missing [doc/gif_lib.html] +giflib source: source-is-missing [doc/gifbg.html] +giflib source: source-is-missing [doc/gifbuild.html] +giflib source: source-is-missing [doc/gifclrmp.html] +giflib source: source-is-missing [doc/gifcolor.html] +giflib source: source-is-missing [doc/gifecho.html] +giflib source: source-is-missing [doc/giffilter.html] +giflib source: source-is-missing [doc/giffix.html] +giflib source: source-is-missing [doc/gifhisto.html] +giflib source: source-is-missing [doc/gifinto.html] +giflib source: source-is-missing [doc/giflib.html] +giflib source: source-is-missing [doc/gifsponge.html] +giflib source: source-is-missing [doc/giftext.html] +giflib source: source-is-missing [doc/giftool.html] +giflib source: source-is-missing [doc/gifwedge.html] +giflib source: source-is-missing [doc/intro.html] diff --git a/debian/upstream/metadata b/debian/upstream/metadata index 08e273c..e1f3306 100644 --- a/debian/upstream/metadata +++ b/debian/upstream/metadata @@ -1,7 +1,5 @@ --- Archive: Sourceforge Bug-Database: https://sourceforge.net/p/giflib/bugs/ -Contact: https://sourceforge.net/p/giflib/discussion/ -Name: giflib Repository: https://git.code.sf.net/p/giflib/code Repository-Browse: https://sourceforge.net/p/giflib/code/ci/master/tree/ diff --git a/dgif_lib.c b/dgif_lib.c index c6291e7..cbcf23f 100644 --- a/dgif_lib.c +++ b/dgif_lib.c @@ -6,13 +6,15 @@ The functions here and in egif_lib.c are partitioned carefully so that if you only require one of read and write capability, only one of these two modules will be linked. Preserve this property! +SPDX-License-Identifier: MIT + *****************************************************************************/ -#include +#include #include #include -#include #include +#include #include #ifdef _WIN32 @@ -25,22 +27,23 @@ two modules will be linked. Preserve this property! #include "gif_lib_private.h" /* compose unsigned little endian value */ -#define UNSIGNED_LITTLE_ENDIAN(lo, hi) ((lo) | ((hi) << 8)) +#define UNSIGNED_LITTLE_ENDIAN(lo, hi) ((lo) | ((hi) << 8)) /* avoid extra function call in case we use fread (TVT) */ static int InternalRead(GifFileType *gif, GifByteType *buf, int len) { - //fprintf(stderr, "### Read: %d\n", len); - return - (((GifFilePrivateType*)gif->Private)->Read ? - ((GifFilePrivateType*)gif->Private)->Read(gif,buf,len) : - fread(buf,1,len,((GifFilePrivateType*)gif->Private)->File)); + // fprintf(stderr, "### Read: %d\n", len); + return (((GifFilePrivateType *)gif->Private)->Read + ? ((GifFilePrivateType *)gif->Private)->Read(gif, buf, len) + : fread(buf, 1, len, + ((GifFilePrivateType *)gif->Private)->File)); } static int DGifGetWord(GifFileType *GifFile, GifWord *Word); static int DGifSetupDecompress(GifFileType *GifFile); static int DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, int LineLen); -static int DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode); +static int DGifGetPrefixChar(const GifPrefixType *Prefix, int Code, + int ClearCode); static int DGifDecompressInput(GifFileType *GifFile, int *Code); static int DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, GifByteType *NextByte); @@ -50,20 +53,19 @@ static int DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, Returns dynamically allocated GifFileType pointer which serves as the GIF info record. ******************************************************************************/ -GifFileType * -DGifOpenFileName(const char *FileName, int *Error) -{ - int FileHandle; - GifFileType *GifFile; - - if ((FileHandle = open(FileName, O_RDONLY)) == -1) { - if (Error != NULL) - *Error = D_GIF_ERR_OPEN_FAILED; - return NULL; - } - - GifFile = DGifOpenFileHandle(FileHandle, Error); - return GifFile; +GifFileType *DGifOpenFileName(const char *FileName, int *Error) { + int FileHandle; + GifFileType *GifFile; + + if ((FileHandle = open(FileName, O_RDONLY)) == -1) { + if (Error != NULL) { + *Error = D_GIF_ERR_OPEN_FAILED; + } + return NULL; + } + + GifFile = DGifOpenFileHandle(FileHandle, Error); + return GifFile; } /****************************************************************************** @@ -71,484 +73,487 @@ DGifOpenFileName(const char *FileName, int *Error) Returns dynamically allocated GifFileType pointer which serves as the GIF info record. ******************************************************************************/ -GifFileType * -DGifOpenFileHandle(int FileHandle, int *Error) -{ - char Buf[GIF_STAMP_LEN + 1]; - GifFileType *GifFile; - GifFilePrivateType *Private; - FILE *f; - - GifFile = (GifFileType *)malloc(sizeof(GifFileType)); - if (GifFile == NULL) { - if (Error != NULL) - *Error = D_GIF_ERR_NOT_ENOUGH_MEM; - (void)close(FileHandle); - return NULL; - } - - /*@i1@*/memset(GifFile, '\0', sizeof(GifFileType)); - - /* Belt and suspenders, in case the null pointer isn't zero */ - GifFile->SavedImages = NULL; - GifFile->SColorMap = NULL; - - Private = (GifFilePrivateType *)calloc(1, sizeof(GifFilePrivateType)); - if (Private == NULL) { - if (Error != NULL) - *Error = D_GIF_ERR_NOT_ENOUGH_MEM; - (void)close(FileHandle); - free((char *)GifFile); - return NULL; - } - - /*@i1@*/memset(Private, '\0', sizeof(GifFilePrivateType)); +GifFileType *DGifOpenFileHandle(int FileHandle, int *Error) { + char Buf[GIF_STAMP_LEN + 1]; + GifFileType *GifFile; + GifFilePrivateType *Private; + FILE *f; + + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + if (Error != NULL) { + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + } + (void)close(FileHandle); + return NULL; + } + + /*@i1@*/ memset(GifFile, '\0', sizeof(GifFileType)); + + /* Belt and suspenders, in case the null pointer isn't zero */ + GifFile->SavedImages = NULL; + GifFile->SColorMap = NULL; + + Private = (GifFilePrivateType *)calloc(1, sizeof(GifFilePrivateType)); + if (Private == NULL) { + if (Error != NULL) { + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + } + (void)close(FileHandle); + free((char *)GifFile); + return NULL; + } + + /*@i1@*/ memset(Private, '\0', sizeof(GifFilePrivateType)); #ifdef _WIN32 - _setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */ -#endif /* _WIN32 */ + _setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */ +#endif /* _WIN32 */ + + f = fdopen(FileHandle, "rb"); /* Make it into a stream: */ + + /*@-mustfreeonly@*/ + GifFile->Private = (void *)Private; + Private->FileHandle = FileHandle; + Private->File = f; + Private->FileState = FILE_STATE_READ; + Private->Read = NULL; /* don't use alternate input method (TVT) */ + GifFile->UserData = NULL; /* TVT */ + /*@=mustfreeonly@*/ - f = fdopen(FileHandle, "rb"); /* Make it into a stream: */ - - /*@-mustfreeonly@*/ - GifFile->Private = (void *)Private; - Private->FileHandle = FileHandle; - Private->File = f; - Private->FileState = FILE_STATE_READ; - Private->Read = NULL; /* don't use alternate input method (TVT) */ - GifFile->UserData = NULL; /* TVT */ - /*@=mustfreeonly@*/ - - /* Let's see if this is a GIF file: */ - /* coverity[check_return] */ - if (InternalRead(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) { - if (Error != NULL) - *Error = D_GIF_ERR_READ_FAILED; - (void)fclose(f); - free((char *)Private); - free((char *)GifFile); - return NULL; - } - - /* Check for GIF prefix at start of file */ - Buf[GIF_STAMP_LEN] = 0; - if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { - if (Error != NULL) - *Error = D_GIF_ERR_NOT_GIF_FILE; - (void)fclose(f); - free((char *)Private); - free((char *)GifFile); - return NULL; - } - - if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { - (void)fclose(f); - free((char *)Private); - free((char *)GifFile); - return NULL; - } - - GifFile->Error = 0; - - /* What version of GIF? */ - Private->gif89 = (Buf[GIF_VERSION_POS] == '9'); - - return GifFile; + /* Let's see if this is a GIF file: */ + /* coverity[check_return] */ + if (InternalRead(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != + GIF_STAMP_LEN) { + if (Error != NULL) { + *Error = D_GIF_ERR_READ_FAILED; + } + (void)fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + /* Check for GIF prefix at start of file */ + Buf[GIF_STAMP_LEN] = 0; + if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { + if (Error != NULL) { + *Error = D_GIF_ERR_NOT_GIF_FILE; + } + (void)fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { + (void)fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + GifFile->Error = 0; + + /* What version of GIF? */ + Private->gif89 = (Buf[GIF_VERSION_POS + 1] == '9'); + + return GifFile; } /****************************************************************************** GifFileType constructor with user supplied input function (TVT) ******************************************************************************/ -GifFileType * -DGifOpen(void *userData, InputFunc readFunc, int *Error) -{ - char Buf[GIF_STAMP_LEN + 1]; - GifFileType *GifFile; - GifFilePrivateType *Private; - - GifFile = (GifFileType *)malloc(sizeof(GifFileType)); - if (GifFile == NULL) { - if (Error != NULL) - *Error = D_GIF_ERR_NOT_ENOUGH_MEM; - return NULL; - } - - memset(GifFile, '\0', sizeof(GifFileType)); - - /* Belt and suspenders, in case the null pointer isn't zero */ - GifFile->SavedImages = NULL; - GifFile->SColorMap = NULL; - - Private = (GifFilePrivateType *)calloc(1, sizeof(GifFilePrivateType)); - if (!Private) { - if (Error != NULL) - *Error = D_GIF_ERR_NOT_ENOUGH_MEM; - free((char *)GifFile); - return NULL; - } - /*@i1@*/memset(Private, '\0', sizeof(GifFilePrivateType)); - - GifFile->Private = (void *)Private; - Private->FileHandle = 0; - Private->File = NULL; - Private->FileState = FILE_STATE_READ; - - Private->Read = readFunc; /* TVT */ - GifFile->UserData = userData; /* TVT */ - - /* Lets see if this is a GIF file: */ - /* coverity[check_return] */ - if (InternalRead(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) { - if (Error != NULL) - *Error = D_GIF_ERR_READ_FAILED; - free((char *)Private); - free((char *)GifFile); - return NULL; - } - - /* Check for GIF prefix at start of file */ - Buf[GIF_STAMP_LEN] = '\0'; - if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { - if (Error != NULL) - *Error = D_GIF_ERR_NOT_GIF_FILE; - free((char *)Private); - free((char *)GifFile); - return NULL; - } - - if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { - free((char *)Private); - free((char *)GifFile); - if (Error != NULL) - *Error = D_GIF_ERR_NO_SCRN_DSCR; - return NULL; - } - - GifFile->Error = 0; - - /* What version of GIF? */ - Private->gif89 = (Buf[GIF_VERSION_POS] == '9'); - - return GifFile; +GifFileType *DGifOpen(void *userData, InputFunc readFunc, int *Error) { + char Buf[GIF_STAMP_LEN + 1]; + GifFileType *GifFile; + GifFilePrivateType *Private; + + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + if (Error != NULL) { + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + } + return NULL; + } + + memset(GifFile, '\0', sizeof(GifFileType)); + + /* Belt and suspenders, in case the null pointer isn't zero */ + GifFile->SavedImages = NULL; + GifFile->SColorMap = NULL; + + Private = (GifFilePrivateType *)calloc(1, sizeof(GifFilePrivateType)); + if (!Private) { + if (Error != NULL) { + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + } + free((char *)GifFile); + return NULL; + } + /*@i1@*/ memset(Private, '\0', sizeof(GifFilePrivateType)); + + GifFile->Private = (void *)Private; + Private->FileHandle = 0; + Private->File = NULL; + Private->FileState = FILE_STATE_READ; + + Private->Read = readFunc; /* TVT */ + GifFile->UserData = userData; /* TVT */ + + /* Lets see if this is a GIF file: */ + /* coverity[check_return] */ + if (InternalRead(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != + GIF_STAMP_LEN) { + if (Error != NULL) { + *Error = D_GIF_ERR_READ_FAILED; + } + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + /* Check for GIF prefix at start of file */ + Buf[GIF_STAMP_LEN] = '\0'; + if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { + if (Error != NULL) { + *Error = D_GIF_ERR_NOT_GIF_FILE; + } + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { + free((char *)Private); + free((char *)GifFile); + if (Error != NULL) { + *Error = D_GIF_ERR_NO_SCRN_DSCR; + } + return NULL; + } + + GifFile->Error = 0; + + /* What version of GIF? */ + Private->gif89 = (Buf[GIF_VERSION_POS + 1] == '9'); + + return GifFile; } /****************************************************************************** This routine should be called before any other DGif calls. Note that this routine is called automatically from DGif file open routines. ******************************************************************************/ -int -DGifGetScreenDesc(GifFileType *GifFile) -{ - int BitsPerPixel; - bool SortFlag; - GifByteType Buf[3]; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - GifFile->Error = D_GIF_ERR_NOT_READABLE; - return GIF_ERROR; - } - - /* Put the screen descriptor into the file: */ - if (DGifGetWord(GifFile, &GifFile->SWidth) == GIF_ERROR || - DGifGetWord(GifFile, &GifFile->SHeight) == GIF_ERROR) - return GIF_ERROR; - - if (InternalRead(GifFile, Buf, 3) != 3) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - GifFreeMapObject(GifFile->SColorMap); - GifFile->SColorMap = NULL; - return GIF_ERROR; - } - GifFile->SColorResolution = (((Buf[0] & 0x70) + 1) >> 4) + 1; - SortFlag = (Buf[0] & 0x08) != 0; - BitsPerPixel = (Buf[0] & 0x07) + 1; - GifFile->SBackGroundColor = Buf[1]; - GifFile->AspectByte = Buf[2]; - if (Buf[0] & 0x80) { /* Do we have global color map? */ - int i; - - GifFile->SColorMap = GifMakeMapObject(1 << BitsPerPixel, NULL); - if (GifFile->SColorMap == NULL) { - GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; - return GIF_ERROR; - } - - /* Get the global color map: */ - GifFile->SColorMap->SortFlag = SortFlag; - for (i = 0; i < GifFile->SColorMap->ColorCount; i++) { - /* coverity[check_return] */ - if (InternalRead(GifFile, Buf, 3) != 3) { - GifFreeMapObject(GifFile->SColorMap); - GifFile->SColorMap = NULL; - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; - } - GifFile->SColorMap->Colors[i].Red = Buf[0]; - GifFile->SColorMap->Colors[i].Green = Buf[1]; - GifFile->SColorMap->Colors[i].Blue = Buf[2]; - } - } else { - GifFile->SColorMap = NULL; - } - - /* - * No check here for whether the background color is in range for the - * screen color map. Possibly there should be. - */ - - return GIF_OK; +int DGifGetScreenDesc(GifFileType *GifFile) { + int BitsPerPixel; + bool SortFlag; + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + /* Put the screen descriptor into the file: */ + if (DGifGetWord(GifFile, &GifFile->SWidth) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->SHeight) == GIF_ERROR) { + return GIF_ERROR; + } + + if (InternalRead(GifFile, Buf, 3) != 3) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + GifFreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + return GIF_ERROR; + } + GifFile->SColorResolution = (((Buf[0] & 0x70) + 1) >> 4) + 1; + SortFlag = (Buf[0] & 0x08) != 0; + BitsPerPixel = (Buf[0] & 0x07) + 1; + GifFile->SBackGroundColor = Buf[1]; + GifFile->AspectByte = Buf[2]; + if (Buf[0] & 0x80) { /* Do we have global color map? */ + int i; + + GifFile->SColorMap = GifMakeMapObject(1 << BitsPerPixel, NULL); + if (GifFile->SColorMap == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + + /* Get the global color map: */ + GifFile->SColorMap->SortFlag = SortFlag; + for (i = 0; i < GifFile->SColorMap->ColorCount; i++) { + /* coverity[check_return] */ + if (InternalRead(GifFile, Buf, 3) != 3) { + GifFreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + GifFile->SColorMap->Colors[i].Red = Buf[0]; + GifFile->SColorMap->Colors[i].Green = Buf[1]; + GifFile->SColorMap->Colors[i].Blue = Buf[2]; + } + } else { + GifFile->SColorMap = NULL; + } + + /* + * No check here for whether the background color is in range for the + * screen color map. Possibly there should be. + */ + + return GIF_OK; } -const char * -DGifGetGifVersion(GifFileType *GifFile) -{ - GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; +const char *DGifGetGifVersion(GifFileType *GifFile) { + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - if (Private->gif89) - return GIF89_STAMP; - else - return GIF87_STAMP; + if (Private->gif89) { + return GIF89_STAMP; + } else { + return GIF87_STAMP; + } } /****************************************************************************** This routine should be called before any attempt to read an image. ******************************************************************************/ -int -DGifGetRecordType(GifFileType *GifFile, GifRecordType* Type) -{ - GifByteType Buf; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - GifFile->Error = D_GIF_ERR_NOT_READABLE; - return GIF_ERROR; - } - - /* coverity[check_return] */ - if (InternalRead(GifFile, &Buf, 1) != 1) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; - } - - //fprintf(stderr, "### DGifGetRecordType: %02x\n", Buf); - switch (Buf) { - case DESCRIPTOR_INTRODUCER: - *Type = IMAGE_DESC_RECORD_TYPE; - break; - case EXTENSION_INTRODUCER: - *Type = EXTENSION_RECORD_TYPE; - break; - case TERMINATOR_INTRODUCER: - *Type = TERMINATE_RECORD_TYPE; - break; - default: - *Type = UNDEFINED_RECORD_TYPE; - GifFile->Error = D_GIF_ERR_WRONG_RECORD; - return GIF_ERROR; - } - - return GIF_OK; +int DGifGetRecordType(GifFileType *GifFile, GifRecordType *Type) { + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + /* coverity[check_return] */ + if (InternalRead(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + // fprintf(stderr, "### DGifGetRecordType: %02x\n", Buf); + switch (Buf) { + case DESCRIPTOR_INTRODUCER: + *Type = IMAGE_DESC_RECORD_TYPE; + break; + case EXTENSION_INTRODUCER: + *Type = EXTENSION_RECORD_TYPE; + break; + case TERMINATOR_INTRODUCER: + *Type = TERMINATE_RECORD_TYPE; + break; + default: + *Type = UNDEFINED_RECORD_TYPE; + GifFile->Error = D_GIF_ERR_WRONG_RECORD; + return GIF_ERROR; + } + + return GIF_OK; } -int -DGifGetImageHeader(GifFileType *GifFile) -{ - unsigned int BitsPerPixel; - GifByteType Buf[3]; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - GifFile->Error = D_GIF_ERR_NOT_READABLE; - return GIF_ERROR; - } - - if (DGifGetWord(GifFile, &GifFile->Image.Left) == GIF_ERROR || - DGifGetWord(GifFile, &GifFile->Image.Top) == GIF_ERROR || - DGifGetWord(GifFile, &GifFile->Image.Width) == GIF_ERROR || - DGifGetWord(GifFile, &GifFile->Image.Height) == GIF_ERROR) - return GIF_ERROR; - if (InternalRead(GifFile, Buf, 1) != 1) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - GifFreeMapObject(GifFile->Image.ColorMap); - GifFile->Image.ColorMap = NULL; - return GIF_ERROR; - } - BitsPerPixel = (Buf[0] & 0x07) + 1; - GifFile->Image.Interlace = (Buf[0] & 0x40) ? true : false; - - /* Setup the colormap */ - if (GifFile->Image.ColorMap) { - GifFreeMapObject(GifFile->Image.ColorMap); - GifFile->Image.ColorMap = NULL; - } - /* Does this image have local color map? */ - if (Buf[0] & 0x80) { - unsigned int i; - - GifFile->Image.ColorMap = GifMakeMapObject(1 << BitsPerPixel, NULL); - if (GifFile->Image.ColorMap == NULL) { - GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; - return GIF_ERROR; - } - - /* Get the image local color map: */ - for (i = 0; i < GifFile->Image.ColorMap->ColorCount; i++) { - /* coverity[check_return] */ - if (InternalRead(GifFile, Buf, 3) != 3) { - GifFreeMapObject(GifFile->Image.ColorMap); - GifFile->Error = D_GIF_ERR_READ_FAILED; - GifFile->Image.ColorMap = NULL; - return GIF_ERROR; - } - GifFile->Image.ColorMap->Colors[i].Red = Buf[0]; - GifFile->Image.ColorMap->Colors[i].Green = Buf[1]; - GifFile->Image.ColorMap->Colors[i].Blue = Buf[2]; - } - } - - Private->PixelCount = (long)GifFile->Image.Width * - (long)GifFile->Image.Height; - - /* Reset decompress algorithm parameters. */ - return DGifSetupDecompress(GifFile); +int DGifGetImageHeader(GifFileType *GifFile) { + unsigned int BitsPerPixel; + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (DGifGetWord(GifFile, &GifFile->Image.Left) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Top) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Width) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Height) == GIF_ERROR) { + return GIF_ERROR; + } + if (InternalRead(GifFile, Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + return GIF_ERROR; + } + BitsPerPixel = (Buf[0] & 0x07) + 1; + GifFile->Image.Interlace = (Buf[0] & 0x40) ? true : false; + + /* Setup the colormap */ + if (GifFile->Image.ColorMap) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + /* Does this image have local color map? */ + if (Buf[0] & 0x80) { + unsigned int i; + + GifFile->Image.ColorMap = + GifMakeMapObject(1 << BitsPerPixel, NULL); + if (GifFile->Image.ColorMap == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + + /* Get the image local color map: */ + for (i = 0; i < GifFile->Image.ColorMap->ColorCount; i++) { + /* coverity[check_return] */ + if (InternalRead(GifFile, Buf, 3) != 3) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Error = D_GIF_ERR_READ_FAILED; + GifFile->Image.ColorMap = NULL; + return GIF_ERROR; + } + GifFile->Image.ColorMap->Colors[i].Red = Buf[0]; + GifFile->Image.ColorMap->Colors[i].Green = Buf[1]; + GifFile->Image.ColorMap->Colors[i].Blue = Buf[2]; + } + } + + Private->PixelCount = + (long)GifFile->Image.Width * (long)GifFile->Image.Height; + + /* Reset decompress algorithm parameters. */ + return DGifSetupDecompress(GifFile); } /****************************************************************************** This routine should be called before any attempt to read an image. Note it is assumed the Image desc. header has been read. ******************************************************************************/ -int -DGifGetImageDesc(GifFileType *GifFile) -{ - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - SavedImage *sp; - - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - GifFile->Error = D_GIF_ERR_NOT_READABLE; - return GIF_ERROR; - } - - if (DGifGetImageHeader(GifFile) == GIF_ERROR) { - return GIF_ERROR; - } - - if (GifFile->SavedImages) { - SavedImage* new_saved_images = - (SavedImage *)reallocarray(GifFile->SavedImages, - (GifFile->ImageCount + 1), sizeof(SavedImage)); - if (new_saved_images == NULL) { - GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; - return GIF_ERROR; - } - GifFile->SavedImages = new_saved_images; - } else { - if ((GifFile->SavedImages = - (SavedImage *) malloc(sizeof(SavedImage))) == NULL) { - GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; - return GIF_ERROR; - } - } - - sp = &GifFile->SavedImages[GifFile->ImageCount]; - memcpy(&sp->ImageDesc, &GifFile->Image, sizeof(GifImageDesc)); - if (GifFile->Image.ColorMap != NULL) { - sp->ImageDesc.ColorMap = GifMakeMapObject( - GifFile->Image.ColorMap->ColorCount, - GifFile->Image.ColorMap->Colors); - if (sp->ImageDesc.ColorMap == NULL) { - GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; - return GIF_ERROR; - } - } - sp->RasterBits = (unsigned char *)NULL; - sp->ExtensionBlockCount = 0; - sp->ExtensionBlocks = (ExtensionBlock *) NULL; - - GifFile->ImageCount++; - - return GIF_OK; +int DGifGetImageDesc(GifFileType *GifFile) { + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + SavedImage *sp; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (DGifGetImageHeader(GifFile) == GIF_ERROR) { + return GIF_ERROR; + } + + if (GifFile->SavedImages) { + SavedImage *new_saved_images = (SavedImage *)reallocarray( + GifFile->SavedImages, (GifFile->ImageCount + 1), + sizeof(SavedImage)); + if (new_saved_images == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + GifFile->SavedImages = new_saved_images; + } else { + if ((GifFile->SavedImages = + (SavedImage *)malloc(sizeof(SavedImage))) == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } + + sp = &GifFile->SavedImages[GifFile->ImageCount]; + memcpy(&sp->ImageDesc, &GifFile->Image, sizeof(GifImageDesc)); + if (GifFile->Image.ColorMap != NULL) { + sp->ImageDesc.ColorMap = + GifMakeMapObject(GifFile->Image.ColorMap->ColorCount, + GifFile->Image.ColorMap->Colors); + if (sp->ImageDesc.ColorMap == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } + sp->RasterBits = (unsigned char *)NULL; + sp->ExtensionBlockCount = 0; + sp->ExtensionBlocks = (ExtensionBlock *)NULL; + + GifFile->ImageCount++; + + return GIF_OK; } /****************************************************************************** Get one full scanned line (Line) of length LineLen from GIF file. ******************************************************************************/ -int -DGifGetLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) -{ - GifByteType *Dummy; - GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; - - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - GifFile->Error = D_GIF_ERR_NOT_READABLE; - return GIF_ERROR; - } - - if (!LineLen) - LineLen = GifFile->Image.Width; - - if ((Private->PixelCount -= LineLen) > 0xffff0000UL) { - GifFile->Error = D_GIF_ERR_DATA_TOO_BIG; - return GIF_ERROR; - } - - if (DGifDecompressLine(GifFile, Line, LineLen) == GIF_OK) { - if (Private->PixelCount == 0) { - /* We probably won't be called any more, so let's clean up - * everything before we return: need to flush out all the - * rest of image until an empty block (size 0) - * detected. We use GetCodeNext. - */ - do - if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR) - return GIF_ERROR; - while (Dummy != NULL) ; - } - return GIF_OK; - } else - return GIF_ERROR; +int DGifGetLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) { + GifByteType *Dummy; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (!LineLen) { + LineLen = GifFile->Image.Width; + } + + if ((Private->PixelCount -= LineLen) > 0xffff0000UL) { + GifFile->Error = D_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + + if (DGifDecompressLine(GifFile, Line, LineLen) == GIF_OK) { + if (Private->PixelCount == 0) { + /* We probably won't be called any more, so let's clean + * up everything before we return: need to flush out all + * the rest of image until an empty block (size 0) + * detected. We use GetCodeNext. + */ + do { + if (DGifGetCodeNext(GifFile, &Dummy) == + GIF_ERROR) { + return GIF_ERROR; + } + } while (Dummy != NULL); + } + return GIF_OK; + } else { + return GIF_ERROR; + } } /****************************************************************************** Put one pixel (Pixel) into GIF file. ******************************************************************************/ -int -DGifGetPixel(GifFileType *GifFile, GifPixelType Pixel) -{ - GifByteType *Dummy; - GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; - - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - GifFile->Error = D_GIF_ERR_NOT_READABLE; - return GIF_ERROR; - } - if (--Private->PixelCount > 0xffff0000UL) - { - GifFile->Error = D_GIF_ERR_DATA_TOO_BIG; - return GIF_ERROR; - } - - if (DGifDecompressLine(GifFile, &Pixel, 1) == GIF_OK) { - if (Private->PixelCount == 0) { - /* We probably won't be called any more, so let's clean up - * everything before we return: need to flush out all the - * rest of image until an empty block (size 0) - * detected. We use GetCodeNext. - */ - do - if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR) - return GIF_ERROR; - while (Dummy != NULL) ; - } - return GIF_OK; - } else - return GIF_ERROR; +int DGifGetPixel(GifFileType *GifFile, GifPixelType Pixel) { + GifByteType *Dummy; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + if (--Private->PixelCount > 0xffff0000UL) { + GifFile->Error = D_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + + if (DGifDecompressLine(GifFile, &Pixel, 1) == GIF_OK) { + if (Private->PixelCount == 0) { + /* We probably won't be called any more, so let's clean + * up everything before we return: need to flush out all + * the rest of image until an empty block (size 0) + * detected. We use GetCodeNext. + */ + do { + if (DGifGetCodeNext(GifFile, &Dummy) == + GIF_ERROR) { + return GIF_ERROR; + } + } while (Dummy != NULL); + } + return GIF_OK; + } else { + return GIF_ERROR; + } } /****************************************************************************** @@ -558,28 +563,28 @@ DGifGetPixel(GifFileType *GifFile, GifPixelType Pixel) The Extension should NOT be freed by the user (not dynamically allocated). Note it is assumed the Extension description header has been read. ******************************************************************************/ -int -DGifGetExtension(GifFileType *GifFile, int *ExtCode, GifByteType **Extension) -{ - GifByteType Buf; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - - //fprintf(stderr, "### -> DGifGetExtension:\n"); - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - GifFile->Error = D_GIF_ERR_NOT_READABLE; - return GIF_ERROR; - } - - /* coverity[check_return] */ - if (InternalRead(GifFile, &Buf, 1) != 1) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; - } - *ExtCode = Buf; - //fprintf(stderr, "### <- DGifGetExtension: %02x, about to call next\n", Buf); - - return DGifGetExtensionNext(GifFile, Extension); +int DGifGetExtension(GifFileType *GifFile, int *ExtCode, + GifByteType **Extension) { + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + // fprintf(stderr, "### -> DGifGetExtension:\n"); + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + /* coverity[check_return] */ + if (InternalRead(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + *ExtCode = Buf; + // fprintf(stderr, "### <- DGifGetExtension: %02x, about to call + // next\n", Buf); + + return DGifGetExtensionNext(GifFile, Extension); } /****************************************************************************** @@ -587,32 +592,32 @@ DGifGetExtension(GifFileType *GifFile, int *ExtCode, GifByteType **Extension) routine should be called until NULL Extension is returned. The Extension should NOT be freed by the user (not dynamically allocated). ******************************************************************************/ -int -DGifGetExtensionNext(GifFileType *GifFile, GifByteType ** Extension) -{ - GifByteType Buf; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - - //fprintf(stderr, "### -> DGifGetExtensionNext\n"); - if (InternalRead(GifFile, &Buf, 1) != 1) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; - } - //fprintf(stderr, "### DGifGetExtensionNext sees %d\n", Buf); - - if (Buf > 0) { - *Extension = Private->Buf; /* Use private unused buffer. */ - (*Extension)[0] = Buf; /* Pascal strings notation (pos. 0 is len.). */ - /* coverity[tainted_data,check_return] */ - if (InternalRead(GifFile, &((*Extension)[1]), Buf) != Buf) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; - } - } else - *Extension = NULL; - //fprintf(stderr, "### <- DGifGetExtensionNext: %p\n", Extension); - - return GIF_OK; +int DGifGetExtensionNext(GifFileType *GifFile, GifByteType **Extension) { + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + // fprintf(stderr, "### -> DGifGetExtensionNext\n"); + if (InternalRead(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + // fprintf(stderr, "### DGifGetExtensionNext sees %d\n", Buf); + + if (Buf > 0) { + *Extension = Private->Buf; /* Use private unused buffer. */ + (*Extension)[0] = + Buf; /* Pascal strings notation (pos. 0 is len.). */ + /* coverity[tainted_data,check_return] */ + if (InternalRead(GifFile, &((*Extension)[1]), Buf) != Buf) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + } else { + *Extension = NULL; + } + // fprintf(stderr, "### <- DGifGetExtensionNext: %p\n", Extension); + + return GIF_OK; } /****************************************************************************** @@ -620,120 +625,126 @@ DGifGetExtensionNext(GifFileType *GifFile, GifByteType ** Extension) ******************************************************************************/ int DGifExtensionToGCB(const size_t GifExtensionLength, - const GifByteType *GifExtension, - GraphicsControlBlock *GCB) -{ - if (GifExtensionLength != 4) { - return GIF_ERROR; - } - - GCB->DisposalMode = (GifExtension[0] >> 2) & 0x07; - GCB->UserInputFlag = (GifExtension[0] & 0x02) != 0; - GCB->DelayTime = UNSIGNED_LITTLE_ENDIAN(GifExtension[1], GifExtension[2]); - if (GifExtension[0] & 0x01) - GCB->TransparentColor = (int)GifExtension[3]; - else - GCB->TransparentColor = NO_TRANSPARENT_COLOR; - - return GIF_OK; + const GifByteType *GifExtension, + GraphicsControlBlock *GCB) { + if (GifExtensionLength != 4) { + return GIF_ERROR; + } + + GCB->DisposalMode = (GifExtension[0] >> 2) & 0x07; + GCB->UserInputFlag = (GifExtension[0] & 0x02) != 0; + GCB->DelayTime = + UNSIGNED_LITTLE_ENDIAN(GifExtension[1], GifExtension[2]); + if (GifExtension[0] & 0x01) { + GCB->TransparentColor = (int)GifExtension[3]; + } else { + GCB->TransparentColor = NO_TRANSPARENT_COLOR; + } + + return GIF_OK; } /****************************************************************************** Extract the Graphics Control Block for a saved image, if it exists. ******************************************************************************/ -int DGifSavedExtensionToGCB(GifFileType *GifFile, - int ImageIndex, GraphicsControlBlock *GCB) -{ - int i; +int DGifSavedExtensionToGCB(GifFileType *GifFile, int ImageIndex, + GraphicsControlBlock *GCB) { + int i; - if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) - return GIF_ERROR; + if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) { + return GIF_ERROR; + } - GCB->DisposalMode = DISPOSAL_UNSPECIFIED; - GCB->UserInputFlag = false; - GCB->DelayTime = 0; - GCB->TransparentColor = NO_TRANSPARENT_COLOR; + GCB->DisposalMode = DISPOSAL_UNSPECIFIED; + GCB->UserInputFlag = false; + GCB->DelayTime = 0; + GCB->TransparentColor = NO_TRANSPARENT_COLOR; - for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; i++) { - ExtensionBlock *ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i]; - if (ep->Function == GRAPHICS_EXT_FUNC_CODE) - return DGifExtensionToGCB(ep->ByteCount, ep->Bytes, GCB); - } + for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; + i++) { + ExtensionBlock *ep = + &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i]; + if (ep->Function == GRAPHICS_EXT_FUNC_CODE) { + return DGifExtensionToGCB(ep->ByteCount, ep->Bytes, + GCB); + } + } - return GIF_ERROR; + return GIF_ERROR; } /****************************************************************************** This routine should be called last, to close the GIF file. ******************************************************************************/ -int -DGifCloseFile(GifFileType *GifFile, int *ErrorCode) -{ - GifFilePrivateType *Private; - - if (GifFile == NULL || GifFile->Private == NULL) - return GIF_ERROR; - - if (GifFile->Image.ColorMap) { - GifFreeMapObject(GifFile->Image.ColorMap); - GifFile->Image.ColorMap = NULL; - } - - if (GifFile->SColorMap) { - GifFreeMapObject(GifFile->SColorMap); - GifFile->SColorMap = NULL; - } - - if (GifFile->SavedImages) { - GifFreeSavedImages(GifFile); - GifFile->SavedImages = NULL; - } +int DGifCloseFile(GifFileType *GifFile, int *ErrorCode) { + GifFilePrivateType *Private; + + if (GifFile == NULL || GifFile->Private == NULL) { + return GIF_ERROR; + } + + if (GifFile->Image.ColorMap) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + + if (GifFile->SColorMap) { + GifFreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + } + + if (GifFile->SavedImages) { + GifFreeSavedImages(GifFile); + GifFile->SavedImages = NULL; + } + + GifFreeExtensions(&GifFile->ExtensionBlockCount, + &GifFile->ExtensionBlocks); + + Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + if (ErrorCode != NULL) { + *ErrorCode = D_GIF_ERR_NOT_READABLE; + } + free((char *)GifFile->Private); + free(GifFile); + return GIF_ERROR; + } + + if (Private->File && (fclose(Private->File) != 0)) { + if (ErrorCode != NULL) { + *ErrorCode = D_GIF_ERR_CLOSE_FAILED; + } + free((char *)GifFile->Private); + free(GifFile); + return GIF_ERROR; + } - GifFreeExtensions(&GifFile->ExtensionBlockCount, &GifFile->ExtensionBlocks); - - Private = (GifFilePrivateType *) GifFile->Private; - - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - if (ErrorCode != NULL) - *ErrorCode = D_GIF_ERR_NOT_READABLE; - free((char *)GifFile->Private); - free(GifFile); - return GIF_ERROR; - } - - if (Private->File && (fclose(Private->File) != 0)) { - if (ErrorCode != NULL) - *ErrorCode = D_GIF_ERR_CLOSE_FAILED; free((char *)GifFile->Private); free(GifFile); - return GIF_ERROR; - } - - free((char *)GifFile->Private); - free(GifFile); - if (ErrorCode != NULL) - *ErrorCode = D_GIF_SUCCEEDED; - return GIF_OK; + if (ErrorCode != NULL) { + *ErrorCode = D_GIF_SUCCEEDED; + } + return GIF_OK; } /****************************************************************************** Get 2 bytes (word) from the given file: ******************************************************************************/ -static int -DGifGetWord(GifFileType *GifFile, GifWord *Word) -{ - unsigned char c[2]; - - /* coverity[check_return] */ - if (InternalRead(GifFile, c, 2) != 2) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; - } - - *Word = (GifWord)UNSIGNED_LITTLE_ENDIAN(c[0], c[1]); - return GIF_OK; +static int DGifGetWord(GifFileType *GifFile, GifWord *Word) { + unsigned char c[2]; + + /* coverity[check_return] */ + if (InternalRead(GifFile, c, 2) != 2) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + *Word = (GifWord)UNSIGNED_LITTLE_ENDIAN(c[0], c[1]); + return GIF_OK; } /****************************************************************************** @@ -743,20 +754,18 @@ DGifGetWord(GifFileType *GifFile, GifWord *Word) to DGifGetCodeNext, until NULL block is returned. The block should NOT be freed by the user (not dynamically allocated). ******************************************************************************/ -int -DGifGetCode(GifFileType *GifFile, int *CodeSize, GifByteType **CodeBlock) -{ - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; +int DGifGetCode(GifFileType *GifFile, int *CodeSize, GifByteType **CodeBlock) { + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - GifFile->Error = D_GIF_ERR_NOT_READABLE; - return GIF_ERROR; - } + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } - *CodeSize = Private->BitsPerPixel; + *CodeSize = Private->BitsPerPixel; - return DGifGetCodeNext(GifFile, CodeBlock); + return DGifGetCodeNext(GifFile, CodeBlock); } /****************************************************************************** @@ -764,77 +773,79 @@ DGifGetCode(GifFileType *GifFile, int *CodeSize, GifByteType **CodeBlock) called until NULL block is returned. The block should NOT be freed by the user (not dynamically allocated). ******************************************************************************/ -int -DGifGetCodeNext(GifFileType *GifFile, GifByteType **CodeBlock) -{ - GifByteType Buf; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - - /* coverity[tainted_data_argument] */ - /* coverity[check_return] */ - if (InternalRead(GifFile, &Buf, 1) != 1) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; - } - - /* coverity[lower_bounds] */ - if (Buf > 0) { - *CodeBlock = Private->Buf; /* Use private unused buffer. */ - (*CodeBlock)[0] = Buf; /* Pascal strings notation (pos. 0 is len.). */ - /* coverity[tainted_data] */ - if (InternalRead(GifFile, &((*CodeBlock)[1]), Buf) != Buf) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; - } - } else { - *CodeBlock = NULL; - Private->Buf[0] = 0; /* Make sure the buffer is empty! */ - Private->PixelCount = 0; /* And local info. indicate image read. */ - } - - return GIF_OK; +int DGifGetCodeNext(GifFileType *GifFile, GifByteType **CodeBlock) { + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + /* coverity[tainted_data_argument] */ + /* coverity[check_return] */ + if (InternalRead(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + /* coverity[lower_bounds] */ + if (Buf > 0) { + *CodeBlock = Private->Buf; /* Use private unused buffer. */ + (*CodeBlock)[0] = + Buf; /* Pascal strings notation (pos. 0 is len.). */ + /* coverity[tainted_data] */ + if (InternalRead(GifFile, &((*CodeBlock)[1]), Buf) != Buf) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + } else { + *CodeBlock = NULL; + Private->Buf[0] = 0; /* Make sure the buffer is empty! */ + Private->PixelCount = + 0; /* And local info. indicate image read. */ + } + + return GIF_OK; } /****************************************************************************** Setup the LZ decompression for this image: ******************************************************************************/ -static int -DGifSetupDecompress(GifFileType *GifFile) -{ - int i, BitsPerPixel; - GifByteType CodeSize; - GifPrefixType *Prefix; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - - /* coverity[check_return] */ - if (InternalRead(GifFile, &CodeSize, 1) < 1) { /* Read Code size from file. */ - return GIF_ERROR; /* Failed to read Code size. */ - } - BitsPerPixel = CodeSize; - - /* this can only happen on a severely malformed GIF */ - if (BitsPerPixel > 8) { - GifFile->Error = D_GIF_ERR_READ_FAILED; /* somewhat bogus error code */ - return GIF_ERROR; /* Failed to read Code size. */ - } - - Private->Buf[0] = 0; /* Input Buffer empty. */ - Private->BitsPerPixel = BitsPerPixel; - Private->ClearCode = (1 << BitsPerPixel); - Private->EOFCode = Private->ClearCode + 1; - Private->RunningCode = Private->EOFCode + 1; - Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */ - Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */ - Private->StackPtr = 0; /* No pixels on the pixel stack. */ - Private->LastCode = NO_SUCH_CODE; - Private->CrntShiftState = 0; /* No information in CrntShiftDWord. */ - Private->CrntShiftDWord = 0; - - Prefix = Private->Prefix; - for (i = 0; i <= LZ_MAX_CODE; i++) - Prefix[i] = NO_SUCH_CODE; - - return GIF_OK; +static int DGifSetupDecompress(GifFileType *GifFile) { + int i, BitsPerPixel; + GifByteType CodeSize; + GifPrefixType *Prefix; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + /* coverity[check_return] */ + if (InternalRead(GifFile, &CodeSize, 1) < + 1) { /* Read Code size from file. */ + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; /* Failed to read Code size. */ + } + BitsPerPixel = CodeSize; + + /* this can only happen on a severely malformed GIF */ + if (BitsPerPixel > 8) { + GifFile->Error = + D_GIF_ERR_READ_FAILED; /* somewhat bogus error code */ + return GIF_ERROR; /* Failed to read Code size. */ + } + + Private->Buf[0] = 0; /* Input Buffer empty. */ + Private->BitsPerPixel = BitsPerPixel; + Private->ClearCode = (1 << BitsPerPixel); + Private->EOFCode = Private->ClearCode + 1; + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */ + Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */ + Private->StackPtr = 0; /* No pixels on the pixel stack. */ + Private->LastCode = NO_SUCH_CODE; + Private->CrntShiftState = 0; /* No information in CrntShiftDWord. */ + Private->CrntShiftDWord = 0; + + Prefix = Private->Prefix; + for (i = 0; i <= LZ_MAX_CODE; i++) { + Prefix[i] = NO_SUCH_CODE; + } + + return GIF_OK; } /****************************************************************************** @@ -843,127 +854,149 @@ DGifSetupDecompress(GifFileType *GifFile) This routine can be called few times (one per scan line, for example), in order the complete the whole image. ******************************************************************************/ -static int -DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) -{ - int i = 0; - int j, CrntCode, EOFCode, ClearCode, CrntPrefix, LastCode, StackPtr; - GifByteType *Stack, *Suffix; - GifPrefixType *Prefix; - GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; - - StackPtr = Private->StackPtr; - Prefix = Private->Prefix; - Suffix = Private->Suffix; - Stack = Private->Stack; - EOFCode = Private->EOFCode; - ClearCode = Private->ClearCode; - LastCode = Private->LastCode; - - if (StackPtr > LZ_MAX_CODE) { - return GIF_ERROR; - } - - if (StackPtr != 0) { - /* Let pop the stack off before continueing to read the GIF file: */ - while (StackPtr != 0 && i < LineLen) - Line[i++] = Stack[--StackPtr]; - } - - while (i < LineLen) { /* Decode LineLen items. */ - if (DGifDecompressInput(GifFile, &CrntCode) == GIF_ERROR) - return GIF_ERROR; - - if (CrntCode == EOFCode) { - /* Note however that usually we will not be here as we will stop - * decoding as soon as we got all the pixel, or EOF code will - * not be read at all, and DGifGetLine/Pixel clean everything. */ - GifFile->Error = D_GIF_ERR_EOF_TOO_SOON; - return GIF_ERROR; - } else if (CrntCode == ClearCode) { - /* We need to start over again: */ - for (j = 0; j <= LZ_MAX_CODE; j++) - Prefix[j] = NO_SUCH_CODE; - Private->RunningCode = Private->EOFCode + 1; - Private->RunningBits = Private->BitsPerPixel + 1; - Private->MaxCode1 = 1 << Private->RunningBits; - LastCode = Private->LastCode = NO_SUCH_CODE; - } else { - /* Its regular code - if in pixel range simply add it to output - * stream, otherwise trace to codes linked list until the prefix - * is in pixel range: */ - if (CrntCode < ClearCode) { - /* This is simple - its pixel scalar, so add it to output: */ - Line[i++] = CrntCode; - } else { - /* Its a code to needed to be traced: trace the linked list - * until the prefix is a pixel, while pushing the suffix - * pixels on our stack. If we done, pop the stack in reverse - * (thats what stack is good for!) order to output. */ - if (Prefix[CrntCode] == NO_SUCH_CODE) { - CrntPrefix = LastCode; - - /* Only allowed if CrntCode is exactly the running code: - * In that case CrntCode = XXXCode, CrntCode or the - * prefix code is last code and the suffix char is - * exactly the prefix of last code! */ - if (CrntCode == Private->RunningCode - 2) { - Suffix[Private->RunningCode - 2] = - Stack[StackPtr++] = DGifGetPrefixChar(Prefix, - LastCode, - ClearCode); - } else { - Suffix[Private->RunningCode - 2] = - Stack[StackPtr++] = DGifGetPrefixChar(Prefix, - CrntCode, - ClearCode); - } - } else - CrntPrefix = CrntCode; - - /* Now (if image is O.K.) we should not get a NO_SUCH_CODE - * during the trace. As we might loop forever, in case of - * defective image, we use StackPtr as loop counter and stop - * before overflowing Stack[]. */ - while (StackPtr < LZ_MAX_CODE && - CrntPrefix > ClearCode && CrntPrefix <= LZ_MAX_CODE) { - Stack[StackPtr++] = Suffix[CrntPrefix]; - CrntPrefix = Prefix[CrntPrefix]; - } - if (StackPtr >= LZ_MAX_CODE || CrntPrefix > LZ_MAX_CODE) { - GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; - return GIF_ERROR; - } - /* Push the last character on stack: */ - Stack[StackPtr++] = CrntPrefix; - - /* Now lets pop all the stack into output: */ - while (StackPtr != 0 && i < LineLen) - Line[i++] = Stack[--StackPtr]; - } - if (LastCode != NO_SUCH_CODE && Private->RunningCode - 2 < LZ_MAX_CODE && Prefix[Private->RunningCode - 2] == NO_SUCH_CODE) { - Prefix[Private->RunningCode - 2] = LastCode; - - if (CrntCode == Private->RunningCode - 2) { - /* Only allowed if CrntCode is exactly the running code: - * In that case CrntCode = XXXCode, CrntCode or the - * prefix code is last code and the suffix char is - * exactly the prefix of last code! */ - Suffix[Private->RunningCode - 2] = - DGifGetPrefixChar(Prefix, LastCode, ClearCode); - } else { - Suffix[Private->RunningCode - 2] = - DGifGetPrefixChar(Prefix, CrntCode, ClearCode); - } - } - LastCode = CrntCode; - } - } - - Private->LastCode = LastCode; - Private->StackPtr = StackPtr; - - return GIF_OK; +static int DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, + int LineLen) { + int i = 0; + int j, CrntCode, EOFCode, ClearCode, CrntPrefix, LastCode, StackPtr; + GifByteType *Stack, *Suffix; + GifPrefixType *Prefix; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + StackPtr = Private->StackPtr; + Prefix = Private->Prefix; + Suffix = Private->Suffix; + Stack = Private->Stack; + EOFCode = Private->EOFCode; + ClearCode = Private->ClearCode; + LastCode = Private->LastCode; + + if (StackPtr > LZ_MAX_CODE) { + return GIF_ERROR; + } + + if (StackPtr != 0) { + /* Let pop the stack off before continueing to read the GIF + * file: */ + while (StackPtr != 0 && i < LineLen) { + Line[i++] = Stack[--StackPtr]; + } + } + + while (i < LineLen) { /* Decode LineLen items. */ + if (DGifDecompressInput(GifFile, &CrntCode) == GIF_ERROR) { + return GIF_ERROR; + } + + if (CrntCode == EOFCode) { + /* Note however that usually we will not be here as we + * will stop decoding as soon as we got all the pixel, + * or EOF code will not be read at all, and + * DGifGetLine/Pixel clean everything. */ + GifFile->Error = D_GIF_ERR_EOF_TOO_SOON; + return GIF_ERROR; + } else if (CrntCode == ClearCode) { + /* We need to start over again: */ + for (j = 0; j <= LZ_MAX_CODE; j++) { + Prefix[j] = NO_SUCH_CODE; + } + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = Private->BitsPerPixel + 1; + Private->MaxCode1 = 1 << Private->RunningBits; + LastCode = Private->LastCode = NO_SUCH_CODE; + } else { + /* Its regular code - if in pixel range simply add it to + * output stream, otherwise trace to codes linked list + * until the prefix is in pixel range: */ + if (CrntCode < ClearCode) { + /* This is simple - its pixel scalar, so add it + * to output: */ + Line[i++] = CrntCode; + } else { + /* Its a code to needed to be traced: trace the + * linked list until the prefix is a pixel, + * while pushing the suffix pixels on our stack. + * If we done, pop the stack in reverse (thats + * what stack is good for!) order to output. */ + if (Prefix[CrntCode] == NO_SUCH_CODE) { + CrntPrefix = LastCode; + + /* Only allowed if CrntCode is exactly + * the running code: In that case + * CrntCode = XXXCode, CrntCode or the + * prefix code is last code and the + * suffix char is exactly the prefix of + * last code! */ + if (CrntCode == + Private->RunningCode - 2) { + Suffix[Private->RunningCode - + 2] = Stack[StackPtr++] = + DGifGetPrefixChar( + Prefix, LastCode, + ClearCode); + } else { + Suffix[Private->RunningCode - + 2] = Stack[StackPtr++] = + DGifGetPrefixChar( + Prefix, CrntCode, + ClearCode); + } + } else { + CrntPrefix = CrntCode; + } + + /* Now (if image is O.K.) we should not get a + * NO_SUCH_CODE during the trace. As we might + * loop forever, in case of defective image, we + * use StackPtr as loop counter and stop before + * overflowing Stack[]. */ + while (StackPtr < LZ_MAX_CODE && + CrntPrefix > ClearCode && + CrntPrefix <= LZ_MAX_CODE) { + Stack[StackPtr++] = Suffix[CrntPrefix]; + CrntPrefix = Prefix[CrntPrefix]; + } + if (StackPtr >= LZ_MAX_CODE || + CrntPrefix > LZ_MAX_CODE) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + /* Push the last character on stack: */ + Stack[StackPtr++] = CrntPrefix; + + /* Now lets pop all the stack into output: */ + while (StackPtr != 0 && i < LineLen) { + Line[i++] = Stack[--StackPtr]; + } + } + if (LastCode != NO_SUCH_CODE && + Private->RunningCode - 2 < (LZ_MAX_CODE + 1) && + Prefix[Private->RunningCode - 2] == NO_SUCH_CODE) { + Prefix[Private->RunningCode - 2] = LastCode; + + if (CrntCode == Private->RunningCode - 2) { + /* Only allowed if CrntCode is exactly + * the running code: In that case + * CrntCode = XXXCode, CrntCode or the + * prefix code is last code and the + * suffix char is exactly the prefix of + * last code! */ + Suffix[Private->RunningCode - 2] = + DGifGetPrefixChar(Prefix, LastCode, + ClearCode); + } else { + Suffix[Private->RunningCode - 2] = + DGifGetPrefixChar(Prefix, CrntCode, + ClearCode); + } + } + LastCode = CrntCode; + } + } + + Private->LastCode = LastCode; + Private->StackPtr = StackPtr; + + return GIF_OK; } /****************************************************************************** @@ -972,55 +1005,55 @@ DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) If image is defective, we might loop here forever, so we limit the loops to the maximum possible if image O.k. - LZ_MAX_CODE times. ******************************************************************************/ -static int -DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode) -{ - int i = 0; - - while (Code > ClearCode && i++ <= LZ_MAX_CODE) { - if (Code > LZ_MAX_CODE) { - return NO_SUCH_CODE; - } - Code = Prefix[Code]; - } - return Code; +static int DGifGetPrefixChar(const GifPrefixType *Prefix, int Code, + int ClearCode) { + int i = 0; + + while (Code > ClearCode && i++ <= LZ_MAX_CODE) { + if (Code > LZ_MAX_CODE) { + return NO_SUCH_CODE; + } + Code = Prefix[Code]; + } + return Code; } /****************************************************************************** Interface for accessing the LZ codes directly. Set Code to the real code (12bits), or to -1 if EOF code is returned. ******************************************************************************/ -int -DGifGetLZCodes(GifFileType *GifFile, int *Code) -{ - GifByteType *CodeBlock; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - - if (!IS_READABLE(Private)) { - /* This file was NOT open for reading: */ - GifFile->Error = D_GIF_ERR_NOT_READABLE; - return GIF_ERROR; - } - - if (DGifDecompressInput(GifFile, Code) == GIF_ERROR) - return GIF_ERROR; - - if (*Code == Private->EOFCode) { - /* Skip rest of codes (hopefully only NULL terminating block): */ - do { - if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR) - return GIF_ERROR; - } while (CodeBlock != NULL) ; - - *Code = -1; - } else if (*Code == Private->ClearCode) { - /* We need to start over again: */ - Private->RunningCode = Private->EOFCode + 1; - Private->RunningBits = Private->BitsPerPixel + 1; - Private->MaxCode1 = 1 << Private->RunningBits; - } - - return GIF_OK; +int DGifGetLZCodes(GifFileType *GifFile, int *Code) { + GifByteType *CodeBlock; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (DGifDecompressInput(GifFile, Code) == GIF_ERROR) { + return GIF_ERROR; + } + + if (*Code == Private->EOFCode) { + /* Skip rest of codes (hopefully only NULL terminating block): + */ + do { + if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR) { + return GIF_ERROR; + } + } while (CodeBlock != NULL); + + *Code = -1; + } else if (*Code == Private->ClearCode) { + /* We need to start over again: */ + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = Private->BitsPerPixel + 1; + Private->MaxCode1 = 1 << Private->RunningBits; + } + + return GIF_OK; } /****************************************************************************** @@ -1029,52 +1062,48 @@ DGifGetLZCodes(GifFileType *GifFile, int *Code) 8 bits (bytes) packets, into the real codes. Returns GIF_OK if read successfully. ******************************************************************************/ -static int -DGifDecompressInput(GifFileType *GifFile, int *Code) -{ - static const unsigned short CodeMasks[] = { - 0x0000, 0x0001, 0x0003, 0x0007, - 0x000f, 0x001f, 0x003f, 0x007f, - 0x00ff, 0x01ff, 0x03ff, 0x07ff, - 0x0fff - }; - - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - - GifByteType NextByte; - - /* The image can't contain more than LZ_BITS per code. */ - if (Private->RunningBits > LZ_BITS) { - GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; - return GIF_ERROR; - } - - while (Private->CrntShiftState < Private->RunningBits) { - /* Needs to get more bytes from input stream for next code: */ - if (DGifBufferedInput(GifFile, Private->Buf, &NextByte) == GIF_ERROR) { - return GIF_ERROR; - } - Private->CrntShiftDWord |= - ((unsigned long)NextByte) << Private->CrntShiftState; - Private->CrntShiftState += 8; - } - *Code = Private->CrntShiftDWord & CodeMasks[Private->RunningBits]; - - Private->CrntShiftDWord >>= Private->RunningBits; - Private->CrntShiftState -= Private->RunningBits; - - /* If code cannot fit into RunningBits bits, must raise its size. Note - * however that codes above 4095 are used for special signaling. - * If we're using LZ_BITS bits already and we're at the max code, just - * keep using the table as it is, don't increment Private->RunningCode. - */ - if (Private->RunningCode < LZ_MAX_CODE + 2 && - ++Private->RunningCode > Private->MaxCode1 && - Private->RunningBits < LZ_BITS) { - Private->MaxCode1 <<= 1; - Private->RunningBits++; - } - return GIF_OK; +static int DGifDecompressInput(GifFileType *GifFile, int *Code) { + static const unsigned short CodeMasks[] = { + 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, + 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff}; + + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + GifByteType NextByte; + + /* The image can't contain more than LZ_BITS per code. */ + if (Private->RunningBits > LZ_BITS) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + + while (Private->CrntShiftState < Private->RunningBits) { + /* Needs to get more bytes from input stream for next code: */ + if (DGifBufferedInput(GifFile, Private->Buf, &NextByte) == + GIF_ERROR) { + return GIF_ERROR; + } + Private->CrntShiftDWord |= ((unsigned long)NextByte) + << Private->CrntShiftState; + Private->CrntShiftState += 8; + } + *Code = Private->CrntShiftDWord & CodeMasks[Private->RunningBits]; + + Private->CrntShiftDWord >>= Private->RunningBits; + Private->CrntShiftState -= Private->RunningBits; + + /* If code cannot fit into RunningBits bits, must raise its size. Note + * however that codes above 4095 are used for special signaling. + * If we're using LZ_BITS bits already and we're at the max code, just + * keep using the table as it is, don't increment Private->RunningCode. + */ + if (Private->RunningCode < LZ_MAX_CODE + 2 && + ++Private->RunningCode > Private->MaxCode1 && + Private->RunningBits < LZ_BITS) { + Private->MaxCode1 <<= 1; + Private->RunningBits++; + } + return GIF_OK; } /****************************************************************************** @@ -1083,37 +1112,56 @@ DGifDecompressInput(GifFileType *GifFile, int *Code) The routine returns the next byte from its internal buffer (or read next block in if buffer empty) and returns GIF_OK if succesful. ******************************************************************************/ -static int -DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, GifByteType *NextByte) -{ - if (Buf[0] == 0) { - /* Needs to read the next buffer - this one is empty: */ - /* coverity[check_return] */ - if (InternalRead(GifFile, Buf, 1) != 1) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; - } - /* There shouldn't be any empty data blocks here as the LZW spec - * says the LZW termination code should come first. Therefore we - * shouldn't be inside this routine at that point. - */ - if (Buf[0] == 0) { - GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; - return GIF_ERROR; - } - if (InternalRead(GifFile, &Buf[1], Buf[0]) != Buf[0]) { - GifFile->Error = D_GIF_ERR_READ_FAILED; - return GIF_ERROR; - } - *NextByte = Buf[1]; - Buf[1] = 2; /* We use now the second place as last char read! */ - Buf[0]--; - } else { - *NextByte = Buf[Buf[1]++]; - Buf[0]--; - } - - return GIF_OK; +static int DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, + GifByteType *NextByte) { + if (Buf[0] == 0) { + /* Needs to read the next buffer - this one is empty: */ + /* coverity[check_return] */ + if (InternalRead(GifFile, Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + /* There shouldn't be any empty data blocks here as the LZW spec + * says the LZW termination code should come first. Therefore + * we shouldn't be inside this routine at that point. + */ + if (Buf[0] == 0) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + if (InternalRead(GifFile, &Buf[1], Buf[0]) != Buf[0]) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + *NextByte = Buf[1]; + Buf[1] = 2; /* We use now the second place as last char read! */ + Buf[0]--; + } else { + *NextByte = Buf[Buf[1]++]; + Buf[0]--; + } + + return GIF_OK; +} + +/****************************************************************************** + This routine is called in case of error during parsing image. We need to + decrease image counter and reallocate memory for saved images. Not decreasing + ImageCount may lead to null pointer dereference, because the last element in + SavedImages may point to the spoilt image and null pointer buffers. +*******************************************************************************/ +void DGifDecreaseImageCounter(GifFileType *GifFile) { + GifFile->ImageCount--; + if (GifFile->SavedImages[GifFile->ImageCount].RasterBits != NULL) { + free(GifFile->SavedImages[GifFile->ImageCount].RasterBits); + } + + // Realloc array according to the new image counter. + SavedImage *correct_saved_images = (SavedImage *)reallocarray( + GifFile->SavedImages, GifFile->ImageCount, sizeof(SavedImage)); + if (correct_saved_images != NULL) { + GifFile->SavedImages = correct_saved_images; + } } /****************************************************************************** @@ -1121,119 +1169,144 @@ DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, GifByteType *NextByte) the GifFileType pointer. Call DGifOpenFileName() or DGifOpenFileHandle() first to initialize I/O. Its inverse is EGifSpew(). *******************************************************************************/ -int -DGifSlurp(GifFileType *GifFile) -{ - size_t ImageSize; - GifRecordType RecordType; - SavedImage *sp; - GifByteType *ExtData; - int ExtFunction; - - GifFile->ExtensionBlocks = NULL; - GifFile->ExtensionBlockCount = 0; - - do { - if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) - return (GIF_ERROR); - - switch (RecordType) { - case IMAGE_DESC_RECORD_TYPE: - if (DGifGetImageDesc(GifFile) == GIF_ERROR) - return (GIF_ERROR); - - sp = &GifFile->SavedImages[GifFile->ImageCount - 1]; - /* Allocate memory for the image */ - if (sp->ImageDesc.Width <= 0 || sp->ImageDesc.Height <= 0 || - sp->ImageDesc.Width > (INT_MAX / sp->ImageDesc.Height)) { - return GIF_ERROR; - } - ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height; - - if (ImageSize > (SIZE_MAX / sizeof(GifPixelType))) { - return GIF_ERROR; - } - sp->RasterBits = (unsigned char *)reallocarray(NULL, ImageSize, - sizeof(GifPixelType)); - - if (sp->RasterBits == NULL) { - return GIF_ERROR; - } - - if (sp->ImageDesc.Interlace) { - int i, j; - /* - * The way an interlaced image should be read - - * offsets and jumps... - */ - int InterlacedOffset[] = { 0, 4, 2, 1 }; - int InterlacedJumps[] = { 8, 8, 4, 2 }; - /* Need to perform 4 passes on the image */ - for (i = 0; i < 4; i++) - for (j = InterlacedOffset[i]; - j < sp->ImageDesc.Height; - j += InterlacedJumps[i]) { - if (DGifGetLine(GifFile, - sp->RasterBits+j*sp->ImageDesc.Width, - sp->ImageDesc.Width) == GIF_ERROR) - return GIF_ERROR; - } - } - else { - if (DGifGetLine(GifFile,sp->RasterBits,ImageSize)==GIF_ERROR) - return (GIF_ERROR); - } - - if (GifFile->ExtensionBlocks) { - sp->ExtensionBlocks = GifFile->ExtensionBlocks; - sp->ExtensionBlockCount = GifFile->ExtensionBlockCount; - - GifFile->ExtensionBlocks = NULL; - GifFile->ExtensionBlockCount = 0; - } - break; - - case EXTENSION_RECORD_TYPE: - if (DGifGetExtension(GifFile,&ExtFunction,&ExtData) == GIF_ERROR) - return (GIF_ERROR); - /* Create an extension block with our data */ - if (ExtData != NULL) { - if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount, - &GifFile->ExtensionBlocks, - ExtFunction, ExtData[0], &ExtData[1]) - == GIF_ERROR) - return (GIF_ERROR); - } - for (;;) { - if (DGifGetExtensionNext(GifFile, &ExtData) == GIF_ERROR) - return (GIF_ERROR); - if (ExtData == NULL) - break; - /* Continue the extension block */ - if (ExtData != NULL) - if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount, - &GifFile->ExtensionBlocks, - CONTINUE_EXT_FUNC_CODE, - ExtData[0], &ExtData[1]) == GIF_ERROR) - return (GIF_ERROR); - } - break; - - case TERMINATE_RECORD_TYPE: - break; - - default: /* Should be trapped by DGifGetRecordType */ - break; - } - } while (RecordType != TERMINATE_RECORD_TYPE); - - /* Sanity check for corrupted file */ - if (GifFile->ImageCount == 0) { - GifFile->Error = D_GIF_ERR_NO_IMAG_DSCR; - return(GIF_ERROR); - } - - return (GIF_OK); +int DGifSlurp(GifFileType *GifFile) { + size_t ImageSize; + GifRecordType RecordType; + SavedImage *sp; + GifByteType *ExtData; + int ExtFunction; + + GifFile->ExtensionBlocks = NULL; + GifFile->ExtensionBlockCount = 0; + + do { + if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) { + return (GIF_ERROR); + } + + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFile) == GIF_ERROR) { + return (GIF_ERROR); + } + + sp = &GifFile->SavedImages[GifFile->ImageCount - 1]; + /* Allocate memory for the image */ + if (sp->ImageDesc.Width <= 0 || + sp->ImageDesc.Height <= 0 || + sp->ImageDesc.Width > + (INT_MAX / sp->ImageDesc.Height)) { + DGifDecreaseImageCounter(GifFile); + return GIF_ERROR; + } + ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height; + + if (ImageSize > (SIZE_MAX / sizeof(GifPixelType))) { + DGifDecreaseImageCounter(GifFile); + return GIF_ERROR; + } + sp->RasterBits = (unsigned char *)reallocarray( + NULL, ImageSize, sizeof(GifPixelType)); + + if (sp->RasterBits == NULL) { + DGifDecreaseImageCounter(GifFile); + return GIF_ERROR; + } + + if (sp->ImageDesc.Interlace) { + int i, j; + /* + * The way an interlaced image should be read - + * offsets and jumps... + */ + static const int InterlacedOffset[] = {0, 4, 2, + 1}; + static const int InterlacedJumps[] = {8, 8, 4, + 2}; + /* Need to perform 4 passes on the image */ + for (i = 0; i < 4; i++) { + for (j = InterlacedOffset[i]; + j < sp->ImageDesc.Height; + j += InterlacedJumps[i]) { + if (DGifGetLine( + GifFile, + sp->RasterBits + + j * sp->ImageDesc + .Width, + sp->ImageDesc.Width) == + GIF_ERROR) { + DGifDecreaseImageCounter( + GifFile); + return GIF_ERROR; + } + } + } + } else { + if (DGifGetLine(GifFile, sp->RasterBits, + ImageSize) == GIF_ERROR) { + DGifDecreaseImageCounter(GifFile); + return GIF_ERROR; + } + } + + if (GifFile->ExtensionBlocks) { + sp->ExtensionBlocks = GifFile->ExtensionBlocks; + sp->ExtensionBlockCount = + GifFile->ExtensionBlockCount; + + GifFile->ExtensionBlocks = NULL; + GifFile->ExtensionBlockCount = 0; + } + break; + + case EXTENSION_RECORD_TYPE: + if (DGifGetExtension(GifFile, &ExtFunction, &ExtData) == + GIF_ERROR) { + return (GIF_ERROR); + } + /* Create an extension block with our data */ + if (ExtData != NULL) { + if (GifAddExtensionBlock( + &GifFile->ExtensionBlockCount, + &GifFile->ExtensionBlocks, ExtFunction, + ExtData[0], &ExtData[1]) == GIF_ERROR) { + return (GIF_ERROR); + } + } + for (;;) { + if (DGifGetExtensionNext(GifFile, &ExtData) == + GIF_ERROR) { + return (GIF_ERROR); + } + if (ExtData == NULL) { + break; + } + /* Continue the extension block */ + if (GifAddExtensionBlock( + &GifFile->ExtensionBlockCount, + &GifFile->ExtensionBlocks, + CONTINUE_EXT_FUNC_CODE, ExtData[0], + &ExtData[1]) == GIF_ERROR) { + return (GIF_ERROR); + } + } + break; + + case TERMINATE_RECORD_TYPE: + break; + + default: /* Should be trapped by DGifGetRecordType */ + break; + } + } while (RecordType != TERMINATE_RECORD_TYPE); + + /* Sanity check for corrupted file */ + if (GifFile->ImageCount == 0) { + GifFile->Error = D_GIF_ERR_NO_IMAG_DSCR; + return (GIF_ERROR); + } + + return (GIF_OK); } /* end */ diff --git a/doc/Makefile b/doc/Makefile index 0293103..b34ece0 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,4 +1,4 @@ -.SUFFIXES: .xml .html .txt .adoc .1 +.SUFFIXES: .xml .html .txt .adoc .1 .7 .xml.html: xmlto xhtml-nochunks $< @@ -6,6 +6,9 @@ .xml.1: xmlto man $< +.xml.7: + xmlto man $< + .xml.txt: xmlto txt $< @@ -17,34 +20,40 @@ all: allhtml manpages # The distinction between XMLMAN and XMLINTERNAL is because # some pages shouldn't be installed as part of a binary package; # they're just for test-pattern generators. -XMLMAN = \ +XMLMAN1 = \ gif2rgb.xml \ gifbuild.xml \ gifclrmp.xml \ - gifecho.xml \ + giffilter.xml \ giffix.xml \ - gifinto.xml \ - giflib.xml \ + gifsponge.xml \ giftext.xml \ giftool.xml +XMLMAN7 = \ + giflib.xml XMLINTERNAL = \ gifbg.xml \ gifcolor.xml \ + gifecho.xml \ + gifinto.xml \ gifhisto.xml \ gifwedge.xml XMLDOC = intro.xml gif_lib.xml -XMLALL = $(XMLMAN) $(XMLINTERNAL) $(XMLDOC) +XMLALL = $(XMLMAN1) $(XMLMAN7) $(XMLINTERNAL) $(XMLDOC) + +# Logo image file for HTML docs +giflib-logo.gif: ../pic/gifgrid.gif + convert $^ -resize 50x50 $@ # Philosophical choice: the website gets the internal manual pages -allhtml: $(XMLALL:.xml=.html) $(XMLINTERNAL:.xml=.html) +allhtml: $(XMLALL:.xml=.html) giflib-logo.gif -manpages: $(XMLMAN:.xml=.1) $(XMLINTERNAL:.xml=.1) +manpages: $(XMLMAN1:.xml=.1) $(XMLMAN7:.xml=.7) $(XMLINTERNAL:.xml=.1) # Prepare the website directory to deliver an update. # ImageMagick and asciidoc are required. website: allhtml rm -fr staging; mkdir staging; - cp -r $(XMLALL:.xml=.html) gifstandard whatsinagif staging + cp -r $(XMLALL:.xml=.html) gifstandard whatsinagif giflib-logo.gif staging cp index.html.in staging/index.html - convert ../pic/gifgrid.gif -resize 50x50 staging/giflib-logo.gif asciidoc - <../history.adoc >staging/history.html diff --git a/doc/gif2rgb.1 b/doc/gif2rgb.1 index 2ece115..2554609 100644 --- a/doc/gif2rgb.1 +++ b/doc/gif2rgb.1 @@ -1,7 +1,7 @@ '\" t .\" Title: gif2rgb .\" Author: [see the "Author" section] -.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Generator: DocBook XSL Stylesheets vsnapshot .\" Date: 2 May 2012 .\" Manual: GIFLIB Documentation .\" Source: GIFLIB @@ -67,6 +67,9 @@ Print one line of command line help, similar to Usage above\&. By default, convert a GIF input file to RGB triplets\&. If \-s is specified, convert RGB input to a GIF\&. .PP If no input file is given, gif2rgb will try to read data from stdin\&. +.SH "BUGS" +.PP +Feeding this utility a GIF with an invalid colormap, or other kinds of malformations, index will produce invalid output and may core\-dump the tool\&. Don\*(Aqt do that\&. .SH "AUTHOR" .PP Gershon Elber\&. diff --git a/doc/gif2rgb.html b/doc/gif2rgb.html index 6ad2423..ff0eb01 100644 --- a/doc/gif2rgb.html +++ b/doc/gif2rgb.html @@ -1,5 +1,5 @@ -gif2rgb

Name

gif2rgb — convert images saved as GIF to 24-bit RGB triplets

Synopsis

gif2rgb [-v] [-1] [-c colors] [-s +gif2rgb

Name

gif2rgb — convert images saved as GIF to 24-bit RGB triplets

Synopsis

gif2rgb [-v] [-1] [-c colors] [-s width height] [-o outfile] [-h] [gif-file]

Options

-v

Verbose mode (show progress). Enables printout of running scan lines.

-1

Only one file in the format of RGBRGB... triplets (Each of R, @@ -12,4 +12,6 @@ default).

-s width height

Sets RGB-to-GIF conversion mode and specifies the size of the image to read.

-o

specifies the name of the out file (see also `-1' above).

-h

Print one line of command line help, similar to Usage above.

By default, convert a GIF input file to RGB triplets. If -s is specified, convert RGB input to a GIF.

If no input file is given, gif2rgb will try to read data -from stdin.

Author

Gershon Elber.

\ No newline at end of file +from stdin.

Bugs

Feeding this utility a GIF with an invalid colormap, or other +kinds of malformations, index will produce invalid output and may +core-dump the tool. Don't do that.

Author

Gershon Elber.

\ No newline at end of file diff --git a/doc/gif2rgb.xml b/doc/gif2rgb.xml index 3d958dc..d54f4c7 100644 --- a/doc/gif2rgb.xml +++ b/doc/gif2rgb.xml @@ -87,6 +87,13 @@ specified, convert RGB input to a GIF. If no input file is given, gif2rgb will try to read data from stdin. + +Bugs + +Feeding this utility a GIF with an invalid colormap, or other +kinds of malformations, index will produce invalid output and may +core-dump the tool. Don't do that. + Author diff --git a/doc/gif_lib.html b/doc/gif_lib.html index cd7691a..cf4aa05 100644 --- a/doc/gif_lib.html +++ b/doc/gif_lib.html @@ -1,5 +1,5 @@ -The GIFLIB Library

The GIFLIB Library

Eric Steven Raymond

+The GIFLIB Library

The GIFLIB Library

Eric Steven Raymond


Introduction

The Graphics Interchange Format(c) is the Copyright property of @@ -113,7 +113,7 @@

Create the union of two given color maps and return it. If the result won't fit into 256 colors, NULL is returned, the allocated union otherwise. ColorIn1 is copied as it to ColorUnion, while colors from -ColorIn2 are copied iff they didn't exist before. ColorTransIn2 maps +ColorIn2 are copied if they didn't exist before. ColorTransIn2 maps the old ColorIn2 into ColorUnion color map table.

 SavedImage *GifAttachImage(GifFileType *GifFile)
 

Add an image block to the SavedImages array. The image block is @@ -148,9 +148,10 @@ will create a new leading extension block.

Error Handling (gif_err.c)

 int GifErrorString(int ErrCode)
 

Returns a sting describing the specified GIFLIB error code. -Return NULL if the argument is not a valid error code.

The GIF Utility Font

The 8x8 utility font used in gifecho and gifcolor lives in the library -module gif_font.c, in a table called GifAsciiTable. The library header file -includes suitable externs and defines.

The GIF utility font support includes entry points for drawing legends +Return NULL if the argument is not a valid error code.

The GIF Utility Font

The 8x8 utility font used in the (obsolete, no longer installed) +gifecho and gifcolor lives in the library module gif_font.c, in a +table called GifAsciiTable. The library header file includes suitable +externs and defines.

The GIF utility font support includes entry points for drawing legends on in-core images, drawing boxes and rectangles, and boxing text. These entry points are as follows:

 void GifDrawText8x8(
diff --git a/doc/gif_lib.xml b/doc/gif_lib.xml
index 8150a6d..f896ee5 100644
--- a/doc/gif_lib.xml
+++ b/doc/gif_lib.xml
@@ -280,7 +280,7 @@ ColorMapObject *GifUnionColorMap(
 Create the union of two given color maps and return it.  If the result
 won't fit into 256 colors, NULL is returned, the allocated union
 otherwise.  ColorIn1 is copied as it to ColorUnion, while colors from
-ColorIn2 are copied iff they didn't exist before.  ColorTransIn2 maps
+ColorIn2 are copied if they didn't exist before.  ColorTransIn2 maps
 the old ColorIn2 into ColorUnion color map table.
 
 
@@ -340,9 +340,10 @@ Return NULL if the argument is not a valid error code.
 
 The GIF Utility Font
 
-The 8x8 utility font used in gifecho and gifcolor lives in the library
-module gif_font.c, in a table called GifAsciiTable.  The library header file
-includes suitable externs and defines.
+The 8x8 utility font used in the (obsolete, no longer installed)
+gifecho and gifcolor lives in the library module gif_font.c, in a
+table called GifAsciiTable.  The library header file includes suitable
+externs and defines.
 
 The GIF utility font support includes entry points for drawing legends
 on in-core images, drawing boxes and rectangles, and boxing text.
diff --git a/doc/gifbg.1 b/doc/gifbg.1
index 59c5ea9..c52f972 100644
--- a/doc/gifbg.1
+++ b/doc/gifbg.1
@@ -1,7 +1,7 @@
 '\" t
 .\"     Title: gifbg
 .\"    Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.79.1 
+.\" Generator: DocBook XSL Stylesheets vsnapshot 
 .\"      Date: 2 May 2012
 .\"    Manual: GIFLIB Documentation
 .\"    Source: GIFLIB
diff --git a/doc/gifbuild.1 b/doc/gifbuild.1
index 7df7d5f..05c0c65 100644
--- a/doc/gifbuild.1
+++ b/doc/gifbuild.1
@@ -1,7 +1,7 @@
 '\" t
 .\"     Title: gifbuild
 .\"    Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.79.1 
+.\" Generator: DocBook XSL Stylesheets vsnapshot 
 .\"      Date: 2 May 2012
 .\"    Manual: GIFLIB Documentation
 .\"    Source: GIFLIB
diff --git a/doc/gifclrmp.1 b/doc/gifclrmp.1
index ab61b02..dc6d1d1 100644
--- a/doc/gifclrmp.1
+++ b/doc/gifclrmp.1
@@ -1,7 +1,7 @@
 '\" t
 .\"     Title: gifclrmp
 .\"    Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.79.1 
+.\" Generator: DocBook XSL Stylesheets vsnapshot 
 .\"      Date: 2 May 2012
 .\"    Manual: GIFLIB Documentation
 .\"    Source: GIFLIB
diff --git a/doc/gifcolor.1 b/doc/gifcolor.1
index 391e7a1..58f4852 100644
--- a/doc/gifcolor.1
+++ b/doc/gifcolor.1
@@ -1,7 +1,7 @@
 '\" t
 .\"     Title: gifcolor
 .\"    Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.79.1 
+.\" Generator: DocBook XSL Stylesheets vsnapshot 
 .\"      Date: 2 May 2012
 .\"    Manual: GIFLIB Documentation
 .\"    Source: GIFLIB
diff --git a/doc/gifecho.1 b/doc/gifecho.1
index 0422f50..7096c7e 100644
--- a/doc/gifecho.1
+++ b/doc/gifecho.1
@@ -1,7 +1,7 @@
 '\" t
 .\"     Title: gifecho
 .\"    Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.79.1 
+.\" Generator: DocBook XSL Stylesheets vsnapshot 
 .\"      Date: 2 May 2012
 .\"    Manual: GIFLIB Documentation
 .\"    Source: GIFLIB
diff --git a/doc/giffilter.1 b/doc/giffilter.1
new file mode 100644
index 0000000..a080352
--- /dev/null
+++ b/doc/giffilter.1
@@ -0,0 +1,43 @@
+'\" t
+.\"     Title: giffilter
+.\"    Author: [see the "Author" section]
+.\" Generator: DocBook XSL Stylesheets vsnapshot 
+.\"      Date: 2 May 2012
+.\"    Manual: GIFLIB Documentation
+.\"    Source: GIFLIB
+.\"  Language: English
+.\"
+.TH "GIFFILTER" "1" "2 May 2012" "GIFLIB" "GIFLIB Documentation"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el       .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+giffilter \- expensive GIF copy, a model for filter utilities
+.SH "SYNOPSIS"
+.HP \w'\fBgiffilter\fR\ 'u
+\fBgiffilter\fR
+.SH "DESCRIPTION"
+.PP
+This is an expensive way to copy a GIF\&. The source is included as a skeleton for more sophisticated filters\&. See the source in the util directory for details\&.
+.PP
+Also has some utility as a test of the sequential GIF record I/O routines\&. The output should be bytewise identical to the input\&.
+.SH "AUTHOR"
+.PP
+Eric S\&. Raymond
+
diff --git a/doc/giffilter.html b/doc/giffilter.html
new file mode 100644
index 0000000..f8e6723
--- /dev/null
+++ b/doc/giffilter.html
@@ -0,0 +1,5 @@
+
+giffilter

Name

giffilter — expensive GIF copy, a model for filter utilities

Synopsis

giffilter

Description

This is an expensive way to copy a GIF. The source is included +as a skeleton for more sophisticated filters. See the source in the +util directory for details.

Also has some utility as a test of the sequential GIF record I/O +routines. The output should be bytewise identical to the input.

Author

Eric S. Raymond

\ No newline at end of file diff --git a/doc/giffilter.xml b/doc/giffilter.xml new file mode 100644 index 0000000..059e70e --- /dev/null +++ b/doc/giffilter.xml @@ -0,0 +1,42 @@ + + +]> + +2 May 2012 + +giffilter +1 +GIFLIB +GIFLIB Documentation + + +giffilter +expensive GIF copy, a model for filter utilities + + + + + + giffilter + + + +Description + +This is an expensive way to copy a GIF. The source is included +as a skeleton for more sophisticated filters. See the source in the +util directory for details. + +Also has some utility as a test of the sequential GIF record I/O +routines. The output should be bytewise identical to the input. + + +Author + +Eric S. Raymond &email; + + + diff --git a/doc/giffix.1 b/doc/giffix.1 index 87396e7..778cc7f 100644 --- a/doc/giffix.1 +++ b/doc/giffix.1 @@ -1,7 +1,7 @@ '\" t .\" Title: giffix .\" Author: [see the "Author" section] -.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Generator: DocBook XSL Stylesheets vsnapshot .\" Date: 2 May 2012 .\" Manual: GIFLIB Documentation .\" Source: GIFLIB diff --git a/doc/gifhisto.1 b/doc/gifhisto.1 index a9d7c07..29408e6 100644 --- a/doc/gifhisto.1 +++ b/doc/gifhisto.1 @@ -1,7 +1,7 @@ '\" t .\" Title: gifhisto .\" Author: [see the "Author" section] -.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Generator: DocBook XSL Stylesheets vsnapshot .\" Date: 2 May 2012 .\" Manual: GIFLIB Documentation .\" Source: GIFLIB diff --git a/doc/gifinto.1 b/doc/gifinto.1 index d3921a1..dbd46b5 100644 --- a/doc/gifinto.1 +++ b/doc/gifinto.1 @@ -1,7 +1,7 @@ '\" t .\" Title: gifinto .\" Author: [see the "Author" section] -.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Generator: DocBook XSL Stylesheets vsnapshot .\" Date: 2 May 2012 .\" Manual: GIFLIB Documentation .\" Source: GIFLIB @@ -34,7 +34,7 @@ gifinto \- save GIF on stdin to file if size over set threshold \fBgifinto\fR [\-v] [\-s\ \fIminsize\fR] [\-h] [\fIoutfile\fR] .SH "DESCRIPTION" .PP -A program to save stdin into a file with given name, iff the result file has size bigger than specified (see below)\&. This can be used to save a result under the same filename we started with in a chain of pipes\&. +A program to save stdin into a file with given name, if the result file has size bigger than specified (see below)\&. This can be used to save a result under the same filename we started with in a chain of pipes\&. .PP Always reads a GIF file from stdin\&. .SH "OPTIONS" diff --git a/doc/gifinto.html b/doc/gifinto.html index d048cbe..62a7db2 100644 --- a/doc/gifinto.html +++ b/doc/gifinto.html @@ -1,5 +1,5 @@ -gifinto

Name

gifinto — save GIF on stdin to file if size over set threshold

Synopsis

gifinto [-v] [-s minsize] [-h] [outfile]

Description

A program to save stdin into a file with given name, iff the +gifinto

Name

gifinto — save GIF on stdin to file if size over set threshold

Synopsis

gifinto [-v] [-s minsize] [-h] [outfile]

Description

A program to save stdin into a file with given name, if the result file has size bigger than specified (see below). This can be used to save a result under the same filename we started with in a chain of pipes.

Always reads a GIF file from stdin.

Options

-v

Verbose mode (show progress). diff --git a/doc/gifinto.xml b/doc/gifinto.xml index 458c8e0..801f061 100644 --- a/doc/gifinto.xml +++ b/doc/gifinto.xml @@ -28,7 +28,7 @@ Description -A program to save stdin into a file with given name, iff the +A program to save stdin into a file with given name, if the result file has size bigger than specified (see below). This can be used to save a result under the same filename we started with in a chain of pipes. diff --git a/doc/giflib.1 b/doc/giflib.7 similarity index 91% rename from doc/giflib.1 rename to doc/giflib.7 index edacf83..63a360b 100644 --- a/doc/giflib.1 +++ b/doc/giflib.7 @@ -1,13 +1,13 @@ '\" t .\" Title: giflib .\" Author: [see the "Authors" section] -.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Generator: DocBook XSL Stylesheets vsnapshot .\" Date: 3 June 2012 .\" Manual: GIFLIB Documentation .\" Source: GIFLIB .\" Language: English .\" -.TH "GIFLIB" "1" "3 June 2012" "GIFLIB" "GIFLIB Documentation" +.TH "GIFLIB" "7" "3 June 2012" "GIFLIB" "GIFLIB Documentation" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -38,12 +38,9 @@ These utilities are not intended to compete with or replace multi\-format graphi API documentation for the service library is best viewed through a browser at the project website: http://sourceforge\&.net/projects/giflib/?source=directory\&. .SH "SEE ALSO" .PP -\fBgif2raw\fR(1), \fBgif2rgb\fR(1), \fBgifbuild\fR(1), -\fBgifecho\fR(1), \fBgiffix\fR(1), -\fBgifinto\fR(1), \fBgiftext\fR(1), \fBgiftool\fR(1), \fBgifclrmap\fR(1)\&. diff --git a/doc/giflib.html b/doc/giflib.html index a435ae4..bd592bd 100644 --- a/doc/giflib.html +++ b/doc/giflib.html @@ -1,5 +1,5 @@ -giflib

Name

giflib — GIFLIB utilities

Description

GIFLIB is a linkable service library and a set of utilities for +giflib

Name

giflib — GIFLIB utilities

Description

GIFLIB is a linkable service library and a set of utilities for processing images encoded using GIF (Graphics Interchange Format).

These utilities are not intended to compete with or replace multi-format graphics toolkits like ImageMagick or the Python Imaging Library, or graphics editors such as the GIMP. Find one of those, or @@ -8,13 +8,10 @@ intended to facilitate GIF-specific operations which multi-format tools may not adequately support.

API documentation for the service library is best viewed through a browser at the project website: http://sourceforge.net/projects/giflib/?source=directory.

See Also

-gif2raw(1), gif2rgb(1), gifbuild(1), -gifecho(1), giffix(1), -gifinto(1), giftext(1), giftool(1), gifclrmap(1). -

Authors

Gershon Elber, Eric S. Raymond, Toshio Kuratomi.

\ No newline at end of file +

Authors

Gershon Elber, Eric S. Raymond, Toshio Kuratomi.

\ No newline at end of file diff --git a/doc/giflib.xml b/doc/giflib.xml index 6f759e8..00a2219 100644 --- a/doc/giflib.xml +++ b/doc/giflib.xml @@ -4,11 +4,11 @@ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [ ]> - + 3 June 2012 giflib -1 +7 GIFLIB GIFLIB Documentation @@ -37,12 +37,9 @@ a browser at the project website: &project;. See Also -gif2raw1, gif2rgb1, gifbuild1, -gifecho1, giffix1, -gifinto1, giftext1, giftool1, gifclrmap1. diff --git a/doc/gifsponge.1 b/doc/gifsponge.1 new file mode 100644 index 0000000..3365641 --- /dev/null +++ b/doc/gifsponge.1 @@ -0,0 +1,41 @@ +'\" t +.\" Title: gifsponge +.\" Author: [see the "Author" section] +.\" Generator: DocBook XSL Stylesheets vsnapshot +.\" Date: 20 Dec 2020 +.\" Manual: GIFLIB Documentation +.\" Source: GIFLIB +.\" Language: English +.\" +.TH "GIFSPONGE" "1" "20 Dec 2020" "GIFLIB" "GIFLIB Documentation" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" ----------------------------------------------------------------- +.\" * set default formatting +.\" ----------------------------------------------------------------- +.\" disable hyphenation +.nh +.\" disable justification (adjust text to left margin only) +.ad l +.\" ----------------------------------------------------------------- +.\" * MAIN CONTENT STARTS HERE * +.\" ----------------------------------------------------------------- +.SH "NAME" +gifsponge \- expensive GIF copy, a model for slurp utilities +.SH "SYNOPSIS" +.HP \w'\fBgifsponge\fR\ 'u +\fBgifsponge\fR +.SH "DESCRIPTION" +.PP +Slurp a GIF into core, operate on it, spew it out again\&. This is an expensive way to copy a GIF\&. The source is included as a skeleton for more sophisticated slurp utilities\&. See the source in the util directory for details\&. +.SH "AUTHOR" +.PP +David Su\(~A\(r!rez + diff --git a/doc/gifsponge.html b/doc/gifsponge.html new file mode 100644 index 0000000..72ff7c1 --- /dev/null +++ b/doc/gifsponge.html @@ -0,0 +1,5 @@ + +gifsponge

Name

gifsponge — expensive GIF copy, a model for slurp utilities

Synopsis

gifsponge

Description

Slurp a GIF into core, operate on it, spew it out again. +This is an expensive way to copy a GIF. The source is included +as a skeleton for more sophisticated slurp utilities. See the source in the +util directory for details.

Author

David Suárez

\ No newline at end of file diff --git a/doc/gifsponge.xml b/doc/gifsponge.xml new file mode 100644 index 0000000..e1a31bc --- /dev/null +++ b/doc/gifsponge.xml @@ -0,0 +1,40 @@ + + +]> + +20 Dec 2020 + +gifsponge +1 +GIFLIB +GIFLIB Documentation + + +gifsponge +expensive GIF copy, a model for slurp utilities + + + + + + gifsponge + + + +Description + +Slurp a GIF into core, operate on it, spew it out again. +This is an expensive way to copy a GIF. The source is included +as a skeleton for more sophisticated slurp utilities. See the source in the +util directory for details. + + +Author + +David Suárez &email; + + + diff --git a/doc/gifstandard/GIF89a.html b/doc/gifstandard/GIF89a.html new file mode 100644 index 0000000..6319564 --- /dev/null +++ b/doc/gifstandard/GIF89a.html @@ -0,0 +1,2820 @@ + + + + +Graphics Interchange Format Version 89a + +

Graphics Interchange Format Version 89a

+ + +
+ +

The document below is a copy of http://www.w3.org/Graphics/GIF/spec-gif89a.txt, +reformatted into HTML to make it easier to read and print.

+ +

Notable changes:

+ +
    +
  • moved chapter "Cover Sheet for the GIF89a Specification" from the + beginning to the end
  • +
  • removed page numbers
  • +
  • removed redundant rows from Quick + Reference Table
  • +
+ +You can also read the original flat text. +
+ +
+ +

© 1987, 1988, 1989, 1990

+ +

Copyright
+CompuServe Incorporated
+Columbus, Ohio

+ +

Graphics Interchange Format Programming Reference

+ +

CompuServe Incorporated

+ +

Document Date: 31 July 1990

+ +
+hide all chapters | +show all chapters +
+ +

Table of Contents (hide/show)

+ + + +

1. Disclaimer (hide/show)

+ +
+ +

The information provided herein is subject to change without notice. In no +event will CompuServe Incorporated be liable for damages, including any loss of +revenue, loss of profits or other incidental or consequential damages arising +out of the use or inability to use the information; CompuServe Incorporated +makes no claim as to the suitability of the information.

+ +
+ +

2. Foreword (hide/show)

+ +
+ +

This document defines the Graphics Interchange Format℠. The +specification given here defines version 89a, which is an extension of version +87a.

+ +

The Graphics Interchange Format℠ as specified here should be +considered complete; any deviation from it should be considered invalid, +including but not limited to, the use of reserved or undefined fields within +control or data blocks, the inclusion of extraneous data within or between +blocks, the use of methods or algorithms not specifically listed as part of the +format, etc. In general, any and all deviations, extensions or modifications +not specified in this document should be considered to be in violation of the +format and should be avoided.

+ +
+ +

3. Licensing (hide/show)

+ +
+ +

The Graphics Interchange Format© is the copyright property of +CompuServe Incorporated. Only CompuServe Incorporated is authorized to define, +redefine, enhance, alter, modify or change in any way the definition of the +format.

+ +

CompuServe Incorporated hereby grants a limited, non-exclusive, royalty-free +license for the use of the Graphics Interchange Format℠ in computer +software; computer software utilizing GIF℠ must acknowledge ownership of +the Graphics Interchange Format and its Service Mark by CompuServe +Incorporated, in User and Technical Documentation. Computer software utilizing +GIF, which is distributed or may be distributed without User or Technical +Documentation must display to the screen or printer a message acknowledging +ownership of the Graphics Interchange Format and the Service Mark by CompuServe +Incorporated; in this case, the acknowledgement may be displayed in an opening +screen or leading banner, or a closing screen or trailing banner. A message +such as the following may be used:

+ +
+

The Graphics Interchange Format© is the Copyright property of + CompuServe Incorporated. GIF℠ is a Service Mark property of + CompuServe Incorporated.

+
+ +

For further information, please contact:

+ +
+

CompuServe Incorporated
+ Graphics Technology Department
+ 5000 Arlington Center Boulevard
+ Columbus, Ohio 43220
+ U. S. A.

+
+ +

CompuServe Incorporated maintains a mailing list with all those individuals +and organizations who wish to receive copies of this document when it is +corrected or revised. This service is offered free of charge; please provide us +with your mailing address.

+ +
+ +

4. About the Document (hide/show)

+ +
+ +

This document describes in detail the definition of the Graphics Interchange +Format. This document is intended as a programming reference; it is recommended +that the entire document be read carefully before programming, because of the +interdependence of the various parts. There is an individual section for each +of the Format blocks. Within each section, the sub-section labeled Required +Version refers to the version number that an encoder will have to use if the +corresponding block is used in the Data Stream. Within each section, a diagram +describes the individual fields in the block; the diagrams are drawn +vertically; top bytes in the diagram appear first in the Data Stream. Bits +within a byte are drawn most significant on the left end. Multi-byte numeric +fields are ordered Least Significant Byte first. Numeric constants are +represented as Hexadecimal numbers, preceded by "0x". Bit fields +within a byte are described in order from most significant bits to least +significant bits.

+ +
+ +

5. General Description (hide/show)

+ +
+ +

The Graphics Interchange Format℠ defines a protocol intended for the +on-line transmission and interchange of raster graphic data in a way that is +independent of the hardware used in their creation or display.

+ +

The Graphics Interchange Format is defined in terms of blocks and +sub-blocks which contain relevant parameters and data +used in the reproduction of a graphic. A GIF Data Stream is a sequence of +protocol blocks and sub-blocks representing a collection of graphics. In +general, the graphics in a Data Stream are assumed to be related to some +degree, and to share some control information; it is recommended that encoders +attempt to group together related graphics in order to minimize hardware +changes during processing and to minimize control information overhead. For the +same reason, unrelated graphics or graphics which require resetting hardware +parameters should be encoded separately to the extent possible.

+ +

A Data Stream may originate locally, as when read from a file, or it may +originate remotely, as when transmitted over a data communications line. The +Format is defined with the assumption that an error-free Transport Level +Protocol is used for communications; the Format makes no provisions for +error-detection and error-correction.

+ +

The GIF Data Stream must be interpreted in context, that is, the application +program must rely on information external to the Data Stream to invoke the +decoder process.

+ +
+ +

6. Version Numbers (hide/show)

+ +
+ +

The version number in the Header of a Data Stream is +intended to identify the minimum set of capabilities required of a decoder in +order to fully process the Data Stream. An encoder should use the earliest +possible version number that includes all the blocks used in the Data Stream. +Within each block section in this document, there is an entry labeled Required +Version which specifies the earliest version number that includes the +corresponding block. The encoder should make every attempt to use the earliest +version number covering all the blocks in the Data Stream; the unnecessary use +of later version numbers will hinder processing by some decoders.

+ +
+ +

7. The Encoder (hide/show)

+ +
+ +

The Encoder is the program used to create a GIF Data Stream. From raster +data and other information, the encoder produces the necessary control and data +blocks needed for reproducing the original graphics.

+ +

The encoder has the following primary responsibilities.

+ +
    +
  • Include in the Data Stream all the necessary information to reproduce + the graphics. +
  • Insure that a Data Stream is labeled with the earliest possible Version + Number that will cover the definition of all the blocks in it; this is to + ensure that the largest number of decoders can process the Data Stream. +
  • Ensure encoding of the graphics in such a way that the decoding process + is optimized. Avoid redundant information as much as possible. +
  • To the extent possible, avoid grouping graphics which might require + resetting hardware parameters during the decoding process. +
  • Set to zero (off) each of the bits of each and every field designated + as reserved. Note that some fields in the Logical Screen Descriptor and the + Image Descriptor were reserved under Version + 87a, but are used under version 89a. +
+ +
+ +

8. The Decoder (hide/show)

+ +
+ +

The Decoder is the program used to process a GIF Data Stream. It processes +the Data Stream sequentially, parsing the various blocks and +sub-blocks, using the control information to set +hardware and process parameters and interpreting the data to render the +graphics.

+ +

The decoder has the following primary responsibilities.

+ +
    +
  • Process each graphic in the Data Stream in sequence, without delays + other than those specified in the control information. +
  • Set its hardware parameters to fit, as closely as possible, the control + information contained in the Data Stream. +
+ +
+ +

9. Compliance (hide/show)

+ +
+ +

An encoder or a decoder is said to comply with a given version of the +Graphics Interchange Format if and only if it fully conforms with and correctly +implements the definition of the standard associated with that version. An +encoder or a decoder may be compliant with a given version number and not +compliant with some subsequent version.

+ +
+ +

10. About Recommendations (hide/show)

+ +
+ +

Each block section in this document contains an entry labeled +Recommendation; this section lists a set of recommendations intended to guide +and organize the use of the particular blocks. Such recommendations are geared +towards making the functions of encoders and decoders more efficient, as well +as making optimal use of the communications bandwidth. It is advised that these +recommendations be followed.

+ +
+ +

11. About Color Tables (hide/show)

+ +
+ +

The GIF format utilizes color tables to render raster-based graphics. A +color table can have one of two different scopes: global or local.

+ +

A Global Color Table is used by all those +graphics in the Data Stream which do not have a +Local Color Table associated with them. The +scope of the Global Color Table is the entire Data Stream.

+ +

A Local Color Table is always associated with the graphic that immediately +follows it; the scope of a Local Color Table is limited to that single graphic. +A Local Color Table supersedes a Global Color Table, that is, if a Data Stream +contains a Global Color Table, and an image has a Local Color Table associated +with it, the decoder must save the Global Color Table, use the Local Color +Table to render the image, and then restore the Global Color Table.

+ +

Both types of color tables are optional, making it possible for a Data +Stream to contain numerous graphics without a color table at all. For this +reason, it is recommended that the decoder save the last Global Color Table +used until another Global Color Table is encountered. In this way, a Data +Stream which does not contain either a Global Color Table or a Local Color +Table may be processed using the last Global Color Table saved. If a Global +Color Table from a previous Stream is used, that table becomes the Global Color +Table of the present Stream. This is intended to reduce the overhead incurred +by color tables. In particular, it is recommended that an encoder use only one +Global Color Table if all the images in related Data Streams can be rendered +with the same table. If no color table is available at all, the decoder is free +to use a system color table or a table of its own. In that case, the decoder +may use a color table with as many colors as its hardware is able to support; +it is recommended that such a table have black and white as its first two +entries, so that monochrome images can be rendered adequately.

+ +

The Definition of the GIF Format allows for a Data Stream to contain only +the Header, the +Logical Screen Descriptor, a Global +Color Table and the GIF Trailer. Such a Data Stream +would be used to load a decoder with a Global Color Table, in preparation for +subsequent Data Streams without a color table at all.

+ +
+ +

12. Blocks, Extensions and Scope (hide/show)

+ +
+ +

Blocks can be classified into three groups: Control, Graphic-Rendering and +Special Purpose.

+ +

Control blocks, such as the Header, the Logical Screen +Descriptor, the Graphic Control +Extension and the Trailer, contain information used +to control the process of the Data Stream or information used in setting +hardware parameters.

+ +

Graphic-Rendering blocks such as the Image +Descriptor and the Plain Text Extension +contain information and data used to render a graphic on the display +device.

+ +

Special Purpose blocks such as the Comment +Extension and the Application Extension +are neither used to control the process of the Data Stream nor do they contain +information or data used to render a graphic on the display device.

+ +

With the exception of the Logical Screen +Descriptor and the Global Color Table, +whose scope is the entire Data Stream, all other Control blocks have a limited +scope, restricted to the Graphic-Rendering block that follows them. Special +Purpose blocks do not delimit the scope of any Control blocks; Special Purpose +blocks are transparent to the decoding process. Graphic-Rendering blocks and +extensions are used as scope delimiters for Control blocks and extensions.

+ +

The labels used to identify labeled blocks fall into three ranges:

+ +
    +
  • 0x000x7F (0–127) are the Graphic + Rendering blocks, excluding the Trailer + (0x3B, 59) +
  • 0x800xF9 (128–249) are the Control + blocks +
  • 0xFA0xFF (250–255) are the Special + Purpose blocks +
+ +

These ranges are defined so that decoders can handle block scope by +appropriately identifying block labels, even when the block itself cannot be +processed.

+ +
+ +

13. Block Sizes (hide/show)

+ +
+ +

The Block Size field in a block, counts the number of bytes remaining in the +block, not counting the Block Size field itself, and not counting the +Block Terminator, if one is to follow. Blocks +other than Data Blocks are intended to be of fixed length; the Block Size field +is provided in order to facilitate skipping them, not to allow their size to +change in the future. Data blocks and sub-blocks are +of variable length to accommodate the amount of data.

+ +
+ +

14. Using GIF as an embedded protocol (hide/show)

+ +
+ +

As an embedded protocol, GIF may be part of larger application protocols, +within which GIF is used to render graphics. In such a case, the application +protocol could define a block within which the GIF Data Stream would be +contained. The application program would then invoke a GIF decoder upon +encountering a block of type GIF. This approach is recommended in favor of +using Application Extensions, which become +overhead for all other applications that do not process them. Because a GIF +Data Stream must be processed in context, the application must rely on some +means of identifying the GIF Data Stream outside of the Stream itself.

+ +
+ +

15. Data Sub-blocks (hide/show)

+ +
+ +

a. Description

+ +

Data Sub-blocks are units containing data. They do not have a label, these +blocks are processed in the context of control blocks, wherever data blocks are +specified in the format. The first byte of the Data sub-block indicates the +number of data bytes to follow. A data sub-block may contain from 0 to 255 data +bytes. The size of the block does not account for the size byte itself, +therefore, the empty sub-block is one whose size field contains 0x00 +(0).

+ +

b. Required Version

+ +

87a.

+ +

c. Syntax

+ + + + + + + + + +
Byte # + Field Name + Type +
0 + Block Size + Byte +
1 + Data Values + Byte +
2 +
3 +
⋮ +
up to 255 +
+ +
    +
  1. Block Size: +
      +
    • Number of bytes in the Data Sub-block; the size must be within 0 + and 255 bytes, inclusive. +
    +
  2. Data Values: +
      +
    • Any 8-bit value. There must be exactly as many Data Values as + specified by the Block Size field. +
    +
+ +

d. Extensions and Scope

+ +

This type of block always occurs as part of a larger unit. It does not have +a scope of itself.

+ +

e. Recommendation

+ +

None.

+ +
+ +

16. Block Terminator (hide/show)

+ +
+ +

a. Description

+ +

This zero-length Data Sub-block is used to +terminate a sequence of Data Sub-blocks. It contains a single byte in the +position of the Block Size field and does not contain data.

+ +

b. Required Version

+ +

87a.

+ +

c. Syntax

+ + + + +
Byte # + Field Name + Type +
0 + Block Size + Byte +
+ +
    +
  1. Block Size: +
      +
    • Number of bytes in the Data Sub-block; this field contains the + fixed value 0x00 (0). +
    +
  2. Data Values: +
      +
    • This block does not contain any data. +
    +
+ +

d. Extensions and Scope

+ +

This block terminates the immediately preceding sequence of Data Sub-blocks. +This block cannot be modified by any extension.

+ +

e. Recommendation

+ +

None.

+ +
+ + + +
+ +

a. Description

+ +

The Header identifies the GIF Data Stream in context. The Signature field +marks the beginning of the Data Stream, and the Version field identifies the +set of capabilities required of a decoder to fully process the Data Stream. +This block is required; exactly one Header must be present per Data +Stream.

+ +

b. Required Version

+ +

Not applicable. This block is not subject to a version number. This block +must appear at the beginning of every Data Stream.

+ +

c. Syntax

+ + + + + + + + + +
Byte # + Field Name + Type +
0 + Signature + 3 Bytes +
1 +
2 +
3 + Version + 3 Bytes +
4 +
5 +
+ +
    +
  1. Signature: +
      +
    • Identifies the GIF Data Stream. This field contains the fixed value + 'GIF' (0x47 0x49 0x46). +
    +
  2. Version: +
      +
    • Version number used to format the data stream. Identifies the + minimum set of capabilities necessary to a decoder to fully process the + contents of the Data Stream. +
    • Version Numbers as of 10 July 1990: +
        +
      • "87a" (0x38 0x37 0x61) – May 1987 +
      • "89a" (0x38 0x39 0x61) – July 1989 +
      +
    • Version numbers are ordered numerically increasing on the first two + digits starting with 87 (87, 88, …, 99, 00, …, 85, 86) + and alphabetically increasing on the third character (a, …, z). +
    +
  3. Extensions and Scope: +
      +
    • The scope of this block is the entire Data Stream. This block + cannot be modified by any extension. +
    +
+ +

d. Recommendations

+ +
    +
  1. Signature: +
      +
    • This field identifies the beginning of the GIF Data Stream; it is + not intended to provide a unique signature for the identification of + the data. It is recommended that the GIF Data Stream be identified + externally by the application. (Refer to Appendix + G for on-line identification of the GIF Data Stream.) +
    +
  2. Version: +
      +
    • Encoder: An encoder should use the earliest possible version number + that defines all the blocks used in the Data Stream. When two or more + Data Streams are combined, the latest of the individual version numbers + should be used for the resulting Data Stream. +
    • Decoder: A decoder should attempt to process the data stream to the + best of its ability; if it encounters a version number which it is not + capable of processing fully, it should nevertheless, attempt to process + the data stream to the best of its ability, perhaps after warning the + user that the data may be incomplete. +
    +
+ +
+ +

18. Logical Screen Descriptor (hide/show)

+ +
+ +

a. Description

+ +

The Logical Screen Descriptor contains the parameters necessary to define +the area of the display device within which the images will be rendered. The +coordinates in this block are given with respect to the top-left corner of the +virtual screen; they do not necessarily refer to absolute coordinates on the +display device. This implies that they could refer to window coordinates in a +window-based environment or printer coordinates when a printer is used.

+ +

This block is required; exactly one Logical Screen Descriptor must be +present per Data Stream.

+ +

b. Required Version

+ +

Not applicable. This block is not subject to a version number. This block +must appear immediately after the Header.

+ +

c. Syntax

+ + + + + + + + + + + +
Byte # + Bits + Field Name + Type +
7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 +
0 + + Logical Screen Width + Unsigned +
1 +
2 + + Logical Screen Height + Unsigned +
3 +
4 + + + + + <Packed Fields> + See below +
5 + + Background Color Index + Byte +
6 + + Pixel Aspect Ratio + Byte +
+ +

<Packed Fields> =

+ + + + + + +
Global Color Table Flag + 1 Bit +
Color Resolution + 3 Bits +
Sort Flag + 1 Bit +
Size of Global Color Table + 3 Bits +
+ +
    +
  1. Logical Screen Width: +
      +
    • Width, in pixels, of the Logical Screen where the images will be + rendered in the displaying device. +
    +
  2. Logical Screen Height: +
      +
    • Height, in pixels, of the Logical Screen where the images will be + rendered in the displaying device. +
    +
  3. Global Color Table Flag: +
      +
    • Flag indicating the presence of a Global Color Table; if the flag + is set, the Global Color Table will immediately follow the Logical + Screen Descriptor. This flag also selects the interpretation of the + Background Color Index; if the flag is set, the value of the Background + Color Index field should be used as the table index of the background + color. (This field is the most significant bit of the byte.) +
    • Values: +
        +
      • 0: No Global Color Table follows, the Background Color Index + field is meaningless. +
      • 1: A Global Color Table will immediately follow, the Background + Color Index field is meaningful. +
      +
    +
  4. Color Resolution: +
      +
    • Number of bits per primary color available to the original image, + minus 1. This value represents the size of the entire palette from + which the colors in the graphic were selected, not the number of colors + actually used in the graphic. For example, if the value in this field + is 3, then the palette of the original image had 4 bits per primary + color available to create the image. This value should be set to + indicate the richness of the original palette, even if not every color + from the whole palette is available on the source machine. +
    +
  5. Sort Flag: +
      +
    • Indicates whether the Global Color Table is sorted. If the flag is + set, the Global Color Table is sorted, in order of decreasing + importance. Typically, the order would be decreasing frequency, with + most frequent color first. This assists a decoder, with fewer available + colors, in choosing the best subset of colors; the decoder may use an + initial segment of the table to render the graphic. +
    • Values: +
        +
      • 0: Not ordered. +
      • 1: Ordered by decreasing importance, most important color + first. +
      +
    +
  6. Size of Global Color Table: +
      +
    • If the Global Color Table Flag is set to 1, the value in this field + is used to calculate the number of bytes contained in the Global Color + Table. To determine that actual size of the color table, raise 2 to + [the value of the field + 1]. Even if there is no Global Color Table + specified, set this field according to the above formula so that + decoders can choose the best graphics mode to display the stream in. + (This field is made up of the 3 least significant bits of the byte.) +
    +
  7. Background Color Index: +
      +
    • Index into the Global Color Table for the Background Color. The + Background Color is the color used for those pixels on the screen that + are not covered by an image. If the Global Color Table Flag is set to + (zero), this field should be zero and should be ignored. +
    +
  8. Pixel Aspect Ratio: +
      +
    • Factor used to compute an approximation of the aspect ratio of the + pixel in the original image. If the value of the field is not 0, this + approximation of the aspect ratio is computed based on the formula:
      + Aspect Ratio = (Pixel Aspect Ratio + 15) ÷ 64 +
    • The Pixel Aspect Ratio is defined to be the quotient of the pixel's + width over its height. The value range in this field allows + specification of the widest pixel of 4:1 to the tallest pixel of 1:4 in + increments of 1/64. +
    • Values: +
        +
      • 0: No aspect ratio information is given. +
      • 1…255: Value used in the computation. +
      +
    +
+ +

d. Extensions and Scope

+ +

The scope of this block is the entire Data Stream. This block cannot be +modified by any extension.

+ +

e. Recommendations

+ +

None.

+ +
+ +

19. Global Color Table (hide/show)

+ +
+ +

a. Description

+ +

This block contains a color table, which is a sequence of bytes representing +red-green-blue color triplets. The Global Color Table is used by +images without a Local Color Table and by Plain Text Extensions. Its presence is marked by +the Global Color Table Flag being set to 1 in the Logical Screen Descriptor; if present, it +immediately follows the Logical Screen Descriptor and contains a number of +bytes equal to:

+ +
+

3 × 2Size of Global Color Table + 1

+
+ +

This block is optional; at most one Global Color Table may be present +per Data Stream.

+ +

b. Required Version

+ +

87a.

+ +

c. Syntax

+ + + + + + + + + + + +
Byte # + Field Name + Type +
0 + Red 0 + Byte +
1 + Green 0 + Byte +
2 + Blue 0 + Byte +
3 + Red 1 + Byte +
4 + Green 1 + Byte +
⋮ + ⋮ + ⋮ +
766 + Green 255 + Byte +
up to 767 + Blue 255 + Byte +
+ +

d. Extensions and Scope

+ +

The scope of this block is the entire Data Stream. This block cannot be +modified by any extension.

+ +

e. Recommendation

+ +

None.

+ +
+ +

20. Image Descriptor (hide/show)

+ +
+ +

a. Description

+ +

Each image in the Data Stream is composed of an Image Descriptor, an +optional Local Color Table, and the image data. Each image must fit within the +boundaries of the Logical Screen, as defined in the Logical Screen Descriptor.

+ +

The Image Descriptor contains the parameters necessary to process a table +based image. The coordinates given in this block refer to coordinates within +the Logical Screen, and are given in pixels. This block is a Graphic-Rendering +Block, optionally preceded by one or more Control blocks such as the Graphic Control Extension, and may be +optionally followed by a Local Color Table; the Image Descriptor is always +followed by the image data.

+ +

This block is required for an image. Exactly one Image Descriptor +must be present per image in the Data Stream. An unlimited number of images may +be present per Data Stream.

+ +

b. Required Version

+ +

87a.

+ +

c. Syntax

+ + + + + + + + + + + + + + +
Byte # + Bits + Field Name + Type +
7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 +
0 + + Image Separator + Byte +
1 + + Image Left Position + Unsigned +
2 +
3 + + Image Top Position + Unsigned +
4 +
5 + + Image Width + Unsigned +
6 +
7 + + Image Height + Unsigned +
8 +
9 + + + + + + <Packed Fields> + See below +
+ +

<Packed Fields> =

+ + + + + + + +
Local Color Table Flag + 1 Bit +
Interlace Flag + 1 Bit +
Sort Flag + 1 Bit +
Reserved + 2 Bits +
Size of Local Color Table + 3 Bits +
+ +
    +
  1. Image Separator: +
      +
    • Identifies the beginning of an Image Descriptor. This field + contains the fixed value 0x2C (44, ','). +
    +
  2. Image Left Position: +
      +
    • Column number, in pixels, of the left edge of the image, with + respect to the left edge of the Logical Screen. Leftmost column of the + Logical Screen is 0. +
    +
  3. Image Top Position: +
      +
    • Row number, in pixels, of the top edge of the image with respect to + the top edge of the Logical Screen. Top row of the Logical Screen is 0. +
    +
  4. Image Width: +
      +
    • Width of the image in pixels. +
    +
  5. Image Height: +
      +
    • Height of the image in pixels. +
    +
  6. Local Color Table Flag: +
      +
    • Indicates the presence of a Local Color Table immediately following + this Image Descriptor. (This field is the most significant bit of the + byte.) +
    • Values: +
        +
      • 0: Local Color Table is not present. Use Global Color Table if available. +
      • 1: Local Color Table present, and to follow immediately after + this Image Descriptor. +
      +
    +
  7. Interlace Flag: +
      +
    • Indicates if the image is interlaced. An image is interlaced in a + four-pass interlace pattern; see Appendix + E for details. +
    • Values: +
        +
      • 0: Image is not interlaced. +
      • 1: Image is interlaced. +
      +
    +
  8. Sort Flag: +
      +
    • Indicates whether the Local Color Table is sorted. If the flag is + set, the Local Color Table is sorted, in order of decreasing + importance. Typically, the order would be decreasing frequency, with + most frequent color first. This assists a decoder, with fewer available + colors, in choosing the best subset of colors; the decoder may use an + initial segment of the table to render the graphic. +
    • Values: +
        +
      • 0: Not ordered. +
      • 1: Ordered by decreasing importance, most important color + first. +
      +
    +
  9. Size of Local Color Table: +
      +
    • If the Local Color Table Flag is set to 1, the value in this field + is used to calculate the number of bytes contained in the Local Color + Table. To determine that actual size of the color table, raise 2 to + [the value of the field + 1]. This value should be 0 if there is no + Local Color Table specified. (This field is made up of the 3 least + significant bits of the byte.) +
    +
+ +

d. Extensions and Scope

+ +

The scope of this block is the Table-based Image Data Block that follows it. +This block may be modified by the Graphic Control Extension.

+ +

e. Recommendation

+ +

None.

+ +
+ +

21. Local Color Table (hide/show)

+ +
+ +

a. Description

+ +

This block contains a color table, which is a sequence of bytes representing +red-green-blue color triplets. The Local Color Table is used by the +image that immediately follows. Its presence is marked by the Local Color Table +Flag being set to 1 in the Image Descriptor; if +present, the Local Color Table immediately follows the Image Descriptor and +contains a number of bytes equal to:

+ +
+

3 × 2Size of Local Color Table + 1

+
+ +

If present, this color table temporarily becomes the active color table and +the following image should be processed using it. This block is +optional; at most one Local Color Table may be present per Image +Descriptor and its scope is the single image associated with the Image +Descriptor that precedes it.

+ +

b. Required Version

+ +

87a.

+ +

c. Syntax

+ + + + + + + + + + + +
Byte # + Field Name + Type +
0 + Red 0 + Byte +
1 + Green 0 + Byte +
2 + Blue 0 + Byte +
3 + Red 1 + Byte +
4 + Green 1 + Byte +
⋮ + ⋮ + ⋮ +
766 + Green 255 + Byte +
up to 767 + Blue 255 + Byte +
+ +

d. Extensions and Scope

+ +

The scope of this block is the Table-based Image Data Block that immediately +follows it. This block cannot be modified by any extension.

+ +

e. Recommendations

+ +

None.

+ +
+ +

22. Table Based Image Data (hide/show)

+ +
+ +

a. Description

+ +

The image data for a table based image consists of a sequence of sub-blocks, of size at most 255 bytes each, containing an +index into the active color table, for each pixel in the image. Pixel indices +are in order of left to right and from top to bottom. Each index must be within +the range of the size of the active color table, starting at 0. The sequence of +indices is encoded using the LZW Algorithm with variable-length code, as +described in Appendix F.

+ +

b. Required Version

+ +

87a.

+ +

c. Syntax

+ +

The image data format is as follows:

+ + + + +
Byte # + Field Name + Type +
0 + LZW Minimum Code Size + Byte +
+ +

+ + + + +
Field Name + Type +
Image Data + Data Sub-blocks +
+ +
    +
  1. LZW Minimum Code Size: +
      +
    • This byte determines the initial number of bits used for LZW codes + in the image data, as described in Appendix F. +
    +
+ +

d. Extensions and Scope

+ +

This block has no scope, it contains raster data. Extensions intended to +modify a Table-based image must appear before the corresponding Image Descriptor.

+ +

e. Recommendations

+ +

None.

+ +
+ +

23. Graphic Control Extension (hide/show)

+ +
+ +

a. Description

+ +

The Graphic Control Extension contains parameters used when processing a +graphic rendering block. The scope of this extension is the first graphic +rendering block to follow. The extension contains only one data sub-block.

+ +

This block is optional; at most one Graphic Control Extension may +precede a graphic rendering block. This is the only limit to the number of +Graphic Control Extensions that may be contained in a Data Stream.

+ +

b. Required Version

+ +

89a.

+ +

c. Syntax

+ + + + + +
Byte # + Field Name + Type +
0 + Extension Introducer + Byte +
1 + Graphic Control Label + Byte +
+ +

+ + + + + + + + + +
Byte # + Bits + Field Name + Type +
7 + 6 + 5 + 4 + 3 + 2 + 1 + 0 +
0 + + Block Size + Byte +
1 + + + + + <Packed Fields> + See below +
2 + + Delay Time + Unsigned +
3 +
4 + + Transparent Color Index + Byte +
+ +

+ + + + +
Byte # + Field Name + Type +
0 + Block Terminator + Byte +
+ +

<Packed Fields> =

+ + + + + + +
Reserved + 3 Bits +
Disposal Method + 3 Bits +
User Input Flag + 1 Bit +
Transparent Color Flag + 1 Bit +
+ +
    +
  1. Extension Introducer: +
      +
    • Identifies the beginning of an extension block. This field contains + the fixed value 0x21 (33, '!'). +
    +
  2. Graphic Control Label: +
      +
    • Identifies the current block as a Graphic Control Extension. This + field contains the fixed value 0xF9 (249). +
    +
  3. Block Size: +
      +
    • Number of bytes in the block, after the Block Size field and up to + but not including the Block Terminator. This field contains the fixed + value 0x04 (4). +
    +
  4. Disposal Method: +
      +
    • Indicates the way in which the graphic is to be treated after being + displayed. +
    • Values: +
        +
      • 0: No disposal specified. The decoder is not required to take + any action. +
      • 1: Do not dispose. The graphic is to be left in place. +
      • 2: Restore to background color. The area used by the graphic + must be restored to the background color. +
      • 3: Restore to previous. The decoder is required to restore the + area overwritten by the graphic with what was there prior to rendering the graphic. +
      • 4–7: To be defined. +
      +
    +
  5. User Input Flag: +
      +
    • Indicates whether or not user input is expected before continuing. + If the flag is set, processing will continue when user input is + entered. The nature of the User input is determined by the application + (Carriage Return, Mouse Button Click, etc.). +
    • Values: +
        +
      • 0: User input is not expected. +
      • 1: User input is expected. +
      +
    • When a Delay Time is used and the User Input Flag is set, + processing will continue when user input is received or when the delay + time expires, whichever occurs first. +
    +
  6. Transparency Flag: +
      +
    • Indicates whether a transparency index is given in the Transparent + Index field. (This field is the least significant bit of the byte.) +
    • Values: +
        +
      • 0: Transparent Index is not given. +
      • 1: Transparent Index is given. +
      +
    +
  7. Delay Time: +
      +
    • If not 0, this field specifies the number of hundredths (1/100) of a second to wait before + continuing with the processing of the Data Stream. The clock starts + ticking immediately after the graphic is rendered. This field may be + used in conjunction with the User Input Flag field. +
    +
  8. Transparency Index: +
      +
    • The Transparency Index is such that when encountered, the + corresponding pixel of the display device is not modified and + processing goes on to the next pixel. The index is present if and only + if the Transparency Flag is set to 1. +
    +
  9. Block Terminator: +
      +
    • This zero-length data block marks the end of the Graphic Control + Extension. +
    +
+ +

d. Extensions and Scope

+ +

The scope of this Extension is the graphic rendering block that follows it; +it is possible for other extensions to be present between this block and its +target. This block can modify the Image Descriptor +Block and the Plain Text Extension.

+ +

e. Recommendations

+ +
    +
  1. Disposal Method: +
      +
    • The mode Restore To Previous is intended to be used in small + sections of the graphic; the use of this mode imposes severe demands on + the decoder to store the section of the graphic that needs to be saved. + For this reason, this mode should be used sparingly. This mode is not + intended to save an entire graphic or large areas of a graphic; when + this is the case, the encoder should make every attempt to make the + sections of the graphic to be restored be separate graphics in the data + stream. In the case where a decoder is not capable of saving an area of + a graphic marked as Restore To Previous, it is recommended that a + decoder restore to the background color. +
    +
  2. User Input Flag: +
      +
    • When the flag is set, indicating that user input is expected, the + decoder may sound the bell (0x07, 7) to alert the user that + input is being expected. In the absence of a specified Delay Time, the + decoder should wait for user input indefinitely. It is recommended that + the encoder not set the User Input Flag without a Delay Time specified. +
    +
+ +
+ +

24. Comment Extension (hide/show)

+ +
+ +

a. Description

+ +

The Comment Extension contains textual information which is not part of the +actual graphics in the GIF Data Stream. It is suitable for including comments +about the graphics, credits, descriptions or any other type of non-control and +non-graphic data. The Comment Extension may be ignored by the decoder, or it +may be saved for later processing; under no circumstances should a Comment +Extension disrupt or interfere with the processing of the Data Stream.

+ +

This block is optional; any number of them may appear in the Data +Stream.

+ +

b. Required Version

+ +

89a.

+ +

c. Syntax

+ + + + + +
Byte # + Field Name + Type +
0 + Extension Introducer + Byte +
1 + Comment Label + Byte +
+ +

+ + + + +
Byte # + Field Name + Type +
N + Comment Data + Data Sub-blocks +
+ +

+ + + + +
Byte # + Field Name + Type +
0 + Block Terminator + Byte +
+ +
    +
  1. Extension Introducer: +
      +
    • Identifies the beginning of an extension block. This field contains + the fixed value 0x21 (33, '!'). +
    +
  2. Comment Label: +
      +
    • Identifies the block as a Comment Extension. This field contains + the fixed value 0xFE (254). +
    +
  3. Comment Data: +
      +
    • Sequence of sub-blocks, each of size at most 255 bytes and at least + 1 byte, with the size in a byte preceding the data. The end of the + sequence is marked by the Block + Terminator. +
    +
  4. Block Terminator: +
      +
    • This zero-length data block marks the end of the Comment Extension. +
    +
+ +

d. Extensions and Scope

+ +

This block does not have scope. This block cannot be modified by any +extension.

+ +

e. Recommendations

+ +
    +
  1. Data: +
      +
    • This block is intended for humans. It should contain text using the + 7-bit ASCII character set. This block should not be used to store + control information for custom processing. +
    +
  2. Position: +
      +
    • This block may appear at any point in the Data Stream at which a + block can begin; however, it is recommended that Comment Extensions do + not interfere with Control or Data blocks; they should be located at + the beginning or at the end of the Data Stream to the extent possible. +
    +
+ +
+ +

25. Plain Text Extension (hide/show)

+ +
+ +

a. Description

+ +

The Plain Text Extension contains textual data and the parameters necessary +to render that data as a graphic, in a simple form. The textual data will be +encoded with the 7-bit printable ASCII characters. Text data are rendered using +a grid of character cells defined by the parameters in the block fields. Each +character is rendered in an individual cell. The textual data in this block is +to be rendered as mono-spaced characters, one character per cell, with a best +fitting font and size. For further information, see the section on +Recommendations below.

+ +

The data characters are taken sequentially from the data portion of the +block and rendered within a cell, starting with the upper left cell in the grid +and proceeding from left to right and from top to bottom. Text data is rendered +until the end of data is reached or the character grid is filled. The Character +Grid contains an integral number of cells; in the case that the cell dimensions +do not allow for an integral number, fractional cells must be discarded; an +encoder must be careful to specify the grid dimensions accurately so that this +does not happen.

+ +

This block requires a Global Color Table to +be available; the colors used by this block reference the Global Color Table in +the Stream if there is one, or the Global Color Table from a previous Stream, +if one was saved.

+ +

This block is a graphic rendering block, therefore it may be modified by a +Graphic Control Extension.

+ +

This block is optional; any number of them may appear in the Data +Stream.

+ +

b. Required Version

+ +

89a.

+ +

c. Syntax

+ + + + + +
Byte # + Field Name + Type +
0 + Extension Introducer + Byte +
1 + Plain Text Label + Byte +
+ +

+ + + + + + + + + + + + + + + + +
Byte # + Field Name + Type +
0 + Block Size + Byte +
1 + Text Grid Left Position + Unsigned +
2 +
3 + Text Grid Top Position + Unsigned +
4 +
5 + Text Grid Width + Unsigned +
6 +
7 + Text Grid Height + Unsigned +
8 +
9 + Character Cell Width + Byte +
10 + Character Cell Height + Byte +
11 + Text Foreground Color Index + Byte +
12 + Text Background Color Index + Byte +
+ +

+ + + + +
Byte # + Field Name + Type +
N + Plain Text Data + Data Sub-blocks +
+ +

+ + + + +
Byte # + Field Name + Type +
0 + Block Terminator + Byte +
+ +
    +
  1. Extension Introducer: +
      +
    • Identifies the beginning of an extension block. This field contains + the fixed value 0x21 (33, '!'). +
    +
  2. Plain Text Label: +
      +
    • Identifies the current block as a Plain Text Extension. This field + contains the fixed value 0x01 (1). +
    +
  3. Block Size: +
      +
    • Number of bytes in the extension, after the Block Size field and up + to but not including the beginning of the data portion. This field + contains the fixed value 0x0C (12). +
    +
  4. Text Grid Left Position: +
      +
    • Column number, in pixels, of the left edge of the text grid, with + respect to the left edge of the Logical Screen. +
    +
  5. Text Grid Top Position: +
      +
    • Row number, in pixels, of the top edge of the text grid, with + respect to the top edge of the Logical Screen. +
    +
  6. Image Grid Width: +
      +
    • Width of the text grid in pixels. +
    +
  7. Image Grid Height: +
      +
    • Height of the text grid in pixels. +
    +
  8. Character Cell Width: +
      +
    • Width, in pixels, of each cell in the grid. +
    +
  9. Character Cell Height: +
      +
    • Height, in pixels, of each cell in the grid. +
    +
  10. Text Foreground Color Index: +
      +
    • Index into the Global Color Table to be used to render the text + foreground. +
    +
  11. Text Background Color Index: +
      +
    • Index into the Global Color Table to be used to render the text + background. +
    +
  12. Plain Text Data: +
      +
    • Sequence of sub-blocks, each of size at most 255 bytes and at least + 1 byte, with the size in a byte preceding the data. The end of the + sequence is marked by the Block Terminator. +
    +
  13. Block Terminator: +
      +
    • This zero-length data block marks the end of the Plain Text Data + Blocks. +
    +
+ +

d. Extensions and Scope

+ +

The scope of this block is the Plain Text Data Block contained in it. This +block may be modified by the Graphic Control Extension.

+ +

e. Recommendations

+ +

The data in the Plain Text Extension is assumed to be preformatted. The +selection of font and size is left to the discretion of the decoder. If +characters less than 0x20 (32) or greater than 0xF7 (247) are +encountered, it is recommended that the decoder display a Space character +(0x20, 32). The encoder should use grid and cell dimensions such that +an integral number of cells fit in the grid both horizontally as well as +vertically. For broadest compatibility, character cell dimensions should be +around 8×8 or 8×16 (width × height); consider an image for +unusual sized text.

+ +

Editor's (Kalle's) note: the upper limit for a printable character, +0xF7, seems odd. Maybe they meant 0x7E (126, '~', +the last printable 7-bit ASCII character)?

+ +
+ +

26. Application Extension (hide/show)

+ +
+ +

a. Description

+ +

The Application Extension contains application-specific information; it +conforms with the extension block syntax, as described below, and its block +label is 0xFF (255).

+ +

b. Required Version

+ +

89a.

+ +

c. Syntax

+ + + + + +
Byte # + Field Name + Type +
0 + Extension Introducer + Byte +
1 + Extension Label + Byte +
+ +

+ + + + + + +
Byte # + Field Name + Type +
0 + Block Size + Byte +
1…8 + Application Identifier + 8 Bytes +
9…11 + Application Authentication Code + 3 Bytes +
+ +

+ + + + +
Field Name + Type +
Application Data + Data Sub-blocks +
+ +

+ + + + +
Byte # + Field Name + Type +
0 + Block Terminator + Byte +
+ +
    +
  1. Extension Introducer: +
      +
    • Defines this block as an extension. This field contains the fixed + value 0x21 (33, '!'). +
    +
  2. Application Extension Label: +
      +
    • Identifies the block as an Application Extension. This field + contains the fixed value 0xFF (255). +
    +
  3. Block Size: +
      +
    • Number of bytes in this extension block, following the Block Size + field, up to but not including the beginning of the Application Data. + This field contains the fixed value 0x0B (11). +
    +
  4. Application Identifier: +
      +
    • Sequence of eight printable ASCII characters used to identify the + application owning the Application Extension. +
    +
  5. Application Authentication Code: +
      +
    • Sequence of three bytes used to authenticate the Application + Identifier. An Application program may use an algorithm to compute a + binary code that uniquely identifies it as the application owning the + Application Extension. +
    +
+ +

d. Extensions and Scope

+ +

This block does not have scope. This block cannot be modified by any extension.

+ +

e. Recommendation

+ +

None.

+ +
+ +

27. Trailer (hide/show)

+ +
+ +

a. Description

+ +

This block is a single-field block indicating the end of the GIF Data +Stream. It contains the fixed value 0x3B (59, ';').

+ +

b. Required Version

+ +

87a.

+ +

c. Syntax

+ + + + +
Byte # + Field Name + Type +
0 + GIF Trailer + Byte +
+ +

d. Extensions and Scope

+ +

This block does not have scope, it terminates the GIF Data Stream. This +block may not be modified by any extension.

+ +

e. Recommendations

+ +

None.

+ +
+ +

Appendix A. Quick Reference Table (hide/show)

+ +
+ + + + + + + + + + + + + + + + + +
Block Name + Required? + Label + Extension + Version +
Unlabeled Blocks +
Header + required (one occurrence) + none + no + N/A +
Logical Screen Descriptor + required (one occurrence) + none + no + 87a (89a) +
Global Color Table + optional (at most one occurrence) + none + no + 87a +
Local Color Table + optional (zero or more occurrences) + none + no + 87a +
Graphic-Rendering Blocks +
Plain Text Extension + optional (zero or more occurrences) + 0x01 (1) + yes + 89a +
Image Descriptor + optional (zero or more occurrences) + 0x2C (44, ',') + no + 87a (89a) +
Control Blocks +
Graphic Control Extension + optional (zero or more occurrences) + 0xF9 (249) + yes + 89a +
Special Purpose Blocks +
Trailer + required (one occurrence) + 0x3B (59, ';') + no + 87a +
Comment Extension + optional (zero or more occurrences) + 0xFE (254) + yes + 89a +
Application Extension + optional (zero or more occurrences) + 0xFF (255) + yes + 89a +
+ +

Notes:

+ +
    +
  • The Header is not subject to Version Numbers. +
  • (89a) The Logical Screen Descriptor and the Image Descriptor retained + their syntax from version 87a to version 89a, but some fields reserved + under version 87a are used under version 89a. +
+ +
+ +

Appendix B. GIF Grammar (hide/show)

+ +
+ +

A Grammar is a form of notation to represent the sequence in which certain +objects form larger objects. A grammar is also used to represent the number of +objects that can occur at a given position. The grammar given here represents +the sequence of blocks that form the GIF Data Stream. A grammar is given by +listing its rules. Each rule consists of the left-hand side, followed by some +form of equals sign, followed by the right-hand side. In a rule, the right-hand +side describes how the left-hand side is defined. The right-hand side consists +of a sequence of entities, with the possible presence of special symbols. The +following legend defines the symbols used in this grammar for GIF.

+ +

Legend:

+ + + + + + + + +
< > + grammar word +
::= + defines symbol +
* + zero or more occurrences +
+ + one or more occurrences +
| + alternate element +
[ ] + optional element +
+ +

Example:

+ +
+

<GIF Data Stream> ::= Header <Logical + Screen> <Data>* Trailer

+
+ +

This rule defines the entity <GIF Data Stream> as follows. It must +begin with a Header. The Header is followed by an entity called Logical Screen, +which is defined below by another rule. The Logical Screen is followed by the +entity Data, which is also defined below by another rule. Finally, the entity +Data is followed by the Trailer. Since there is no rule defining the Header or +the Trailer, this means that these blocks are defined in the document. The +entity Data has a special symbol (*) following it which means that, at this +position, the entity Data may be repeated any number of times, including 0 +times. For further reading on this subject, refer to a standard text on +Programming Languages.

+ +

The Grammar

+ + + + + + + + + + + +

Note: The grammar indicates that it is possible for a GIF Data Stream +to contain the Header, the Logical Screen Descriptor, a Global Color Table and +the GIF Trailer. This special case is used to load a GIF decoder with a Global +Color Table, in preparation for subsequent Data Streams without color tables at +all.

+ +
+ +

Appendix C. Glossary (hide/show)

+ +
+ + + + + + + + + + + + +
Active Color Table + Color table used to render the next graphic. If the next graphic is + an image which has a Local Color Table + associated with it, the active color table becomes the Local Color + Table associated with that image. If the next graphic is an image + without a Local Color Table, or a Plain + Text Extension, the active color table is the Global Color Table associated with the + Data Stream, if there is one; if there is no Global Color Table in the + Data Stream, the active color table is a color table saved from a + previous Data Stream, or one supplied by the decoder. +
Block + Collection of bytes forming a protocol unit. In general, the term + includes labeled and unlabeled blocks, as well as Extensions. +
Data Stream + The GIF Data Stream is composed of blocks and sub-blocks representing images and graphics, + together with control information to render them on a display device. + All control and data blocks in the Data Stream must follow the Header and must precede the Trailer. +
Decoder + A program capable of processing a GIF Data Stream to render the + images and graphics contained in it. +
Encoder + A program capable of capturing and formatting image and graphic + raster data, following the definitions of the Graphics Interchange + Format. +
Extension + A protocol block labeled by the Extension Introducer 0x21 + (33, '!'). +
Extension Introducer + Label (0x21, 33, '!') defining an Extension. +
Graphic + Data which can be rendered on the screen by virtue of some + algorithm. The term graphic is more general than the term image; in + addition to images, the term graphic also includes data such as text, + which is rendered using character bit-maps. +
Image + Data representing a picture or a drawing; an image is represented + by an array of pixels called the raster of the image. +
Raster + Array of pixel values representing an image. +
+ +
+ +

Appendix D. Conventions (hide/show)

+ +
+ + + + + + + + + +
Animation + The Graphics Interchange Format is not intended as a platform for + animation, even though it can be done in a limited way. +
Byte Ordering + Unless otherwise stated, multi-byte numeric fields are ordered with + the Least Significant Byte first. +
Color Indices + Color indices always refer to the active color table, either the Global Color Table or the Local Color Table. +
Color Order + Unless otherwise stated, all triple-component RGB color values are + specified in Red-Green-Blue order. +
Color Tables + Both color tables, the Global and the Local, are optional; if + present, the Global Color Table is to be used with every image in the + Data Stream for which a Local Color Table is not given; if present, a + Local Color Table overrides the Global Color Table. However, if neither + color table is present, the application program is free to use an + arbitrary color table. If the graphics in several Data Streams are + related and all use the same color table, an encoder could place the + color table as the Global Color Table in the first Data Stream and + leave subsequent Data Streams without a Global Color Table or any Local + Color Tables; in this way, the overhead for the table is eliminated. It + is recommended that the decoder save the previous Global Color Table to + be used with the Data Stream that follows, in case it does not contain + either a Global Color Table or any Local Color Tables. In general, this + allows the application program to use past color tables, significantly + reducing transmission overhead. +
Extension Blocks + Extensions are defined using the Extension Introducer code + (0x21, 33, '!') to mark the beginning of the block, + followed by a block label, identifying the type of extension. Extension + Codes are numbers in the range from 0x00 (0) to 0xFF + (255), inclusive. Special purpose extensions are transparent to the + decoder and may be omitted when transmitting the Data Stream on-line. + The GIF capabilities dialogue makes the provision for the receiver to + request the transmission of all blocks; the default state in this + regard is no transmission of Special purpose blocks. +
Reserved Fields + All Reserved Fields are expected to have each bit set to zero + (off). +
+ +
+ +

Appendix E. Interlaced Images (hide/show)

+ +
+ +

The rows of an Interlaced images are arranged in the following order:

+ + + + + + +
Group 1 + Pass 1 + Every 8th row, starting with row 0 +
Group 2 + Pass 2 + Every 8th row, starting with row 4 +
Group 3 + Pass 3 + Every 4th row, starting with row 2 +
Group 4 + Pass 4 + Every 2nd row, starting with row 1 +
+ +

The following example illustrates how the rows of an interlaced image are +ordered.

+ + + + + + + + + + + + + + + + + + + + + + + +
Row Number + Interlace Pass +
0 + 1 + + + +
1 + + + + 4 +
2 + + + 3 + +
3 + + + + 4 +
4 + + 2 + + +
5 + + + + 4 +
6 + + + 3 + +
7 + + + + 4 +
8 + 1 + + + +
9 + + + + 4 +
10 + + + 3 + +
11 + + + + 4 +
12 + + 2 + + +
13 + + + + 4 +
14 + + + 3 + +
15 + + + + 4 +
16 + 1 + + + +
17 + + + + 4 +
18 + + + 3 + +
19 + + + + 4 +
+ +
+ +

Appendix F. Variable-Length-Code LZW Compression (hide/show)

+ +
+ +

The Variable-Length-Code LZW Compression is a variation of the Lempel-Ziv +Compression algorithm in which variable-length codes are used to replace +patterns detected in the original data. The algorithm uses a code or +translation table constructed from the patterns encountered in the original +data; each new pattern is entered into the table and its index is used to +replace it in the compressed stream.

+ +

The compressor takes the data from the input stream and builds a code or +translation table with the patterns as it encounters them; each new pattern is +entered into the code table and its index is added to the output stream; when a +pattern is encountered which had been detected since the last code table +refresh, its index from the code table is put on the output stream, thus +achieving the data compression. The expander takes input from the compressed +data stream and builds the code or translation table from it; as the compressed +data stream is processed, codes are used to index into the code table and the +corresponding data is put on the decompressed output stream, thus achieving +data decompression. The details of the algorithm are explained below. The +Variable-Length-Code aspect of the algorithm is based on an initial code size +(LZW-initial code size), which specifies the initial number of bits used for +the compression codes. When the number of patterns detected by the compressor +in the input stream exceeds the number of patterns encodable with the current +number of bits, the number of bits per LZW code is increased by one.

+ +

The Raster Data stream that represents the actual output image can be +represented as:

+ + + + +
Field name +
LZW code size +
+ +

+ + + + + +
Field name + +
block size + Repeated as many times as necessary. +
data bytes +
+ +

(The code that terminates the LZW compressed data must appear before Block Terminator.)

+ + + + +
Field name +
Block Terminator (0x00, 0) +
+ +

The conversion of the image from a series of pixel values to a transmitted +or stored character stream involves several steps. In brief these steps +are:

+ +
    +
  1. Establish the Code Size – Define the number of bits needed to + represent the actual data. +
  2. Compress the Data – Compress the series of image pixels to a + series of compression codes. +
  3. Build a Series of Bytes – Take the set of compression codes and + convert to a string of 8-bit bytes. +
  4. Package the Bytes – Package sets of bytes into blocks preceded by + character counts and output. +
+ +

1. Establish Code Size

+ +

The first byte of the Compressed Data stream is a value indicating the +minimum number of bits required to represent the set of actual pixel values. +Normally this will be the same as the number of color bits. Because of some +algorithmic constraints however, black & white images which have one color +bit must be indicated as having a code size of 2. This code size value also +implies that the compression codes must start out one bit longer.

+ +

2. Compression

+ +

The LZW algorithm converts a series of data values into a series of codes +which may be raw values or a code designating a series of values. Using text +characters as an analogy, the output code consists of a character or a code +representing a string of characters.

+ +

The LZW algorithm used in GIF matches algorithmically with the standard LZW +algorithm with the following differences:

+ +
    +
  1. A special Clear code is defined which resets all + compression/decompression parameters and tables to a start-up state. The + value of this code is 2<code size>. For example if the + code size indicated was 4 (image was 4 bits/pixel) the Clear code value + would be 16 (10000 binary). The Clear code can appear at any point + in the image data stream and therefore requires the LZW algorithm to + process succeeding codes as if a new data stream was starting. Encoders + should output a Clear code as the first code of each image data stream. +
  2. An End of Information code is defined that explicitly indicates the end + of the image data stream. LZW processing terminates when this code is + encountered. It must be the last code output by the encoder for an image. + The value of this code is <Clear code>+1. +
  3. The first available compression code value is <Clear code> + 2. +
  4. The output codes are of variable length, starting at <code size> + + 1 bits per code, up to 12 bits per code. This defines a maximum code + value of 4095 (0xFFF). Whenever the LZW code value would exceed + the current code length, the code length is increased by one. The + packing/unpacking of these codes must then be altered to reflect the new + code length. +
+ +

Editor's (Kalle's) note: see also the Cover +Sheet.

+ +

3. Build 8-bit Bytes

+ +

Because the LZW compression used for GIF creates a series of variable length +codes, of between 3 and 12 bits each, these codes must be reformed into a +series of 8-bit bytes that will be the characters actually stored or +transmitted. This provides additional compression of the image. The codes are +formed into a stream of bits as if they were packed right to left and then +picked off 8 bits at a time to be output.

+ +

Assuming a character array of 8 bits per character and using 5 bit codes to +be packed, an example layout would be similar to:

+ + + + + + + + + + +
Byte # + Bits +
0 + bbbaaaaa +
1 + dcccccbb +
2 + eeeedddd +
3 + ggfffffe +
4 + hhhhhggg +
⋮ + +
N + +
+ +

Note that the physical packing arrangement will change as the number of bits +per compression code change but the concept remains the same.

+ +

4. Package the Bytes

+ +

Once the bytes have been created, they are grouped into blocks for output by +preceding each block of 0 to 255 bytes with a character count byte. A block +with a zero byte count terminates the Raster Data stream for a given image. +These blocks are what are actually output for the GIF image. This block format +has the side effect of allowing a decoding program the ability to read past the +actual image data if necessary by reading block counts and then skipping over +the data.

+ +

Further Reading

+ +
    +
  1. Ziv, J. and Lempel, A.: A Universal Algorithm for Sequential Data + Compression, IEEE Transactions on Information Theory, May 1977 +
  2. Welch, T.: A Technique for High-Performance Data Compression, + Computer, June 1984 +
  3. Nelson, M.R.: LZW Data Compression, Dr. Dobb's Journal, October + 1989 +
+ +

Editor's (Kalle's) note: see also Wikipedia – +Lempel–Ziv–Welch.

+ +
+ +

Appendix G. On-line Capabilities Dialogue (hide/show)

+ +
+ +

Note: This section is currently (10 July 1990) under revision; the +information provided here should be used as general guidelines. Code written +based on this information should be designed in a flexible way to accommodate +any changes resulting from the revisions.

+ +

The following sequences are defined for use in mediating control between a +GIF sender and GIF receiver over an interactive communications line. These +sequences do not apply to applications that involve downloading of static GIF +files and are not considered part of a GIF file.

+ +

GIF Capabilities Enquiry

+ +

The GIF Capabilities Enquiry sequence is issued from a host and requests an +interactive GIF decoder to return a response message that defines the graphics +parameters for the decoder. This involves returning information about available +screen sizes, number of bits/color supported and the amount of color detail +supported. The escape sequence for the GIF Capabilities Enquiry is defined +as:

+ + + + + + + +
ESC + 0x1B (27) +
[ + 0x5B (91) +
> + 0x3E (62) +
0 + 0x30 (48) +
g + 0x67 (103) +
+ +

GIF Capabilities Response

+ +

The GIF Capabilities Response message is returned by an interactive GIF +decoder and defines the decoder's display capabilities for all graphics modes +that are supported by the software. Note that this can also include graphics +printers as well as a monitor screen. The general format of this message +is:

+ +

#version;protocol{;dev, width, height, color-bits, +color-res}…<CR>

+ + + + + + + + + + + + +
'#' + GIF Capabilities Response identifier character. +
version + GIF format version number; initially '87a'. +
protocol='0' + No end-to-end protocol supported by decoder Transfer as direct + 8-bit data stream. +
protocol='1' + Can use CIS B+ error correction protocol to transfer GIF data + interactively from the host directly to the display. +
dev='0' + Screen parameter set follows. +
dev='1' + Printer parameter set follows. +
width + Maximum supported display width in pixels. +
height + Maximum supported display height in pixels. +
color-bits + Number of bits per pixel supported. The number of supported colors + is therefore 2color-bits. +
color-res + Number of bits per color component supported in the hardware color + palette. If color-res is '0' then no hardware palette table is + available. +
+ +

Note that all values in the GIF Capabilities Response are returned as ASCII +decimal numbers and the message is terminated by a Carriage Return character +(0x0D, 13).

+ +

The following GIF Capabilities Response message describes three standard IBM +PC Enhanced +Graphics Adapter configurations with no printer; the GIF data stream can be +processed within an error correcting protocol:

+ +
+

#87a;1;0,320,200,4,0;0,640,200,2,2;0,640,350,4,2<CR>

+
+ +

Enter GIF Graphics Mode

+ +

Two sequences are currently defined to invoke an interactive GIF decoder +into action. The only difference between them is that different output media +are selected. These sequences are:

+ + + + + + + +
ESC + 0x1B (27) + Display GIF image on screen +
[ + 0x5B (91) +
> + 0x3E (62) +
1 + 0x31 (49) +
g + 0x67 (103) +
+ +

+ + + + + + + +
ESC + 0x1B (27) + Display image directly to an attached graphics printer. + The image may optionally be displayed on the screen as well. +
[ + 0x5B (91) +
> + 0x3E (62) +
2 + 0x32 (50) +
g + 0x67 (103) +
+ +

Note that the 'g' character terminating each sequence is in +lowercase.

+ +

Interactive Environment

+ +

The assumed environment for the transmission of GIF image data from an +interactive application is a full 8-bit data stream from host to micro. All 256 +character codes must be transferrable. The establishing of an 8-bit data path +for communications will normally be taken care of by the host application +programs. It is however up to the receiving communications programs supporting +GIF to be able to receive and pass on all 256 8-bit codes to the GIF decoder +software.

+ +
+ +

Cover Sheet for the GIF89a Specification (hide/show)

+ +
+ +

Deferred clear code in LZW compression

+ +

There has been confusion about where clear codes can be found in the data +stream. As the specification says, they may appear at anytime. There is not a +requirement to send a clear code when the string table is full.

+ +

It is the encoder's decision as to when the table should be cleared. When +the table is full, the encoder can chose to use the table as is, making no +changes to it until the encoder chooses to clear it. The encoder during this +time sends out codes that are of the maximum Code Size.

+ +

As we can see from the above, when the decoder's table is full, it must not +change the table until a clear code is received. The Code Size is that of the +maximum Code Size. Processing other than this is done normally.

+ +

Because of a large base of decoders that do not handle the decompression in +this manner, we ask developers of GIF encoding software to not implement +this feature until at least January 1991 and later if they see that their +particular market is not ready for it. This will give developers of GIF +decoding software time to implement this feature and to get it into the hands +of their clients before the decoders start "breaking" on the new GIF's. It is +not required that encoders change their software to take advantage of the +deferred clear code, but it is for decoders.

+ +

Application Extension Block – Application Identifier

+ +

There will be a Courtesy Directory file located on CompuServe in the PICS +forum. This directory will contain Application Identifiers for Application Extension Blocks that have been +used by developers of GIF applications. This file is intended to help keep +developers that wish to create Application Extension Blocks from using the same +Application Identifiers. This is not an official directory; it is for voluntary +participation only and does not guarantee that someone will not use the same +identifier.

+ +

E-Mail can be sent to Larry Wood (forum manager of PICS) indicating the +request for inclusion in this file with an identifier.

+ +
+
+ + diff --git a/doc/gifstandard/LZW-and-GIF-explained.html b/doc/gifstandard/LZW-and-GIF-explained.html new file mode 100644 index 0000000..a5ec5fc --- /dev/null +++ b/doc/gifstandard/LZW-and-GIF-explained.html @@ -0,0 +1,352 @@ + + +An Introduction to Data Compression + + + +

LZW and GIF explained
+by Steve Blackstock +

+ +

I hope this little document will help enlighten those of you out there +who want to know more about the Lempel-Ziv Welch (LZW) compression algorithm, and, +specifically, the implementation that GIF uses.

+ +

Before we start, here's a little terminology, for the purposes of this +document:

+ +
    +
  • + character: a fundamental data element. In normal text files, this is +just a single byte. In raster images, which is what we're interested in, it's +an index that specifies the color of a given pixel. I'll refer to an arbitray +character as "K". +
  • + charstream: a stream of characters, as in a data file. +
  • + string: a number of continuous characters, anywhere from one to very +many characters in length. I can specify an arbitrary string as "[...]K". +
  • + prefix: almost the same as a string, but with the implication that a +prefix immediately precedes a character, and a prefix can have a length of +zero. So, a prefix and a character make up a string. I will refer to an +arbitrary prefix as "[...]". +
  • + root: a single-character string. For most purposes, this is a +character, but we may occasionally make a distinction. It is [...]K, where +[...] is empty. +
  • + code: a number, specified by a known number of bits, which maps to a +string. +
  • + codestream: the output stream of codes, as in the "raster data" +
  • + entry: a code and its string. +
  • + string table: a list of entries; usually, but not necessarily, unique. +
  • +
+ +

+ LZW is a way of compressing data that takes advantage of repetition of +strings in the data. Since raster data usually contains a lot of this +repetition, LZW is a good way of compressing and decompressing it. + For the moment, lets consider normal LZW encoding and decoding. GIF's +variation on the concept is just an extension from there. + +

+ LZW manipulates three objects in both compression and decompression: the +charstream, the codestream, and the string table. In compression, the +charstream is the input and the codestream is the output. In decompression, +the codestream is the input and the charstream is the output. The string table +is a product of both compression and decompression, but is never passed from +one to the other. + +

Compression

+ +

+ The first thing we do in LZW compression is initialize our string table. +To do this, we need to choose a code size (how many bits) and know how many +values our characters can possibly take. Let's say our code size is 12 bits, +meaning we can store 0->FFF, or 4096 entries in our string table. Lets also +say that we have 32 possible different characters. (This corresponds to, say, +a picture in which there are 32 different colors possible for each pixel.) To +initialize the table, we set code#0 to character#0, code #1 to character#1, +and so on, until code#31 to character#31. Actually, we are specifying that +each code from 0 to 31 maps to a root. There will be no more entries in the +table that have this property. + +

+ +Now we start compressing data. Let's first define something +called the "current prefix". It's just a prefix that we'll store +things in and compare things to now and then. I will refer to it as +"[.c.]". Initially, the current prefix has nothing in it. Let's also +define a "current string", which will be the current prefix plus the +next character in the charstream. I will refer to the current string +as "[.c.]K", where K is some character. OK, look at the first +character in the charstream. Call it P. Make [.c.]P the current +string. (At this point, of course, it's just the root P.) Now search +through the string table to see if [.c.]P appears in it. Of course, it +does now, because our string table is initialized to have all roots. +So we don't do anything. Now make [.c.]P the current prefix. Look at +the next character in the charstream. Call it Q. Add it to the +current prefix to form [.c.]Q, the current string. Now search through +the string table to see if [.c.]Q appears in it. In this case, of +course, it doesn't. Aha! Now we get to do something. Add [.c.]Q +(which is PQ in this case) to the string table for code#32, and output +the code for [.c.] to the codestream. Now start over again with the +current prefix being just the root Q. Keep adding characters to [.c.] +to form [.c.]K, until you can't find [.c.]K in the string table. Then +output the code for [.c.] and add [.c.]K to the string table. In +pseudo-code, the algorithm goes something like this: + +

     [1] Initialize string table;
+     [2] [.c.] <- empty;
+     [3] K <- next character in charstream;
+     [4] Is [.c.]K in string table?
+         (yes: [.c.] <- [.c.]K;
+               go to [3];
+         )
+         (no: add [.c.]K to the string table;
+              output the code for [.c.] to the codestream;
+              [.c.] <- K;
+              go to [3];
+         )
+
+ +

+ It's as simple as that! Of course, when you get to step [3] and there +aren't any more characters left, you just output the code for [.c.] and throw +the table away. You're done. + +

+ Wanna do an example? Let's pretend we have a four-character alphabet: +A,B,C,D. The charstream looks like ABACABA. Let's compress it. First, we +initialize our string table to: #0=A, #1=B, #2=C, #3=D. The first character is +A, which is in the string table, so [.c.] becomes A. Next we get AB, which is +not in the table, so we output code #0 (for [.c.]), + and add AB to the string table as code #4. [.c.] becomes B. Next we get +[.c.]A = BA, which is not in the string table, so output code #1, and add BA +to the string table as code #5. [.c.] becomes A. Next we get AC, which is not +in the string table. Output code #0, and add AC to the string table as code +#6. Now [.c.] becomes C. Next we get [.c.]A = CA, which is not in the table. +Output #2 for C, and add CA to table as code#7. Now [.c.] becomes A. Next we +get AB, which IS in the string table, so [.c.] gets AB, and we look at ABA, +which is not in the string table, so output the code for AB, which is #4, and +add ABA to the string table as code #8. [.c.] becomes A. We can't get any more +characters, so we just output #0 for the code for A, and we're done. So, the +codestream is #0#1#0#2#4#0. + +

+ A few words (four) should be said here about efficiency: use a hashing +strategy. The search through the string table can be computationally +intensive, and some hashing is well worth the effort. Also, note that +"straight LZW" compression runs the risk of overflowing the string table - +getting to a code which can't be represented in the number of bits you've set +aside for codes. There are several ways of dealing with this problem, and GIF +implements a very clever one, but we'll get to that. + +

+ An important thing to notice is that, at any point during the +compression, if [...]K is in the string table, [...] is there also. This fact +suggests an efficient method for storing strings in the table. Rather than +store the entire string of K's in the table, realize that any string can be +expressed as a prefix plus a character: [...]K. If we're about to store [...]K +in the table, we know that [...] is already there, so we can just store the +code for [...] plus the final character K. + + +

Decompression

+ +Decompression is perhaps more +difficult conceptually, but it is really easier to program. +We again have to start with an initialized string +table. This table comes from what knowledge we have about the charstream that +we will eventually get, like what possible values the characters can take. In +GIF files, this information is in the header as the number of possible pixel +values. The beauty of LZW, though, is that this is all we need to know. We +will build the rest of the string table as we decompress the codestream. The +compression is done in such a way that we will never encounter a code in the +codestream that we can't translate into a string. + +

+We need to define something called a "current code", which I +will refer to as "<code>", and an "old-code", which I will refer +to as "<old>". To start things off, look at the first code. This +is now <code>. This code will be in the intialized string table as +the code for a root. Output the root to the charstream. Make this code +the old-code <old>. *Now look at the next code, and make it +<code>. It is possible that this code will not be in the string +table, but let's assume for now that it is. Output the string +corresponding to <code> to the codestream. Now find the first +character in the string you just translated. Call this K. Add this to +the prefix [...] generated by <old> to form a new string +[...]K. Add this string [...]K to the string table, and set the +old-code <old> to the current code <code>. Repeat from where I +typed the asterisk, and you're all set. +This is the most common case so you should understand this before going +on. + +

+ +Now let's consider the possibility that <code> is not in the +string table, which as we will see can only occur for strings of the +form P[...]P (for any character P). Think back to compression, and +try to understand what happens when you have a string like +P[...]P[...]PQ appear in the charstream. Suppose P[...] is already in +the string table, but P[...]P is not. The compressor will parse out +P[...], and find that P[...]P is not in the string table. It will +output the code for P[...], and add P[...]P to the string table. Then +it will get up to P[...]P for the next string, and find that P[...]P +is in the table, as the code just added. So it will output the code +for P[...]P if it finds that P[...]PQ is not in the table. The +decompressor is always "one step behind" the compressor. When the +decompressor sees the code for P[...]P, it will not have added that +code to it's string table yet because it needed the beginning +character of P[...]P to add to the string for the last code, P[...], +to form the code for P[...]P. However, when a decompressor finds a +code that it doesn't know yet, it will always be the very next one to +be added to the string table. So it can guess at what the string for +the code should be, and, in fact, it will always be correct. If I am a +decompressor, and I see code#124, and yet my string table has entries +only up to code#123, I can figure out what code#124 must be, add it to +my string table, and output the string. If code#123 generated the +string [...], which I will refer to here as a prefix, then code#124, +in this special case, will be [...] plus the first character of [...]. +So just add the first character of [...] to the end of itself. Not +too bad. + + +

+ +As an example (and a very common one) of this special case, let's +assume we have a raster image in which the first three pixels have the +same color value. That is, my charstream looks like: QQQ.... For the +sake of argument, let's say we have 32 colors, and Q is the +color#12. The compressor will generate the code sequence +12,32,.... (if you don't know why, take a minute to understand it.) +Remember that #32 is not in the initial table, which goes from #0 to +#31. The decompressor will see #12 and translate it just fine as color +Q. Then it will see #32 and not yet know what that means. But if it +thinks about it long enough, it can figure out that QQ should be +entry#32 in the table and QQ should be the next string output. So the +decompression pseudo-code goes something like: + +

     [1] Initialize string table;
+     [2] get first code: <code>
+     [3] output the string for <code> to the charstream;
+     [4] <old> = <code>
+     [5] <code> <- next code in codestream;
+     [6] does <code> exist in the string table?
+         (yes: output the string for <code> to the charstream;
+            [...] <- translation for <old>
+            K <- first character of translation for <code>
+            add [...]K to the string table;        
+            <old> <- <code>
+         )
+         (no: [...] <- translation for <old>
+            K <- first character of [...];
+            output [...]K to charstream and add it to string table;
+            <old> <- <code>
+         )
+     [7] go to [5];
+
+ +

+ Again, when you get to step [5] and there are no more codes, you're +finished. Outputting of strings, and finding of initial characters in strings +are efficiency problems all to themselves, but I'm not going to suggest ways +to do them here. Half the fun of programming is figuring these things out! + +

GIF variation

+ +

+ +Now for the GIF variations on the theme. In part of the header of a +GIF file, there is a field, in the Raster Data stream, called "code +size". This is a very misleading name for the field, but we have to +live with it. What it is really is the "root size". The actual size, +in bits, of the compression codes actually changes during +compression/decompression, and I will refer to that size here as the +"compression size". The initial table is just the codes for all the +roots, as usual, but two special codes are added on top of those. The +"code size" N is set to max(2,bits-per-pixel). In the table the roots +take up slots #0 through #(2**N-1), and the special codes are (2**N) +and (2**N + 1). The initial compression size will be N+1 bits per +code. If you're encoding, you output the codes (N+1) bits at a time to +start with, and if you're decoding, you grab (N+1) bits from the +codestream at a time. As for the special codes: <CC> or the clear +code, is (2**N), and <EOI>, or end-of-information, is (2**N + +1). <CC> tells the compressor to re- initialize the string table, +and to reset the compression size to (N+1). <EOI> means there's no +more in the codestream. + +

+ +If you're encoding or decoding, you should +start adding things to the string table at <CC> + 2. If you're +encoding, you should output <CC> as the very first code, and then +whenever after that you reach code #4095 (hex FFF), because GIF does +not allow compression sizes to be greater than 12 bits. If you're +decoding, you should reinitialize your string table when you observe +<CC>. The variable compression sizes are really no big deal. If +you're encoding, you start with a compression size of (N+1) bits, and, +whenever you output the code (2**(compression size)-1), you bump the +compression size up one bit. So the next code you output will be one +bit longer. Remember that the largest compression size is 12 bits, +corresponding to a code of 4095. If you get that far, you must output +<CC> as the next code, and start over. If you're decoding, you +must increase your compression size AS SOON AS YOU write entry +#(2**(compression size) - 1) to the string table. The next code you +READ will be one bit longer. Don't make the mistake of waiting until +you need to add the code (2**compression size) to the table. You'll +have already missed a bit from the last code. The packaging of codes +into a bitsream for the raster data is also a potential stumbling +block for the novice encoder or decoder. The lowest order bit in the +code should coincide with the lowest available bit in the first +available byte in the codestream. For example, if you're starting with +5-bit compression codes, and your first three codes are, say, +<abcde>, <fghij>, <klmno>, where e, j, and o are bit#0, +then your codestream will start off like: + +

       byte#0: hijabcde
+       byte#1: .klmnofg
+
+ +

+ + So the differences between straight LZW and GIF LZW are: two additional +special codes and variable compression sizes. If you understand LZW, and you +understand those variations, you understand it all! + +

+ Just as sort of a P.S., you may have noticed that a compressor has a +little bit of flexibility at compression time. I specified a "greedy" approach +to the compression, grabbing as many characters as possible before outputting +codes. This is, in fact, the standard LZW way of doing things, and it will +yield the best compression ratio. But there's no rule saying you can't stop +anywhere along the line and just output the code for the current prefix, +whether it's already in the table or not, and add that string plus the next +character to the string table. There are various reasons for wanting to do +this, especially if the strings get extremely long and make hashing difficult. +If you need to, do it. + +

+ Hope this helps out.----steve blackstock + +

Further information

+ +The original paper that describes the LZW algorithm is: + +
+Terry A. Welch. +A Technique for High Performance Data Compression. +IEEE Computer, Vol. 17, No. 6, 1984, pp. 8-19. +
+ +The GIF format is described in more detail in the +GIF87(5) - GIF 87 and +GIF89a(5) - GIF 89a standards. + + diff --git a/doc/gifstandard/gif-1over100.png b/doc/gifstandard/gif-1over100.png new file mode 100644 index 0000000..5007a5b Binary files /dev/null and b/doc/gifstandard/gif-1over100.png differ diff --git a/doc/gifstandard/gif-1over64.png b/doc/gifstandard/gif-1over64.png new file mode 100644 index 0000000..81ea1b0 Binary files /dev/null and b/doc/gifstandard/gif-1over64.png differ diff --git a/doc/gifstandard/gif-aspectratio.png b/doc/gifstandard/gif-aspectratio.png new file mode 100644 index 0000000..8ffdc5b Binary files /dev/null and b/doc/gifstandard/gif-aspectratio.png differ diff --git a/doc/gifstandard/gif87.txt b/doc/gifstandard/gif87.txt new file mode 100644 index 0000000..0660834 --- /dev/null +++ b/doc/gifstandard/gif87.txt @@ -0,0 +1,680 @@ + + + + + G I F (tm) + + Graphics Interchange Format (tm) + + A standard defining a mechanism + for the storage and transmission + of raster-based graphics information + + June 15, 1987 + + (c) CompuServe Incorporated, 1987 + All rights reserved + + While this document is copyrighted, the information + contained within is made available for use in computer + software without royalties, or licensing restrictions. + GIF and 'Graphics Interchange Format' are trademarks of + + CompuServe, Incorporated. + an H&R Block Company + 5000 Arlington Centre Blvd. + Columbus, Ohio 43220 + (614) 457-8600 + + Page 2 + + + + Graphics Interchange Format (GIF) Specification + Table of Contents + INTRODUCTION . . . . . . . . . . . . . . . . . page 3 + GENERAL FILE FORMAT . . . . . . . . . . . . . page 3 + GIF SIGNATURE . . . . . . . . . . . . . . . . page 4 + SCREEN DESCRIPTOR . . . . . . . . . . . . . . page 4 + GLOBAL COLOR MAP . . . . . . . . . . . . . . . page 5 + IMAGE DESCRIPTOR . . . . . . . . . . . . . . . page 6 + LOCAL COLOR MAP . . . . . . . . . . . . . . . page 7 + RASTER DATA . . . . . . . . . . . . . . . . . page 7 + GIF TERMINATOR . . . . . . . . . . . . . . . . page 8 + GIF EXTENSION BLOCKS . . . . . . . . . . . . . page 8 + APPENDIX A - GLOSSARY . . . . . . . . . . . . page 9 + APPENDIX B - INTERACTIVE SEQUENCES . . . . . . page 10 + APPENDIX C - IMAGE PACKAGING & COMPRESSION . . page 12 + APPENDIX D - MULTIPLE IMAGE PROCESSING . . . . page 15 + +Graphics Interchange Format (GIF) Page 3 + + + +Specification + +INTRODUCTION + + 'GIF' (tm) is CompuServe's standard for defining generalized color + raster images. This 'Graphics Interchange Format' (tm) allows + high-quality, high-resolution graphics to be displayed on a variety of + graphics hardware and is intended as an exchange and display mechanism + for graphics images. The image format described in this document is + designed to support current and future image technology and will in + addition serve as a basis for future CompuServe graphics products. + + The main focus of this document is to provide the technical + information necessary for a programmer to implement GIF encoders and + decoders. As such, some assumptions are made as to terminology relavent + to graphics and programming in general. + + The first section of this document describes the GIF data format + and its components and applies to all GIF decoders, either as standalone + programs or as part of a communications package. Appendix B is a + section relavent to decoders that are part of a communications software + package and describes the protocol requirements for entering and exiting + GIF mode, and responding to host interrogations. A glossary in Appendix + A defines some of the terminology used in this document. Appendix C + gives a detailed explanation of how the graphics image itself is + packaged as a series of data bytes. + + + Graphics Interchange Format Data Definition + + GENERAL FILE FORMAT + + +-----------------------+ + | +-------------------+ | + | | GIF Signature | | + | +-------------------+ | + | +-------------------+ | + | | Screen Descriptor | | + | +-------------------+ | + | +-------------------+ | + | | Global Color Map | | + | +-------------------+ | + . . . . . . + | +-------------------+ | ---+ + | | Image Descriptor | | | + | +-------------------+ | | + | +-------------------+ | | + | | Local Color Map | | |- Repeated 1 to n times + | +-------------------+ | | + | +-------------------+ | | + | | Raster Data | | | + | +-------------------+ | ---+ + . . . . . . + |- GIF Terminator -| + +-----------------------+ + +Graphics Interchange Format (GIF) Page 4 + + + +Specification + + GIF SIGNATURE + + The following GIF Signature identifies the data following as a + valid GIF image stream. It consists of the following six characters: + + G I F 8 7 a + + The last three characters '87a' may be viewed as a version number + for this particular GIF definition and will be used in general as a + reference in documents regarding GIF that address any version + dependencies. + + SCREEN DESCRIPTOR + + The Screen Descriptor describes the overall parameters for all GIF + images following. It defines the overall dimensions of the image space + or logical screen required, the existance of color mapping information, + background screen color, and color depth information. This information + is stored in a series of 8-bit bytes as described below. + + bits + 7 6 5 4 3 2 1 0 Byte # + +---------------+ + | | 1 + +-Screen Width -+ Raster width in pixels (LSB first) + | | 2 + +---------------+ + | | 3 + +-Screen Height-+ Raster height in pixels (LSB first) + | | 4 + +-+-----+-+-----+ M = 1, Global color map follows Descriptor + |M| cr |0|pixel| 5 cr+1 = # bits of color resolution + +-+-----+-+-----+ pixel+1 = # bits/pixel in image + | background | 6 background=Color index of screen background + +---------------+ (color is defined from the Global color + |0 0 0 0 0 0 0 0| 7 map or default map if none specified) + +---------------+ + + The logical screen width and height can both be larger than the + physical display. How images larger than the physical display are + handled is implementation dependent and can take advantage of hardware + characteristics (e.g. Macintosh scrolling windows). Otherwise images + can be clipped to the edges of the display. + + The value of 'pixel' also defines the maximum number of colors + within an image. The range of values for 'pixel' is 0 to 7 which + represents 1 to 8 bits. This translates to a range of 2 (B & W) to 256 + colors. Bit 3 of word 5 is reserved for future definition and must be + zero. + +Graphics Interchange Format (GIF) Page 5 + + + +Specification + + GLOBAL COLOR MAP + + The Global Color Map is optional but recommended for images where + accurate color rendition is desired. The existence of this color map is + indicated in the 'M' field of byte 5 of the Screen Descriptor. A color + map can also be associated with each image in a GIF file as described + later. However this global map will normally be used because of + hardware restrictions in equipment available today. In the individual + Image Descriptors the 'M' flag will normally be zero. If the Global + Color Map is present, it's definition immediately follows the Screen + Descriptor. The number of color map entries following a Screen + Descriptor is equal to 2**(# bits per pixel), where each entry consists + of three byte values representing the relative intensities of red, green + and blue respectively. The structure of the Color Map block is: + + bits + 7 6 5 4 3 2 1 0 Byte # + +---------------+ + | red intensity | 1 Red value for color index 0 + +---------------+ + |green intensity| 2 Green value for color index 0 + +---------------+ + | blue intensity| 3 Blue value for color index 0 + +---------------+ + | red intensity | 4 Red value for color index 1 + +---------------+ + |green intensity| 5 Green value for color index 1 + +---------------+ + | blue intensity| 6 Blue value for color index 1 + +---------------+ + : : (Continues for remaining colors) + + Each image pixel value received will be displayed according to its + closest match with an available color of the display based on this color + map. The color components represent a fractional intensity value from + none (0) to full (255). White would be represented as (255,255,255), + black as (0,0,0) and medium yellow as (180,180,0). For display, if the + device supports fewer than 8 bits per color component, the higher order + bits of each component are used. In the creation of a GIF color map + entry with hardware supporting fewer than 8 bits per component, the + component values for the hardware should be converted to the 8-bit + format with the following calculation: + + = *255/(2** -1) + + This assures accurate translation of colors for all displays. In + the cases of creating GIF images from hardware without color palette + capability, a fixed palette should be created based on the available + display colors for that hardware. If no Global Color Map is indicated, + a default color map is generated internally which maps each possible + incoming color index to the same hardware color index modulo where + is the number of available hardware colors. + +Graphics Interchange Format (GIF) Page 6 + + + +Specification + + IMAGE DESCRIPTOR + + The Image Descriptor defines the actual placement and extents of + the following image within the space defined in the Screen Descriptor. + Also defined are flags to indicate the presence of a local color lookup + map, and to define the pixel display sequence. Each Image Descriptor is + introduced by an image separator character. The role of the Image + Separator is simply to provide a synchronization character to introduce + an Image Descriptor. This is desirable if a GIF file happens to contain + more than one image. This character is defined as 0x2C hex or ',' + (comma). When this character is encountered between images, the Image + Descriptor will follow immediately. + + Any characters encountered between the end of a previous image and + the image separator character are to be ignored. This allows future GIF + enhancements to be present in newer image formats and yet ignored safely + by older software decoders. + + bits + 7 6 5 4 3 2 1 0 Byte # + +---------------+ + |0 0 1 0 1 1 0 0| 1 ',' - Image separator character + +---------------+ + | | 2 Start of image in pixels from the + +- Image Left -+ left side of the screen (LSB first) + | | 3 + +---------------+ + | | 4 + +- Image Top -+ Start of image in pixels from the + | | 5 top of the screen (LSB first) + +---------------+ + | | 6 + +- Image Width -+ Width of the image in pixels (LSB first) + | | 7 + +---------------+ + | | 8 + +- Image Height-+ Height of the image in pixels (LSB first) + | | 9 + +-+-+-+-+-+-----+ M=0 - Use global color map, ignore 'pixel' + |M|I|0|0|0|pixel| 10 M=1 - Local color map follows, use 'pixel' + +-+-+-+-+-+-----+ I=0 - Image formatted in Sequential order + I=1 - Image formatted in Interlaced order + pixel+1 - # bits per pixel for this image + + The specifications for the image position and size must be confined + to the dimensions defined by the Screen Descriptor. On the other hand + it is not necessary that the image fill the entire screen defined. + +Graphics Interchange Format (GIF) Page 7 + + +Specification + + LOCAL COLOR MAP + + A Local Color Map is optional and defined here for future use. If + the 'M' bit of byte 10 of the Image Descriptor is set, then a color map + follows the Image Descriptor that applies only to the following image. + At the end of the image, the color map will revert to that defined after + the Screen Descriptor. Note that the 'pixel' field of byte 10 of the + Image Descriptor is used only if a Local Color Map is indicated. This + defines the parameters not only for the image pixel size, but determines + the number of color map entries that follow. The bits per pixel value + will also revert to the value specified in the Screen Descriptor when + processing of the image is complete. + + RASTER DATA + + The format of the actual image is defined as the series of pixel + color index values that make up the image. The pixels are stored left + to right sequentially for an image row. By default each image row is + written sequentially, top to bottom. In the case that the Interlace or + 'I' bit is set in byte 10 of the Image Descriptor then the row order of + the image display follows a four-pass process in which the image is + filled in by widely spaced rows. The first pass writes every 8th row, + starting with the top row of the image window. The second pass writes + every 8th row starting at the fifth row from the top. The third pass + writes every 4th row starting at the third row from the top. The fourth + pass completes the image, writing every other row, starting at the + second row from the top. A graphic description of this process follows: + + Image + + Row Pass 1 Pass 2 Pass 3 Pass 4 Result + --------------------------------------------------- + 0 **1a** **1a** + 1 **4a** **4a** + 2 **3a** **3a** + 3 **4b** **4b** + 4 **2a** **2a** + 5 **4c** **4c** + 6 **3b** **3b** + 7 **4d** **4d** + 8 **1b** **1b** + 9 **4e** **4e** + 10 **3c** **3c** + 11 **4f** **4f** + 12 **2b** **2b** + . . . + + The image pixel values are processed as a series of color indices + which map into the existing color map. The resulting color value from + the map is what is actually displayed. This series of pixel indices, + the number of which is equal to image-width*image-height pixels, are + passed to the GIF image data stream one value per pixel, compressed and + packaged according to a version of the LZW compression algorithm as + defined in Appendix C. + +Graphics Interchange Format (GIF) Page 8 + + + +Specification + + GIF TERMINATOR + + In order to provide a synchronization for the termination of a GIF + image file, a GIF decoder will process the end of GIF mode when the + character 0x3B hex or ';' is found after an image has been processed. + By convention the decoding software will pause and wait for an action + indicating that the user is ready to continue. This may be a carriage + return entered at the keyboard or a mouse click. For interactive + applications this user action must be passed on to the host as a + carriage return character so that the host application can continue. + The decoding software will then typically leave graphics mode and resume + any previous process. + + GIF EXTENSION BLOCKS + + To provide for orderly extension of the GIF definition, a mechanism + for defining the packaging of extensions within a GIF data stream is + necessary. Specific GIF extensions are to be defined and documented by + CompuServe in order to provide a controlled enhancement path. + + GIF Extension Blocks are packaged in a manner similar to that used + by the raster data though not compressed. The basic structure is: + + 7 6 5 4 3 2 1 0 Byte # + +---------------+ + |0 0 1 0 0 0 0 1| 1 '!' - GIF Extension Block Introducer + +---------------+ + | function code | 2 Extension function code (0 to 255) + +---------------+ ---+ + | byte count | | + +---------------+ | + : : +-- Repeated as many times as necessary + |func data bytes| | + : : | + +---------------+ ---+ + . . . . . . + +---------------+ + |0 0 0 0 0 0 0 0| zero byte count (terminates block) + +---------------+ + + A GIF Extension Block may immediately preceed any Image Descriptor + or occur before the GIF Terminator. + + All GIF decoders must be able to recognize the existence of GIF + Extension Blocks and read past them if unable to process the function + code. This ensures that older decoders will be able to process extended + GIF image files in the future, though without the additional + functionality. + +Graphics Interchange Format (GIF) Page 9 + +Appendix A - Glossary + + GLOSSARY + +Pixel - The smallest picture element of a graphics image. This usually + corresponds to a single dot on a graphics screen. Image resolution is + typically given in units of pixels. For example a fairly standard + graphics screen format is one 320 pixels across and 200 pixels high. + Each pixel can appear as one of several colors depending on the + capabilities of the graphics hardware. + +Raster - A horizontal row of pixels representing one line of an image. A + typical method of working with images since most hardware is oriented to + work most efficiently in this manner. + +LSB - Least Significant Byte. Refers to a convention for two byte numeric + values in which the less significant byte of the value preceeds the more + significant byte. This convention is typical on many microcomputers. + +Color Map - The list of definitions of each color used in a GIF image. + These desired colors are converted to available colors through a table + which is derived by assigning an incoming color index (from the image) + to an output color index (of the hardware). While the color map + definitons are specified in a GIF image, the output pixel colors will + vary based on the hardware used and its ability to match the defined + color. + +Interlace - The method of displaying a GIF image in which multiple passes + are made, outputting raster lines spaced apart to provide a way of + visualizing the general content of an entire image before all of the + data has been processed. + +B Protocol - A CompuServe-developed error-correcting file transfer protocol + available in the public domain and implemented in CompuServe VIDTEX + products. This error checking mechanism will be used in transfers of + GIF images for interactive applications. + +LZW - A sophisticated data compression algorithm based on work done by + Lempel-Ziv & Welch which has the feature of very efficient one-pass + encoding and decoding. This allows the image to be decompressed and + displayed at the same time. The original article from which this + technique was adapted is: + + Terry A. Welch, "A Technique for High Performance Data + Compression", IEEE Computer, vol 17 no 6 (June 1984) + + This basic algorithm is also used in the public domain ARC file + compression utilities. The CompuServe adaptation of LZW for GIF is + described in Appendix C. + +Graphics Interchange Format (GIF) Page 10 + + + +Appendix B - Interactive Sequences + + GIF Sequence Exchanges for an Interactive Environment + + The following sequences are defined for use in mediating control + between a GIF sender and GIF receiver over an interactive communications + line. These sequences do not apply to applications that involve + downloading of static GIF files and are not considered part of a GIF + file. + + GIF CAPABILITIES ENQUIRY + + The GCE sequence is issued from a host and requests an interactive + GIF decoder to return a response message that defines the graphics + parameters for the decoder. This involves returning information about + available screen sizes, number of bits/color supported and the amount of + color detail supported. The escape sequence for the GCE is defined as: + + ESC [ > 0 g (g is lower case, spaces inserted for clarity) + (0x1B 0x5B 0x3E 0x30 0x67) + + GIF CAPABILITIES RESPONSE + + The GIF Capabilities Response message is returned by an interactive + GIF decoder and defines the decoder's display capabilities for all + graphics modes that are supported by the software. Note that this can + also include graphics printers as well as a monitor screen. The general + format of this message is: + + #version;protocol{;dev, width, height, color-bits, color-res}... + + '#' - GCR identifier character (Number Sign) + version - GIF format version number; initially '87a' + protocol='0' - No end-to-end protocol supported by decoder + Transfer as direct 8-bit data stream. + protocol='1' - Can use an error correction protocol to transfer GIF data + interactively from the host directly to the display. + dev = '0' - Screen parameter set follows + dev = '1' - Printer parameter set follows + width - Maximum supported display width in pixels + height - Maximum supported display height in pixels + color-bits - Number of bits per pixel supported. The number of + supported colors is therefore 2**color-bits. + color-res - Number of bits per color component supported in the + hardware color palette. If color-res is '0' then no + hardware palette table is available. + + Note that all values in the GCR are returned as ASCII decimal + numbers and the message is terminated by a Carriage Return character. + +Graphics Interchange Format (GIF) Page 11 + + + +Appendix B - Interactive Sequences + + The following GCR message describes three standard EGA + configurations with no printer; the GIF data stream can be processed + within an error correcting protocol: + + #87a;1 ;0,320,200,4,0 ;0,640,200,2,2 ;0,640,350,4,2 + + ENTER GIF GRAPHICS MODE + + Two sequences are currently defined to invoke an interactive GIF + decoder into action. The only difference between them is that different + output media are selected. These sequences are: + + ESC [ > 1 g Display GIF image on screen + (0x1B 0x5B 0x3E 0x31 0x67) + ESC [ > 2 g Display image directly to an attached graphics printer. + The image may optionally be displayed on the screen as + well. + (0x1B 0x5B 0x3E 0x32 0x67) + + Note that the 'g' character terminating each sequence is in lower + case. + + INTERACTIVE ENVIRONMENT + + The assumed environment for the transmission of GIF image data from + an interactive application is a full 8-bit data stream from host to + micro. All 256 character codes must be transferrable. The establishing + of an 8-bit data path for communications will normally be taken care of + by the host application programs. It is however up to the receiving + communications programs supporting GIF to be able to receive and pass on + all 256 8-bit codes to the GIF decoder software. + +Graphics Interchange Format (GIF) Page 12 + + + +Appendix C - Image Packaging & Compression + + The Raster Data stream that represents the actual output image can + be represented as: + + 7 6 5 4 3 2 1 0 + +---------------+ + | code size | + +---------------+ ---+ + |blok byte count| | + +---------------+ | + : : +-- Repeated as many times as necessary + | data bytes | | + : : | + +---------------+ ---+ + . . . . . . + +---------------+ + |0 0 0 0 0 0 0 0| zero byte count (terminates data stream) + +---------------+ + + The conversion of the image from a series of pixel values to a + transmitted or stored character stream involves several steps. In brief + these steps are: + + 1. Establish the Code Size - Define the number of bits needed to + represent the actual data. + 2. Compress the Data - Compress the series of image pixels to a series + of compression codes. + 3. Build a Series of Bytes - Take the set of compression codes and + convert to a string of 8-bit bytes. + 4. Package the Bytes - Package sets of bytes into blocks preceeded by + character counts and output. + +ESTABLISH CODE SIZE + + The first byte of the GIF Raster Data stream is a value indicating + the minimum number of bits required to represent the set of actual pixel + values. Normally this will be the same as the number of color bits. + Because of some algorithmic constraints however, black & white images + which have one color bit must be indicated as having a code size of 2. + This code size value also implies that the compression codes must start + out one bit longer. + +COMPRESSION + + The LZW algorithm converts a series of data values into a series of + codes which may be raw values or a code designating a series of values. + Using text characters as an analogy, the output code consists of a + character or a code representing a string of characters. + +Graphics Interchange Format (GIF) Page 13 + +Appendix C - Image Packaging & Compression + + The LZW algorithm used in GIF matches algorithmically with the + standard LZW algorithm with the following differences: + + 1. A special Clear code is defined which resets all + compression/decompression parameters and tables to a start-up state. + The value of this code is 2**. For example if the code + size indicated was 4 (image was 4 bits/pixel) the Clear code value + would be 16 (10000 binary). The Clear code can appear at any point + in the image data stream and therefore requires the LZW algorithm to + process succeeding codes as if a new data stream was starting. + Encoders should output a Clear code as the first code of each image + data stream. + + 2. An End of Information code is defined that explicitly indicates the + end of the image data stream. LZW processing terminates when this + code is encountered. It must be the last code output by the encoder + for an image. The value of this code is +1. + + 3. The first available compression code value is +2. + + 4. The output codes are of variable length, starting at +1 + bits per code, up to 12 bits per code. This defines a maximum code + value of 4095 (hex FFF). Whenever the LZW code value would exceed + the current code length, the code length is increased by one. The + packing/unpacking of these codes must then be altered to reflect the + new code length. + +BUILD 8-BIT BYTES + + Because the LZW compression used for GIF creates a series of + variable length codes, of between 3 and 12 bits each, these codes must + be reformed into a series of 8-bit bytes that will be the characters + actually stored or transmitted. This provides additional compression of + the image. The codes are formed into a stream of bits as if they were + packed right to left and then picked off 8 bits at a time to be output. + Assuming a character array of 8 bits per character and using 5 bit codes + to be packed, an example layout would be similar to: + + byte n byte 5 byte 4 byte 3 byte 2 byte 1 + +-.....-----+--------+--------+--------+--------+--------+ + | and so on |hhhhhggg|ggfffffe|eeeedddd|dcccccbb|bbbaaaaa| + +-.....-----+--------+--------+--------+--------+--------+ + + Note that the physical packing arrangement will change as the + number of bits per compression code change but the concept remains the + same. + +PACKAGE THE BYTES + + Once the bytes have been created, they are grouped into blocks for + output by preceeding each block of 0 to 255 bytes with a character count + byte. A block with a zero byte count terminates the Raster Data stream + for a given image. These blocks are what are actually output for the + +Graphics Interchange Format (GIF) Page 14 + + + +Appendix C - Image Packaging & Compression + + GIF image. This block format has the side effect of allowing a decoding + program the ability to read past the actual image data if necessary by + reading block counts and then skipping over the data. + +Graphics Interchange Format (GIF) Page 15 + + + + +Appendix D - Multiple Image Processing + + Since a GIF data stream can contain multiple images, it is + necessary to describe processing and display of such a file. Because + the image descriptor allows for placement of the image within the + logical screen, it is possible to define a sequence of images that may + each be a partial screen, but in total fill the entire screen. The + guidelines for handling the multiple image situation are: + + 1. There is no pause between images. Each is processed immediately as + seen by the decoder. + + 2. Each image explicitly overwrites any image already on the screen + inside of its window. The only screen clears are at the beginning + and end of the GIF image process. See discussion on the GIF + terminator. diff --git a/doc/gifstandard/gif89.txt b/doc/gifstandard/gif89.txt new file mode 100644 index 0000000..9516eec --- /dev/null +++ b/doc/gifstandard/gif89.txt @@ -0,0 +1,2475 @@ + + + Cover Sheet for the GIF89a Specification + + + DEFERRED CLEAR CODE IN LZW COMPRESSION + + There has been confusion about where clear codes can be found in the + data stream. As the specification says, they may appear at anytime. There + is not a requirement to send a clear code when the string table is full. + + It is the encoder's decision as to when the table should be cleared. When + the table is full, the encoder can chose to use the table as is, making no + changes to it until the encoder chooses to clear it. The encoder during + this time sends out codes that are of the maximum Code Size. + + As we can see from the above, when the decoder's table is full, it must + not change the table until a clear code is received. The Code Size is that + of the maximum Code Size. Processing other than this is done normally. + + Because of a large base of decoders that do not handle the decompression in + this manner, we ask developers of GIF encoding software to NOT implement + this feature until at least January 1991 and later if they see that their + particular market is not ready for it. This will give developers of GIF + decoding software time to implement this feature and to get it into the + hands of their clients before the decoders start "breaking" on the new + GIF's. It is not required that encoders change their software to take + advantage of the deferred clear code, but it is for decoders. + + APPLICATION EXTENSION BLOCK - APPLICATION IDENTIFIER + + There will be a Courtesy Directory file located on CompuServe in the PICS + forum. This directory will contain Application Identifiers for Application + Extension Blocks that have been used by developers of GIF applications. + This file is intended to help keep developers that wish to create + Application Extension Blocks from using the same Application Identifiers. + This is not an official directory; it is for voluntary participation only + and does not guarantee that someone will not use the same identifier. + + E-Mail can be sent to Larry Wood (forum manager of PICS) indicating the + request for inclusion in this file with an identifier. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GRAPHICS INTERCHANGE FORMAT(sm) + + Version 89a + + (c)1987,1988,1989,1990 + + Copyright + CompuServe Incorporated + Columbus, Ohio + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +CompuServe Incorporated Graphics Interchange Format +Document Date : 31 July 1990 Programming Reference + + + + + + + + + + + Table of Contents + +Disclaimer................................................................. 1 + +Foreword................................................................... 1 + +Licensing.................................................................. 1 + +About the Document......................................................... 2 + +General Description........................................................ 2 + +Version Numbers............................................................ 2 + +The Encoder................................................................ 3 + +The Decoder................................................................ 3 + +Compliance................................................................. 3 + +About Recommendations...................................................... 4 + +About Color Tables......................................................... 4 + +Blocks, Extensions and Scope............................................... 4 + +Block Sizes................................................................ 5 + +Using GIF as an embedded protocol.......................................... 5 + +Data Sub-blocks............................................................ 5 + +Block Terminator........................................................... 6 + +Header..................................................................... 7 + +Logical Screen Descriptor.................................................. 8 + +Global Color Table......................................................... 10 + +Image Descriptor........................................................... 11 + +Local Color Table.......................................................... 13 + +Table Based Image Data..................................................... 14 + +Graphic Control Extension.................................................. 15 + +Comment Extension.......................................................... 17 + +Plain Text Extension....................................................... 18 + +Application Extension...................................................... 21 + +Trailer.................................................................... 23 + + + + + + + + + + + +Quick Reference Table...................................................... 24 + +GIF Grammar................................................................ 25 + +Glossary................................................................... 27 + +Conventions................................................................ 28 + +Interlaced Images.......................................................... 29 + +Variable-Length-Code LZW Compression....................................... 30 + +On-line Capabilities Dialogue.............................................. 33 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + +1. Disclaimer. + +The information provided herein is subject to change without notice. In no +event will CompuServe Incorporated be liable for damages, including any loss of +revenue, loss of profits or other incidental or consequential damages arising +out of the use or inability to use the information; CompuServe Incorporated +makes no claim as to the suitability of the information. + + +2. Foreword. + +This document defines the Graphics Interchange Format(sm). The specification +given here defines version 89a, which is an extension of version 87a. + +The Graphics Interchange Format(sm) as specified here should be considered +complete; any deviation from it should be considered invalid, including but not +limited to, the use of reserved or undefined fields within control or data +blocks, the inclusion of extraneous data within or between blocks, the use of +methods or algorithms not specifically listed as part of the format, etc. In +general, any and all deviations, extensions or modifications not specified in +this document should be considered to be in violation of the format and should +be avoided. + + +3. Licensing. + +The Graphics Interchange Format(c) is the copyright property of CompuServe +Incorporated. Only CompuServe Incorporated is authorized to define, redefine, +enhance, alter, modify or change in any way the definition of the format. + +CompuServe Incorporated hereby grants a limited, non-exclusive, royalty-free +license for the use of the Graphics Interchange Format(sm) in computer +software; computer software utilizing GIF(sm) must acknowledge ownership of the +Graphics Interchange Format and its Service Mark by CompuServe Incorporated, in +User and Technical Documentation. Computer software utilizing GIF, which is +distributed or may be distributed without User or Technical Documentation must +display to the screen or printer a message acknowledging ownership of the +Graphics Interchange Format and the Service Mark by CompuServe Incorporated; in +this case, the acknowledgement may be displayed in an opening screen or leading +banner, or a closing screen or trailing banner. A message such as the following +may be used: + + "The Graphics Interchange Format(c) is the Copyright property of + CompuServe Incorporated. GIF(sm) is a Service Mark property of + CompuServe Incorporated." + +For further information, please contact : + + CompuServe Incorporated + Graphics Technology Department + 5000 Arlington Center Boulevard + Columbus, Ohio 43220 + U. S. A. + +CompuServe Incorporated maintains a mailing list with all those individuals and +organizations who wish to receive copies of this document when it is corrected + + + + + + + + 2 + + +or revised. This service is offered free of charge; please provide us with your +mailing address. + + +4. About the Document. + +This document describes in detail the definition of the Graphics Interchange +Format. This document is intended as a programming reference; it is +recommended that the entire document be read carefully before programming, +because of the interdependence of the various parts. There is an individual +section for each of the Format blocks. Within each section, the sub-section +labeled Required Version refers to the version number that an encoder will have +to use if the corresponding block is used in the Data Stream. Within each +section, a diagram describes the individual fields in the block; the diagrams +are drawn vertically; top bytes in the diagram appear first in the Data Stream. +Bits within a byte are drawn most significant on the left end. Multi-byte +numeric fields are ordered Least Significant Byte first. Numeric constants are +represented as Hexadecimal numbers, preceded by "0x". Bit fields within a byte +are described in order from most significant bits to least significant bits. + + +5. General Description. + +The Graphics Interchange Format(sm) defines a protocol intended for the on-line +transmission and interchange of raster graphic data in a way that is +independent of the hardware used in their creation or display. + +The Graphics Interchange Format is defined in terms of blocks and sub-blocks +which contain relevant parameters and data used in the reproduction of a +graphic. A GIF Data Stream is a sequence of protocol blocks and sub-blocks +representing a collection of graphics. In general, the graphics in a Data +Stream are assumed to be related to some degree, and to share some control +information; it is recommended that encoders attempt to group together related +graphics in order to minimize hardware changes during processing and to +minimize control information overhead. For the same reason, unrelated graphics +or graphics which require resetting hardware parameters should be encoded +separately to the extent possible. + +A Data Stream may originate locally, as when read from a file, or it may +originate remotely, as when transmitted over a data communications line. The +Format is defined with the assumption that an error-free Transport Level +Protocol is used for communications; the Format makes no provisions for +error-detection and error-correction. + +The GIF Data Stream must be interpreted in context, that is, the application +program must rely on information external to the Data Stream to invoke the +decoder process. + + +6. Version Numbers. + +The version number in the Header of a Data Stream is intended to identify the +minimum set of capabilities required of a decoder in order to fully process the +Data Stream. An encoder should use the earliest possible version number that +includes all the blocks used in the Data Stream. Within each block section in +this document, there is an entry labeled Required Version which specifies the + + + + + + + + 3 + + +earliest version number that includes the corresponding block. The encoder +should make every attempt to use the earliest version number covering all the +blocks in the Data Stream; the unnecessary use of later version numbers will +hinder processing by some decoders. + + +7. The Encoder. + +The Encoder is the program used to create a GIF Data Stream. From raster data +and other information, the encoder produces the necessary control and data +blocks needed for reproducing the original graphics. + +The encoder has the following primary responsibilities. + + - Include in the Data Stream all the necessary information to + reproduce the graphics. + + - Insure that a Data Stream is labeled with the earliest possible + Version Number that will cover the definition of all the blocks in + it; this is to ensure that the largest number of decoders can + process the Data Stream. + + - Ensure encoding of the graphics in such a way that the decoding + process is optimized. Avoid redundant information as much as + possible. + + - To the extent possible, avoid grouping graphics which might + require resetting hardware parameters during the decoding process. + + - Set to zero (off) each of the bits of each and every field + designated as reserved. Note that some fields in the Logical Screen + Descriptor and the Image Descriptor were reserved under Version + 87a, but are used under version 89a. + + +8. The Decoder. + +The Decoder is the program used to process a GIF Data Stream. It processes the +Data Stream sequentially, parsing the various blocks and sub-blocks, using the +control information to set hardware and process parameters and interpreting the +data to render the graphics. + +The decoder has the following primary responsibilities. + + - Process each graphic in the Data Stream in sequence, without + delays other than those specified in the control information. + + - Set its hardware parameters to fit, as closely as possible, the + control information contained in the Data Stream. + + +9. Compliance. + +An encoder or a decoder is said to comply with a given version of the Graphics +Interchange Format if and only if it fully conforms with and correctly +implements the definition of the standard associated with that version. An + + + + + + + + 4 + + +encoder or a decoder may be compliant with a given version number and not +compliant with some subsequent version. + + +10. About Recommendations. + +Each block section in this document contains an entry labeled Recommendation; +this section lists a set of recommendations intended to guide and organize the +use of the particular blocks. Such recommendations are geared towards making +the functions of encoders and decoders more efficient, as well as making +optimal use of the communications bandwidth. It is advised that these +recommendations be followed. + + +11. About Color Tables. + +The GIF format utilizes color tables to render raster-based graphics. A color +table can have one of two different scopes: global or local. A Global Color +Table is used by all those graphics in the Data Stream which do not have a +Local Color Table associated with them. The scope of the Global Color Table is +the entire Data Stream. A Local Color Table is always associated with the +graphic that immediately follows it; the scope of a Local Color Table is +limited to that single graphic. A Local Color Table supersedes a Global Color +Table, that is, if a Data Stream contains a Global Color Table, and an image +has a Local Color Table associated with it, the decoder must save the Global +Color Table, use the Local Color Table to render the image, and then restore +the Global Color Table. Both types of color tables are optional, making it +possible for a Data Stream to contain numerous graphics without a color table +at all. For this reason, it is recommended that the decoder save the last +Global Color Table used until another Global Color Table is encountered. In +this way, a Data Stream which does not contain either a Global Color Table or +a Local Color Table may be processed using the last Global Color Table saved. +If a Global Color Table from a previous Stream is used, that table becomes the +Global Color Table of the present Stream. This is intended to reduce the +overhead incurred by color tables. In particular, it is recommended that an +encoder use only one Global Color Table if all the images in related Data +Streams can be rendered with the same table. If no color table is available at +all, the decoder is free to use a system color table or a table of its own. In +that case, the decoder may use a color table with as many colors as its +hardware is able to support; it is recommended that such a table have black and +white as its first two entries, so that monochrome images can be rendered +adequately. + +The Definition of the GIF Format allows for a Data Stream to contain only the +Header, the Logical Screen Descriptor, a Global Color Table and the GIF +Trailer. Such a Data Stream would be used to load a decoder with a Global Color +Table, in preparation for subsequent Data Streams without a color table at all. + + +12. Blocks, Extensions and Scope. + +Blocks can be classified into three groups : Control, Graphic-Rendering and +Special Purpose. Control blocks, such as the Header, the Logical Screen +Descriptor, the Graphic Control Extension and the Trailer, contain information +used to control the process of the Data Stream or information used in setting +hardware parameters. Graphic-Rendering blocks such as the Image Descriptor and + + + + + + + + 5 + + +the Plain Text Extension contain information and data used to render a graphic +on the display device. Special Purpose blocks such as the Comment Extension and +the Application Extension are neither used to control the process of the Data +Stream nor do they contain information or data used to render a graphic on the +display device. With the exception of the Logical Screen Descriptor and the +Global Color Table, whose scope is the entire Data Stream, all other Control +blocks have a limited scope, restricted to the Graphic-Rendering block that +follows them. Special Purpose blocks do not delimit the scope of any Control +blocks; Special Purpose blocks are transparent to the decoding process. +Graphic-Rendering blocks and extensions are used as scope delimiters for +Control blocks and extensions. The labels used to identify labeled blocks fall +into three ranges : 0x00-0x7F (0-127) are the Graphic Rendering blocks, +excluding the Trailer (0x3B); 0x80-0xF9 (128-249) are the Control blocks; +0xFA-0xFF (250-255) are the Special Purpose blocks. These ranges are defined so +that decoders can handle block scope by appropriately identifying block labels, +even when the block itself cannot be processed. + + +13. Block Sizes. + +The Block Size field in a block, counts the number of bytes remaining in the +block, not counting the Block Size field itself, and not counting the Block +Terminator, if one is to follow. Blocks other than Data Blocks are intended to +be of fixed length; the Block Size field is provided in order to facilitate +skipping them, not to allow their size to change in the future. Data blocks +and sub-blocks are of variable length to accommodate the amount of data. + + +14. Using GIF as an embedded protocol. + +As an embedded protocol, GIF may be part of larger application protocols, +within which GIF is used to render graphics. In such a case, the application +protocol could define a block within which the GIF Data Stream would be +contained. The application program would then invoke a GIF decoder upon +encountering a block of type GIF. This approach is recommended in favor of +using Application Extensions, which become overhead for all other applications +that do not process them. Because a GIF Data Stream must be processed in +context, the application must rely on some means of identifying the GIF Data +Stream outside of the Stream itself. + + +15. Data Sub-blocks. + + a. Description. Data Sub-blocks are units containing data. They do not + have a label, these blocks are processed in the context of control + blocks, wherever data blocks are specified in the format. The first byte + of the Data sub-block indicates the number of data bytes to follow. A + data sub-block may contain from 0 to 255 data bytes. The size of the + block does not account for the size byte itself, therefore, the empty + sub-block is one whose size field contains 0x00. + + b. Required Version. 87a. + + + + + + + + + + + + 6 + + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | Block Size Byte + +---------------+ + 1 | | + +- -+ + 2 | | + +- -+ + 3 | | + +- -+ + | | Data Values Byte + +- -+ + up | | + +- . . . . -+ + to | | + +- -+ + | | + +- -+ +255 | | + +---------------+ + + i) Block Size - Number of bytes in the Data Sub-block; the size + must be within 0 and 255 bytes, inclusive. + + ii) Data Values - Any 8-bit value. There must be exactly as many + Data Values as specified by the Block Size field. + + d. Extensions and Scope. This type of block always occurs as part of a + larger unit. It does not have a scope of itself. + + e. Recommendation. None. + + +16. Block Terminator. + + a. Description. This zero-length Data Sub-block is used to terminate a + sequence of Data Sub-blocks. It contains a single byte in the position of + the Block Size field and does not contain data. + + b. Required Version. 87a. + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | Block Size Byte + +---------------+ + + i) Block Size - Number of bytes in the Data Sub-block; this field + contains the fixed value 0x00. + + ii) Data Values - This block does not contain any data. + + + + + + + + + + 7 + + + d. Extensions and Scope. This block terminates the immediately preceding + sequence of Data Sub-blocks. This block cannot be modified by any + extension. + + e. Recommendation. None. + + +17. Header. + + a. Description. The Header identifies the GIF Data Stream in context. The + Signature field marks the beginning of the Data Stream, and the Version + field identifies the set of capabilities required of a decoder to fully + process the Data Stream. This block is REQUIRED; exactly one Header must + be present per Data Stream. + + b. Required Version. Not applicable. This block is not subject to a + version number. This block must appear at the beginning of every Data + Stream. + + c. Syntax. + + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | Signature 3 Bytes + +- -+ + 1 | | + +- -+ + 2 | | + +---------------+ + 3 | | Version 3 Bytes + +- -+ + 4 | | + +- -+ + 5 | | + +---------------+ + + i) Signature - Identifies the GIF Data Stream. This field contains + the fixed value 'GIF'. + + ii) Version - Version number used to format the data stream. + Identifies the minimum set of capabilities necessary to a decoder + to fully process the contents of the Data Stream. + + Version Numbers as of 10 July 1990 : "87a" - May 1987 + "89a" - July 1989 + + Version numbers are ordered numerically increasing on the first two + digits starting with 87 (87,88,...,99,00,...,85,86) and + alphabetically increasing on the third character (a,...,z). + + iii) Extensions and Scope. The scope of this block is the entire + Data Stream. This block cannot be modified by any extension. + + + + + + + + + + + 8 + + + d. Recommendations. + + i) Signature - This field identifies the beginning of the GIF Data + Stream; it is not intended to provide a unique signature for the + identification of the data. It is recommended that the GIF Data + Stream be identified externally by the application. (Refer to + Appendix G for on-line identification of the GIF Data Stream.) + + ii) Version - ENCODER : An encoder should use the earliest possible + version number that defines all the blocks used in the Data Stream. + When two or more Data Streams are combined, the latest of the + individual version numbers should be used for the resulting Data + Stream. DECODER : A decoder should attempt to process the data + stream to the best of its ability; if it encounters a version + number which it is not capable of processing fully, it should + nevertheless, attempt to process the data stream to the best of its + ability, perhaps after warning the user that the data may be + incomplete. + + +18. Logical Screen Descriptor. + + a. Description. The Logical Screen Descriptor contains the parameters + necessary to define the area of the display device within which the + images will be rendered. The coordinates in this block are given with + respect to the top-left corner of the virtual screen; they do not + necessarily refer to absolute coordinates on the display device. This + implies that they could refer to window coordinates in a window-based + environment or printer coordinates when a printer is used. + + This block is REQUIRED; exactly one Logical Screen Descriptor must be + present per Data Stream. + + b. Required Version. Not applicable. This block is not subject to a + version number. This block must appear immediately after the Header. + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | Logical Screen Width Unsigned + +- -+ + 1 | | + +---------------+ + 2 | | Logical Screen Height Unsigned + +- -+ + 3 | | + +---------------+ + 4 | | | | | See below + +---------------+ + 5 | | Background Color Index Byte + +---------------+ + 6 | | Pixel Aspect Ratio Byte + +---------------+ + + + + + + + + + + 9 + + + = Global Color Table Flag 1 Bit + Color Resolution 3 Bits + Sort Flag 1 Bit + Size of Global Color Table 3 Bits + + i) Logical Screen Width - Width, in pixels, of the Logical Screen + where the images will be rendered in the displaying device. + + ii) Logical Screen Height - Height, in pixels, of the Logical + Screen where the images will be rendered in the displaying device. + + iii) Global Color Table Flag - Flag indicating the presence of a + Global Color Table; if the flag is set, the Global Color Table will + immediately follow the Logical Screen Descriptor. This flag also + selects the interpretation of the Background Color Index; if the + flag is set, the value of the Background Color Index field should + be used as the table index of the background color. (This field is + the most significant bit of the byte.) + + Values : 0 - No Global Color Table follows, the Background + Color Index field is meaningless. + 1 - A Global Color Table will immediately follow, the + Background Color Index field is meaningful. + + iv) Color Resolution - Number of bits per primary color available + to the original image, minus 1. This value represents the size of + the entire palette from which the colors in the graphic were + selected, not the number of colors actually used in the graphic. + For example, if the value in this field is 3, then the palette of + the original image had 4 bits per primary color available to create + the image. This value should be set to indicate the richness of + the original palette, even if not every color from the whole + palette is available on the source machine. + + v) Sort Flag - Indicates whether the Global Color Table is sorted. + If the flag is set, the Global Color Table is sorted, in order of + decreasing importance. Typically, the order would be decreasing + frequency, with most frequent color first. This assists a decoder, + with fewer available colors, in choosing the best subset of colors; + the decoder may use an initial segment of the table to render the + graphic. + + Values : 0 - Not ordered. + 1 - Ordered by decreasing importance, most + important color first. + + vi) Size of Global Color Table - If the Global Color Table Flag is + set to 1, the value in this field is used to calculate the number + of bytes contained in the Global Color Table. To determine that + actual size of the color table, raise 2 to [the value of the field + + 1]. Even if there is no Global Color Table specified, set this + field according to the above formula so that decoders can choose + the best graphics mode to display the stream in. (This field is + made up of the 3 least significant bits of the byte.) + + vii) Background Color Index - Index into the Global Color Table for + + + + + + + + 10 + + + the Background Color. The Background Color is the color used for + those pixels on the screen that are not covered by an image. If the + Global Color Table Flag is set to (zero), this field should be zero + and should be ignored. + + viii) Pixel Aspect Ratio - Factor used to compute an approximation + of the aspect ratio of the pixel in the original image. If the + value of the field is not 0, this approximation of the aspect ratio + is computed based on the formula: + + Aspect Ratio = (Pixel Aspect Ratio + 15) / 64 + + The Pixel Aspect Ratio is defined to be the quotient of the pixel's + width over its height. The value range in this field allows + specification of the widest pixel of 4:1 to the tallest pixel of + 1:4 in increments of 1/64th. + + Values : 0 - No aspect ratio information is given. + 1..255 - Value used in the computation. + + d. Extensions and Scope. The scope of this block is the entire Data + Stream. This block cannot be modified by any extension. + + e. Recommendations. None. + + +19. Global Color Table. + + a. Description. This block contains a color table, which is a sequence of + bytes representing red-green-blue color triplets. The Global Color Table + is used by images without a Local Color Table and by Plain Text + Extensions. Its presence is marked by the Global Color Table Flag being + set to 1 in the Logical Screen Descriptor; if present, it immediately + follows the Logical Screen Descriptor and contains a number of bytes + equal to + 3 x 2^(Size of Global Color Table+1). + + This block is OPTIONAL; at most one Global Color Table may be present + per Data Stream. + + b. Required Version. 87a + + + + + + + + + + + + + + + + + + + + + + + 11 + + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +===============+ + 0 | | Red 0 Byte + +- -+ + 1 | | Green 0 Byte + +- -+ + 2 | | Blue 0 Byte + +- -+ + 3 | | Red 1 Byte + +- -+ + | | Green 1 Byte + +- -+ + up | | + +- . . . . -+ ... + to | | + +- -+ + | | Green 255 Byte + +- -+ +767 | | Blue 255 Byte + +===============+ + + + d. Extensions and Scope. The scope of this block is the entire Data + Stream. This block cannot be modified by any extension. + + e. Recommendation. None. + + +20. Image Descriptor. + + a. Description. Each image in the Data Stream is composed of an Image + Descriptor, an optional Local Color Table, and the image data. Each + image must fit within the boundaries of the Logical Screen, as defined + in the Logical Screen Descriptor. + + The Image Descriptor contains the parameters necessary to process a table + based image. The coordinates given in this block refer to coordinates + within the Logical Screen, and are given in pixels. This block is a + Graphic-Rendering Block, optionally preceded by one or more Control + blocks such as the Graphic Control Extension, and may be optionally + followed by a Local Color Table; the Image Descriptor is always followed + by the image data. + + This block is REQUIRED for an image. Exactly one Image Descriptor must + be present per image in the Data Stream. An unlimited number of images + may be present per Data Stream. + + b. Required Version. 87a. + + + + + + + + + + + + + + 12 + + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | Image Separator Byte + +---------------+ + 1 | | Image Left Position Unsigned + +- -+ + 2 | | + +---------------+ + 3 | | Image Top Position Unsigned + +- -+ + 4 | | + +---------------+ + 5 | | Image Width Unsigned + +- -+ + 6 | | + +---------------+ + 7 | | Image Height Unsigned + +- -+ + 8 | | + +---------------+ + 9 | | | | | | See below + +---------------+ + + = Local Color Table Flag 1 Bit + Interlace Flag 1 Bit + Sort Flag 1 Bit + Reserved 2 Bits + Size of Local Color Table 3 Bits + + i) Image Separator - Identifies the beginning of an Image + Descriptor. This field contains the fixed value 0x2C. + + ii) Image Left Position - Column number, in pixels, of the left edge + of the image, with respect to the left edge of the Logical Screen. + Leftmost column of the Logical Screen is 0. + + iii) Image Top Position - Row number, in pixels, of the top edge of + the image with respect to the top edge of the Logical Screen. Top + row of the Logical Screen is 0. + + iv) Image Width - Width of the image in pixels. + + v) Image Height - Height of the image in pixels. + + vi) Local Color Table Flag - Indicates the presence of a Local Color + Table immediately following this Image Descriptor. (This field is + the most significant bit of the byte.) + + + Values : 0 - Local Color Table is not present. Use + Global Color Table if available. + 1 - Local Color Table present, and to follow + immediately after this Image Descriptor. + + + + + + + + + 13 + + + vii) Interlace Flag - Indicates if the image is interlaced. An image + is interlaced in a four-pass interlace pattern; see Appendix E for + details. + + Values : 0 - Image is not interlaced. + 1 - Image is interlaced. + + viii) Sort Flag - Indicates whether the Local Color Table is + sorted. If the flag is set, the Local Color Table is sorted, in + order of decreasing importance. Typically, the order would be + decreasing frequency, with most frequent color first. This assists + a decoder, with fewer available colors, in choosing the best subset + of colors; the decoder may use an initial segment of the table to + render the graphic. + + Values : 0 - Not ordered. + 1 - Ordered by decreasing importance, most + important color first. + + ix) Size of Local Color Table - If the Local Color Table Flag is + set to 1, the value in this field is used to calculate the number + of bytes contained in the Local Color Table. To determine that + actual size of the color table, raise 2 to the value of the field + + 1. This value should be 0 if there is no Local Color Table + specified. (This field is made up of the 3 least significant bits + of the byte.) + + d. Extensions and Scope. The scope of this block is the Table-based Image + Data Block that follows it. This block may be modified by the Graphic + Control Extension. + + e. Recommendation. None. + + +21. Local Color Table. + + a. Description. This block contains a color table, which is a sequence of + bytes representing red-green-blue color triplets. The Local Color Table + is used by the image that immediately follows. Its presence is marked by + the Local Color Table Flag being set to 1 in the Image Descriptor; if + present, the Local Color Table immediately follows the Image Descriptor + and contains a number of bytes equal to + 3x2^(Size of Local Color Table+1). + If present, this color table temporarily becomes the active color table + and the following image should be processed using it. This block is + OPTIONAL; at most one Local Color Table may be present per Image + Descriptor and its scope is the single image associated with the Image + Descriptor that precedes it. + + b. Required Version. 87a. + + + + + + + + + + + + + + 14 + + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +===============+ + 0 | | Red 0 Byte + +- -+ + 1 | | Green 0 Byte + +- -+ + 2 | | Blue 0 Byte + +- -+ + 3 | | Red 1 Byte + +- -+ + | | Green 1 Byte + +- -+ + up | | + +- . . . . -+ ... + to | | + +- -+ + | | Green 255 Byte + +- -+ +767 | | Blue 255 Byte + +===============+ + + + d. Extensions and Scope. The scope of this block is the Table-based Image + Data Block that immediately follows it. This block cannot be modified by + any extension. + + e. Recommendations. None. + + +22. Table Based Image Data. + + a. Description. The image data for a table based image consists of a + sequence of sub-blocks, of size at most 255 bytes each, containing an + index into the active color table, for each pixel in the image. Pixel + indices are in order of left to right and from top to bottom. Each index + must be within the range of the size of the active color table, starting + at 0. The sequence of indices is encoded using the LZW Algorithm with + variable-length code, as described in Appendix F + + b. Required Version. 87a. + + c. Syntax. The image data format is as follows: + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + | | LZW Minimum Code Size Byte + +---------------+ + + +===============+ + | | + / / Image Data Data Sub-blocks + | | + +===============+ + + + + + + + + + 15 + + + i) LZW Minimum Code Size. This byte determines the initial number + of bits used for LZW codes in the image data, as described in + Appendix F. + + d. Extensions and Scope. This block has no scope, it contains raster + data. Extensions intended to modify a Table-based image must appear + before the corresponding Image Descriptor. + + e. Recommendations. None. + + +23. Graphic Control Extension. + + a. Description. The Graphic Control Extension contains parameters used + when processing a graphic rendering block. The scope of this extension is + the first graphic rendering block to follow. The extension contains only + one data sub-block. + + This block is OPTIONAL; at most one Graphic Control Extension may precede + a graphic rendering block. This is the only limit to the number of + Graphic Control Extensions that may be contained in a Data Stream. + + b. Required Version. 89a. + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | Extension Introducer Byte + +---------------+ + 1 | | Graphic Control Label Byte + +---------------+ + + +---------------+ + 0 | | Block Size Byte + +---------------+ + 1 | | | | | See below + +---------------+ + 2 | | Delay Time Unsigned + +- -+ + 3 | | + +---------------+ + 4 | | Transparent Color Index Byte + +---------------+ + + +---------------+ + 0 | | Block Terminator Byte + +---------------+ + + + = Reserved 3 Bits + Disposal Method 3 Bits + User Input Flag 1 Bit + Transparent Color Flag 1 Bit + + i) Extension Introducer - Identifies the beginning of an extension + + + + + + + + 16 + + + block. This field contains the fixed value 0x21. + + ii) Graphic Control Label - Identifies the current block as a + Graphic Control Extension. This field contains the fixed value + 0xF9. + + iii) Block Size - Number of bytes in the block, after the Block + Size field and up to but not including the Block Terminator. This + field contains the fixed value 4. + + iv) Disposal Method - Indicates the way in which the graphic is to + be treated after being displayed. + + Values : 0 - No disposal specified. The decoder is + not required to take any action. + 1 - Do not dispose. The graphic is to be left + in place. + 2 - Restore to background color. The area used by the + graphic must be restored to the background color. + 3 - Restore to previous. The decoder is required to + restore the area overwritten by the graphic with + what was there prior to rendering the graphic. + 4-7 - To be defined. + + v) User Input Flag - Indicates whether or not user input is + expected before continuing. If the flag is set, processing will + continue when user input is entered. The nature of the User input + is determined by the application (Carriage Return, Mouse Button + Click, etc.). + + Values : 0 - User input is not expected. + 1 - User input is expected. + + When a Delay Time is used and the User Input Flag is set, + processing will continue when user input is received or when the + delay time expires, whichever occurs first. + + vi) Transparency Flag - Indicates whether a transparency index is + given in the Transparent Index field. (This field is the least + significant bit of the byte.) + + Values : 0 - Transparent Index is not given. + 1 - Transparent Index is given. + + vii) Delay Time - If not 0, this field specifies the number of + hundredths (1/100) of a second to wait before continuing with the + processing of the Data Stream. The clock starts ticking immediately + after the graphic is rendered. This field may be used in + conjunction with the User Input Flag field. + + viii) Transparency Index - The Transparency Index is such that when + encountered, the corresponding pixel of the display device is not + modified and processing goes on to the next pixel. The index is + present if and only if the Transparency Flag is set to 1. + + ix) Block Terminator - This zero-length data block marks the end of + + + + + + + + 17 + + the Graphic Control Extension. + + d. Extensions and Scope. The scope of this Extension is the graphic + rendering block that follows it; it is possible for other extensions to + be present between this block and its target. This block can modify the + Image Descriptor Block and the Plain Text Extension. + + e. Recommendations. + + i) Disposal Method - The mode Restore To Previous is intended to be + used in small sections of the graphic; the use of this mode imposes + severe demands on the decoder to store the section of the graphic + that needs to be saved. For this reason, this mode should be used + sparingly. This mode is not intended to save an entire graphic or + large areas of a graphic; when this is the case, the encoder should + make every attempt to make the sections of the graphic to be + restored be separate graphics in the data stream. In the case where + a decoder is not capable of saving an area of a graphic marked as + Restore To Previous, it is recommended that a decoder restore to + the background color. + + ii) User Input Flag - When the flag is set, indicating that user + input is expected, the decoder may sound the bell (0x07) to alert + the user that input is being expected. In the absence of a + specified Delay Time, the decoder should wait for user input + indefinitely. It is recommended that the encoder not set the User + Input Flag without a Delay Time specified. + + +24. Comment Extension. + + a. Description. The Comment Extension contains textual information which + is not part of the actual graphics in the GIF Data Stream. It is suitable + for including comments about the graphics, credits, descriptions or any + other type of non-control and non-graphic data. The Comment Extension + may be ignored by the decoder, or it may be saved for later processing; + under no circumstances should a Comment Extension disrupt or interfere + with the processing of the Data Stream. + + This block is OPTIONAL; any number of them may appear in the Data Stream. + + b. Required Version. 89a. + + + + + + + + + + + + + + + + + + + + + + + 18 + + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | Extension Introducer Byte + +---------------+ + 1 | | Comment Label Byte + +---------------+ + + +===============+ + | | + N | | Comment Data Data Sub-blocks + | | + +===============+ + + +---------------+ + 0 | | Block Terminator Byte + +---------------+ + + i) Extension Introducer - Identifies the beginning of an extension + block. This field contains the fixed value 0x21. + + ii) Comment Label - Identifies the block as a Comment Extension. + This field contains the fixed value 0xFE. + + iii) Comment Data - Sequence of sub-blocks, each of size at most + 255 bytes and at least 1 byte, with the size in a byte preceding + the data. The end of the sequence is marked by the Block + Terminator. + + iv) Block Terminator - This zero-length data block marks the end of + the Comment Extension. + + d. Extensions and Scope. This block does not have scope. This block + cannot be modified by any extension. + + e. Recommendations. + + i) Data - This block is intended for humans. It should contain + text using the 7-bit ASCII character set. This block should + not be used to store control information for custom processing. + + ii) Position - This block may appear at any point in the Data + Stream at which a block can begin; however, it is recommended that + Comment Extensions do not interfere with Control or Data blocks; + they should be located at the beginning or at the end of the Data + Stream to the extent possible. + + +25. Plain Text Extension. + + a. Description. The Plain Text Extension contains textual data and the + parameters necessary to render that data as a graphic, in a simple form. + The textual data will be encoded with the 7-bit printable ASCII + characters. Text data are rendered using a grid of character cells + + + + + + + + + 19 + + + defined by the parameters in the block fields. Each character is rendered + in an individual cell. The textual data in this block is to be rendered + as mono-spaced characters, one character per cell, with a best fitting + font and size. For further information, see the section on + Recommendations below. The data characters are taken sequentially from + the data portion of the block and rendered within a cell, starting with + the upper left cell in the grid and proceeding from left to right and + from top to bottom. Text data is rendered until the end of data is + reached or the character grid is filled. The Character Grid contains an + integral number of cells; in the case that the cell dimensions do not + allow for an integral number, fractional cells must be discarded; an + encoder must be careful to specify the grid dimensions accurately so that + this does not happen. This block requires a Global Color Table to be + available; the colors used by this block reference the Global Color Table + in the Stream if there is one, or the Global Color Table from a previous + Stream, if one was saved. This block is a graphic rendering block, + therefore it may be modified by a Graphic Control Extension. This block + is OPTIONAL; any number of them may appear in the Data Stream. + + b. Required Version. 89a. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 20 + + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | Extension Introducer Byte + +---------------+ + 1 | | Plain Text Label Byte + +---------------+ + + +---------------+ + 0 | | Block Size Byte + +---------------+ + 1 | | Text Grid Left Position Unsigned + +- -+ + 2 | | + +---------------+ + 3 | | Text Grid Top Position Unsigned + +- -+ + 4 | | + +---------------+ + 5 | | Text Grid Width Unsigned + +- -+ + 6 | | + +---------------+ + 7 | | Text Grid Height Unsigned + +- -+ + 8 | | + +---------------+ + 9 | | Character Cell Width Byte + +---------------+ + 10 | | Character Cell Height Byte + +---------------+ + 11 | | Text Foreground Color Index Byte + +---------------+ + 12 | | Text Background Color Index Byte + +---------------+ + + +===============+ + | | + N | | Plain Text Data Data Sub-blocks + | | + +===============+ + + +---------------+ + 0 | | Block Terminator Byte + +---------------+ + + i) Extension Introducer - Identifies the beginning of an extension + block. This field contains the fixed value 0x21. + + ii) Plain Text Label - Identifies the current block as a Plain Text + Extension. This field contains the fixed value 0x01. + + iii) Block Size - Number of bytes in the extension, after the Block + Size field and up to but not including the beginning of the data + portion. This field contains the fixed value 12. + + + + + + + + 21 + + + iv) Text Grid Left Position - Column number, in pixels, of the left + edge of the text grid, with respect to the left edge of the Logical + Screen. + + v) Text Grid Top Position - Row number, in pixels, of the top edge + of the text grid, with respect to the top edge of the Logical + Screen. + + vi) Image Grid Width - Width of the text grid in pixels. + + vii) Image Grid Height - Height of the text grid in pixels. + + viii) Character Cell Width - Width, in pixels, of each cell in the + grid. + + ix) Character Cell Height - Height, in pixels, of each cell in the + grid. + + x) Text Foreground Color Index - Index into the Global Color Table + to be used to render the text foreground. + + xi) Text Background Color Index - Index into the Global Color Table + to be used to render the text background. + + xii) Plain Text Data - Sequence of sub-blocks, each of size at most + 255 bytes and at least 1 byte, with the size in a byte preceding + the data. The end of the sequence is marked by the Block + Terminator. + + xiii) Block Terminator - This zero-length data block marks the end + of the Plain Text Data Blocks. + + d. Extensions and Scope. The scope of this block is the Plain Text Data + Block contained in it. This block may be modified by the Graphic Control + Extension. + + e. Recommendations. The data in the Plain Text Extension is assumed to be + preformatted. The selection of font and size is left to the discretion of + the decoder. If characters less than 0x20 or greater than 0xf7 are + encountered, it is recommended that the decoder display a Space character + (0x20). The encoder should use grid and cell dimensions such that an + integral number of cells fit in the grid both horizontally as well as + vertically. For broadest compatibility, character cell dimensions should + be around 8x8 or 8x16 (width x height); consider an image for unusual + sized text. + + +26. Application Extension. + + a. Description. The Application Extension contains application-specific + information; it conforms with the extension block syntax, as described + below, and its block label is 0xFF. + + b. Required Version. 89a. + + + + + + + + + + 22 + + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | Extension Introducer Byte + +---------------+ + 1 | | Extension Label Byte + +---------------+ + + +---------------+ + 0 | | Block Size Byte + +---------------+ + 1 | | + +- -+ + 2 | | + +- -+ + 3 | | Application Identifier 8 Bytes + +- -+ + 4 | | + +- -+ + 5 | | + +- -+ + 6 | | + +- -+ + 7 | | + +- -+ + 8 | | + +---------------+ + 9 | | + +- -+ + 10 | | Appl. Authentication Code 3 Bytes + +- -+ + 11 | | + +---------------+ + + +===============+ + | | + | | Application Data Data Sub-blocks + | | + | | + +===============+ + + +---------------+ + 0 | | Block Terminator Byte + +---------------+ + + i) Extension Introducer - Defines this block as an extension. This + field contains the fixed value 0x21. + + ii) Application Extension Label - Identifies the block as an + Application Extension. This field contains the fixed value 0xFF. + + iii) Block Size - Number of bytes in this extension block, + following the Block Size field, up to but not including the + beginning of the Application Data. This field contains the fixed + value 11. + + + + + + + + 23 + + + iv) Application Identifier - Sequence of eight printable ASCII + characters used to identify the application owning the Application + Extension. + + v) Application Authentication Code - Sequence of three bytes used + to authenticate the Application Identifier. An Application program + may use an algorithm to compute a binary code that uniquely + identifies it as the application owning the Application Extension. + + + d. Extensions and Scope. This block does not have scope. This block + cannot be modified by any extension. + + e. Recommendation. None. + + +27. Trailer. + + a. Description. This block is a single-field block indicating the end of + the GIF Data Stream. It contains the fixed value 0x3B. + + b. Required Version. 87a. + + c. Syntax. + + 7 6 5 4 3 2 1 0 Field Name Type + +---------------+ + 0 | | GIF Trailer Byte + +---------------+ + + d. Extensions and Scope. This block does not have scope, it terminates + the GIF Data Stream. This block may not be modified by any extension. + + e. Recommendations. None. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 24 + + +Appendix +A. Quick Reference Table. + +Block Name Required Label Ext. Vers. +Application Extension Opt. (*) 0xFF (255) yes 89a +Comment Extension Opt. (*) 0xFE (254) yes 89a +Global Color Table Opt. (1) none no 87a +Graphic Control Extension Opt. (*) 0xF9 (249) yes 89a +Header Req. (1) none no N/A +Image Descriptor Opt. (*) 0x2C (044) no 87a (89a) +Local Color Table Opt. (*) none no 87a +Logical Screen Descriptor Req. (1) none no 87a (89a) +Plain Text Extension Opt. (*) 0x01 (001) yes 89a +Trailer Req. (1) 0x3B (059) no 87a + +Unlabeled Blocks +Header Req. (1) none no N/A +Logical Screen Descriptor Req. (1) none no 87a (89a) +Global Color Table Opt. (1) none no 87a +Local Color Table Opt. (*) none no 87a + +Graphic-Rendering Blocks +Plain Text Extension Opt. (*) 0x01 (001) yes 89a +Image Descriptor Opt. (*) 0x2C (044) no 87a (89a) + +Control Blocks +Graphic Control Extension Opt. (*) 0xF9 (249) yes 89a + +Special Purpose Blocks +Trailer Req. (1) 0x3B (059) no 87a +Comment Extension Opt. (*) 0xFE (254) yes 89a +Application Extension Opt. (*) 0xFF (255) yes 89a + +legend: (1) if present, at most one occurrence + (*) zero or more occurrences + (+) one or more occurrences + +Notes : The Header is not subject to Version Numbers. +(89a) The Logical Screen Descriptor and the Image Descriptor retained their +syntax from version 87a to version 89a, but some fields reserved under version +87a are used under version 89a. + + + + + + + + + + + + + + + + + + + + + + + 25 + + +Appendix +B. GIF Grammar. + +A Grammar is a form of notation to represent the sequence in which certain +objects form larger objects. A grammar is also used to represent the number of +objects that can occur at a given position. The grammar given here represents +the sequence of blocks that form the GIF Data Stream. A grammar is given by +listing its rules. Each rule consists of the left-hand side, followed by some +form of equals sign, followed by the right-hand side. In a rule, the +right-hand side describes how the left-hand side is defined. The right-hand +side consists of a sequence of entities, with the possible presence of special +symbols. The following legend defines the symbols used in this grammar for GIF. + +Legend: <> grammar word + ::= defines symbol + * zero or more occurrences + + one or more occurrences + | alternate element + [] optional element + +Example: + + ::= Header * Trailer + +This rule defines the entity as follows. It must begin with a +Header. The Header is followed by an entity called Logical Screen, which is +defined below by another rule. The Logical Screen is followed by the entity +Data, which is also defined below by another rule. Finally, the entity Data is +followed by the Trailer. Since there is no rule defining the Header or the +Trailer, this means that these blocks are defined in the document. The entity +Data has a special symbol (*) following it which means that, at this position, +the entity Data may be repeated any number of times, including 0 times. For +further reading on this subject, refer to a standard text on Programming +Languages. + + +The Grammar. + + ::= Header * Trailer + + ::= Logical Screen Descriptor [Global Color Table] + + ::= | + + + ::= [Graphic Control Extension] + + ::= | + Plain Text Extension + + ::= Image Descriptor [Local Color Table] Image Data + + ::= Application Extension | + Comment Extension + + + + + + + + + + 26 + + +NOTE : The grammar indicates that it is possible for a GIF Data Stream to +contain the Header, the Logical Screen Descriptor, a Global Color Table and the +GIF Trailer. This special case is used to load a GIF decoder with a Global +Color Table, in preparation for subsequent Data Streams without color tables at +all. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 27 + + +Appendix +C. Glossary. + +Active Color Table - Color table used to render the next graphic. If the next +graphic is an image which has a Local Color Table associated with it, the +active color table becomes the Local Color Table associated with that image. +If the next graphic is an image without a Local Color Table, or a Plain Text +Extension, the active color table is the Global Color Table associated with the +Data Stream, if there is one; if there is no Global Color Table in the Data +Stream, the active color table is a color table saved from a previous Data +Stream, or one supplied by the decoder. + +Block - Collection of bytes forming a protocol unit. In general, the term +includes labeled and unlabeled blocks, as well as Extensions. + +Data Stream - The GIF Data Stream is composed of blocks and sub-blocks +representing images and graphics, together with control information to render +them on a display device. All control and data blocks in the Data Stream must +follow the Header and must precede the Trailer. + +Decoder - A program capable of processing a GIF Data Stream to render the +images and graphics contained in it. + +Encoder - A program capable of capturing and formatting image and graphic +raster data, following the definitions of the Graphics Interchange Format. + +Extension - A protocol block labeled by the Extension Introducer 0x21. + +Extension Introducer - Label (0x21) defining an Extension. + +Graphic - Data which can be rendered on the screen by virtue of some algorithm. +The term graphic is more general than the term image; in addition to images, +the term graphic also includes data such as text, which is rendered using +character bit-maps. + +Image - Data representing a picture or a drawing; an image is represented by an +array of pixels called the raster of the image. + +Raster - Array of pixel values representing an image. + + + + + + + + + + + + + + + + + + + + + + + + + 28 + + +Appendix +D. Conventions. + +Animation - The Graphics Interchange Format is not intended as a platform for +animation, even though it can be done in a limited way. + +Byte Ordering - Unless otherwise stated, multi-byte numeric fields are ordered +with the Least Significant Byte first. + +Color Indices - Color indices always refer to the active color table, either +the Global Color Table or the Local Color Table. + +Color Order - Unless otherwise stated, all triple-component RGB color values +are specified in Red-Green-Blue order. + +Color Tables - Both color tables, the Global and the Local, are optional; if +present, the Global Color Table is to be used with every image in the Data +Stream for which a Local Color Table is not given; if present, a Local Color +Table overrides the Global Color Table. However, if neither color table is +present, the application program is free to use an arbitrary color table. If +the graphics in several Data Streams are related and all use the same color +table, an encoder could place the color table as the Global Color Table in the +first Data Stream and leave subsequent Data Streams without a Global Color +Table or any Local Color Tables; in this way, the overhead for the table is +eliminated. It is recommended that the decoder save the previous Global Color +Table to be used with the Data Stream that follows, in case it does not contain +either a Global Color Table or any Local Color Tables. In general, this allows +the application program to use past color tables, significantly reducing +transmission overhead. + +Extension Blocks - Extensions are defined using the Extension Introducer code +to mark the beginning of the block, followed by a block label, identifying the +type of extension. Extension Codes are numbers in the range from 0x00 to 0xFF, +inclusive. Special purpose extensions are transparent to the decoder and may be +omitted when transmitting the Data Stream on-line. The GIF capabilities +dialogue makes the provision for the receiver to request the transmission of +all blocks; the default state in this regard is no transmission of Special +purpose blocks. + +Reserved Fields - All Reserved Fields are expected to have each bit set to zero +(off). + + + + + + + + + + + + + + + + + + + + + + + 29 + + +Appendix +E. Interlaced Images. + +The rows of an Interlaced images are arranged in the following order: + + Group 1 : Every 8th. row, starting with row 0. (Pass 1) + Group 2 : Every 8th. row, starting with row 4. (Pass 2) + Group 3 : Every 4th. row, starting with row 2. (Pass 3) + Group 4 : Every 2nd. row, starting with row 1. (Pass 4) + +The Following example illustrates how the rows of an interlaced image are +ordered. + + Row Number Interlace Pass + + 0 ----------------------------------------- 1 + 1 ----------------------------------------- 4 + 2 ----------------------------------------- 3 + 3 ----------------------------------------- 4 + 4 ----------------------------------------- 2 + 5 ----------------------------------------- 4 + 6 ----------------------------------------- 3 + 7 ----------------------------------------- 4 + 8 ----------------------------------------- 1 + 9 ----------------------------------------- 4 + 10 ----------------------------------------- 3 + 11 ----------------------------------------- 4 + 12 ----------------------------------------- 2 + 13 ----------------------------------------- 4 + 14 ----------------------------------------- 3 + 15 ----------------------------------------- 4 + 16 ----------------------------------------- 1 + 17 ----------------------------------------- 4 + 18 ----------------------------------------- 3 + 19 ----------------------------------------- 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 30 + + +Appendix +F. Variable-Length-Code LZW Compression. + +The Variable-Length-Code LZW Compression is a variation of the Lempel-Ziv +Compression algorithm in which variable-length codes are used to replace +patterns detected in the original data. The algorithm uses a code or +translation table constructed from the patterns encountered in the original +data; each new pattern is entered into the table and its index is used to +replace it in the compressed stream. + +The compressor takes the data from the input stream and builds a code or +translation table with the patterns as it encounters them; each new pattern is +entered into the code table and its index is added to the output stream; when a +pattern is encountered which had been detected since the last code table +refresh, its index from the code table is put on the output stream, thus +achieving the data compression. The expander takes input from the compressed +data stream and builds the code or translation table from it; as the compressed +data stream is processed, codes are used to index into the code table and the +corresponding data is put on the decompressed output stream, thus achieving +data decompression. The details of the algorithm are explained below. The +Variable-Length-Code aspect of the algorithm is based on an initial code size +(LZW-initial code size), which specifies the initial number of bits used for +the compression codes. When the number of patterns detected by the compressor +in the input stream exceeds the number of patterns encodable with the current +number of bits, the number of bits per LZW code is increased by one. + +The Raster Data stream that represents the actual output image can be +represented as: + + 7 6 5 4 3 2 1 0 + +---------------+ + | LZW code size | + +---------------+ + + +---------------+ ----+ + | block size | | + +---------------+ | + | | +-- Repeated as many + | data bytes | | times as necessary. + | | | + +---------------+ ----+ + + . . . . . . ------- The code that terminates the LZW + compressed data must appear before + Block Terminator. + +---------------+ + |0 0 0 0 0 0 0 0| Block Terminator + +---------------+ + +The conversion of the image from a series of pixel values to a transmitted or +stored character stream involves several steps. In brief these steps are: + +1. Establish the Code Size - Define the number of bits needed to represent the +actual data. + +2. Compress the Data - Compress the series of image pixels to a series of + + + + + + + + 31 + + +compression codes. + +3. Build a Series of Bytes - Take the set of compression codes and convert to a +string of 8-bit bytes. + +4. Package the Bytes - Package sets of bytes into blocks preceded by character +counts and output. + +ESTABLISH CODE SIZE + +The first byte of the Compressed Data stream is a value indicating the minimum +number of bits required to represent the set of actual pixel values. Normally +this will be the same as the number of color bits. Because of some algorithmic +constraints however, black & white images which have one color bit must be +indicated as having a code size of 2. +This code size value also implies that the compression codes must start out one +bit longer. + +COMPRESSION + +The LZW algorithm converts a series of data values into a series of codes which +may be raw values or a code designating a series of values. Using text +characters as an analogy, the output code consists of a character or a code +representing a string of characters. + +The LZW algorithm used in GIF matches algorithmically with the standard LZW +algorithm with the following differences: + +1. A special Clear code is defined which resets all compression/decompression +parameters and tables to a start-up state. The value of this code is 2**. For example if the code size indicated was 4 (image was 4 bits/pixel) +the Clear code value would be 16 (10000 binary). The Clear code can appear at +any point in the image data stream and therefore requires the LZW algorithm to +process succeeding codes as if a new data stream was starting. Encoders should +output a Clear code as the first code of each image data stream. + +2. An End of Information code is defined that explicitly indicates the end of +the image data stream. LZW processing terminates when this code is encountered. +It must be the last code output by the encoder for an image. The value of this +code is +1. + +3. The first available compression code value is +2. + +4. The output codes are of variable length, starting at +1 bits per +code, up to 12 bits per code. This defines a maximum code value of 4095 +(0xFFF). Whenever the LZW code value would exceed the current code length, the +code length is increased by one. The packing/unpacking of these codes must then +be altered to reflect the new code length. + +BUILD 8-BIT BYTES + +Because the LZW compression used for GIF creates a series of variable length +codes, of between 3 and 12 bits each, these codes must be reformed into a +series of 8-bit bytes that will be the characters actually stored or +transmitted. This provides additional compression of the image. The codes are +formed into a stream of bits as if they were packed right to left and then + + + + + + + + 32 + + +picked off 8 bits at a time to be output. + +Assuming a character array of 8 bits per character and using 5 bit codes to be +packed, an example layout would be similar to: + + + +---------------+ + 0 | | bbbaaaaa + +---------------+ + 1 | | dcccccbb + +---------------+ + 2 | | eeeedddd + +---------------+ + 3 | | ggfffffe + +---------------+ + 4 | | hhhhhggg + +---------------+ + . . . + +---------------+ + N | | + +---------------+ + + +Note that the physical packing arrangement will change as the number of bits +per compression code change but the concept remains the same. + +PACKAGE THE BYTES + +Once the bytes have been created, they are grouped into blocks for output by +preceding each block of 0 to 255 bytes with a character count byte. A block +with a zero byte count terminates the Raster Data stream for a given image. +These blocks are what are actually output for the GIF image. This block format +has the side effect of allowing a decoding program the ability to read past the +actual image data if necessary by reading block counts and then skipping over +the data. + + + +FURTHER READING + +[1] Ziv, J. and Lempel, A. : "A Universal Algorithm for Sequential Data +Compression", IEEE Transactions on Information Theory, May 1977. +[2] Welch, T. : "A Technique for High-Performance Data Compression", Computer, +June 1984. +[3] Nelson, M.R. : "LZW Data Compression", Dr. Dobb's Journal, October 1989. + + + + + + + + + + + + + + + + + + + 33 + + +Appendix +G. On-line Capabilities Dialogue. + +NOTE : This section is currently (10 July 1990) under revision; the information +provided here should be used as general guidelines. Code written based on this +information should be designed in a flexible way to accommodate any changes +resulting from the revisions. + +The following sequences are defined for use in mediating control between a GIF +sender and GIF receiver over an interactive communications line. These +sequences do not apply to applications that involve downloading of static GIF +files and are not considered part of a GIF file. + +GIF CAPABILITIES ENQUIRY + +The GIF Capabilities Enquiry sequence is issued from a host and requests an +interactive GIF decoder to return a response message that defines the graphics +parameters for the decoder. This involves returning information about available +screen sizes, number of bits/color supported and the amount of color detail +supported. The escape sequence for the GIF Capabilities Enquiry is defined as: + +ESC[>0g 0x1B 0x5B 0x3E 0x30 0x67 + +GIF CAPABILITIES RESPONSE + +The GIF Capabilities Response message is returned by an interactive GIF decoder +and defines the decoder's display capabilities for all graphics modes that are +supported by the software. Note that this can also include graphics printers as +well as a monitor screen. The general format of this message is: + +#version;protocol{;dev, width, height, color-bits, color-res}... + + +'#' GIF Capabilities Response identifier character. +version GIF format version number; initially '87a'. +protocol='0' No end-to-end protocol supported by decoder Transfer as direct + 8-bit data stream. +protocol='1' Can use CIS B+ error correction protocol to transfer GIF data + interactively from the host directly to the display. +dev = '0' Screen parameter set follows. +dev = '1' Printer parameter set follows. +width Maximum supported display width in pixels. +height Maximum supported display height in pixels. +color-bits Number of bits per pixel supported. The number of supported + colors is therefore 2**color-bits. +color-res Number of bits per color component supported in the hardware + color palette. If color-res is '0' then no hardware palette + table is available. + +Note that all values in the GIF Capabilities Response are returned as ASCII +decimal numbers and the message is terminated by a Carriage Return character. + +The following GIF Capabilities Response message describes three standard IBM PC +Enhanced Graphics Adapter configurations with no printer; the GIF data stream + + + + + + + + + + 34 + + +can be processed within an error correcting protocol: + +#87a;1;0,320,200,4,0;0,640,200,2,2;0,640,350,4,2 + +ENTER GIF GRAPHICS MODE + +Two sequences are currently defined to invoke an interactive GIF decoder into +action. The only difference between them is that different output media are +selected. These sequences are: + +ESC[>1g Display GIF image on screen + + 0x1B 0x5B 0x3E 0x31 0x67 + +ESC[>2g Display image directly to an attached graphics printer. The image may +optionally be displayed on the screen as well. + + 0x1B 0x5B 0x3E 0x32 0x67 + +Note that the 'g' character terminating each sequence is in lowercase. + +INTERACTIVE ENVIRONMENT + +The assumed environment for the transmission of GIF image data from an +interactive application is a full 8-bit data stream from host to micro. All +256 character codes must be transferrable. The establishing of an 8-bit data +path for communications will normally be taken care of by the host application +programs. It is however up to the receiving communications programs supporting +GIF to be able to receive and pass on all 256 8-bit codes to the GIF decoder +software. + + diff --git a/doc/gifstandard/gif89a.css b/doc/gifstandard/gif89a.css new file mode 100644 index 0000000..417975e --- /dev/null +++ b/doc/gifstandard/gif89a.css @@ -0,0 +1,62 @@ +ul { + list-style-type:disc; +} +ul ul { + list-style-type:circle; +} + +div#notes { + font-style:italic; +} + +table.invisible { + border-spacing:0; +} + +table#interlace td { + text-align:center; +} + +div.hiddenparagraph { + display:none; +} + +@media not print { + a[href*="#"] { + text-decoration:none; + color:blue; + } + + span.rcomp { + background:#FAA; + } + span.gcomp { + background:#8F8; + } + span.bcomp { + background:#BBF; + } + + h2 span { + font-size:small; + font-weight:normal; + text-decoration:underline; + } + + div.togglevisall span { + font-weight:bold; + text-decoration:underline; + } +} + +@media print { + table, + li { + page-break-inside:avoid; + } + + h2 span, + div.togglevisall { + display:none; + } +} diff --git a/doc/gifstandard/gif89a.js b/doc/gifstandard/gif89a.js new file mode 100644 index 0000000..78e0fea --- /dev/null +++ b/doc/gifstandard/gif89a.js @@ -0,0 +1,15 @@ +function ToggleVis(number) { + // toggle visibility of a single chapter + id = "p" + number; + c = document.getElementById(id).className; + c = (c == "" ? "hiddenparagraph" : ""); + document.getElementById(id).className = c; +} + +function SetEveryVis(status) { + // set visibility of every chapter + c = (status == 0 ? "hiddenparagraph" : ""); + for(i = 0; i <= 35; i++) { + document.getElementById("p" + i).className = c; + } +} diff --git a/doc/gifstandard/main.css b/doc/gifstandard/main.css new file mode 100644 index 0000000..b661500 --- /dev/null +++ b/doc/gifstandard/main.css @@ -0,0 +1,43 @@ +/* some pages also use their own CSS files, so: NO CUSTOM CLASS NAMES HERE */ + +body { + background:white; + color:black; + font-family:sans-serif; +} + +table { + background:black; + border-spacing:2px; +} +th, td { + padding:3px 5px; + vertical-align:middle; + empty-cells:show; +} +th { + background:silver; +} +td { + background:white; +} + +h1 { + text-align:center; +} +pre, tt { + font-size:120%; +} +abbr { + text-decoration:underline; +} + +@media print { + .noprint { + display:none; + } + a[href] { + color:black; + text-decoration:none; + } +} diff --git a/doc/giftext.1 b/doc/giftext.1 index cc94a20..adee2d6 100644 --- a/doc/giftext.1 +++ b/doc/giftext.1 @@ -1,7 +1,7 @@ '\" t .\" Title: giftext .\" Author: [see the "Author" section] -.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Generator: DocBook XSL Stylesheets vsnapshot .\" Date: 2 May 2012 .\" Manual: GIFLIB Documentation .\" Source: GIFLIB diff --git a/doc/giftool.1 b/doc/giftool.1 index d794c62..84895cd 100644 --- a/doc/giftool.1 +++ b/doc/giftool.1 @@ -1,7 +1,7 @@ '\" t .\" Title: giftool .\" Author: [see the "Author" section] -.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Generator: DocBook XSL Stylesheets vsnapshot .\" Date: 3 June 2012 .\" Manual: GIFLIB Documentation .\" Source: GIFLIB diff --git a/doc/gifwedge.1 b/doc/gifwedge.1 index aee1a64..187e715 100644 --- a/doc/gifwedge.1 +++ b/doc/gifwedge.1 @@ -1,7 +1,7 @@ '\" t .\" Title: gifwedge .\" Author: [see the "Author" section] -.\" Generator: DocBook XSL Stylesheets v1.79.1 +.\" Generator: DocBook XSL Stylesheets vsnapshot .\" Date: 2 May 2012 .\" Manual: GIFLIB Documentation .\" Source: GIFLIB diff --git a/doc/index.html.in b/doc/index.html.in index 6c6b7a9..3cbe2c5 100644 --- a/doc/index.html.in +++ b/doc/index.html.in @@ -119,6 +119,7 @@ continuing maintainance on Patreon.

  • Wikipedia on GIF
  • +

    To support this ubiquitous code, Become a Patreon patron!

    diff --git a/doc/intro.html b/doc/intro.html index 59fd0cc..e0c5ec1 100644 --- a/doc/intro.html +++ b/doc/intro.html @@ -1,8 +1,8 @@ -Introduction to GIFLIB

    Introduction to GIFLIB

    Eric Steven Raymond

    +Introduction to GIFLIB

    Introduction to GIFLIB

    Eric Steven Raymond


    GIFLIB is a package of portable tools and library routines for +    


    GIFLIB is a package of portable tools and library routines for working with GIF images.

    The Graphics Interchange Format(c) specification is the copyrighted property of CompuServe Incorporated. GIF(sm) is a service mark property of CompuServe Incorporated.

    This package has been released under an X Consortium-like open-source @@ -25,15 +25,16 @@ the current input scan line number (counting up) whenever they read image input, and will print output image line number (counting down) when they dump output. Utilities that only read or write always print -in increasing order.

    Conversion Utilities

    gif2rgb

    convert images saved as GIF to 24-bit RGB image(s) or vice-versa

    Test Pattern Generators

    gifbg

    generate a single-color test pattern GIF

    gifcolor

    generate color test patterns

    gifwedge

    create a test GIF image resembling a color monitor test pattern

    Image Manipulation Components

    gifclrmp

    modify GIF image colormaps

    giffix

    clumsily attempts to fix truncated GIF images

    Report Generators

    giftext

    print (text only) general information about a GIF

    gifhisto

    generate color-frequency histogram from a GIF

    GIF Composition Tools

    gifbuild

    converter/deconverter to/from an editable text format

    gifecho

    generate GIF images out of regular text in 8x8 font

    gifinto

    end-of-pipe fitting for GIF-processing pipelines

    giftool

    GIF transformation tool

    Library Functions

    The library contains two groups of C functions. One group does +in increasing order.

    Conversion Utilities

    gif2rgb

    convert images saved as GIF to 24-bit RGB image(s) or vice-versa

    Image Manipulation Components

    gifclrmp

    modify GIF image colormaps

    giffix

    clumsily attempts to fix truncated GIF images

    Report Generators

    giftext

    print (text only) general information about a GIF

    GIF Composition Tools

    gifbuild

    converter/deconverter to/from an editable text format

    giftool

    GIF transformation tool

    Obsolete utilities

    These are used for testing by the GFLIB developers and no longer +installed by a normal build.

    gifbg

    generate a single-color test pattern GIF

    gifcolor

    generate color test patterns

    gifwedge

    create a test GIF image resembling a color monitor test pattern

    gifhisto

    generate color-frequency histogram from a GIF

    gifecho

    generate GIF images out of regular text in 8x8 font

    gifinto

    end-of-pipe fitting for GIF-processing pipelines

    Library Functions

    The library contains two groups of C functions. One group does sequential I/O on the stream-oriented GIF format. The other supports grabbing an entire GIF into allocated core, operating on it in core, and then writing the modified in-core GIF out to disk.

    Unless you are on extremely memory-limited machine, you probably want -to use the second group.

    Detailed documentation on the library entry points is in gif_lib.html.

    The GIF Standard

    The doc subdirectory includes an HTML presentation of the GIF standard; an explanation of Lempel-Ziv compression, and the original flat-ASCII description of GIF89 format . For historical completeness, we also include a copy of the GIF87 standard.

    You can also read a detailed narrative description of how GIFs are laid out. It -clarifies some points on which the standard is obscure.

    Package Status

    GIFLIB's current maintainer is Eric S. Raymond. You can find his home +clarifies some points on which the standard is obscure.

    Package Status

    GIFLIB's current maintainer is Eric S. Raymond. You can find his home page at http://catb.org/~esr/.

    GIFLIB is not under active development, but bug fixes are being accepted.

    \ No newline at end of file diff --git a/doc/intro.xml b/doc/intro.xml index f668c83..2a3fe5f 100644 --- a/doc/intro.xml +++ b/doc/intro.xml @@ -88,95 +88,99 @@ of a local installation of the tools. -Test Pattern Generators +Image Manipulation Components -gifbg +gifclrmp -generate a single-color test pattern GIF +modify GIF image colormaps -gifcolor +giffix -generate color test patterns +clumsily attempts to fix truncated GIF images + + + +Report Generators + + -gifwedge +giftext -create a test GIF image resembling a color monitor test pattern +print (text only) general information about a GIF -Image Manipulation Components +GIF Composition Tools -gifclrmp +gifbuild -modify GIF image colormaps +converter/deconverter to/from an editable text format -giffix +giftool -clumsily attempts to fix truncated GIF images +GIF transformation tool -Report Generators +Obsolete utilities + +These are used for testing by the GFLIB developers and no longer +installed by a normal build. -giftext +gifbg -print (text only) general information about a GIF +generate a single-color test pattern GIF -gifhisto +gifcolor -generate color-frequency histogram from a GIF +generate color test patterns - - - -GIF Composition Tools - - -gifbuild +gifwedge -converter/deconverter to/from an editable text format +create a test GIF image resembling a color monitor test pattern -gifecho +gifhisto -generate GIF images out of regular text in 8x8 font +generate color-frequency histogram from a GIF -gifinto +gifecho -end-of-pipe fitting for GIF-processing pipelines +generate GIF images out of regular text in 8x8 font -giftool +gifinto -GIF transformation tool +end-of-pipe fitting for GIF-processing pipelines + Library Functions diff --git a/doc/whatsinagif/animation_and_transparency.html b/doc/whatsinagif/animation_and_transparency.html index 0f0205c..269a3db 100644 --- a/doc/whatsinagif/animation_and_transparency.html +++ b/doc/whatsinagif/animation_and_transparency.html @@ -275,7 +275,7 @@

    Animation

    A3 19 82 - 45 + 47 02 00 3B diff --git a/doc/whatsinagif/bits_and_bytes.html b/doc/whatsinagif/bits_and_bytes.html index b5d6b22..8be557f 100644 --- a/doc/whatsinagif/bits_and_bytes.html +++ b/doc/whatsinagif/bits_and_bytes.html @@ -221,7 +221,7 @@

    Logical Screen Descriptor

    The next three bits are the color resolution. They are only meaningful if there is a global color table, and allow you to -compute its size. If the value of this filed is N, the number of +compute its size. If the value of this field is N, the number of entries in the global color table will be 2 ^ (N+1) - that is, two raised to the power (N+1). Thus, the 001 in the sample image represents 2 bits/pixel; @@ -134,13 +132,13 @@

    LZW Compression

    the special codes depends on the value of the LZW minimum code size from the image data block. If the LZW minimum code size is the same as the color table size, then special codes immediatly follow the colors; however -it is possible to specify a larger LWZ minimum code size which may leave +it is possible to specify a larger LZW minimum code size which may leave a gap in the codes where no colors are assigned. This can be summarizaed in the following table.

    - + @@ -226,14 +224,16 @@

    LZW Compression

    K. [Step 6].

    LWZ Min Code
    Size
    Color
    Codes
    Clear
    Code
    EOI
    Code
    LZW Min Code
    Size
    Color
    Codes
    Clear
    Code
    EOI
    Code
    2#0-#3#4#5
    3#0-#7#8#9
    4#0-#15#16#17
    - + - + + + @@ -1055,7 +1055,7 @@

    Saving the Code Stream as Bytes

    You can see in the first byte that was returned (8C) that the lowest three bits (because that was -our first code size) contain 110 which is the binary value of 4 so +our first code size) contain 010 which is the binary value of 6 so that would be the clear code we started with, #4. In the three bits to the left, you see 001 which out or first data code of #1. You can also see when we switched into code sizes of 4 bits in the second byte diff --git a/doc/whatsinagif/sample_2_animation.gif b/doc/whatsinagif/sample_2_animation.gif new file mode 100644 index 0000000..3d1c19c Binary files /dev/null and b/doc/whatsinagif/sample_2_animation.gif differ diff --git a/egif_lib.c b/egif_lib.c index a413a80..1526868 100644 --- a/egif_lib.c +++ b/egif_lib.c @@ -6,19 +6,21 @@ The functions here and in dgif_lib.c are partitioned carefully so that if you only require one of read and write capability, only one of these two modules will be linked. Preserve this property! +SPDX-License-Identifier: MIT + *****************************************************************************/ +#include #include -#include #include +#include #include -#include #ifdef _WIN32 #include #else -#include #include +#include #endif /* _WIN32 */ #include @@ -27,22 +29,20 @@ two modules will be linked. Preserve this property! /* Masks given codes to BitsPerPixel, to make sure all codes are in range: */ /*@+charint@*/ -static const GifPixelType CodeMask[] = { - 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff -}; +static const GifPixelType CodeMask[] = {0x00, 0x01, 0x03, 0x07, 0x0f, + 0x1f, 0x3f, 0x7f, 0xff}; /*@-charint@*/ -static int EGifPutWord(int Word, GifFileType * GifFile); -static int EGifSetupCompress(GifFileType * GifFile); -static int EGifCompressLine(GifFileType * GifFile, GifPixelType * Line, - int LineLen); -static int EGifCompressOutput(GifFileType * GifFile, int Code); -static int EGifBufferedOutput(GifFileType * GifFile, GifByteType * Buf, - int c); +static int EGifPutWord(int Word, GifFileType *GifFile); +static int EGifSetupCompress(GifFileType *GifFile); +static int EGifCompressLine(GifFileType *GifFile, const GifPixelType *Line, + const int LineLen); +static int EGifCompressOutput(GifFileType *GifFile, int Code); +static int EGifBufferedOutput(GifFileType *GifFile, GifByteType *Buf, int c); /* extract bytes from an unsigned word */ -#define LOBYTE(x) ((x) & 0xff) -#define HIBYTE(x) (((x) >> 8) & 0xff) +#define LOBYTE(x) ((x)&0xff) +#define HIBYTE(x) (((x) >> 8) & 0xff) #ifndef S_IREAD #define S_IREAD S_IRUSR @@ -57,29 +57,31 @@ static int EGifBufferedOutput(GifFileType * GifFile, GifByteType * Buf, Returns a dynamically allocated GifFileType pointer which serves as the GIF info record. The Error member is cleared if successful. ******************************************************************************/ -GifFileType * -EGifOpenFileName(const char *FileName, const bool TestExistence, int *Error) -{ - - int FileHandle; - GifFileType *GifFile; - - if (TestExistence) - FileHandle = open(FileName, O_WRONLY | O_CREAT | O_EXCL, - S_IREAD | S_IWRITE); - else - FileHandle = open(FileName, O_WRONLY | O_CREAT | O_TRUNC, - S_IREAD | S_IWRITE); - - if (FileHandle == -1) { - if (Error != NULL) - *Error = E_GIF_ERR_OPEN_FAILED; - return NULL; - } - GifFile = EGifOpenFileHandle(FileHandle, Error); - if (GifFile == (GifFileType *) NULL) - (void)close(FileHandle); - return GifFile; +GifFileType *EGifOpenFileName(const char *FileName, const bool TestExistence, + int *Error) { + + int FileHandle; + GifFileType *GifFile; + + if (TestExistence) { + FileHandle = open(FileName, O_WRONLY | O_CREAT | O_EXCL, + S_IREAD | S_IWRITE); + } else { + FileHandle = open(FileName, O_WRONLY | O_CREAT | O_TRUNC, + S_IREAD | S_IWRITE); + } + + if (FileHandle == -1) { + if (Error != NULL) { + *Error = E_GIF_ERR_OPEN_FAILED; + } + return NULL; + } + GifFile = EGifOpenFileHandle(FileHandle, Error); + if (GifFile == (GifFileType *)NULL) { + (void)close(FileHandle); + } + return GifFile; } /****************************************************************************** @@ -89,462 +91,458 @@ EGifOpenFileName(const char *FileName, const bool TestExistence, int *Error) info record. Only fails on a memory allocation error. ******************************************************************************/ -GifFileType * -EGifOpenFileHandle(const int FileHandle, int *Error) -{ - GifFileType *GifFile; - GifFilePrivateType *Private; - FILE *f; - - GifFile = (GifFileType *) malloc(sizeof(GifFileType)); - if (GifFile == NULL) { - return NULL; - } - - memset(GifFile, '\0', sizeof(GifFileType)); - - Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); - if (Private == NULL) { - free(GifFile); - if (Error != NULL) - *Error = E_GIF_ERR_NOT_ENOUGH_MEM; - return NULL; - } - /*@i1@*/memset(Private, '\0', sizeof(GifFilePrivateType)); - if ((Private->HashTable = _InitHashTable()) == NULL) { - free(GifFile); - free(Private); - if (Error != NULL) - *Error = E_GIF_ERR_NOT_ENOUGH_MEM; - return NULL; - } +GifFileType *EGifOpenFileHandle(const int FileHandle, int *Error) { + GifFileType *GifFile; + GifFilePrivateType *Private; + FILE *f; + + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + return NULL; + } + + memset(GifFile, '\0', sizeof(GifFileType)); + + Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); + if (Private == NULL) { + free(GifFile); + if (Error != NULL) { + *Error = E_GIF_ERR_NOT_ENOUGH_MEM; + } + return NULL; + } + /*@i1@*/ memset(Private, '\0', sizeof(GifFilePrivateType)); + if ((Private->HashTable = _InitHashTable()) == NULL) { + free(GifFile); + free(Private); + if (Error != NULL) { + *Error = E_GIF_ERR_NOT_ENOUGH_MEM; + } + return NULL; + } #ifdef _WIN32 - _setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */ -#endif /* _WIN32 */ + _setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */ +#endif /* _WIN32 */ - f = fdopen(FileHandle, "wb"); /* Make it into a stream: */ + f = fdopen(FileHandle, "wb"); /* Make it into a stream: */ - GifFile->Private = (void *)Private; - Private->FileHandle = FileHandle; - Private->File = f; - Private->FileState = FILE_STATE_WRITE; - Private->gif89 = false; + GifFile->Private = (void *)Private; + Private->FileHandle = FileHandle; + Private->File = f; + Private->FileState = FILE_STATE_WRITE; + Private->gif89 = false; - Private->Write = (OutputFunc) 0; /* No user write routine (MRB) */ - GifFile->UserData = (void *)NULL; /* No user write handle (MRB) */ + Private->Write = (OutputFunc)0; /* No user write routine (MRB) */ + GifFile->UserData = (void *)NULL; /* No user write handle (MRB) */ - GifFile->Error = 0; + GifFile->Error = 0; - return GifFile; + return GifFile; } /****************************************************************************** Output constructor that takes user supplied output function. Basically just a copy of EGifOpenFileHandle. (MRB) ******************************************************************************/ -GifFileType * -EGifOpen(void *userData, OutputFunc writeFunc, int *Error) -{ - GifFileType *GifFile; - GifFilePrivateType *Private; - - GifFile = (GifFileType *)malloc(sizeof(GifFileType)); - if (GifFile == NULL) { - if (Error != NULL) - *Error = E_GIF_ERR_NOT_ENOUGH_MEM; - return NULL; - } - - memset(GifFile, '\0', sizeof(GifFileType)); - - Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); - if (Private == NULL) { - free(GifFile); - if (Error != NULL) - *Error = E_GIF_ERR_NOT_ENOUGH_MEM; - return NULL; - } - - memset(Private, '\0', sizeof(GifFilePrivateType)); - - Private->HashTable = _InitHashTable(); - if (Private->HashTable == NULL) { - free (GifFile); - free (Private); - if (Error != NULL) - *Error = E_GIF_ERR_NOT_ENOUGH_MEM; - return NULL; - } - - GifFile->Private = (void *)Private; - Private->FileHandle = 0; - Private->File = (FILE *) 0; - Private->FileState = FILE_STATE_WRITE; - - Private->Write = writeFunc; /* User write routine (MRB) */ - GifFile->UserData = userData; /* User write handle (MRB) */ - - Private->gif89 = false; /* initially, write GIF87 */ - - GifFile->Error = 0; - - return GifFile; +GifFileType *EGifOpen(void *userData, OutputFunc writeFunc, int *Error) { + GifFileType *GifFile; + GifFilePrivateType *Private; + + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + if (Error != NULL) { + *Error = E_GIF_ERR_NOT_ENOUGH_MEM; + } + return NULL; + } + + memset(GifFile, '\0', sizeof(GifFileType)); + + Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); + if (Private == NULL) { + free(GifFile); + if (Error != NULL) { + *Error = E_GIF_ERR_NOT_ENOUGH_MEM; + } + return NULL; + } + + memset(Private, '\0', sizeof(GifFilePrivateType)); + + Private->HashTable = _InitHashTable(); + if (Private->HashTable == NULL) { + free(GifFile); + free(Private); + if (Error != NULL) { + *Error = E_GIF_ERR_NOT_ENOUGH_MEM; + } + return NULL; + } + + GifFile->Private = (void *)Private; + Private->FileHandle = 0; + Private->File = (FILE *)0; + Private->FileState = FILE_STATE_WRITE; + + Private->Write = writeFunc; /* User write routine (MRB) */ + GifFile->UserData = userData; /* User write handle (MRB) */ + + Private->gif89 = false; /* initially, write GIF87 */ + + GifFile->Error = 0; + + return GifFile; } /****************************************************************************** Routine to compute the GIF version that will be written on output. ******************************************************************************/ -const char * -EGifGetGifVersion(GifFileType *GifFile) -{ - GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; - int i, j; - - /* - * Bulletproofing - always write GIF89 if we need to. - * Note, we don't clear the gif89 flag here because - * users of the sequential API might have called EGifSetGifVersion() - * in order to set that flag. - */ - for (i = 0; i < GifFile->ImageCount; i++) { - for (j = 0; j < GifFile->SavedImages[i].ExtensionBlockCount; j++) { - int function = - GifFile->SavedImages[i].ExtensionBlocks[j].Function; - - if (function == COMMENT_EXT_FUNC_CODE - || function == GRAPHICS_EXT_FUNC_CODE - || function == PLAINTEXT_EXT_FUNC_CODE - || function == APPLICATION_EXT_FUNC_CODE) - Private->gif89 = true; - } - } - for (i = 0; i < GifFile->ExtensionBlockCount; i++) { - int function = GifFile->ExtensionBlocks[i].Function; - - if (function == COMMENT_EXT_FUNC_CODE - || function == GRAPHICS_EXT_FUNC_CODE - || function == PLAINTEXT_EXT_FUNC_CODE - || function == APPLICATION_EXT_FUNC_CODE) - Private->gif89 = true; - } - - if (Private->gif89) - return GIF89_STAMP; - else - return GIF87_STAMP; +const char *EGifGetGifVersion(GifFileType *GifFile) { + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + int i, j; + + /* + * Bulletproofing - always write GIF89 if we need to. + * Note, we don't clear the gif89 flag here because + * users of the sequential API might have called EGifSetGifVersion() + * in order to set that flag. + */ + for (i = 0; i < GifFile->ImageCount; i++) { + for (j = 0; j < GifFile->SavedImages[i].ExtensionBlockCount; + j++) { + int function = + GifFile->SavedImages[i].ExtensionBlocks[j].Function; + + if (function == COMMENT_EXT_FUNC_CODE || + function == GRAPHICS_EXT_FUNC_CODE || + function == PLAINTEXT_EXT_FUNC_CODE || + function == APPLICATION_EXT_FUNC_CODE) { + Private->gif89 = true; + } + } + } + for (i = 0; i < GifFile->ExtensionBlockCount; i++) { + int function = GifFile->ExtensionBlocks[i].Function; + + if (function == COMMENT_EXT_FUNC_CODE || + function == GRAPHICS_EXT_FUNC_CODE || + function == PLAINTEXT_EXT_FUNC_CODE || + function == APPLICATION_EXT_FUNC_CODE) { + Private->gif89 = true; + } + } + + if (Private->gif89) { + return GIF89_STAMP; + } else { + return GIF87_STAMP; + } } /****************************************************************************** Set the GIF version. In the extremely unlikely event that there is ever - another version, replace the bool argument with an enum in which the + another version, replace the bool argument with an enum in which the GIF87 value is 0 (numerically the same as bool false) and the GIF89 value is 1 (numerically the same as bool true). That way we'll even preserve object-file compatibility! ******************************************************************************/ -void EGifSetGifVersion(GifFileType *GifFile, const bool gif89) -{ - GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; +void EGifSetGifVersion(GifFileType *GifFile, const bool gif89) { + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - Private->gif89 = gif89; + Private->gif89 = gif89; } /****************************************************************************** All writes to the GIF should go through this. ******************************************************************************/ -static int InternalWrite(GifFileType *GifFileOut, - const unsigned char *buf, size_t len) -{ - GifFilePrivateType *Private = (GifFilePrivateType*)GifFileOut->Private; - if (Private->Write) - return Private->Write(GifFileOut,buf,len); - else - return fwrite(buf, 1, len, Private->File); +static int InternalWrite(GifFileType *GifFileOut, const unsigned char *buf, + size_t len) { + GifFilePrivateType *Private = (GifFilePrivateType *)GifFileOut->Private; + if (Private->Write) { + return Private->Write(GifFileOut, buf, len); + } else { + return fwrite(buf, 1, len, Private->File); + } } /****************************************************************************** This routine should be called before any other EGif calls, immediately following the GIF file opening. ******************************************************************************/ -int -EGifPutScreenDesc(GifFileType *GifFile, - const int Width, - const int Height, - const int ColorRes, - const int BackGround, - const ColorMapObject *ColorMap) -{ - GifByteType Buf[3]; - GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; - const char *write_version; - GifFile->SColorMap = NULL; - - if (Private->FileState & FILE_STATE_SCREEN) { - /* If already has screen descriptor - something is wrong! */ - GifFile->Error = E_GIF_ERR_HAS_SCRN_DSCR; - return GIF_ERROR; - } - if (!IS_WRITEABLE(Private)) { - /* This file was NOT open for writing: */ - GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; - return GIF_ERROR; - } - - write_version = EGifGetGifVersion(GifFile); - - /* First write the version prefix into the file. */ - if (InternalWrite(GifFile, (unsigned char *)write_version, - strlen(write_version)) != strlen(write_version)) { - GifFile->Error = E_GIF_ERR_WRITE_FAILED; - return GIF_ERROR; - } - - GifFile->SWidth = Width; - GifFile->SHeight = Height; - GifFile->SColorResolution = ColorRes; - GifFile->SBackGroundColor = BackGround; - if (ColorMap) { - GifFile->SColorMap = GifMakeMapObject(ColorMap->ColorCount, - ColorMap->Colors); - if (GifFile->SColorMap == NULL) { - GifFile->Error = E_GIF_ERR_NOT_ENOUGH_MEM; - return GIF_ERROR; - } - } else - GifFile->SColorMap = NULL; - - /* - * Put the logical screen descriptor into the file: - */ - /* Logical Screen Descriptor: Dimensions */ - (void)EGifPutWord(Width, GifFile); - (void)EGifPutWord(Height, GifFile); - - /* Logical Screen Descriptor: Packed Fields */ - /* Note: We have actual size of the color table default to the largest - * possible size (7+1 == 8 bits) because the decoder can use it to decide - * how to display the files. - */ - Buf[0] = (ColorMap ? 0x80 : 0x00) | /* Yes/no global colormap */ - ((ColorRes - 1) << 4) | /* Bits allocated to each primary color */ - (ColorMap ? ColorMap->BitsPerPixel - 1 : 0x07 ); /* Actual size of the - color table. */ - if (ColorMap != NULL && ColorMap->SortFlag) - Buf[0] |= 0x08; - Buf[1] = BackGround; /* Index into the ColorTable for background color */ - Buf[2] = GifFile->AspectByte; /* Pixel Aspect Ratio */ - InternalWrite(GifFile, Buf, 3); - - /* If we have Global color map - dump it also: */ - if (ColorMap != NULL) { - int i; - for (i = 0; i < ColorMap->ColorCount; i++) { - /* Put the ColorMap out also: */ - Buf[0] = ColorMap->Colors[i].Red; - Buf[1] = ColorMap->Colors[i].Green; - Buf[2] = ColorMap->Colors[i].Blue; - if (InternalWrite(GifFile, Buf, 3) != 3) { - GifFile->Error = E_GIF_ERR_WRITE_FAILED; - return GIF_ERROR; - } - } - } - - /* Mark this file as has screen descriptor, and no pixel written yet: */ - Private->FileState |= FILE_STATE_SCREEN; - - return GIF_OK; +int EGifPutScreenDesc(GifFileType *GifFile, const int Width, const int Height, + const int ColorRes, const int BackGround, + const ColorMapObject *ColorMap) { + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + const char *write_version; + GifFile->SColorMap = NULL; + + if (Private->FileState & FILE_STATE_SCREEN) { + /* If already has screen descriptor - something is wrong! */ + GifFile->Error = E_GIF_ERR_HAS_SCRN_DSCR; + return GIF_ERROR; + } + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + write_version = EGifGetGifVersion(GifFile); + + /* First write the version prefix into the file. */ + if (InternalWrite(GifFile, (unsigned char *)write_version, + strlen(write_version)) != strlen(write_version)) { + GifFile->Error = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + + GifFile->SWidth = Width; + GifFile->SHeight = Height; + GifFile->SColorResolution = ColorRes; + GifFile->SBackGroundColor = BackGround; + if (ColorMap) { + GifFile->SColorMap = + GifMakeMapObject(ColorMap->ColorCount, ColorMap->Colors); + if (GifFile->SColorMap == NULL) { + GifFile->Error = E_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } else { + GifFile->SColorMap = NULL; + } + + /* + * Put the logical screen descriptor into the file: + */ + /* Logical Screen Descriptor: Dimensions */ + (void)EGifPutWord(Width, GifFile); + (void)EGifPutWord(Height, GifFile); + + /* Logical Screen Descriptor: Packed Fields */ + /* Note: We have actual size of the color table default to the largest + * possible size (7+1 == 8 bits) because the decoder can use it to + * decide how to display the files. + */ + Buf[0] = + (ColorMap ? 0x80 : 0x00) | /* Yes/no global colormap */ + ((ColorRes - 1) << 4) | /* Bits allocated to each primary color */ + (ColorMap ? ColorMap->BitsPerPixel - 1 + : 0x07); /* Actual size of the + color table. */ + if (ColorMap != NULL && ColorMap->SortFlag) { + Buf[0] |= 0x08; + } + Buf[1] = + BackGround; /* Index into the ColorTable for background color */ + Buf[2] = GifFile->AspectByte; /* Pixel Aspect Ratio */ + InternalWrite(GifFile, Buf, 3); + + /* If we have Global color map - dump it also: */ + if (ColorMap != NULL) { + int i; + for (i = 0; i < ColorMap->ColorCount; i++) { + /* Put the ColorMap out also: */ + Buf[0] = ColorMap->Colors[i].Red; + Buf[1] = ColorMap->Colors[i].Green; + Buf[2] = ColorMap->Colors[i].Blue; + if (InternalWrite(GifFile, Buf, 3) != 3) { + GifFile->Error = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + } + } + + /* Mark this file as has screen descriptor, and no pixel written yet: */ + Private->FileState |= FILE_STATE_SCREEN; + + return GIF_OK; } /****************************************************************************** This routine should be called before any attempt to dump an image - any call to any of the pixel dump routines. ******************************************************************************/ -int -EGifPutImageDesc(GifFileType *GifFile, - const int Left, - const int Top, - const int Width, - const int Height, - const bool Interlace, - const ColorMapObject *ColorMap) -{ - GifByteType Buf[3]; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - - if (Private->FileState & FILE_STATE_IMAGE && - Private->PixelCount > 0xffff0000UL) { - /* If already has active image descriptor - something is wrong! */ - GifFile->Error = E_GIF_ERR_HAS_IMAG_DSCR; - return GIF_ERROR; - } - if (!IS_WRITEABLE(Private)) { - /* This file was NOT open for writing: */ - GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; - return GIF_ERROR; - } - GifFile->Image.Left = Left; - GifFile->Image.Top = Top; - GifFile->Image.Width = Width; - GifFile->Image.Height = Height; - GifFile->Image.Interlace = Interlace; - if (ColorMap != GifFile->Image.ColorMap) { - if (ColorMap) { - if (GifFile->Image.ColorMap != NULL) { - GifFreeMapObject(GifFile->Image.ColorMap); - GifFile->Image.ColorMap = NULL; - } - GifFile->Image.ColorMap = GifMakeMapObject(ColorMap->ColorCount, - ColorMap->Colors); - if (GifFile->Image.ColorMap == NULL) { - GifFile->Error = E_GIF_ERR_NOT_ENOUGH_MEM; +int EGifPutImageDesc(GifFileType *GifFile, const int Left, const int Top, + const int Width, const int Height, const bool Interlace, + const ColorMapObject *ColorMap) { + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (Private->FileState & FILE_STATE_IMAGE && + Private->PixelCount > 0xffff0000UL) { + /* If already has active image descriptor - something is wrong! + */ + GifFile->Error = E_GIF_ERR_HAS_IMAG_DSCR; return GIF_ERROR; - } - } else { - GifFile->Image.ColorMap = NULL; } - } - - /* Put the image descriptor into the file: */ - Buf[0] = DESCRIPTOR_INTRODUCER; /* Image separator character. */ - InternalWrite(GifFile, Buf, 1); - (void)EGifPutWord(Left, GifFile); - (void)EGifPutWord(Top, GifFile); - (void)EGifPutWord(Width, GifFile); - (void)EGifPutWord(Height, GifFile); - Buf[0] = (ColorMap ? 0x80 : 0x00) | - (Interlace ? 0x40 : 0x00) | - (ColorMap ? ColorMap->BitsPerPixel - 1 : 0); - InternalWrite(GifFile, Buf, 1); - - /* If we have Global color map - dump it also: */ - if (ColorMap != NULL) { - int i; - for (i = 0; i < ColorMap->ColorCount; i++) { - /* Put the ColorMap out also: */ - Buf[0] = ColorMap->Colors[i].Red; - Buf[1] = ColorMap->Colors[i].Green; - Buf[2] = ColorMap->Colors[i].Blue; - if (InternalWrite(GifFile, Buf, 3) != 3) { - GifFile->Error = E_GIF_ERR_WRITE_FAILED; - return GIF_ERROR; - } - } - } - if (GifFile->SColorMap == NULL && GifFile->Image.ColorMap == NULL) { - GifFile->Error = E_GIF_ERR_NO_COLOR_MAP; - return GIF_ERROR; - } - - /* Mark this file as has screen descriptor: */ - Private->FileState |= FILE_STATE_IMAGE; - Private->PixelCount = (long)Width *(long)Height; - - /* Reset compress algorithm parameters. */ - (void)EGifSetupCompress(GifFile); - - return GIF_OK; + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + GifFile->Image.Left = Left; + GifFile->Image.Top = Top; + GifFile->Image.Width = Width; + GifFile->Image.Height = Height; + GifFile->Image.Interlace = Interlace; + if (ColorMap != GifFile->Image.ColorMap) { + if (ColorMap) { + if (GifFile->Image.ColorMap != NULL) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + GifFile->Image.ColorMap = GifMakeMapObject( + ColorMap->ColorCount, ColorMap->Colors); + if (GifFile->Image.ColorMap == NULL) { + GifFile->Error = E_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } else { + GifFile->Image.ColorMap = NULL; + } + } + + /* Put the image descriptor into the file: */ + Buf[0] = DESCRIPTOR_INTRODUCER; /* Image separator character. */ + InternalWrite(GifFile, Buf, 1); + (void)EGifPutWord(Left, GifFile); + (void)EGifPutWord(Top, GifFile); + (void)EGifPutWord(Width, GifFile); + (void)EGifPutWord(Height, GifFile); + Buf[0] = (ColorMap ? 0x80 : 0x00) | (Interlace ? 0x40 : 0x00) | + (ColorMap ? ColorMap->BitsPerPixel - 1 : 0); + InternalWrite(GifFile, Buf, 1); + + /* If we have Global color map - dump it also: */ + if (ColorMap != NULL) { + int i; + for (i = 0; i < ColorMap->ColorCount; i++) { + /* Put the ColorMap out also: */ + Buf[0] = ColorMap->Colors[i].Red; + Buf[1] = ColorMap->Colors[i].Green; + Buf[2] = ColorMap->Colors[i].Blue; + if (InternalWrite(GifFile, Buf, 3) != 3) { + GifFile->Error = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + } + } + if (GifFile->SColorMap == NULL && GifFile->Image.ColorMap == NULL) { + GifFile->Error = E_GIF_ERR_NO_COLOR_MAP; + return GIF_ERROR; + } + + /* Mark this file as has screen descriptor: */ + Private->FileState |= FILE_STATE_IMAGE; + Private->PixelCount = (long)Width * (long)Height; + + /* Reset compress algorithm parameters. */ + (void)EGifSetupCompress(GifFile); + + return GIF_OK; } /****************************************************************************** Put one full scanned line (Line) of length LineLen into GIF file. ******************************************************************************/ -int -EGifPutLine(GifFileType * GifFile, GifPixelType *Line, int LineLen) -{ - int i; - GifPixelType Mask; - GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; - - if (!IS_WRITEABLE(Private)) { - /* This file was NOT open for writing: */ - GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; - return GIF_ERROR; - } - - if (!LineLen) - LineLen = GifFile->Image.Width; - if (Private->PixelCount < (unsigned)LineLen) { - GifFile->Error = E_GIF_ERR_DATA_TOO_BIG; - return GIF_ERROR; - } - Private->PixelCount -= LineLen; - - /* Make sure the codes are not out of bit range, as we might generate - * wrong code (because of overflow when we combine them) in this case: */ - Mask = CodeMask[Private->BitsPerPixel]; - for (i = 0; i < LineLen; i++) - Line[i] &= Mask; - - return EGifCompressLine(GifFile, Line, LineLen); +int EGifPutLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) { + int i; + GifPixelType Mask; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + if (!LineLen) { + LineLen = GifFile->Image.Width; + } + if (Private->PixelCount < (unsigned)LineLen) { + GifFile->Error = E_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + Private->PixelCount -= LineLen; + + /* Make sure the codes are not out of bit range, as we might generate + * wrong code (because of overflow when we combine them) in this case: + */ + Mask = CodeMask[Private->BitsPerPixel]; + for (i = 0; i < LineLen; i++) { + Line[i] &= Mask; + } + + return EGifCompressLine(GifFile, Line, LineLen); } /****************************************************************************** Put one pixel (Pixel) into GIF file. ******************************************************************************/ -int -EGifPutPixel(GifFileType *GifFile, GifPixelType Pixel) -{ - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - - if (!IS_WRITEABLE(Private)) { - /* This file was NOT open for writing: */ - GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; - return GIF_ERROR; - } - - if (Private->PixelCount == 0) { - GifFile->Error = E_GIF_ERR_DATA_TOO_BIG; - return GIF_ERROR; - } - --Private->PixelCount; - - /* Make sure the code is not out of bit range, as we might generate - * wrong code (because of overflow when we combine them) in this case: */ - Pixel &= CodeMask[Private->BitsPerPixel]; - - return EGifCompressLine(GifFile, &Pixel, 1); +int EGifPutPixel(GifFileType *GifFile, GifPixelType Pixel) { + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + if (Private->PixelCount == 0) { + GifFile->Error = E_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + --Private->PixelCount; + + /* Make sure the code is not out of bit range, as we might generate + * wrong code (because of overflow when we combine them) in this case: + */ + Pixel &= CodeMask[Private->BitsPerPixel]; + + return EGifCompressLine(GifFile, &Pixel, 1); } /****************************************************************************** Put a comment into GIF file using the GIF89 comment extension block. ******************************************************************************/ -int -EGifPutComment(GifFileType *GifFile, const char *Comment) -{ - unsigned int length; - char *buf; - - length = strlen(Comment); - if (length <= 255) { - return EGifPutExtension(GifFile, COMMENT_EXT_FUNC_CODE, - length, Comment); - } else { - buf = (char *)Comment; - if (EGifPutExtensionLeader(GifFile, COMMENT_EXT_FUNC_CODE) - == GIF_ERROR) { - return GIF_ERROR; - } - - /* Break the comment into 255 byte sub blocks */ - while (length > 255) { - if (EGifPutExtensionBlock(GifFile, 255, buf) == GIF_ERROR) { - return GIF_ERROR; - } - buf = buf + 255; - length -= 255; - } - /* Output any partial block and the clear code. */ - if (length > 0) { - if (EGifPutExtensionBlock(GifFile, length, buf) == GIF_ERROR) { - return GIF_ERROR; - } - } - if (EGifPutExtensionTrailer(GifFile) == GIF_ERROR) { - return GIF_ERROR; - } - } - return GIF_OK; +int EGifPutComment(GifFileType *GifFile, const char *Comment) { + unsigned int length; + char *buf; + + length = strlen(Comment); + if (length <= 255) { + return EGifPutExtension(GifFile, COMMENT_EXT_FUNC_CODE, length, + Comment); + } else { + buf = (char *)Comment; + if (EGifPutExtensionLeader(GifFile, COMMENT_EXT_FUNC_CODE) == + GIF_ERROR) { + return GIF_ERROR; + } + + /* Break the comment into 255 byte sub blocks */ + while (length > 255) { + if (EGifPutExtensionBlock(GifFile, 255, buf) == + GIF_ERROR) { + return GIF_ERROR; + } + buf = buf + 255; + length -= 255; + } + /* Output any partial block and the clear code. */ + if (length > 0) { + if (EGifPutExtensionBlock(GifFile, length, buf) == + GIF_ERROR) { + return GIF_ERROR; + } + } + if (EGifPutExtensionTrailer(GifFile) == GIF_ERROR) { + return GIF_ERROR; + } + } + return GIF_OK; } /****************************************************************************** @@ -552,69 +550,63 @@ EGifPutComment(GifFileType *GifFile, const char *Comment) extensions can be dumped using EGifPutExtensionBlock until EGifPutExtensionTrailer is invoked. ******************************************************************************/ -int -EGifPutExtensionLeader(GifFileType *GifFile, const int ExtCode) -{ - GifByteType Buf[3]; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - - if (!IS_WRITEABLE(Private)) { - /* This file was NOT open for writing: */ - GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; - return GIF_ERROR; - } - - Buf[0] = EXTENSION_INTRODUCER; - Buf[1] = ExtCode; - InternalWrite(GifFile, Buf, 2); - - return GIF_OK; +int EGifPutExtensionLeader(GifFileType *GifFile, const int ExtCode) { + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + Buf[0] = EXTENSION_INTRODUCER; + Buf[1] = ExtCode; + InternalWrite(GifFile, Buf, 2); + + return GIF_OK; } /****************************************************************************** Put extension block data (see GIF manual) into a GIF file. ******************************************************************************/ -int -EGifPutExtensionBlock(GifFileType *GifFile, - const int ExtLen, - const void *Extension) -{ - GifByteType Buf; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - - if (!IS_WRITEABLE(Private)) { - /* This file was NOT open for writing: */ - GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; - return GIF_ERROR; - } - - Buf = ExtLen; - InternalWrite(GifFile, &Buf, 1); - InternalWrite(GifFile, Extension, ExtLen); - - return GIF_OK; +int EGifPutExtensionBlock(GifFileType *GifFile, const int ExtLen, + const void *Extension) { + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + Buf = ExtLen; + InternalWrite(GifFile, &Buf, 1); + InternalWrite(GifFile, Extension, ExtLen); + + return GIF_OK; } /****************************************************************************** Put a terminating block (see GIF manual) into a GIF file. ******************************************************************************/ -int -EGifPutExtensionTrailer(GifFileType *GifFile) { +int EGifPutExtensionTrailer(GifFileType *GifFile) { - GifByteType Buf; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - if (!IS_WRITEABLE(Private)) { - /* This file was NOT open for writing: */ - GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; - return GIF_ERROR; - } + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } - /* Write the block terminator */ - Buf = 0; - InternalWrite(GifFile, &Buf, 1); + /* Write the block terminator */ + Buf = 0; + InternalWrite(GifFile, &Buf, 1); - return GIF_OK; + return GIF_OK; } /****************************************************************************** @@ -623,34 +615,31 @@ EGifPutExtensionTrailer(GifFileType *GifFile) { most one subblock. Extensions with more than one subblock need to use the EGifPutExtension{Leader,Block,Trailer} functions instead. ******************************************************************************/ -int -EGifPutExtension(GifFileType *GifFile, - const int ExtCode, - const int ExtLen, - const void *Extension) { - - GifByteType Buf[3]; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - - if (!IS_WRITEABLE(Private)) { - /* This file was NOT open for writing: */ - GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; - return GIF_ERROR; - } - - if (ExtCode == 0) - InternalWrite(GifFile, (GifByteType *)&ExtLen, 1); - else { - Buf[0] = EXTENSION_INTRODUCER; - Buf[1] = ExtCode; /* Extension Label */ - Buf[2] = ExtLen; /* Extension length */ - InternalWrite(GifFile, Buf, 3); - } - InternalWrite(GifFile, Extension, ExtLen); - Buf[0] = 0; - InternalWrite(GifFile, Buf, 1); - - return GIF_OK; +int EGifPutExtension(GifFileType *GifFile, const int ExtCode, const int ExtLen, + const void *Extension) { + + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + if (ExtCode == 0) { + InternalWrite(GifFile, (GifByteType *)&ExtLen, 1); + } else { + Buf[0] = EXTENSION_INTRODUCER; + Buf[1] = ExtCode; /* Extension Label */ + Buf[2] = ExtLen; /* Extension length */ + InternalWrite(GifFile, Buf, 3); + } + InternalWrite(GifFile, Extension, ExtLen); + Buf[0] = 0; + InternalWrite(GifFile, Buf, 1); + + return GIF_OK; } /****************************************************************************** @@ -658,49 +647,52 @@ EGifPutExtension(GifFileType *GifFile, ******************************************************************************/ size_t EGifGCBToExtension(const GraphicsControlBlock *GCB, - GifByteType *GifExtension) -{ - GifExtension[0] = 0; - GifExtension[0] |= (GCB->TransparentColor == NO_TRANSPARENT_COLOR) ? 0x00 : 0x01; - GifExtension[0] |= GCB->UserInputFlag ? 0x02 : 0x00; - GifExtension[0] |= ((GCB->DisposalMode & 0x07) << 2); - GifExtension[1] = LOBYTE(GCB->DelayTime); - GifExtension[2] = HIBYTE(GCB->DelayTime); - GifExtension[3] = (char)GCB->TransparentColor; - return 4; + GifByteType *GifExtension) { + GifExtension[0] = 0; + GifExtension[0] |= + (GCB->TransparentColor == NO_TRANSPARENT_COLOR) ? 0x00 : 0x01; + GifExtension[0] |= GCB->UserInputFlag ? 0x02 : 0x00; + GifExtension[0] |= ((GCB->DisposalMode & 0x07) << 2); + GifExtension[1] = LOBYTE(GCB->DelayTime); + GifExtension[2] = HIBYTE(GCB->DelayTime); + GifExtension[3] = (char)GCB->TransparentColor; + return 4; } /****************************************************************************** Replace the Graphics Control Block for a saved image, if it exists. ******************************************************************************/ -int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB, - GifFileType *GifFile, int ImageIndex) -{ - int i; - size_t Len; - GifByteType buf[sizeof(GraphicsControlBlock)]; /* a bit dodgy... */ - - if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) - return GIF_ERROR; - - for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; i++) { - ExtensionBlock *ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i]; - if (ep->Function == GRAPHICS_EXT_FUNC_CODE) { - EGifGCBToExtension(GCB, ep->Bytes); - return GIF_OK; +int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB, + GifFileType *GifFile, int ImageIndex) { + int i; + size_t Len; + GifByteType buf[sizeof(GraphicsControlBlock)]; /* a bit dodgy... */ + + if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) { + return GIF_ERROR; + } + + for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; + i++) { + ExtensionBlock *ep = + &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i]; + if (ep->Function == GRAPHICS_EXT_FUNC_CODE) { + EGifGCBToExtension(GCB, ep->Bytes); + return GIF_OK; + } } - } - Len = EGifGCBToExtension(GCB, (GifByteType *)buf); - if (GifAddExtensionBlock(&GifFile->SavedImages[ImageIndex].ExtensionBlockCount, - &GifFile->SavedImages[ImageIndex].ExtensionBlocks, - GRAPHICS_EXT_FUNC_CODE, - Len, - (unsigned char *)buf) == GIF_ERROR) - return (GIF_ERROR); + Len = EGifGCBToExtension(GCB, (GifByteType *)buf); + if (GifAddExtensionBlock( + &GifFile->SavedImages[ImageIndex].ExtensionBlockCount, + &GifFile->SavedImages[ImageIndex].ExtensionBlocks, + GRAPHICS_EXT_FUNC_CODE, Len, + (unsigned char *)buf) == GIF_ERROR) { + return (GIF_ERROR); + } - return (GIF_OK); + return (GIF_OK); } /****************************************************************************** @@ -710,27 +702,26 @@ int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB, to EGifPutCodeNext, until NULL block is given. The block should NOT be freed by the user (not dynamically allocated). ******************************************************************************/ -int -EGifPutCode(GifFileType *GifFile, int CodeSize, const GifByteType *CodeBlock) -{ - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - - if (!IS_WRITEABLE(Private)) { - /* This file was NOT open for writing: */ - GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; - return GIF_ERROR; - } - - /* No need to dump code size as Compression set up does any for us: */ - /* - * Buf = CodeSize; - * if (InternalWrite(GifFile, &Buf, 1) != 1) { - * GifFile->Error = E_GIF_ERR_WRITE_FAILED; - * return GIF_ERROR; - * } - */ - - return EGifPutCodeNext(GifFile, CodeBlock); +int EGifPutCode(GifFileType *GifFile, int CodeSize, + const GifByteType *CodeBlock) { + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + GifFile->Error = E_GIF_ERR_NOT_WRITEABLE; + return GIF_ERROR; + } + + /* No need to dump code size as Compression set up does any for us: */ + /* + * Buf = CodeSize; + * if (InternalWrite(GifFile, &Buf, 1) != 1) { + * GifFile->Error = E_GIF_ERR_WRITE_FAILED; + * return GIF_ERROR; + * } + */ + + return EGifPutCodeNext(GifFile, CodeBlock); } /****************************************************************************** @@ -738,146 +729,142 @@ EGifPutCode(GifFileType *GifFile, int CodeSize, const GifByteType *CodeBlock) called with blocks of code as read via DGifGetCode/DGifGetCodeNext. If given buffer pointer is NULL, empty block is written to mark end of code. ******************************************************************************/ -int -EGifPutCodeNext(GifFileType *GifFile, const GifByteType *CodeBlock) -{ - GifByteType Buf; - GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; - - if (CodeBlock != NULL) { - if (InternalWrite(GifFile, CodeBlock, CodeBlock[0] + 1) - != (unsigned)(CodeBlock[0] + 1)) { - GifFile->Error = E_GIF_ERR_WRITE_FAILED; - return GIF_ERROR; - } - } else { - Buf = 0; - if (InternalWrite(GifFile, &Buf, 1) != 1) { - GifFile->Error = E_GIF_ERR_WRITE_FAILED; - return GIF_ERROR; - } - Private->PixelCount = 0; /* And local info. indicate image read. */ - } - - return GIF_OK; +int EGifPutCodeNext(GifFileType *GifFile, const GifByteType *CodeBlock) { + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (CodeBlock != NULL) { + if (InternalWrite(GifFile, CodeBlock, CodeBlock[0] + 1) != + (unsigned)(CodeBlock[0] + 1)) { + GifFile->Error = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + } else { + Buf = 0; + if (InternalWrite(GifFile, &Buf, 1) != 1) { + GifFile->Error = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + Private->PixelCount = + 0; /* And local info. indicate image read. */ + } + + return GIF_OK; } /****************************************************************************** This routine should be called last, to close the GIF file. ******************************************************************************/ -int -EGifCloseFile(GifFileType *GifFile, int *ErrorCode) -{ - GifByteType Buf; - GifFilePrivateType *Private; - FILE *File; - - if (GifFile == NULL) - return GIF_ERROR; - - Private = (GifFilePrivateType *) GifFile->Private; - if (Private == NULL) - return GIF_ERROR; - else if (!IS_WRITEABLE(Private)) { - /* This file was NOT open for writing: */ - if (ErrorCode != NULL) - *ErrorCode = E_GIF_ERR_NOT_WRITEABLE; - free(GifFile); - return GIF_ERROR; - } else { - //cppcheck-suppress nullPointerRedundantCheck - File = Private->File; - - Buf = TERMINATOR_INTRODUCER; - InternalWrite(GifFile, &Buf, 1); +int EGifCloseFile(GifFileType *GifFile, int *ErrorCode) { + GifByteType Buf; + GifFilePrivateType *Private; + FILE *File; - if (GifFile->Image.ColorMap) { - GifFreeMapObject(GifFile->Image.ColorMap); - GifFile->Image.ColorMap = NULL; - } - if (GifFile->SColorMap) { - GifFreeMapObject(GifFile->SColorMap); - GifFile->SColorMap = NULL; - } - if (Private) { - if (Private->HashTable) { - free((char *) Private->HashTable); - } - free((char *) Private); + if (GifFile == NULL) { + return GIF_ERROR; } - if (File && fclose(File) != 0) { - if (ErrorCode != NULL) - *ErrorCode = E_GIF_ERR_CLOSE_FAILED; - free(GifFile); - return GIF_ERROR; - } + Private = (GifFilePrivateType *)GifFile->Private; + if (Private == NULL) { + return GIF_ERROR; + } else if (!IS_WRITEABLE(Private)) { + /* This file was NOT open for writing: */ + if (ErrorCode != NULL) { + *ErrorCode = E_GIF_ERR_NOT_WRITEABLE; + } + free(GifFile); + return GIF_ERROR; + } else { + File = Private->File; + + Buf = TERMINATOR_INTRODUCER; + InternalWrite(GifFile, &Buf, 1); + + if (GifFile->Image.ColorMap) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + if (GifFile->SColorMap) { + GifFreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + } + if (Private->HashTable) { + free((char *)Private->HashTable); + } + free((char *)Private); + + if (File && fclose(File) != 0) { + if (ErrorCode != NULL) { + *ErrorCode = E_GIF_ERR_CLOSE_FAILED; + } + free(GifFile); + return GIF_ERROR; + } - free(GifFile); - if (ErrorCode != NULL) - *ErrorCode = E_GIF_SUCCEEDED; - } - return GIF_OK; + free(GifFile); + if (ErrorCode != NULL) { + *ErrorCode = E_GIF_SUCCEEDED; + } + } + return GIF_OK; } /****************************************************************************** Put 2 bytes (a word) into the given file in little-endian order: ******************************************************************************/ -static int -EGifPutWord(int Word, GifFileType *GifFile) -{ - unsigned char c[2]; - - c[0] = LOBYTE(Word); - c[1] = HIBYTE(Word); - if (InternalWrite(GifFile, c, 2) == 2) - return GIF_OK; - else - return GIF_ERROR; +static int EGifPutWord(int Word, GifFileType *GifFile) { + unsigned char c[2]; + + c[0] = LOBYTE(Word); + c[1] = HIBYTE(Word); + if (InternalWrite(GifFile, c, 2) == 2) { + return GIF_OK; + } else { + return GIF_ERROR; + } } /****************************************************************************** Setup the LZ compression for this image: ******************************************************************************/ -static int -EGifSetupCompress(GifFileType *GifFile) -{ - int BitsPerPixel; - GifByteType Buf; - GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; - - /* Test and see what color map to use, and from it # bits per pixel: */ - if (GifFile->Image.ColorMap) - BitsPerPixel = GifFile->Image.ColorMap->BitsPerPixel; - else if (GifFile->SColorMap) - BitsPerPixel = GifFile->SColorMap->BitsPerPixel; - else { - GifFile->Error = E_GIF_ERR_NO_COLOR_MAP; - return GIF_ERROR; - } - - Buf = BitsPerPixel = (BitsPerPixel < 2 ? 2 : BitsPerPixel); - InternalWrite(GifFile, &Buf, 1); /* Write the Code size to file. */ - - Private->Buf[0] = 0; /* Nothing was output yet. */ - Private->BitsPerPixel = BitsPerPixel; - Private->ClearCode = (1 << BitsPerPixel); - Private->EOFCode = Private->ClearCode + 1; - Private->RunningCode = Private->EOFCode + 1; - Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */ - Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */ - Private->CrntCode = FIRST_CODE; /* Signal that this is first one! */ - Private->CrntShiftState = 0; /* No information in CrntShiftDWord. */ - Private->CrntShiftDWord = 0; - - /* Clear hash table and send Clear to make sure the decoder do the same. */ - _ClearHashTable(Private->HashTable); - - if (EGifCompressOutput(GifFile, Private->ClearCode) == GIF_ERROR) { - GifFile->Error = E_GIF_ERR_DISK_IS_FULL; - return GIF_ERROR; - } - return GIF_OK; +static int EGifSetupCompress(GifFileType *GifFile) { + int BitsPerPixel; + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + /* Test and see what color map to use, and from it # bits per pixel: */ + if (GifFile->Image.ColorMap) { + BitsPerPixel = GifFile->Image.ColorMap->BitsPerPixel; + } else if (GifFile->SColorMap) { + BitsPerPixel = GifFile->SColorMap->BitsPerPixel; + } else { + GifFile->Error = E_GIF_ERR_NO_COLOR_MAP; + return GIF_ERROR; + } + + Buf = BitsPerPixel = (BitsPerPixel < 2 ? 2 : BitsPerPixel); + InternalWrite(GifFile, &Buf, 1); /* Write the Code size to file. */ + + Private->Buf[0] = 0; /* Nothing was output yet. */ + Private->BitsPerPixel = BitsPerPixel; + Private->ClearCode = (1 << BitsPerPixel); + Private->EOFCode = Private->ClearCode + 1; + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */ + Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */ + Private->CrntCode = FIRST_CODE; /* Signal that this is first one! */ + Private->CrntShiftState = 0; /* No information in CrntShiftDWord. */ + Private->CrntShiftDWord = 0; + + /* Clear hash table and send Clear to make sure the decoder do the same. + */ + _ClearHashTable(Private->HashTable); + + if (EGifCompressOutput(GifFile, Private->ClearCode) == GIF_ERROR) { + GifFile->Error = E_GIF_ERR_DISK_IS_FULL; + return GIF_ERROR; + } + return GIF_OK; } /****************************************************************************** @@ -886,86 +873,91 @@ EGifSetupCompress(GifFileType *GifFile) This routine can be called a few times (one per scan line, for example), in order to complete the whole image. ******************************************************************************/ -static int -EGifCompressLine(GifFileType *GifFile, - GifPixelType *Line, - const int LineLen) -{ - int i = 0, CrntCode; - GifHashTableType *HashTable; - GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; - - HashTable = Private->HashTable; - - if (Private->CrntCode == FIRST_CODE) /* Its first time! */ - CrntCode = Line[i++]; - else - CrntCode = Private->CrntCode; /* Get last code in compression. */ - - while (i < LineLen) { /* Decode LineLen items. */ - GifPixelType Pixel = Line[i++]; /* Get next pixel from stream. */ - /* Form a new unique key to search hash table for the code combines - * CrntCode as Prefix string with Pixel as postfix char. - */ - int NewCode; - unsigned long NewKey = (((uint32_t) CrntCode) << 8) + Pixel; - if ((NewCode = _ExistsHashTable(HashTable, NewKey)) >= 0) { - /* This Key is already there, or the string is old one, so - * simple take new code as our CrntCode: - */ - CrntCode = NewCode; - } else { - /* Put it in hash table, output the prefix code, and make our - * CrntCode equal to Pixel. - */ - if (EGifCompressOutput(GifFile, CrntCode) == GIF_ERROR) { - GifFile->Error = E_GIF_ERR_DISK_IS_FULL; - return GIF_ERROR; - } - CrntCode = Pixel; - - /* If however the HashTable if full, we send a clear first and - * Clear the hash table. - */ - if (Private->RunningCode >= LZ_MAX_CODE) { - /* Time to do some clearance: */ - if (EGifCompressOutput(GifFile, Private->ClearCode) - == GIF_ERROR) { - GifFile->Error = E_GIF_ERR_DISK_IS_FULL; - return GIF_ERROR; - } - Private->RunningCode = Private->EOFCode + 1; - Private->RunningBits = Private->BitsPerPixel + 1; - Private->MaxCode1 = 1 << Private->RunningBits; - _ClearHashTable(HashTable); - } else { - /* Put this unique key with its relative Code in hash table: */ - _InsertHashTable(HashTable, NewKey, Private->RunningCode++); - } - } - - } - - /* Preserve the current state of the compression algorithm: */ - Private->CrntCode = CrntCode; - - if (Private->PixelCount == 0) { - /* We are done - output last Code and flush output buffers: */ - if (EGifCompressOutput(GifFile, CrntCode) == GIF_ERROR) { - GifFile->Error = E_GIF_ERR_DISK_IS_FULL; - return GIF_ERROR; - } - if (EGifCompressOutput(GifFile, Private->EOFCode) == GIF_ERROR) { - GifFile->Error = E_GIF_ERR_DISK_IS_FULL; - return GIF_ERROR; - } - if (EGifCompressOutput(GifFile, FLUSH_OUTPUT) == GIF_ERROR) { - GifFile->Error = E_GIF_ERR_DISK_IS_FULL; - return GIF_ERROR; - } - } - - return GIF_OK; +static int EGifCompressLine(GifFileType *GifFile, const GifPixelType *Line, + const int LineLen) { + int i = 0, CrntCode; + GifHashTableType *HashTable; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + HashTable = Private->HashTable; + + if (Private->CrntCode == FIRST_CODE) { /* Its first time! */ + CrntCode = Line[i++]; + } else { + CrntCode = + Private->CrntCode; /* Get last code in compression. */ + } + while (i < LineLen) { /* Decode LineLen items. */ + GifPixelType Pixel = + Line[i++]; /* Get next pixel from stream. */ + /* Form a new unique key to search hash table for the code + * combines CrntCode as Prefix string with Pixel as postfix + * char. + */ + int NewCode; + unsigned long NewKey = (((uint32_t)CrntCode) << 8) + Pixel; + if ((NewCode = _ExistsHashTable(HashTable, NewKey)) >= 0) { + /* This Key is already there, or the string is old one, + * so simple take new code as our CrntCode: + */ + CrntCode = NewCode; + } else { + /* Put it in hash table, output the prefix code, and + * make our CrntCode equal to Pixel. + */ + if (EGifCompressOutput(GifFile, CrntCode) == + GIF_ERROR) { + GifFile->Error = E_GIF_ERR_DISK_IS_FULL; + return GIF_ERROR; + } + CrntCode = Pixel; + + /* If however the HashTable if full, we send a clear + * first and Clear the hash table. + */ + if (Private->RunningCode >= LZ_MAX_CODE) { + /* Time to do some clearance: */ + if (EGifCompressOutput(GifFile, + Private->ClearCode) == + GIF_ERROR) { + GifFile->Error = E_GIF_ERR_DISK_IS_FULL; + return GIF_ERROR; + } + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = + Private->BitsPerPixel + 1; + Private->MaxCode1 = 1 << Private->RunningBits; + _ClearHashTable(HashTable); + } else { + /* Put this unique key with its relative Code in + * hash table: */ + _InsertHashTable(HashTable, NewKey, + Private->RunningCode++); + } + } + } + + /* Preserve the current state of the compression algorithm: */ + Private->CrntCode = CrntCode; + + if (Private->PixelCount == 0) { + /* We are done - output last Code and flush output buffers: */ + if (EGifCompressOutput(GifFile, CrntCode) == GIF_ERROR) { + GifFile->Error = E_GIF_ERR_DISK_IS_FULL; + return GIF_ERROR; + } + if (EGifCompressOutput(GifFile, Private->EOFCode) == + GIF_ERROR) { + GifFile->Error = E_GIF_ERR_DISK_IS_FULL; + return GIF_ERROR; + } + if (EGifCompressOutput(GifFile, FLUSH_OUTPUT) == GIF_ERROR) { + GifFile->Error = E_GIF_ERR_DISK_IS_FULL; + return GIF_ERROR; + } + } + + return GIF_OK; } /****************************************************************************** @@ -974,46 +966,49 @@ EGifCompressLine(GifFileType *GifFile, 8 bits (bytes) packets. Returns GIF_OK if written successfully. ******************************************************************************/ -static int -EGifCompressOutput(GifFileType *GifFile, - const int Code) -{ - GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; - int retval = GIF_OK; - - if (Code == FLUSH_OUTPUT) { - while (Private->CrntShiftState > 0) { - /* Get Rid of what is left in DWord, and flush it. */ - if (EGifBufferedOutput(GifFile, Private->Buf, - Private->CrntShiftDWord & 0xff) == GIF_ERROR) - retval = GIF_ERROR; - Private->CrntShiftDWord >>= 8; - Private->CrntShiftState -= 8; - } - Private->CrntShiftState = 0; /* For next time. */ - if (EGifBufferedOutput(GifFile, Private->Buf, - FLUSH_OUTPUT) == GIF_ERROR) - retval = GIF_ERROR; - } else { - Private->CrntShiftDWord |= ((long)Code) << Private->CrntShiftState; - Private->CrntShiftState += Private->RunningBits; - while (Private->CrntShiftState >= 8) { - /* Dump out full bytes: */ - if (EGifBufferedOutput(GifFile, Private->Buf, - Private->CrntShiftDWord & 0xff) == GIF_ERROR) - retval = GIF_ERROR; - Private->CrntShiftDWord >>= 8; - Private->CrntShiftState -= 8; - } - } - - /* If code cannt fit into RunningBits bits, must raise its size. Note */ - /* however that codes above 4095 are used for special signaling. */ - if (Private->RunningCode >= Private->MaxCode1 && Code <= 4095) { - Private->MaxCode1 = 1 << ++Private->RunningBits; - } - - return retval; +static int EGifCompressOutput(GifFileType *GifFile, const int Code) { + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + int retval = GIF_OK; + + if (Code == FLUSH_OUTPUT) { + while (Private->CrntShiftState > 0) { + /* Get Rid of what is left in DWord, and flush it. */ + if (EGifBufferedOutput(GifFile, Private->Buf, + Private->CrntShiftDWord & + 0xff) == GIF_ERROR) { + retval = GIF_ERROR; + } + Private->CrntShiftDWord >>= 8; + Private->CrntShiftState -= 8; + } + Private->CrntShiftState = 0; /* For next time. */ + if (EGifBufferedOutput(GifFile, Private->Buf, FLUSH_OUTPUT) == + GIF_ERROR) { + retval = GIF_ERROR; + } + } else { + Private->CrntShiftDWord |= ((long)Code) + << Private->CrntShiftState; + Private->CrntShiftState += Private->RunningBits; + while (Private->CrntShiftState >= 8) { + /* Dump out full bytes: */ + if (EGifBufferedOutput(GifFile, Private->Buf, + Private->CrntShiftDWord & + 0xff) == GIF_ERROR) { + retval = GIF_ERROR; + } + Private->CrntShiftDWord >>= 8; + Private->CrntShiftState -= 8; + } + } + + /* If code cannt fit into RunningBits bits, must raise its size. Note */ + /* however that codes above 4095 are used for special signaling. */ + if (Private->RunningCode >= Private->MaxCode1 && Code <= 4095) { + Private->MaxCode1 = 1 << ++Private->RunningBits; + } + + return retval; } /****************************************************************************** @@ -1022,37 +1017,35 @@ EGifCompressOutput(GifFileType *GifFile, The buffer is Dumped with first byte as its size, as GIF format requires. Returns GIF_OK if written successfully. ******************************************************************************/ -static int -EGifBufferedOutput(GifFileType *GifFile, - GifByteType *Buf, - int c) -{ - if (c == FLUSH_OUTPUT) { - /* Flush everything out. */ - if (Buf[0] != 0 - && InternalWrite(GifFile, Buf, Buf[0] + 1) != (unsigned)(Buf[0] + 1)) { - GifFile->Error = E_GIF_ERR_WRITE_FAILED; - return GIF_ERROR; - } - /* Mark end of compressed data, by an empty block (see GIF doc): */ - Buf[0] = 0; - if (InternalWrite(GifFile, Buf, 1) != 1) { - GifFile->Error = E_GIF_ERR_WRITE_FAILED; - return GIF_ERROR; - } - } else { - if (Buf[0] == 255) { - /* Dump out this buffer - it is full: */ - if (InternalWrite(GifFile, Buf, Buf[0] + 1) != (unsigned)(Buf[0] + 1)) { - GifFile->Error = E_GIF_ERR_WRITE_FAILED; - return GIF_ERROR; - } - Buf[0] = 0; - } - Buf[++Buf[0]] = c; - } - - return GIF_OK; +static int EGifBufferedOutput(GifFileType *GifFile, GifByteType *Buf, int c) { + if (c == FLUSH_OUTPUT) { + /* Flush everything out. */ + if (Buf[0] != 0 && InternalWrite(GifFile, Buf, Buf[0] + 1) != + (unsigned)(Buf[0] + 1)) { + GifFile->Error = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + /* Mark end of compressed data, by an empty block (see GIF doc): + */ + Buf[0] = 0; + if (InternalWrite(GifFile, Buf, 1) != 1) { + GifFile->Error = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + } else { + if (Buf[0] == 255) { + /* Dump out this buffer - it is full: */ + if (InternalWrite(GifFile, Buf, Buf[0] + 1) != + (unsigned)(Buf[0] + 1)) { + GifFile->Error = E_GIF_ERR_WRITE_FAILED; + return GIF_ERROR; + } + Buf[0] = 0; + } + Buf[++Buf[0]] = c; + } + + return GIF_OK; } /****************************************************************************** @@ -1060,104 +1053,111 @@ EGifBufferedOutput(GifFileType *GifFile, created by DGifSlurp(). ******************************************************************************/ -static int -EGifWriteExtensions(GifFileType *GifFileOut, - ExtensionBlock *ExtensionBlocks, - int ExtensionBlockCount) -{ - if (ExtensionBlocks) { - int j; - - for (j = 0; j < ExtensionBlockCount; j++) { - ExtensionBlock *ep = &ExtensionBlocks[j]; - if (ep->Function != CONTINUE_EXT_FUNC_CODE) - if (EGifPutExtensionLeader(GifFileOut, ep->Function) == GIF_ERROR) - return (GIF_ERROR); - if (EGifPutExtensionBlock(GifFileOut, ep->ByteCount, ep->Bytes) == GIF_ERROR) - return (GIF_ERROR); - if (j == ExtensionBlockCount - 1 || (ep+1)->Function != CONTINUE_EXT_FUNC_CODE) - if (EGifPutExtensionTrailer(GifFileOut) == GIF_ERROR) - return (GIF_ERROR); +static int EGifWriteExtensions(GifFileType *GifFileOut, + ExtensionBlock *ExtensionBlocks, + int ExtensionBlockCount) { + if (ExtensionBlocks) { + int j; + + for (j = 0; j < ExtensionBlockCount; j++) { + ExtensionBlock *ep = &ExtensionBlocks[j]; + if (ep->Function != CONTINUE_EXT_FUNC_CODE) { + if (EGifPutExtensionLeader(GifFileOut, + ep->Function) == + GIF_ERROR) { + return (GIF_ERROR); + } + } + if (EGifPutExtensionBlock(GifFileOut, ep->ByteCount, + ep->Bytes) == GIF_ERROR) { + return (GIF_ERROR); + } + if (j == ExtensionBlockCount - 1 || + (ep + 1)->Function != CONTINUE_EXT_FUNC_CODE) { + if (EGifPutExtensionTrailer(GifFileOut) == + GIF_ERROR) { + return (GIF_ERROR); + } + } + } } - } - return (GIF_OK); + return (GIF_OK); } -int -EGifSpew(GifFileType *GifFileOut) -{ - int i, j; - - if (EGifPutScreenDesc(GifFileOut, - GifFileOut->SWidth, - GifFileOut->SHeight, - GifFileOut->SColorResolution, - GifFileOut->SBackGroundColor, - GifFileOut->SColorMap) == GIF_ERROR) { - return (GIF_ERROR); - } - - for (i = 0; i < GifFileOut->ImageCount; i++) { - SavedImage *sp = &GifFileOut->SavedImages[i]; - int SavedHeight = sp->ImageDesc.Height; - int SavedWidth = sp->ImageDesc.Width; - - /* this allows us to delete images by nuking their rasters */ - if (sp->RasterBits == NULL) - continue; - - if (EGifWriteExtensions(GifFileOut, - sp->ExtensionBlocks, - sp->ExtensionBlockCount) == GIF_ERROR) - return (GIF_ERROR); - - if (EGifPutImageDesc(GifFileOut, - sp->ImageDesc.Left, - sp->ImageDesc.Top, - SavedWidth, - SavedHeight, - sp->ImageDesc.Interlace, - sp->ImageDesc.ColorMap) == GIF_ERROR) - return (GIF_ERROR); - - if (sp->ImageDesc.Interlace) { - /* - * The way an interlaced image should be written - - * offsets and jumps... - */ - int InterlacedOffset[] = { 0, 4, 2, 1 }; - int InterlacedJumps[] = { 8, 8, 4, 2 }; - int k; - /* Need to perform 4 passes on the images: */ - for (k = 0; k < 4; k++) - for (j = InterlacedOffset[k]; - j < SavedHeight; - j += InterlacedJumps[k]) { - if (EGifPutLine(GifFileOut, - sp->RasterBits + j * SavedWidth, - SavedWidth) == GIF_ERROR) +int EGifSpew(GifFileType *GifFileOut) { + int i, j; + + if (EGifPutScreenDesc(GifFileOut, GifFileOut->SWidth, + GifFileOut->SHeight, GifFileOut->SColorResolution, + GifFileOut->SBackGroundColor, + GifFileOut->SColorMap) == GIF_ERROR) { + return (GIF_ERROR); + } + + for (i = 0; i < GifFileOut->ImageCount; i++) { + SavedImage *sp = &GifFileOut->SavedImages[i]; + int SavedHeight = sp->ImageDesc.Height; + int SavedWidth = sp->ImageDesc.Width; + + /* this allows us to delete images by nuking their rasters */ + if (sp->RasterBits == NULL) { + continue; + } + + if (EGifWriteExtensions(GifFileOut, sp->ExtensionBlocks, + sp->ExtensionBlockCount) == GIF_ERROR) { return (GIF_ERROR); } - } else { - for (j = 0; j < SavedHeight; j++) { - if (EGifPutLine(GifFileOut, - sp->RasterBits + j * SavedWidth, - SavedWidth) == GIF_ERROR) - return (GIF_ERROR); - } + + if (EGifPutImageDesc(GifFileOut, sp->ImageDesc.Left, + sp->ImageDesc.Top, SavedWidth, SavedHeight, + sp->ImageDesc.Interlace, + sp->ImageDesc.ColorMap) == GIF_ERROR) { + return (GIF_ERROR); + } + + if (sp->ImageDesc.Interlace) { + /* + * The way an interlaced image should be written - + * offsets and jumps... + */ + static const int InterlacedOffset[] = {0, 4, 2, 1}; + static const int InterlacedJumps[] = {8, 8, 4, 2}; + int k; + /* Need to perform 4 passes on the images: */ + for (k = 0; k < 4; k++) { + for (j = InterlacedOffset[k]; j < SavedHeight; + j += InterlacedJumps[k]) { + if (EGifPutLine( + GifFileOut, + sp->RasterBits + j * SavedWidth, + SavedWidth) == GIF_ERROR) { + return (GIF_ERROR); + } + } + } + } else { + for (j = 0; j < SavedHeight; j++) { + if (EGifPutLine(GifFileOut, + sp->RasterBits + j * SavedWidth, + SavedWidth) == GIF_ERROR) { + return (GIF_ERROR); + } + } + } } - } - if (EGifWriteExtensions(GifFileOut, - GifFileOut->ExtensionBlocks, - GifFileOut->ExtensionBlockCount) == GIF_ERROR) - return (GIF_ERROR); + if (EGifWriteExtensions(GifFileOut, GifFileOut->ExtensionBlocks, + GifFileOut->ExtensionBlockCount) == GIF_ERROR) { + return (GIF_ERROR); + } - if (EGifCloseFile(GifFileOut, NULL) == GIF_ERROR) - return (GIF_ERROR); + if (EGifCloseFile(GifFileOut, NULL) == GIF_ERROR) { + return (GIF_ERROR); + } - return (GIF_OK); + return (GIF_OK); } /* end */ diff --git a/getarg.c b/getarg.c index 56dbbbb..5c1b0dc 100644 --- a/getarg.c +++ b/getarg.c @@ -102,20 +102,22 @@ Example1 [-i OneIngeter] [-s Strings...] [-j] -k Float Files... 1. This module assumes that all the pointers to all kind of data types have the same length and format, i.e. sizeof(int *) == sizeof(char *). +SPDX-License-Identifier: MIT + **************************************************************************/ -#include +#include +#include #include +#include #include -#include -#include - + #include "getarg.h" -#define MAX_PARAM 100 /* maximum number of parameters allowed. */ -#define CTRL_STR_MAX_LEN 1024 +#define MAX_PARAM 100 /* maximum number of parameters allowed. */ +#define CTRL_STR_MAX_LEN 1024 -#define SPACE_CHAR '|' /* The character not to print using HowTo. */ +#define SPACE_CHAR '|' /* The character not to print using HowTo. */ #define ARG_OK false @@ -124,255 +126,267 @@ have the same length and format, i.e. sizeof(int *) == sizeof(char *). /* The two characters '%' and '!' are used in the control string: */ #define ISCTRLCHAR(x) (((x) == '%') || ((x) == '!')) -static char *GAErrorToken; /* On error, ErrorToken is set to point to it. */ -static int GATestAllSatis(char *CtrlStrCopy, char *CtrlStr, char **argv_end, - char ***argv, void *Parameters[MAX_PARAM], - int *ParamCount); -static bool GAUpdateParameters(void *Parameters[], int *ParamCount, - char *Option, char *CtrlStrCopy, char *CtrlStr, - char **argv_end, char ***argv); +static char *GAErrorToken; /* On error, ErrorToken is set to point to it. */ +static int GATestAllSatis(char *CtrlStrCopy, char *CtrlStr, + const char **argv_end, const char ***argv, + void *Parameters[MAX_PARAM], int *ParamCount); +static int GAUpdateParameters(void *Parameters[], int *ParamCount, char *Option, + char *CtrlStrCopy, char *CtrlStr, char **argv_end, + char ***argv); static int GAGetParmeters(void *Parameters[], int *ParamCount, char *CtrlStrCopy, char *Option, char **argv_end, char ***argv); static int GAGetMultiParmeters(void *Parameters[], int *ParamCount, - char *CtrlStrCopy, char **argv_end, char ***argv); -static void GASetParamCount(char *CtrlStr, int Max, int *ParamCount); -static bool GAOptionExists(char **argv_end, char **argv); + char *CtrlStrCopy, char **argv_end, + char ***argv); +static void GASetParamCount(const char *CtrlStr, const int Max, + int *ParamCount); +static bool GAOptionExists(const char **argv_end, const char **argv); /*************************************************************************** Allocate or die ***************************************************************************/ -static void * -xmalloc(unsigned size) { +static void *xmalloc(unsigned size) { - void *p; + void *p; - if ((p = malloc(size)) != NULL) - return p; + if ((p = malloc(size)) != NULL) { + return p; + } - fprintf(stderr, "Not enough memory, exit.\n"); - exit(2); + fprintf(stderr, "Not enough memory, exit.\n"); + exit(2); - return NULL; /* Makes warning silent. */ + return NULL; /* Makes warning silent. */ } /*************************************************************************** - Routine to access the command line argument and interpret them: - Return ARG_OK (0) is case of successful parsing, error code else... + Routine to access the command line argument and interpret them: + Return ARG_OK (0) is case of successful parsing, error code else... ***************************************************************************/ -bool -GAGetArgs(int argc, - char **argv, - char *CtrlStr, ...) { - - int i, ParamCount = 0; - void *Parameters[MAX_PARAM]; /* Save here parameter addresses. */ - char CtrlStrCopy[CTRL_STR_MAX_LEN]; - char **argv_end = argv + argc; - va_list ap; - - strncpy(CtrlStrCopy, CtrlStr, sizeof(CtrlStrCopy)-1); - GASetParamCount(CtrlStr, strlen(CtrlStr), &ParamCount); - va_start(ap, CtrlStr); - for (i = 1; i <= ParamCount; i++) - Parameters[i - 1] = va_arg(ap, void *); - va_end(ap); - - argv++; /* Skip the program name (first in argv/c list). */ - while (argv < argv_end) { - bool Error = false; - if (!GAOptionExists(argv_end, argv)) - break; /* The loop. */ - char *Option = *argv++; - if ((Error = GAUpdateParameters(Parameters, &ParamCount, Option, - CtrlStrCopy, CtrlStr, argv_end, - &argv)) != false) - return Error; - } - /* Check for results and update trail of command line: */ - return GATestAllSatis(CtrlStrCopy, CtrlStr, argv_end, &argv, Parameters, - &ParamCount) != ARG_OK; +bool GAGetArgs(int argc, char **argv, char *CtrlStr, ...) { + + int i, ParamCount = 0; + void *Parameters[MAX_PARAM]; /* Save here parameter addresses. */ + char CtrlStrCopy[CTRL_STR_MAX_LEN]; + const char **argv_end = (const char **)argv + argc; + va_list ap; + + strncpy(CtrlStrCopy, CtrlStr, sizeof(CtrlStrCopy) - 1); + GASetParamCount(CtrlStr, strlen(CtrlStr), &ParamCount); + va_start(ap, CtrlStr); + for (i = 1; i <= ParamCount; i++) { + Parameters[i - 1] = va_arg(ap, void *); + } + va_end(ap); + + argv++; /* Skip the program name (first in argv/c list). */ + while (argv < (char **)argv_end) { + bool Error = false; + if (!GAOptionExists(argv_end, (const char **)argv)) { + break; /* The loop. */ + } + char *Option = *argv++; + if ((Error = GAUpdateParameters( + Parameters, &ParamCount, Option, CtrlStrCopy, CtrlStr, + (char **)argv_end, &argv)) != false) { + return Error; + } + } + /* Check for results and update trail of command line: */ + return GATestAllSatis(CtrlStrCopy, CtrlStr, argv_end, + (const char ***)&argv, Parameters, + &ParamCount) != ARG_OK; } /*************************************************************************** - Routine to search for unsatisfied flags - simply scan the list for !- + Routine to search for unsatisfied flags - simply scan the list for !- sequence. Before this scan, this routine updates the rest of the command - line into the last two parameters if it is requested by the CtrlStr - (last item in CtrlStr is NOT an option). - Return ARG_OK if all satisfied, CMD_ERR_AllSatis error else. + line into the last two parameters if it is requested by the CtrlStr + (last item in CtrlStr is NOT an option). + Return ARG_OK if all satisfied, CMD_ERR_AllSatis error else. ***************************************************************************/ -static int -GATestAllSatis(char *CtrlStrCopy, - char *CtrlStr, - char **argv_end, - char ***argv, - void *Parameters[MAX_PARAM], - int *ParamCount) { - - int i; - static char *LocalToken = NULL; - - /* If LocalToken is not initialized - do it now. Note that this string - * should be writable as well so we can not assign it directly. - */ - if (LocalToken == NULL) { - LocalToken = (char *)malloc(3); - strcpy(LocalToken, "-?"); - } - - /* Check if last item is an option. If not then copy rest of command - * line into it as 1. NumOfprm, 2. pointer to block of pointers. - */ - for (i = strlen(CtrlStr) - 1; i > 0 && !ISSPACE(CtrlStr[i]); i--) ; - if (!ISCTRLCHAR(CtrlStr[i + 2])) { - GASetParamCount(CtrlStr, i, ParamCount); /* Point in correct param. */ - *(int *)Parameters[(*ParamCount)++] = argv_end - *argv; - *(char ***)Parameters[(*ParamCount)++] = *argv; - } - - i = 0; - while (++i < (int)strlen(CtrlStrCopy)) - if ((CtrlStrCopy[i] == '-') && (CtrlStrCopy[i - 1] == '!')) { - GAErrorToken = LocalToken; - LocalToken[1] = CtrlStrCopy[i - 2]; /* Set the correct flag. */ - return CMD_ERR_AllSatis; - } - - return ARG_OK; +static int GATestAllSatis(char *CtrlStrCopy, char *CtrlStr, + const char **argv_end, const char ***argv, + void *Parameters[MAX_PARAM], int *ParamCount) { + + int i; + static char *LocalToken = NULL; + + /* If LocalToken is not initialized - do it now. Note that this string + * should be writable as well so we can not assign it directly. + */ + if (LocalToken == NULL) { + LocalToken = (char *)malloc(3); + strcpy(LocalToken, "-?"); + } + + /* Check if last item is an option. If not then copy rest of command + * line into it as 1. NumOfprm, 2. pointer to block of pointers. + */ + for (i = strlen(CtrlStr) - 1; i > 0 && !ISSPACE(CtrlStr[i]); i--) { + ; + } + if (!ISCTRLCHAR(CtrlStr[i + 2])) { + GASetParamCount(CtrlStr, i, + ParamCount); /* Point in correct param. */ + *(int *)Parameters[(*ParamCount)++] = argv_end - *argv; + *(char ***)Parameters[(*ParamCount)++] = *(char ***)argv; + } + + i = 0; + while (++i < (int)strlen(CtrlStrCopy)) { + if ((CtrlStrCopy[i] == '-') && (CtrlStrCopy[i - 1] == '!')) { + GAErrorToken = LocalToken; + LocalToken[1] = + CtrlStrCopy[i - 2]; /* Set the correct flag. */ + return CMD_ERR_AllSatis; + } + } + + return ARG_OK; } /*************************************************************************** Routine to update the parameters according to the given Option: **************************************************************************/ -static bool -GAUpdateParameters(void *Parameters[], - int *ParamCount, - char *Option, - char *CtrlStrCopy, - char *CtrlStr, - char **argv_end, - char ***argv) { - - int i; - bool BooleanTrue = Option[2] != '-'; - - if (Option[0] != '-') { - GAErrorToken = Option; - return CMD_ERR_NotAnOpt; - } - i = 0; /* Scan the CtrlStrCopy for that option: */ - while (i + 2 < (int)strlen(CtrlStrCopy)) { - if ((CtrlStrCopy[i] == Option[1]) && (ISCTRLCHAR(CtrlStrCopy[i + 1])) - && (CtrlStrCopy[i + 2] == '-')) { - /* We found that option! */ - break; - } - i++; - } - if (i + 2 >= (int)strlen(CtrlStrCopy)) { - GAErrorToken = Option; - return CMD_ERR_NoSuchOpt; - } - - /* If we are here, then we found that option in CtrlStr - Strip it off: */ - CtrlStrCopy[i] = CtrlStrCopy[i + 1] = CtrlStrCopy[i + 2] = (char)' '; - GASetParamCount(CtrlStr, i, ParamCount); /* Set it to point in - correct prm. */ - i += 3; - /* Set boolean flag for that option. */ - *(bool *)Parameters[(*ParamCount)++] = BooleanTrue; - if (ISSPACE(CtrlStrCopy[i])) - return ARG_OK; /* Only a boolean flag is needed. */ - - /* Skip the text between the boolean option and data follows: */ - while (!ISCTRLCHAR(CtrlStrCopy[i])) - i++; - /* Get the parameters and return the appropriete return code: */ - return GAGetParmeters(Parameters, ParamCount, &CtrlStrCopy[i], - Option, argv_end, argv); +static int GAUpdateParameters(void *Parameters[], int *ParamCount, char *Option, + char *CtrlStrCopy, char *CtrlStr, char **argv_end, + char ***argv) { + + int i; + bool BooleanTrue = Option[2] != '-'; + + if (Option[0] != '-') { + GAErrorToken = Option; + return CMD_ERR_NotAnOpt; + } + i = 0; /* Scan the CtrlStrCopy for that option: */ + while (i + 2 < (int)strlen(CtrlStrCopy)) { + if ((CtrlStrCopy[i] == Option[1]) && + (ISCTRLCHAR(CtrlStrCopy[i + 1])) && + (CtrlStrCopy[i + 2] == '-')) { + /* We found that option! */ + break; + } + i++; + } + if (i + 2 >= (int)strlen(CtrlStrCopy)) { + GAErrorToken = Option; + return CMD_ERR_NoSuchOpt; + } + + /* If we are here, then we found that option in CtrlStr - Strip it off: + */ + CtrlStrCopy[i] = CtrlStrCopy[i + 1] = CtrlStrCopy[i + 2] = (char)' '; + GASetParamCount(CtrlStr, i, ParamCount); /* Set it to point in + correct prm. */ + i += 3; + /* Set boolean flag for that option. */ + *(bool *)Parameters[(*ParamCount)++] = BooleanTrue; + if (ISSPACE(CtrlStrCopy[i])) { + return ARG_OK; /* Only a boolean flag is needed. */ + } + /* Skip the text between the boolean option and data follows: */ + while (!ISCTRLCHAR(CtrlStrCopy[i])) { + i++; + } + /* Get the parameters and return the appropriete return code: */ + return GAGetParmeters(Parameters, ParamCount, &CtrlStrCopy[i], Option, + argv_end, argv); } /*************************************************************************** Routine to get parameters according to the CtrlStr given from argv/argc ***************************************************************************/ -static int -GAGetParmeters(void *Parameters[], - int *ParamCount, - char *CtrlStrCopy, - char *Option, - char **argv_end, - char ***argv) { - - int i = 0, ScanRes; - - while (!(ISSPACE(CtrlStrCopy[i]))) { - switch (CtrlStrCopy[i + 1]) { - case 'd': /* Get signed integers. */ - ScanRes = sscanf(*((*argv)++), "%d", - (int *)Parameters[(*ParamCount)++]); - break; - case 'u': /* Get unsigned integers. */ - ScanRes = sscanf(*((*argv)++), "%u", - (unsigned *)Parameters[(*ParamCount)++]); - break; - case 'x': /* Get hex integers. */ - ScanRes = sscanf(*((*argv)++), "%x", - (unsigned int *)Parameters[(*ParamCount)++]); - break; - case 'o': /* Get octal integers. */ - ScanRes = sscanf(*((*argv)++), "%o", - (unsigned int *)Parameters[(*ParamCount)++]); - break; - case 'D': /* Get signed long integers. */ - ScanRes = sscanf(*((*argv)++), "%ld", - (long *)Parameters[(*ParamCount)++]); - break; - case 'U': /* Get unsigned long integers. */ - ScanRes = sscanf(*((*argv)++), "%lu", - (unsigned long *)Parameters[(*ParamCount)++]); - break; - case 'X': /* Get hex long integers. */ - ScanRes = sscanf(*((*argv)++), "%lx", - (unsigned long *)Parameters[(*ParamCount)++]); - break; - case 'O': /* Get octal long integers. */ - ScanRes = sscanf(*((*argv)++), "%lo", - (unsigned long *)Parameters[(*ParamCount)++]); - break; - case 'f': /* Get float number. */ - ScanRes = sscanf(*((*argv)++), "%f", - (float *)Parameters[(*ParamCount)++]); - break; - case 'F': /* Get double float number. */ - ScanRes = sscanf(*((*argv)++), "%lf", - (double *)Parameters[(*ParamCount)++]); - break; - case 's': /* It as a string. */ - ScanRes = 1; /* Allways O.K. */ - *(char **)Parameters[(*ParamCount)++] = *((*argv)++); - break; - case '*': /* Get few parameters into one: */ - ScanRes = GAGetMultiParmeters(Parameters, ParamCount, - &CtrlStrCopy[i], argv_end, argv); - if ((ScanRes == 0) && (CtrlStrCopy[i] == '!')) { - GAErrorToken = Option; - return CMD_ERR_WildEmpty; - } - break; - default: - ScanRes = 0; /* Make optimizer warning silent. */ - } - /* If reading fails and this number is a must (!) then error: */ - if ((ScanRes == 0) && (CtrlStrCopy[i] == '!')) { - GAErrorToken = Option; - return CMD_ERR_NumRead; - } - if (CtrlStrCopy[i + 1] != '*') { - i += 2; /* Skip to next parameter (if any). */ - } else - i += 3; /* Skip the '*' also! */ - } - - return ARG_OK; +static int GAGetParmeters(void *Parameters[], int *ParamCount, + char *CtrlStrCopy, char *Option, char **argv_end, + char ***argv) { + + int i = 0, ScanRes; + + while (!(ISSPACE(CtrlStrCopy[i]))) { + + if ((*argv) == argv_end) { + GAErrorToken = Option; + return CMD_ERR_NumRead; + } + + switch (CtrlStrCopy[i + 1]) { + case 'd': /* Get signed integers. */ + ScanRes = sscanf(*((*argv)++), "%d", + (int *)Parameters[(*ParamCount)++]); + break; + case 'u': /* Get unsigned integers. */ + ScanRes = + sscanf(*((*argv)++), "%u", + (unsigned *)Parameters[(*ParamCount)++]); + break; + case 'x': /* Get hex integers. */ + ScanRes = + sscanf(*((*argv)++), "%x", + (unsigned int *)Parameters[(*ParamCount)++]); + break; + case 'o': /* Get octal integers. */ + ScanRes = + sscanf(*((*argv)++), "%o", + (unsigned int *)Parameters[(*ParamCount)++]); + break; + case 'D': /* Get signed long integers. */ + ScanRes = sscanf(*((*argv)++), "%ld", + (long *)Parameters[(*ParamCount)++]); + break; + case 'U': /* Get unsigned long integers. */ + ScanRes = sscanf( + *((*argv)++), "%lu", + (unsigned long *)Parameters[(*ParamCount)++]); + break; + case 'X': /* Get hex long integers. */ + ScanRes = sscanf( + *((*argv)++), "%lx", + (unsigned long *)Parameters[(*ParamCount)++]); + break; + case 'O': /* Get octal long integers. */ + ScanRes = sscanf( + *((*argv)++), "%lo", + (unsigned long *)Parameters[(*ParamCount)++]); + break; + case 'f': /* Get float number. */ + ScanRes = sscanf(*((*argv)++), "%f", + (float *)Parameters[(*ParamCount)++]); + break; + case 'F': /* Get double float number. */ + ScanRes = sscanf(*((*argv)++), "%lf", + (double *)Parameters[(*ParamCount)++]); + break; + case 's': /* It as a string. */ + ScanRes = 1; /* Allways O.K. */ + *(char **)Parameters[(*ParamCount)++] = *((*argv)++); + break; + case '*': /* Get few parameters into one: */ + ScanRes = GAGetMultiParmeters(Parameters, ParamCount, + &CtrlStrCopy[i], argv_end, + argv); + if ((ScanRes == 0) && (CtrlStrCopy[i] == '!')) { + GAErrorToken = Option; + return CMD_ERR_WildEmpty; + } + break; + default: + ScanRes = 0; /* Make optimizer warning silent. */ + } + /* If reading fails and this number is a must (!) then error: */ + if ((ScanRes == 0) && (CtrlStrCopy[i] == '!')) { + GAErrorToken = Option; + return CMD_ERR_NumRead; + } + if (CtrlStrCopy[i + 1] != '*') { + i += 2; /* Skip to next parameter (if any). */ + } else { + i += 3; /* Skip the '*' also! */ + } + } + + return ARG_OK; } /*************************************************************************** @@ -383,110 +397,113 @@ GAGetParmeters(void *Parameters[], This routine assumes that all pointers (on any kind of scalar) has the same size (and the union below is totally ovelapped bteween dif. arrays) ***************************************************************************/ -static int -GAGetMultiParmeters(void *Parameters[], - int *ParamCount, - char *CtrlStrCopy, - char **argv_end, - char ***argv) { - - int i = 0, ScanRes, NumOfPrm = 0; - void **Pmain, **Ptemp; - union TmpArray { /* Save here the temporary data before copying it to */ - void *VoidArray[MAX_PARAM]; /* the returned pointer block. */ - int *IntArray[MAX_PARAM]; - long *LngArray[MAX_PARAM]; - float *FltArray[MAX_PARAM]; - double *DblArray[MAX_PARAM]; - char *ChrArray[MAX_PARAM]; - } TmpArray; - - do { - switch (CtrlStrCopy[2]) { /* CtrlStr == '!*?' or '%*?' where ? is. */ - case 'd': /* Format to read the parameters: */ - TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int)); - ScanRes = sscanf(*((*argv)++), "%d", - (int *)TmpArray.IntArray[NumOfPrm++]); - break; - case 'u': - TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int)); - ScanRes = sscanf(*((*argv)++), "%u", - (unsigned int *)TmpArray.IntArray[NumOfPrm++]); - break; - case 'o': - TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int)); - ScanRes = sscanf(*((*argv)++), "%o", - (unsigned int *)TmpArray.IntArray[NumOfPrm++]); - break; - case 'x': - TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int)); - ScanRes = sscanf(*((*argv)++), "%x", - (unsigned int *)TmpArray.IntArray[NumOfPrm++]); - break; - case 'D': - TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long)); - ScanRes = sscanf(*((*argv)++), "%ld", - (long *)TmpArray.IntArray[NumOfPrm++]); - break; - case 'U': - TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long)); - ScanRes = sscanf(*((*argv)++), "%lu", - (unsigned long *)TmpArray. - IntArray[NumOfPrm++]); - break; - case 'O': - TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long)); - ScanRes = sscanf(*((*argv)++), "%lo", - (unsigned long *)TmpArray. - IntArray[NumOfPrm++]); - break; - case 'X': - TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long)); - ScanRes = sscanf(*((*argv)++), "%lx", - (unsigned long *)TmpArray. - IntArray[NumOfPrm++]); - break; - case 'f': - TmpArray.FltArray[NumOfPrm] = xmalloc(sizeof(float)); - ScanRes = sscanf(*((*argv)++), "%f", - // cppcheck-suppress invalidPointerCast - (float *)TmpArray.LngArray[NumOfPrm++]); - break; - case 'F': - TmpArray.DblArray[NumOfPrm] = xmalloc(sizeof(double)); - ScanRes = sscanf(*((*argv)++), "%lf", - // cppcheck-suppress invalidPointerCast - (double *)TmpArray.LngArray[NumOfPrm++]); - break; - case 's': - while ((*argv < argv_end) && ((**argv)[0] != '-')) { - TmpArray.ChrArray[NumOfPrm++] = *((*argv)++); - } - ScanRes = 0; /* Force quit from do - loop. */ - NumOfPrm++; /* Updated again immediately after loop! */ - (*argv)++; /* "" */ - break; - default: - ScanRes = 0; /* Make optimizer warning silent. */ - } - } - while (ScanRes == 1); /* Exactly one parameter was read. */ - (*argv)--; - NumOfPrm--; - - /* Now allocate the block with the exact size, and set it: */ - Ptemp = Pmain = xmalloc((unsigned)(NumOfPrm + 1) * sizeof(void *)); - /* And here we use the assumption that all pointers are the same: */ - for (i = 0; i < NumOfPrm; i++) - *Ptemp++ = TmpArray.VoidArray[i]; - *Ptemp = NULL; /* Close the block with NULL pointer. */ - - /* That it save the number of parameters read as first parameter to - * return and the pointer to the block as second, and return: */ - *(int *)Parameters[(*ParamCount)++] = NumOfPrm; - *(void ***)Parameters[(*ParamCount)++] = Pmain; - /* free(Pmain); -- can not free here as caller needs to access memory */ - return NumOfPrm; +static int GAGetMultiParmeters(void *Parameters[], int *ParamCount, + char *CtrlStrCopy, char **argv_end, + char ***argv) { + + int i = 0, ScanRes, NumOfPrm = 0; + void **Pmain, **Ptemp; + union TmpArray { /* Save here the temporary data before copying it to */ + void *VoidArray[MAX_PARAM]; /* the returned pointer block. */ + int *IntArray[MAX_PARAM]; + long *LngArray[MAX_PARAM]; + float *FltArray[MAX_PARAM]; + double *DblArray[MAX_PARAM]; + char *ChrArray[MAX_PARAM]; + } TmpArray; + + do { + switch (CtrlStrCopy[2]) { /* CtrlStr == '!*?' or '%*?' where ? + is. */ + case 'd': /* Format to read the parameters: */ + TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int)); + ScanRes = sscanf(*((*argv)++), "%d", + (int *)TmpArray.IntArray[NumOfPrm++]); + break; + case 'u': + TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int)); + ScanRes = sscanf( + *((*argv)++), "%u", + (unsigned int *)TmpArray.IntArray[NumOfPrm++]); + break; + case 'o': + TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int)); + ScanRes = sscanf( + *((*argv)++), "%o", + (unsigned int *)TmpArray.IntArray[NumOfPrm++]); + break; + case 'x': + TmpArray.IntArray[NumOfPrm] = xmalloc(sizeof(int)); + ScanRes = sscanf( + *((*argv)++), "%x", + (unsigned int *)TmpArray.IntArray[NumOfPrm++]); + break; + case 'D': + TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long)); + ScanRes = sscanf(*((*argv)++), "%ld", + (long *)TmpArray.IntArray[NumOfPrm++]); + break; + case 'U': + TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long)); + ScanRes = sscanf( + *((*argv)++), "%lu", + (unsigned long *)TmpArray.IntArray[NumOfPrm++]); + break; + case 'O': + TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long)); + ScanRes = sscanf( + *((*argv)++), "%lo", + (unsigned long *)TmpArray.IntArray[NumOfPrm++]); + break; + case 'X': + TmpArray.LngArray[NumOfPrm] = xmalloc(sizeof(long)); + ScanRes = sscanf( + *((*argv)++), "%lx", + (unsigned long *)TmpArray.IntArray[NumOfPrm++]); + break; + case 'f': + TmpArray.FltArray[NumOfPrm] = xmalloc(sizeof(float)); + ScanRes = + sscanf(*((*argv)++), "%f", + // cppcheck-suppress invalidPointerCast + (float *)TmpArray.LngArray[NumOfPrm++]); + break; + case 'F': + TmpArray.DblArray[NumOfPrm] = xmalloc(sizeof(double)); + ScanRes = + sscanf(*((*argv)++), "%lf", + // cppcheck-suppress invalidPointerCast + (double *)TmpArray.LngArray[NumOfPrm++]); + break; + case 's': + while ((*argv < argv_end) && ((**argv)[0] != '-')) { + TmpArray.ChrArray[NumOfPrm++] = *((*argv)++); + } + ScanRes = 0; /* Force quit from do - loop. */ + NumOfPrm++; /* Updated again immediately after loop! */ + (*argv)++; /* "" */ + break; + default: + ScanRes = 0; /* Make optimizer warning silent. */ + } + } while (ScanRes == 1); /* Exactly one parameter was read. */ + (*argv)--; + NumOfPrm--; + + /* Now allocate the block with the exact size, and set it: */ + Ptemp = Pmain = xmalloc((unsigned)(NumOfPrm + 1) * sizeof(void *)); + /* And here we use the assumption that all pointers are the same: */ + for (i = 0; i < NumOfPrm; i++) { + *Ptemp++ = TmpArray.VoidArray[i]; + } + *Ptemp = NULL; /* Close the block with NULL pointer. */ + + /* That it save the number of parameters read as first parameter to + * return and the pointer to the block as second, and return: */ + *(int *)Parameters[(*ParamCount)++] = NumOfPrm; + *(void ***)Parameters[(*ParamCount)++] = Pmain; + /* free(Pmain); -- can not free here as caller needs to access memory */ + return NumOfPrm; } /*************************************************************************** @@ -498,140 +515,156 @@ GAGetMultiParmeters(void *Parameters[], and one for pointer to block pointers. Note ALL variables are passed by address and so of fixed size (address). ***************************************************************************/ -static void -GASetParamCount(char *CtrlStr, - int Max, - int *ParamCount) { - int i; - - *ParamCount = 0; - for (i = 0; i < Max; i++) - if (ISCTRLCHAR(CtrlStr[i])) { - if (CtrlStr[i + 1] == '*') - *ParamCount += 2; - else - (*ParamCount)++; - } +static void GASetParamCount(char const *CtrlStr, const int Max, + int *ParamCount) { + int i; + + *ParamCount = 0; + for (i = 0; i < Max; i++) { + if (ISCTRLCHAR(CtrlStr[i])) { + if (CtrlStr[i + 1] == '*') { + *ParamCount += 2; + } else { + (*ParamCount)++; + } + } + } } /*************************************************************************** Routine to check if more option (i.e. first char == '-') exists in the given list argc, argv: ***************************************************************************/ -static bool -GAOptionExists(char **argv_end, - char **argv) { - - while (argv < argv_end) - if ((*argv++)[0] == '-') - return true; - return false; +static bool GAOptionExists(const char **argv_end, const char **argv) { + + while (argv < argv_end) { + if ((*argv++)[0] == '-') { + return true; + } + } + return false; } /*************************************************************************** Routine to print some error messages, for this module: ***************************************************************************/ -void -GAPrintErrMsg(int Error) { - - fprintf(stderr, "Error in command line parsing - "); - switch (Error) { - case 0:; - fprintf(stderr, "Undefined error"); - break; - case CMD_ERR_NotAnOpt: - fprintf(stderr, "None option Found"); - break; - case CMD_ERR_NoSuchOpt: - fprintf(stderr, "Undefined option Found"); - break; - case CMD_ERR_WildEmpty: - fprintf(stderr, "Empty input for '!*?' seq."); - break; - case CMD_ERR_NumRead: - fprintf(stderr, "Failed on reading number"); - break; - case CMD_ERR_AllSatis: - fprintf(stderr, "Fail to satisfy"); - break; - } - fprintf(stderr, " - '%s'.\n", GAErrorToken); +void GAPrintErrMsg(int Error) { + + fprintf(stderr, "Error in command line parsing - "); + switch (Error) { + case 0:; + fprintf(stderr, "Undefined error"); + break; + case CMD_ERR_NotAnOpt: + fprintf(stderr, "None option Found"); + break; + case CMD_ERR_NoSuchOpt: + fprintf(stderr, "Undefined option Found"); + break; + case CMD_ERR_WildEmpty: + fprintf(stderr, "Empty input for '!*?' seq."); + break; + case CMD_ERR_NumRead: + fprintf(stderr, "Failed on reading number"); + break; + case CMD_ERR_AllSatis: + fprintf(stderr, "Fail to satisfy"); + break; + } + fprintf(stderr, " - '%s'.\n", GAErrorToken); } /*************************************************************************** Routine to print correct format of command line allowed: ***************************************************************************/ -void -GAPrintHowTo(char *CtrlStr) { - - int i = 0; - bool SpaceFlag; - - fprintf(stderr, "Usage: "); - /* Print program name - first word in ctrl. str. (optional): */ - while (!(ISSPACE(CtrlStr[i])) && (!ISCTRLCHAR(CtrlStr[i + 1]))) - fprintf(stderr, "%c", CtrlStr[i++]); - - while (i < (int)strlen(CtrlStr)) { - // cppcheck-suppress arrayIndexThenCheck - while ((ISSPACE(CtrlStr[i])) && (i < (int)strlen(CtrlStr))) - i++; - switch (CtrlStr[i + 1]) { - case '%': - fprintf(stderr, " [-%c", CtrlStr[i++]); - i += 2; /* Skip the '%-' or '!- after the char! */ - SpaceFlag = true; - while (!ISCTRLCHAR(CtrlStr[i]) && (i < (int)strlen(CtrlStr)) && - (!ISSPACE(CtrlStr[i]))) - if (SpaceFlag) { - if (CtrlStr[i++] == SPACE_CHAR) - fprintf(stderr, " "); - else - fprintf(stderr, " %c", CtrlStr[i - 1]); - SpaceFlag = false; - } else if (CtrlStr[i++] == SPACE_CHAR) - fprintf(stderr, " "); - else - fprintf(stderr, "%c", CtrlStr[i - 1]); - while (!ISSPACE(CtrlStr[i]) && (i < (int)strlen(CtrlStr))) { - if (CtrlStr[i] == '*') - fprintf(stderr, "..."); - i++; /* Skip the rest of it. */ - } - fprintf(stderr, "]"); - break; - case '!': - fprintf(stderr, " -%c", CtrlStr[i++]); - i += 2; /* Skip the '%-' or '!- after the char! */ - SpaceFlag = true; - while (!ISCTRLCHAR(CtrlStr[i]) && (i < (int)strlen(CtrlStr)) && - (!ISSPACE(CtrlStr[i]))) - if (SpaceFlag) { - if (CtrlStr[i++] == SPACE_CHAR) - fprintf(stderr, " "); - else - fprintf(stderr, " %c", CtrlStr[i - 1]); - SpaceFlag = false; - } else if (CtrlStr[i++] == SPACE_CHAR) - fprintf(stderr, " "); - else - fprintf(stderr, "%c", CtrlStr[i - 1]); - while (!ISSPACE(CtrlStr[i]) && (i < (int)strlen(CtrlStr))) { - if (CtrlStr[i] == '*') - fprintf(stderr, "..."); - i++; /* Skip the rest of it. */ - } - break; - default: /* Not checked, but must be last one! */ - fprintf(stderr, " "); - while (!ISSPACE(CtrlStr[i]) && (i < (int)strlen(CtrlStr)) && - !ISCTRLCHAR(CtrlStr[i])) - fprintf(stderr, "%c", CtrlStr[i++]); - fprintf(stderr, "\n"); - return; - } - } - fprintf(stderr, "\n"); +void GAPrintHowTo(char *CtrlStr) { + + int i = 0; + bool SpaceFlag; + + fprintf(stderr, "Usage: "); + /* Print program name - first word in ctrl. str. (optional): */ + while (!(ISSPACE(CtrlStr[i])) && (!ISCTRLCHAR(CtrlStr[i + 1]))) { + fprintf(stderr, "%c", CtrlStr[i++]); + } + + while (i < (int)strlen(CtrlStr)) { + // cppcheck-suppress arrayIndexThenCheck + while ((ISSPACE(CtrlStr[i])) && (i < (int)strlen(CtrlStr))) { + i++; + } + switch (CtrlStr[i + 1]) { + case '%': + fprintf(stderr, " [-%c", CtrlStr[i++]); + i += 2; /* Skip the '%-' or '!- after the char! */ + SpaceFlag = true; + while (!ISCTRLCHAR(CtrlStr[i]) && + (i < (int)strlen(CtrlStr)) && + (!ISSPACE(CtrlStr[i]))) { + if (SpaceFlag) { + if (CtrlStr[i++] == SPACE_CHAR) { + fprintf(stderr, " "); + } else { + fprintf(stderr, " %c", + CtrlStr[i - 1]); + } + SpaceFlag = false; + } else if (CtrlStr[i++] == SPACE_CHAR) { + fprintf(stderr, " "); + } else { + fprintf(stderr, "%c", CtrlStr[i - 1]); + } + } + while (!ISSPACE(CtrlStr[i]) && + (i < (int)strlen(CtrlStr))) { + if (CtrlStr[i] == '*') { + fprintf(stderr, "..."); + } + i++; /* Skip the rest of it. */ + } + fprintf(stderr, "]"); + break; + case '!': + fprintf(stderr, " -%c", CtrlStr[i++]); + i += 2; /* Skip the '%-' or '!- after the char! */ + SpaceFlag = true; + while (!ISCTRLCHAR(CtrlStr[i]) && + (i < (int)strlen(CtrlStr)) && + (!ISSPACE(CtrlStr[i]))) { + if (SpaceFlag) { + if (CtrlStr[i++] == SPACE_CHAR) { + fprintf(stderr, " "); + } else { + fprintf(stderr, " %c", + CtrlStr[i - 1]); + } + SpaceFlag = false; + } else if (CtrlStr[i++] == SPACE_CHAR) { + fprintf(stderr, " "); + } else { + fprintf(stderr, "%c", CtrlStr[i - 1]); + } + } + while (!ISSPACE(CtrlStr[i]) && + (i < (int)strlen(CtrlStr))) { + if (CtrlStr[i] == '*') { + fprintf(stderr, "..."); + } + i++; /* Skip the rest of it. */ + } + break; + default: /* Not checked, but must be last one! */ + fprintf(stderr, " "); + while (!ISSPACE(CtrlStr[i]) && + (i < (int)strlen(CtrlStr)) && + !ISCTRLCHAR(CtrlStr[i])) { + fprintf(stderr, "%c", CtrlStr[i++]); + } + fprintf(stderr, "\n"); + return; + } + } + fprintf(stderr, "\n"); } /* end */ diff --git a/getarg.h b/getarg.h index 837d006..3bfd22b 100644 --- a/getarg.h +++ b/getarg.h @@ -2,23 +2,26 @@ getarg.h - Support routines for the giflib utilities -**************************************************************************/ +SPDX-License-Identifier: MIT + + **************************************************************************/ #ifndef _GETARG_H #define _GETARG_H +#include "gif_lib.h" #include #define VERSION_COOKIE " Version %d.%d, " /*************************************************************************** - Error numbers as returned by GAGetArg routine: -***************************************************************************/ -#define CMD_ERR_NotAnOpt 1 /* None Option found. */ -#define CMD_ERR_NoSuchOpt 2 /* Undefined Option Found. */ -#define CMD_ERR_WildEmpty 3 /* Empty input for !*? seq. */ -#define CMD_ERR_NumRead 4 /* Failed on reading number. */ -#define CMD_ERR_AllSatis 5 /* Fail to satisfy (must-'!') option. */ + Error numbers as returned by GAGetArg routine: +***************************************************************************/ +#define CMD_ERR_NotAnOpt 1 /* None Option found. */ +#define CMD_ERR_NoSuchOpt 2 /* Undefined Option Found. */ +#define CMD_ERR_WildEmpty 3 /* Empty input for !*? seq. */ +#define CMD_ERR_NumRead 4 /* Failed on reading number. */ +#define CMD_ERR_AllSatis 5 /* Fail to satisfy (must-'!') option. */ bool GAGetArgs(int argc, char **argv, char *CtrlStr, ...); void GAPrintErrMsg(int Error); @@ -27,13 +30,24 @@ void GAPrintHowTo(char *CtrlStr); /****************************************************************************** From qprintf.c ******************************************************************************/ -extern bool GifNoisyPrint; extern void GifQprintf(char *Format, ...); extern void PrintGifError(int ErrorCode); +/****************************************************************************** + Color table quantization +******************************************************************************/ +int GifQuantizeBuffer(unsigned int Width, unsigned int Height, + int *ColorMapSize, GifByteType *RedInput, + GifByteType *GreenInput, GifByteType *BlueInput, + GifByteType *OutputBuffer, GifColorType *OutputColorMap); + /* These used to live in the library header */ #define GIF_MESSAGE(Msg) fprintf(stderr, "\n%s: %s\n", PROGRAM_NAME, Msg) -#define GIF_EXIT(Msg) { GIF_MESSAGE(Msg); exit(-3); } +#define GIF_EXIT(Msg) \ + { \ + GIF_MESSAGE(Msg); \ + exit(-3); \ + } #endif /* _GETARG_H */ diff --git a/gif2rgb.c b/gif2rgb.c index 415ffed..d51226d 100644 --- a/gif2rgb.c +++ b/gif2rgb.c @@ -2,6 +2,8 @@ gif2rgb - convert GIF to 24-bit RGB pixel triples or vice-versa +SPDX-License-Identifier: MIT + *****************************************************************************/ /*************************************************************************** @@ -20,517 +22,559 @@ with our utilities mainly interesting as test tools. ***************************************************************************/ -#include -#include #include -#include -#include #include +#include +#include +#include +#include +#include #ifdef _WIN32 #include #endif /* _WIN32 */ -#include "gif_lib.h" #include "getarg.h" +#include "gif_lib.h" + +#define PROGRAM_NAME "gif2rgb" -#define PROGRAM_NAME "gif2rgb" - -static char - *VersionStr = - PROGRAM_NAME - VERSION_COOKIE - " Gershon Elber, " - __DATE__ ", " __TIME__ "\n" - "(C) Copyright 1989 Gershon Elber.\n"; -static char - *CtrlStr = - PROGRAM_NAME - " v%- c%-#Colors!d s%-Width|Height!d!d 1%- o%-OutFileName!s h%- GifFile!*s"; - -static void LoadRGB(char *FileName, - int OneFileFlag, - GifByteType **RedBuffer, - GifByteType **GreenBuffer, - GifByteType **BlueBuffer, - int Width, int Height); -static void SaveGif(GifByteType *OutputBuffer, - int Width, int Height, - int ExpColorMapSize, ColorMapObject *OutputColorMap); +static char *VersionStr = PROGRAM_NAME VERSION_COOKIE + " Gershon Elber, " __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char *CtrlStr = PROGRAM_NAME + " v%- c%-#Colors!d s%-Width|Height!d!d 1%- o%-OutFileName!s h%- GifFile!*s"; + +static void LoadRGB(char *FileName, int OneFileFlag, GifByteType **RedBuffer, + GifByteType **GreenBuffer, GifByteType **BlueBuffer, + int Width, int Height); +static void SaveGif(GifByteType *OutputBuffer, int Width, int Height, + int ExpColorMapSize, ColorMapObject *OutputColorMap); /****************************************************************************** Load RGB file into internal frame buffer. ******************************************************************************/ -static void LoadRGB(char *FileName, - int OneFileFlag, - GifByteType **RedBuffer, - GifByteType **GreenBuffer, - GifByteType **BlueBuffer, - int Width, int Height) -{ - int i; - unsigned long Size; - GifByteType *RedP, *GreenP, *BlueP; - FILE *rgbfp[3]; - - Size = ((long) Width) * Height * sizeof(GifByteType); - - if ((*RedBuffer = (GifByteType *) malloc((unsigned int) Size)) == NULL || - (*GreenBuffer = (GifByteType *) malloc((unsigned int) Size)) == NULL || - (*BlueBuffer = (GifByteType *) malloc((unsigned int) Size)) == NULL) - GIF_EXIT("Failed to allocate memory required, aborted."); - - RedP = *RedBuffer; - GreenP = *GreenBuffer; - BlueP = *BlueBuffer; - - if (FileName != NULL) { - if (OneFileFlag) { - if ((rgbfp[0] = fopen(FileName, "rb")) == NULL) - GIF_EXIT("Can't open input file name."); +static void LoadRGB(char *FileName, int OneFileFlag, GifByteType **RedBuffer, + GifByteType **GreenBuffer, GifByteType **BlueBuffer, + int Width, int Height) { + int i; + unsigned long Size; + GifByteType *RedP, *GreenP, *BlueP; + FILE *rgbfp[3]; + + Size = ((long)Width) * Height * sizeof(GifByteType); + + if ((*RedBuffer = (GifByteType *)malloc((unsigned int)Size)) == NULL || + (*GreenBuffer = (GifByteType *)malloc((unsigned int)Size)) == + NULL || + (*BlueBuffer = (GifByteType *)malloc((unsigned int)Size)) == NULL) { + GIF_EXIT("Failed to allocate memory required, aborted."); } - else { - static char *Postfixes[] = { ".R", ".G", ".B" }; - char OneFileName[80]; - - for (i = 0; i < 3; i++) { - strncpy(OneFileName, FileName, sizeof(OneFileName)-1); - strncat(OneFileName, Postfixes[i], - sizeof(OneFileName) - 1 - strlen(OneFileName)); - - if ((rgbfp[i] = fopen(OneFileName, "rb")) == NULL) - GIF_EXIT("Can't open input file name."); - } - } - } - else { - OneFileFlag = true; + + RedP = *RedBuffer; + GreenP = *GreenBuffer; + BlueP = *BlueBuffer; + + if (FileName != NULL) { + if (OneFileFlag) { + if ((rgbfp[0] = fopen(FileName, "rb")) == NULL) { + GIF_EXIT("Can't open input file name."); + } + } else { + static const char *Postfixes[] = {".R", ".G", ".B"}; + char OneFileName[80]; + + for (i = 0; i < 3; i++) { + strncpy(OneFileName, FileName, + sizeof(OneFileName) - 1); + strncat(OneFileName, Postfixes[i], + sizeof(OneFileName) - 1 - + strlen(OneFileName)); + + if ((rgbfp[i] = fopen(OneFileName, "rb")) == + NULL) { + GIF_EXIT("Can't open input file name."); + } + } + } + } else { + OneFileFlag = true; #ifdef _WIN32 - _setmode(0, O_BINARY); + _setmode(0, O_BINARY); #endif /* _WIN32 */ - rgbfp[0] = stdin; - } + rgbfp[0] = stdin; + } - GifQprintf("\n%s: RGB image: ", PROGRAM_NAME); + GifQprintf("\n%s: RGB image: ", PROGRAM_NAME); - if (OneFileFlag) { - GifByteType *Buffer, *BufferP; + if (OneFileFlag) { + GifByteType *Buffer, *BufferP; - if ((Buffer = (GifByteType *) malloc(Width * 3)) == NULL) - GIF_EXIT("Failed to allocate memory required, aborted."); + if ((Buffer = (GifByteType *)malloc(Width * 3)) == NULL) { + GIF_EXIT( + "Failed to allocate memory required, aborted."); + } - for (i = 0; i < Height; i++) { - int j; - GifQprintf("\b\b\b\b%-4d", i); - if (fread(Buffer, Width * 3, 1, rgbfp[0]) != 1) - GIF_EXIT("Input file(s) terminated prematurly."); - for (j = 0, BufferP = Buffer; j < Width; j++) { - *RedP++ = *BufferP++; - *GreenP++ = *BufferP++; - *BlueP++ = *BufferP++; - } - } + for (i = 0; i < Height; i++) { + int j; + GifQprintf("\b\b\b\b%-4d", i); + if (fread(Buffer, Width * 3, 1, rgbfp[0]) != 1) { + GIF_EXIT( + "Input file(s) terminated prematurly."); + } + for (j = 0, BufferP = Buffer; j < Width; j++) { + *RedP++ = *BufferP++; + *GreenP++ = *BufferP++; + *BlueP++ = *BufferP++; + } + } - free((char *) Buffer); - fclose(rgbfp[0]); - } - else { - for (i = 0; i < Height; i++) { - GifQprintf("\b\b\b\b%-4d", i); - if (fread(RedP, Width, 1, rgbfp[0]) != 1 || - fread(GreenP, Width, 1, rgbfp[1]) != 1 || - fread(BlueP, Width, 1, rgbfp[2]) != 1) - GIF_EXIT("Input file(s) terminated prematurly."); - RedP += Width; - GreenP += Width; - BlueP += Width; - } + free((char *)Buffer); + fclose(rgbfp[0]); + } else { + for (i = 0; i < Height; i++) { + GifQprintf("\b\b\b\b%-4d", i); + if (fread(RedP, Width, 1, rgbfp[0]) != 1 || + fread(GreenP, Width, 1, rgbfp[1]) != 1 || + fread(BlueP, Width, 1, rgbfp[2]) != 1) { + GIF_EXIT( + "Input file(s) terminated prematurly."); + } + RedP += Width; + GreenP += Width; + BlueP += Width; + } - fclose(rgbfp[0]); - fclose(rgbfp[1]); - fclose(rgbfp[2]); - } + fclose(rgbfp[0]); + fclose(rgbfp[1]); + fclose(rgbfp[2]); + } } /****************************************************************************** Save the GIF resulting image. ******************************************************************************/ -static void SaveGif(GifByteType *OutputBuffer, - int Width, int Height, - int ExpColorMapSize, ColorMapObject *OutputColorMap) -{ - int i, Error; - GifFileType *GifFile; - GifByteType *Ptr = OutputBuffer; - - /* Open stdout for the output file: */ - if ((GifFile = EGifOpenFileHandle(1, &Error)) == NULL) { - PrintGifError(Error); - exit(EXIT_FAILURE); - } - - if (EGifPutScreenDesc(GifFile, - Width, Height, ExpColorMapSize, 0, - OutputColorMap) == GIF_ERROR || - EGifPutImageDesc(GifFile, - 0, 0, Width, Height, false, NULL) == GIF_ERROR) { - PrintGifError(Error); - exit(EXIT_FAILURE); - } - - GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]: ", - PROGRAM_NAME, GifFile->Image.Left, GifFile->Image.Top, - GifFile->Image.Width, GifFile->Image.Height); - - for (i = 0; i < Height; i++) { - if (EGifPutLine(GifFile, Ptr, Width) == GIF_ERROR) - exit(EXIT_FAILURE); - GifQprintf("\b\b\b\b%-4d", Height - i - 1); - - Ptr += Width; - } - - if (EGifCloseFile(GifFile, &Error) == GIF_ERROR) { - PrintGifError(Error); - exit(EXIT_FAILURE); - } +static void SaveGif(GifByteType *OutputBuffer, int Width, int Height, + int ExpColorMapSize, ColorMapObject *OutputColorMap) { + int i, Error; + GifFileType *GifFile; + GifByteType *Ptr = OutputBuffer; + + /* Open stdout for the output file: */ + if ((GifFile = EGifOpenFileHandle(1, &Error)) == NULL) { + PrintGifError(Error); + exit(EXIT_FAILURE); + } + + if (EGifPutScreenDesc(GifFile, Width, Height, ExpColorMapSize, 0, + OutputColorMap) == GIF_ERROR || + EGifPutImageDesc(GifFile, 0, 0, Width, Height, false, NULL) == + GIF_ERROR) { + PrintGifError(Error); + exit(EXIT_FAILURE); + } + + GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]: ", PROGRAM_NAME, + GifFile->Image.Left, GifFile->Image.Top, + GifFile->Image.Width, GifFile->Image.Height); + + for (i = 0; i < Height; i++) { + if (EGifPutLine(GifFile, Ptr, Width) == GIF_ERROR) { + exit(EXIT_FAILURE); + } + GifQprintf("\b\b\b\b%-4d", Height - i - 1); + + Ptr += Width; + } + + if (EGifCloseFile(GifFile, &Error) == GIF_ERROR) { + PrintGifError(Error); + exit(EXIT_FAILURE); + } } /****************************************************************************** Close output file (if open), and exit. ******************************************************************************/ static void RGB2GIF(bool OneFileFlag, int NumFiles, char *FileName, - int ExpNumOfColors, int Width, int Height) -{ - int ColorMapSize; - - GifByteType *RedBuffer = NULL, *GreenBuffer = NULL, *BlueBuffer = NULL, - *OutputBuffer = NULL; - ColorMapObject *OutputColorMap = NULL; - - ColorMapSize = 1 << ExpNumOfColors; - - if (NumFiles == 1) { - LoadRGB(FileName, OneFileFlag, - &RedBuffer, &GreenBuffer, &BlueBuffer, Width, Height); - } - else { - LoadRGB(NULL, OneFileFlag, - &RedBuffer, &GreenBuffer, &BlueBuffer, Width, Height); - } - - if ((OutputColorMap = GifMakeMapObject(ColorMapSize, NULL)) == NULL || - (OutputBuffer = (GifByteType *) malloc(Width * Height * - sizeof(GifByteType))) == NULL) - GIF_EXIT("Failed to allocate memory required, aborted."); - - if (GifQuantizeBuffer(Width, Height, &ColorMapSize, - RedBuffer, GreenBuffer, BlueBuffer, - OutputBuffer, OutputColorMap->Colors) == GIF_ERROR) - exit(EXIT_FAILURE); - free((char *) RedBuffer); - free((char *) GreenBuffer); - free((char *) BlueBuffer); - - SaveGif(OutputBuffer, Width, Height, ExpNumOfColors, OutputColorMap); + int ExpNumOfColors, int Width, int Height) { + int ColorMapSize; + + GifByteType *RedBuffer = NULL, *GreenBuffer = NULL, *BlueBuffer = NULL, + *OutputBuffer = NULL; + ColorMapObject *OutputColorMap = NULL; + + ColorMapSize = 1 << ExpNumOfColors; + + if (NumFiles == 1) { + LoadRGB(FileName, OneFileFlag, &RedBuffer, &GreenBuffer, + &BlueBuffer, Width, Height); + } else { + LoadRGB(NULL, OneFileFlag, &RedBuffer, &GreenBuffer, + &BlueBuffer, Width, Height); + } + + if ((OutputColorMap = GifMakeMapObject(ColorMapSize, NULL)) == NULL || + (OutputBuffer = (GifByteType *)malloc( + Width * Height * sizeof(GifByteType))) == NULL) { + GIF_EXIT("Failed to allocate memory required, aborted."); + } + + if (GifQuantizeBuffer(Width, Height, &ColorMapSize, RedBuffer, + GreenBuffer, BlueBuffer, OutputBuffer, + OutputColorMap->Colors) == GIF_ERROR) { + exit(EXIT_FAILURE); + } + free((char *)RedBuffer); + free((char *)GreenBuffer); + free((char *)BlueBuffer); + + SaveGif(OutputBuffer, Width, Height, ExpNumOfColors, OutputColorMap); } /****************************************************************************** The real screen dumping routine. ******************************************************************************/ static void DumpScreen2RGB(char *FileName, int OneFileFlag, - ColorMapObject *ColorMap, - GifRowType *ScreenBuffer, - int ScreenWidth, int ScreenHeight) -{ - int i, j; - GifRowType GifRow; - GifColorType *ColorMapEntry; - FILE *rgbfp[3]; - - if (FileName != NULL) { - if (OneFileFlag) { - if ((rgbfp[0] = fopen(FileName, "wb")) == NULL) - GIF_EXIT("Can't open input file name."); - } else { - static char *Postfixes[] = { ".R", ".G", ".B" }; - char OneFileName[80]; - - for (i = 0; i < 3; i++) { - strncpy(OneFileName, FileName, sizeof(OneFileName)-1); - strncat(OneFileName, Postfixes[i], - sizeof(OneFileName) - 1 - strlen(OneFileName)); - - if ((rgbfp[i] = fopen(OneFileName, "wb")) == NULL) { - GIF_EXIT("Can't open input file name."); - } - } - } - } else { - OneFileFlag = true; + ColorMapObject *ColorMap, + const GifRowType *ScreenBuffer, int ScreenWidth, + int ScreenHeight) { + int i, j; + GifRowType GifRow; + GifColorType *ColorMapEntry; + FILE *rgbfp[3]; + + if (FileName != NULL) { + if (OneFileFlag) { + if ((rgbfp[0] = fopen(FileName, "wb")) == NULL) { + GIF_EXIT("Can't open input file name."); + } + } else { + static char *Postfixes[] = {".R", ".G", ".B"}; + char OneFileName[80]; + + for (i = 0; i < 3; i++) { + strncpy(OneFileName, FileName, + sizeof(OneFileName) - 1); + strncat(OneFileName, Postfixes[i], + sizeof(OneFileName) - 1 - + strlen(OneFileName)); + + if ((rgbfp[i] = fopen(OneFileName, "wb")) == + NULL) { + GIF_EXIT("Can't open input file name."); + } + } + } + } else { + OneFileFlag = true; #ifdef _WIN32 - _setmode(1, O_BINARY); + _setmode(1, O_BINARY); #endif /* _WIN32 */ - - rgbfp[0] = stdout; - } - - if (ColorMap == NULL) { - fprintf(stderr, "Color map pointer is NULL.\n"); - exit(EXIT_FAILURE); - } - - if (OneFileFlag) { - unsigned char *Buffer, *BufferP; - - if ((Buffer = (unsigned char *) malloc(ScreenWidth * 3)) == NULL) - GIF_EXIT("Failed to allocate memory required, aborted."); - for (i = 0; i < ScreenHeight; i++) { - GifRow = ScreenBuffer[i]; - GifQprintf("\b\b\b\b%-4d", ScreenHeight - i); - for (j = 0, BufferP = Buffer; j < ScreenWidth; j++) { - ColorMapEntry = &ColorMap->Colors[GifRow[j]]; - *BufferP++ = ColorMapEntry->Red; - *BufferP++ = ColorMapEntry->Green; - *BufferP++ = ColorMapEntry->Blue; - } - if (fwrite(Buffer, ScreenWidth * 3, 1, rgbfp[0]) != 1) - GIF_EXIT("Write to file(s) failed."); - } - - free((char *) Buffer); - fclose(rgbfp[0]); - } else { - unsigned char *Buffers[3]; - - if ((Buffers[0] = (unsigned char *) malloc(ScreenWidth)) == NULL || - (Buffers[1] = (unsigned char *) malloc(ScreenWidth)) == NULL || - (Buffers[2] = (unsigned char *) malloc(ScreenWidth)) == NULL) - GIF_EXIT("Failed to allocate memory required, aborted."); - - for (i = 0; i < ScreenHeight; i++) { - GifRow = ScreenBuffer[i]; - GifQprintf("\b\b\b\b%-4d", ScreenHeight - i); - for (j = 0; j < ScreenWidth; j++) { - ColorMapEntry = &ColorMap->Colors[GifRow[j]]; - Buffers[0][j] = ColorMapEntry->Red; - Buffers[1][j] = ColorMapEntry->Green; - Buffers[2][j] = ColorMapEntry->Blue; - } - if (fwrite(Buffers[0], ScreenWidth, 1, rgbfp[0]) != 1 || - fwrite(Buffers[1], ScreenWidth, 1, rgbfp[1]) != 1 || - fwrite(Buffers[2], ScreenWidth, 1, rgbfp[2]) != 1) - GIF_EXIT("Write to file(s) failed."); - } - - free((char *) Buffers[0]); - free((char *) Buffers[1]); - free((char *) Buffers[2]); - fclose(rgbfp[0]); - fclose(rgbfp[1]); - fclose(rgbfp[2]); - } + + rgbfp[0] = stdout; + } + + if (ColorMap == NULL) { + fprintf(stderr, "Color map pointer is NULL.\n"); + exit(EXIT_FAILURE); + } + + if (OneFileFlag) { + unsigned char *Buffer, *BufferP; + + if ((Buffer = (unsigned char *)malloc(ScreenWidth * 3)) == + NULL) { + GIF_EXIT( + "Failed to allocate memory required, aborted."); + } + for (i = 0; i < ScreenHeight; i++) { + GifRow = ScreenBuffer[i]; + GifQprintf("\b\b\b\b%-4d", ScreenHeight - i); + for (j = 0, BufferP = Buffer; j < ScreenWidth; j++) { + /* Check if color is within color palete */ + if (GifRow[j] >= ColorMap->ColorCount) { + GIF_EXIT(GifErrorString( + D_GIF_ERR_IMAGE_DEFECT)); + } + ColorMapEntry = &ColorMap->Colors[GifRow[j]]; + *BufferP++ = ColorMapEntry->Red; + *BufferP++ = ColorMapEntry->Green; + *BufferP++ = ColorMapEntry->Blue; + } + if (fwrite(Buffer, ScreenWidth * 3, 1, rgbfp[0]) != 1) { + GIF_EXIT("Write to file(s) failed."); + } + } + + free((char *)Buffer); + fclose(rgbfp[0]); + } else { + unsigned char *Buffers[3]; + + if ((Buffers[0] = (unsigned char *)malloc(ScreenWidth)) == + NULL || + (Buffers[1] = (unsigned char *)malloc(ScreenWidth)) == + NULL || + (Buffers[2] = (unsigned char *)malloc(ScreenWidth)) == + NULL) { + GIF_EXIT( + "Failed to allocate memory required, aborted."); + } + + for (i = 0; i < ScreenHeight; i++) { + GifRow = ScreenBuffer[i]; + GifQprintf("\b\b\b\b%-4d", ScreenHeight - i); + for (j = 0; j < ScreenWidth; j++) { + ColorMapEntry = &ColorMap->Colors[GifRow[j]]; + Buffers[0][j] = ColorMapEntry->Red; + Buffers[1][j] = ColorMapEntry->Green; + Buffers[2][j] = ColorMapEntry->Blue; + } + if (fwrite(Buffers[0], ScreenWidth, 1, rgbfp[0]) != 1 || + fwrite(Buffers[1], ScreenWidth, 1, rgbfp[1]) != 1 || + fwrite(Buffers[2], ScreenWidth, 1, rgbfp[2]) != 1) { + GIF_EXIT("Write to file(s) failed."); + } + } + + free((char *)Buffers[0]); + free((char *)Buffers[1]); + free((char *)Buffers[2]); + fclose(rgbfp[0]); + fclose(rgbfp[1]); + fclose(rgbfp[2]); + } } -static void GIF2RGB(int NumFiles, char *FileName, - bool OneFileFlag, - char *OutFileName) -{ - int i, j, Size, Row, Col, Width, Height, ExtCode, Count; - GifRecordType RecordType; - GifByteType *Extension; - GifRowType *ScreenBuffer; - GifFileType *GifFile; - int - InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */ - InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */ - int ImageNum = 0; - ColorMapObject *ColorMap; - int Error; - - if (NumFiles == 1) { - int Error; - if ((GifFile = DGifOpenFileName(FileName, &Error)) == NULL) { - PrintGifError(Error); - exit(EXIT_FAILURE); +static void GIF2RGB(int NumFiles, char *FileName, bool OneFileFlag, + char *OutFileName) { + int i, j, Size, Row, Col, Width, Height, ExtCode, Count; + GifRecordType RecordType; + GifByteType *Extension; + GifRowType *ScreenBuffer; + GifFileType *GifFile; + static const int InterlacedOffset[] = { + 0, 4, 2, 1}; /* The way Interlaced image should. */ + static const int InterlacedJumps[] = { + 8, 8, 4, 2}; /* be read - offsets and jumps... */ + int ImageNum = 0; + ColorMapObject *ColorMap; + + if (NumFiles == 1) { + int Error; + if ((GifFile = DGifOpenFileName(FileName, &Error)) == NULL) { + PrintGifError(Error); + exit(EXIT_FAILURE); + } + } else { + int Error; + /* Use stdin instead: */ + if ((GifFile = DGifOpenFileHandle(0, &Error)) == NULL) { + PrintGifError(Error); + exit(EXIT_FAILURE); + } } - } - else { - int Error; - /* Use stdin instead: */ - if ((GifFile = DGifOpenFileHandle(0, &Error)) == NULL) { - PrintGifError(Error); - exit(EXIT_FAILURE); + + if (GifFile->SHeight == 0 || GifFile->SWidth == 0) { + fprintf(stderr, "Image of width or height 0\n"); + exit(EXIT_FAILURE); } - } - - if (GifFile->SHeight == 0 || GifFile->SWidth == 0) { - fprintf(stderr, "Image of width or height 0\n"); - exit(EXIT_FAILURE); - } - - /* - * Allocate the screen as vector of column of rows. Note this - * screen is device independent - it's the screen defined by the - * GIF file parameters. - */ - if ((ScreenBuffer = (GifRowType *) - malloc(GifFile->SHeight * sizeof(GifRowType))) == NULL) - GIF_EXIT("Failed to allocate memory required, aborted."); - - Size = GifFile->SWidth * sizeof(GifPixelType);/* Size in bytes one row.*/ - if ((ScreenBuffer[0] = (GifRowType) malloc(Size)) == NULL) /* First row. */ - GIF_EXIT("Failed to allocate memory required, aborted."); - - for (i = 0; i < GifFile->SWidth; i++) /* Set its color to BackGround. */ - ScreenBuffer[0][i] = GifFile->SBackGroundColor; - for (i = 1; i < GifFile->SHeight; i++) { - /* Allocate the other rows, and set their color to background too: */ - if ((ScreenBuffer[i] = (GifRowType) malloc(Size)) == NULL) - GIF_EXIT("Failed to allocate memory required, aborted."); - - memcpy(ScreenBuffer[i], ScreenBuffer[0], Size); - } - - /* Scan the content of the GIF file and load the image(s) in: */ - do { - if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) { - PrintGifError(GifFile->Error); - exit(EXIT_FAILURE); + + /* + * Allocate the screen as vector of column of rows. Note this + * screen is device independent - it's the screen defined by the + * GIF file parameters. + */ + if ((ScreenBuffer = (GifRowType *)malloc(GifFile->SHeight * + sizeof(GifRowType))) == NULL) { + GIF_EXIT("Failed to allocate memory required, aborted."); } - switch (RecordType) { - case IMAGE_DESC_RECORD_TYPE: - if (DGifGetImageDesc(GifFile) == GIF_ERROR) { - PrintGifError(GifFile->Error); - exit(EXIT_FAILURE); + + Size = + GifFile->SWidth * sizeof(GifPixelType); /* Size in bytes one row.*/ + if ((ScreenBuffer[0] = (GifRowType)malloc(Size)) == + NULL) { /* First row. */ + GIF_EXIT("Failed to allocate memory required, aborted."); + } + + for (i = 0; i < GifFile->SWidth; + i++) { /* Set its color to BackGround. */ + ScreenBuffer[0][i] = GifFile->SBackGroundColor; + } + for (i = 1; i < GifFile->SHeight; i++) { + /* Allocate the other rows, and set their color to background + * too: */ + if ((ScreenBuffer[i] = (GifRowType)malloc(Size)) == NULL) { + GIF_EXIT( + "Failed to allocate memory required, aborted."); } - Row = GifFile->Image.Top; /* Image Position relative to Screen. */ - Col = GifFile->Image.Left; - Width = GifFile->Image.Width; - Height = GifFile->Image.Height; - GifQprintf("\n%s: Image %d at (%d, %d) [%dx%d]: ", - PROGRAM_NAME, ++ImageNum, Col, Row, Width, Height); - if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth || - GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) { - fprintf(stderr, "Image %d is not confined to screen dimension, aborted.\n",ImageNum); - exit(EXIT_FAILURE); + + memcpy(ScreenBuffer[i], ScreenBuffer[0], Size); + } + + /* Scan the content of the GIF file and load the image(s) in: */ + do { + if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); } - if (GifFile->Image.Interlace) { - /* Need to perform 4 passes on the images: */ - for (Count = i = 0; i < 4; i++) - for (j = Row + InterlacedOffset[i]; j < Row + Height; - j += InterlacedJumps[i]) { - GifQprintf("\b\b\b\b%-4d", Count++); - if (DGifGetLine(GifFile, &ScreenBuffer[j][Col], - Width) == GIF_ERROR) { + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFile) == GIF_ERROR) { PrintGifError(GifFile->Error); exit(EXIT_FAILURE); - } } - } - else { - for (i = 0; i < Height; i++) { - GifQprintf("\b\b\b\b%-4d", i); - if (DGifGetLine(GifFile, &ScreenBuffer[Row++][Col], - Width) == GIF_ERROR) { - PrintGifError(GifFile->Error); - exit(EXIT_FAILURE); + Row = GifFile->Image + .Top; /* Image Position relative to Screen. */ + Col = GifFile->Image.Left; + Width = GifFile->Image.Width; + Height = GifFile->Image.Height; + GifQprintf("\n%s: Image %d at (%d, %d) [%dx%d]: ", + PROGRAM_NAME, ++ImageNum, Col, Row, Width, + Height); + if (GifFile->Image.Left + GifFile->Image.Width > + GifFile->SWidth || + GifFile->Image.Top + GifFile->Image.Height > + GifFile->SHeight) { + fprintf(stderr, + "Image %d is not confined to screen " + "dimension, aborted.\n", + ImageNum); + exit(EXIT_FAILURE); } - } - } - break; - case EXTENSION_RECORD_TYPE: - /* Skip any extension blocks in file: */ - if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) { - PrintGifError(GifFile->Error); - exit(EXIT_FAILURE); + if (GifFile->Image.Interlace) { + /* Need to perform 4 passes on the images: */ + for (Count = i = 0; i < 4; i++) { + for (j = Row + InterlacedOffset[i]; + j < Row + Height; + j += InterlacedJumps[i]) { + GifQprintf("\b\b\b\b%-4d", + Count++); + if (DGifGetLine( + GifFile, + &ScreenBuffer[j][Col], + Width) == GIF_ERROR) { + PrintGifError( + GifFile->Error); + exit(EXIT_FAILURE); + } + } + } + } else { + for (i = 0; i < Height; i++) { + GifQprintf("\b\b\b\b%-4d", i); + if (DGifGetLine( + GifFile, + &ScreenBuffer[Row++][Col], + Width) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + } + } + break; + case EXTENSION_RECORD_TYPE: + /* Skip any extension blocks in file: */ + if (DGifGetExtension(GifFile, &ExtCode, &Extension) == + GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + while (Extension != NULL) { + if (DGifGetExtensionNext(GifFile, &Extension) == + GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + } + break; + case TERMINATE_RECORD_TYPE: + break; + default: /* Should be trapped by DGifGetRecordType. */ + break; } - while (Extension != NULL) { - if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) { - PrintGifError(GifFile->Error); + } while (RecordType != TERMINATE_RECORD_TYPE); + + /* Lets dump it - set the global variables required and do it: */ + ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap + : GifFile->SColorMap); + if (ColorMap == NULL) { + fprintf(stderr, "Gif Image does not have a colormap\n"); + exit(EXIT_FAILURE); + } + + /* check that the background color isn't garbage (SF bug #87) */ + if (GifFile->SBackGroundColor < 0 || + GifFile->SBackGroundColor >= ColorMap->ColorCount) { + fprintf(stderr, "Background color out of range for colormap\n"); + exit(EXIT_FAILURE); + } + + DumpScreen2RGB(OutFileName, OneFileFlag, ColorMap, ScreenBuffer, + GifFile->SWidth, GifFile->SHeight); + + (void)free(ScreenBuffer); + + { + int Error; + if (DGifCloseFile(GifFile, &Error) == GIF_ERROR) { + PrintGifError(Error); exit(EXIT_FAILURE); - } } - break; - case TERMINATE_RECORD_TYPE: - break; - default: /* Should be trapped by DGifGetRecordType. */ - break; } - } while (RecordType != TERMINATE_RECORD_TYPE); - - /* Lets dump it - set the global variables required and do it: */ - ColorMap = (GifFile->Image.ColorMap - ? GifFile->Image.ColorMap - : GifFile->SColorMap); - if (ColorMap == NULL) { - fprintf(stderr, "Gif Image does not have a colormap\n"); - exit(EXIT_FAILURE); - } - - /* check that the background color isn't garbage (SF bug #87) */ - if (GifFile->SBackGroundColor < 0 || GifFile->SBackGroundColor >= ColorMap->ColorCount) { - fprintf(stderr, "Background color out of range for colormap\n"); - exit(EXIT_FAILURE); - } - - DumpScreen2RGB(OutFileName, OneFileFlag, - ColorMap, - ScreenBuffer, - GifFile->SWidth, GifFile->SHeight); - - (void)free(ScreenBuffer); - - if (DGifCloseFile(GifFile, &Error) == GIF_ERROR) { - PrintGifError(Error); - exit(EXIT_FAILURE); - } - } /****************************************************************************** -* Interpret the command line and scan the given GIF file. -******************************************************************************/ -int main(int argc, char **argv) -{ - bool Error, OutFileFlag = false, ColorFlag = false, SizeFlag = false; - int NumFiles, Width = 0, Height = 0, ExpNumOfColors = 8; - char *OutFileName, - **FileName = NULL; - static bool - OneFileFlag = false, - HelpFlag = false; - - if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, - &ColorFlag, &ExpNumOfColors, &SizeFlag, &Width, &Height, - &OneFileFlag, &OutFileFlag, &OutFileName, - &HelpFlag, &NumFiles, &FileName)) != false || - (NumFiles > 1 && !HelpFlag)) { - if (Error) - GAPrintErrMsg(Error); - else if (NumFiles > 1) - GIF_MESSAGE("Error in command line parsing - one input file please."); - GAPrintHowTo(CtrlStr); - exit(EXIT_FAILURE); - } - - if (HelpFlag) { - (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); - GAPrintHowTo(CtrlStr); - exit(EXIT_SUCCESS); - } - if (!OutFileFlag) OutFileName = NULL; - - if (SizeFlag && Width > 0 && Height > 0) - RGB2GIF(OneFileFlag, NumFiles, *FileName, - ExpNumOfColors, Width, Height); - else - GIF2RGB(NumFiles, *FileName, OneFileFlag, OutFileName); - - return 0; + * Interpret the command line and scan the given GIF file. + ******************************************************************************/ +int main(int argc, char **argv) { + bool Error, OutFileFlag = false, ColorFlag = false, SizeFlag = false, + GifNoisyPrint = false; + int NumFiles, Width = 0, Height = 0, ExpNumOfColors = 8; + char *OutFileName, **FileName = NULL; + static bool OneFileFlag = false, HelpFlag = false; + + if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &ColorFlag, + &ExpNumOfColors, &SizeFlag, &Width, &Height, + &OneFileFlag, &OutFileFlag, &OutFileName, + &HelpFlag, &NumFiles, &FileName)) != false || + (NumFiles > 1 && !HelpFlag)) { + if (Error) { + GAPrintErrMsg(Error); + } else if (NumFiles > 1) { + GIF_MESSAGE("Error in command line parsing - one input " + "file please."); + } + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); + } + if (!OutFileFlag) { + OutFileName = NULL; + } + + if (SizeFlag) { + if ((Width <= 0 || Height <= 0) || (Height > INT_MAX / Width)) { + GIF_MESSAGE( + "Image size would be overflow, zero or negative"); + exit(EXIT_FAILURE); + } + RGB2GIF(OneFileFlag, NumFiles, *FileName, ExpNumOfColors, Width, + Height); + } else { + GIF2RGB(NumFiles, *FileName, OneFileFlag, OutFileName); + } + + return 0; } /* end */ diff --git a/gif_err.c b/gif_err.c index 3ec2a56..0226194 100644 --- a/gif_err.c +++ b/gif_err.c @@ -2,6 +2,8 @@ gif_err.c - handle error reporting for the GIF library. +SPDX-License-Identifier: MIT + ****************************************************************************/ #include @@ -12,86 +14,84 @@ gif_err.c - handle error reporting for the GIF library. /***************************************************************************** Return a string description of the last GIF error *****************************************************************************/ -const char * -GifErrorString(int ErrorCode) -{ - const char *Err; +const char *GifErrorString(int ErrorCode) { + const char *Err; - switch (ErrorCode) { - case E_GIF_ERR_OPEN_FAILED: - Err = "Failed to open given file"; - break; - case E_GIF_ERR_WRITE_FAILED: - Err = "Failed to write to given file"; - break; - case E_GIF_ERR_HAS_SCRN_DSCR: - Err = "Screen descriptor has already been set"; - break; - case E_GIF_ERR_HAS_IMAG_DSCR: - Err = "Image descriptor is still active"; - break; - case E_GIF_ERR_NO_COLOR_MAP: - Err = "Neither global nor local color map"; - break; - case E_GIF_ERR_DATA_TOO_BIG: - Err = "Number of pixels bigger than width * height"; - break; - case E_GIF_ERR_NOT_ENOUGH_MEM: - Err = "Failed to allocate required memory"; - break; - case E_GIF_ERR_DISK_IS_FULL: - Err = "Write failed (disk full?)"; - break; - case E_GIF_ERR_CLOSE_FAILED: - Err = "Failed to close given file"; - break; - case E_GIF_ERR_NOT_WRITEABLE: - Err = "Given file was not opened for write"; - break; - case D_GIF_ERR_OPEN_FAILED: - Err = "Failed to open given file"; - break; - case D_GIF_ERR_READ_FAILED: - Err = "Failed to read from given file"; - break; - case D_GIF_ERR_NOT_GIF_FILE: - Err = "Data is not in GIF format"; - break; - case D_GIF_ERR_NO_SCRN_DSCR: - Err = "No screen descriptor detected"; - break; - case D_GIF_ERR_NO_IMAG_DSCR: - Err = "No Image Descriptor detected"; - break; - case D_GIF_ERR_NO_COLOR_MAP: - Err = "Neither global nor local color map"; - break; - case D_GIF_ERR_WRONG_RECORD: - Err = "Wrong record type detected"; - break; - case D_GIF_ERR_DATA_TOO_BIG: - Err = "Number of pixels bigger than width * height"; - break; - case D_GIF_ERR_NOT_ENOUGH_MEM: - Err = "Failed to allocate required memory"; - break; - case D_GIF_ERR_CLOSE_FAILED: - Err = "Failed to close given file"; - break; - case D_GIF_ERR_NOT_READABLE: - Err = "Given file was not opened for read"; - break; - case D_GIF_ERR_IMAGE_DEFECT: - Err = "Image is defective, decoding aborted"; - break; - case D_GIF_ERR_EOF_TOO_SOON: - Err = "Image EOF detected before image complete"; - break; - default: - Err = NULL; - break; - } - return Err; + switch (ErrorCode) { + case E_GIF_ERR_OPEN_FAILED: + Err = "Failed to open given file"; + break; + case E_GIF_ERR_WRITE_FAILED: + Err = "Failed to write to given file"; + break; + case E_GIF_ERR_HAS_SCRN_DSCR: + Err = "Screen descriptor has already been set"; + break; + case E_GIF_ERR_HAS_IMAG_DSCR: + Err = "Image descriptor is still active"; + break; + case E_GIF_ERR_NO_COLOR_MAP: + Err = "Neither global nor local color map"; + break; + case E_GIF_ERR_DATA_TOO_BIG: + Err = "Number of pixels bigger than width * height"; + break; + case E_GIF_ERR_NOT_ENOUGH_MEM: + Err = "Failed to allocate required memory"; + break; + case E_GIF_ERR_DISK_IS_FULL: + Err = "Write failed (disk full?)"; + break; + case E_GIF_ERR_CLOSE_FAILED: + Err = "Failed to close given file"; + break; + case E_GIF_ERR_NOT_WRITEABLE: + Err = "Given file was not opened for write"; + break; + case D_GIF_ERR_OPEN_FAILED: + Err = "Failed to open given file"; + break; + case D_GIF_ERR_READ_FAILED: + Err = "Failed to read from given file"; + break; + case D_GIF_ERR_NOT_GIF_FILE: + Err = "Data is not in GIF format"; + break; + case D_GIF_ERR_NO_SCRN_DSCR: + Err = "No screen descriptor detected"; + break; + case D_GIF_ERR_NO_IMAG_DSCR: + Err = "No Image Descriptor detected"; + break; + case D_GIF_ERR_NO_COLOR_MAP: + Err = "Neither global nor local color map"; + break; + case D_GIF_ERR_WRONG_RECORD: + Err = "Wrong record type detected"; + break; + case D_GIF_ERR_DATA_TOO_BIG: + Err = "Number of pixels bigger than width * height"; + break; + case D_GIF_ERR_NOT_ENOUGH_MEM: + Err = "Failed to allocate required memory"; + break; + case D_GIF_ERR_CLOSE_FAILED: + Err = "Failed to close given file"; + break; + case D_GIF_ERR_NOT_READABLE: + Err = "Given file was not opened for read"; + break; + case D_GIF_ERR_IMAGE_DEFECT: + Err = "Image is defective, decoding aborted"; + break; + case D_GIF_ERR_EOF_TOO_SOON: + Err = "Image EOF detected before image complete"; + break; + default: + Err = NULL; + break; + } + return Err; } /* end */ diff --git a/gif_font.c b/gif_font.c index 839c9fa..75f9731 100644 --- a/gif_font.c +++ b/gif_font.c @@ -1,11 +1,13 @@ /***************************************************************************** - + gif_font.c - utility font handling and simple drawing for the GIF library - + +SPDX-License-Identifier: MIT + ****************************************************************************/ -#include #include +#include #include "gif_lib.h" @@ -20,240 +22,240 @@ gif_font.c - utility font handling and simple drawing for the GIF library */ /*@+charint@*/ const unsigned char GifAsciiTable8x8[][GIF_FONT_WIDTH] = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* Ascii 0 */ - {0x3c, 0x42, 0xa5, 0x81, 0xbd, 0x42, 0x3c, 0x00}, /* Ascii 1 */ - {0x3c, 0x7e, 0xdb, 0xff, 0xc3, 0x7e, 0x3c, 0x00}, /* Ascii 2 */ - {0x00, 0xee, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* Ascii 3 */ - {0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* Ascii 4 */ - {0x00, 0x3c, 0x18, 0xff, 0xff, 0x08, 0x18, 0x00}, /* Ascii 5 */ - {0x10, 0x38, 0x7c, 0xfe, 0xfe, 0x10, 0x38, 0x00}, /* Ascii 6 */ - {0x00, 0x00, 0x18, 0x3c, 0x18, 0x00, 0x00, 0x00}, /* Ascii 7 */ - {0xff, 0xff, 0xe7, 0xc3, 0xe7, 0xff, 0xff, 0xff}, /* Ascii 8 */ - {0x00, 0x3c, 0x42, 0x81, 0x81, 0x42, 0x3c, 0x00}, /* Ascii 9 */ - {0xff, 0xc3, 0xbd, 0x7e, 0x7e, 0xbd, 0xc3, 0xff}, /* Ascii 10 */ - {0x1f, 0x07, 0x0d, 0x7c, 0xc6, 0xc6, 0x7c, 0x00}, /* Ascii 11 */ - {0x00, 0x7e, 0xc3, 0xc3, 0x7e, 0x18, 0x7e, 0x18}, /* Ascii 12 */ - {0x04, 0x06, 0x07, 0x04, 0x04, 0xfc, 0xf8, 0x00}, /* Ascii 13 */ - {0x0c, 0x0a, 0x0d, 0x0b, 0xf9, 0xf9, 0x1f, 0x1f}, /* Ascii 14 */ - {0x00, 0x92, 0x7c, 0x44, 0xc6, 0x7c, 0x92, 0x00}, /* Ascii 15 */ - {0x00, 0x00, 0x60, 0x78, 0x7e, 0x78, 0x60, 0x00}, /* Ascii 16 */ - {0x00, 0x00, 0x06, 0x1e, 0x7e, 0x1e, 0x06, 0x00}, /* Ascii 17 */ - {0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x18}, /* Ascii 18 */ - {0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00}, /* Ascii 19 */ - {0xff, 0xb6, 0x76, 0x36, 0x36, 0x36, 0x36, 0x00}, /* Ascii 20 */ - {0x7e, 0xc1, 0xdc, 0x22, 0x22, 0x1f, 0x83, 0x7e}, /* Ascii 21 */ - {0x00, 0x00, 0x00, 0x7e, 0x7e, 0x00, 0x00, 0x00}, /* Ascii 22 */ - {0x18, 0x7e, 0x18, 0x18, 0x7e, 0x18, 0x00, 0xff}, /* Ascii 23 */ - {0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00}, /* Ascii 24 */ - {0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x00}, /* Ascii 25 */ - {0x00, 0x04, 0x06, 0xff, 0x06, 0x04, 0x00, 0x00}, /* Ascii 26 */ - {0x00, 0x20, 0x60, 0xff, 0x60, 0x20, 0x00, 0x00}, /* Ascii 27 */ - {0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xff, 0x00}, /* Ascii 28 */ - {0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00}, /* Ascii 29 */ - {0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x00, 0x00}, /* Ascii 30 */ - {0x00, 0x00, 0x00, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* Ascii 31 */ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* */ - {0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x00}, /* ! */ - {0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* " */ - {0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00}, /* # */ - {0x10, 0x7c, 0xd2, 0x7c, 0x86, 0x7c, 0x10, 0x00}, /* $ */ - {0xf0, 0x96, 0xfc, 0x18, 0x3e, 0x72, 0xde, 0x00}, /* % */ - {0x30, 0x48, 0x30, 0x78, 0xce, 0xcc, 0x78, 0x00}, /* & */ - {0x0c, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ' */ - {0x10, 0x60, 0xc0, 0xc0, 0xc0, 0x60, 0x10, 0x00}, /* ( */ - {0x10, 0x0c, 0x06, 0x06, 0x06, 0x0c, 0x10, 0x00}, /* ) */ - {0x00, 0x54, 0x38, 0xfe, 0x38, 0x54, 0x00, 0x00}, /* * */ - {0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00}, /* + */ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x70}, /* , */ - {0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00}, /* - */ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00}, /* . */ - {0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00}, /* / */ - {0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* 0 */ - {0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x3c, 0x00}, /* 1 */ - {0x7c, 0xc6, 0x06, 0x0c, 0x30, 0x60, 0xfe, 0x00}, /* 2 */ - {0x7c, 0xc6, 0x06, 0x3c, 0x06, 0xc6, 0x7c, 0x00}, /* 3 */ - {0x0e, 0x1e, 0x36, 0x66, 0xfe, 0x06, 0x06, 0x00}, /* 4 */ - {0xfe, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0xfc, 0x00}, /* 5 */ - {0x7c, 0xc6, 0xc0, 0xfc, 0xc6, 0xc6, 0x7c, 0x00}, /* 6 */ - {0xfe, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x60, 0x00}, /* 7 */ - {0x7c, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0x7c, 0x00}, /* 8 */ - {0x7c, 0xc6, 0xc6, 0x7e, 0x06, 0xc6, 0x7c, 0x00}, /* 9 */ - {0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00}, /* : */ - {0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x20, 0x00}, /* }, */ - {0x00, 0x1c, 0x30, 0x60, 0x30, 0x1c, 0x00, 0x00}, /* < */ - {0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00}, /* = */ - {0x00, 0x70, 0x18, 0x0c, 0x18, 0x70, 0x00, 0x00}, /* > */ - {0x7c, 0xc6, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00}, /* ? */ - {0x7c, 0x82, 0x9a, 0xaa, 0xaa, 0x9e, 0x7c, 0x00}, /* @ */ - {0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00}, /* A */ - {0xfc, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xfc, 0x00}, /* B */ - {0x7c, 0xc6, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x00}, /* C */ - {0xf8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x00}, /* D */ - {0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xfe, 0x00}, /* E */ - {0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0x00}, /* F */ - {0x7c, 0xc6, 0xc0, 0xce, 0xc6, 0xc6, 0x7e, 0x00}, /* G */ - {0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00}, /* H */ - {0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00}, /* I */ - {0x1e, 0x06, 0x06, 0x06, 0xc6, 0xc6, 0x7c, 0x00}, /* J */ - {0xc6, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0xc6, 0x00}, /* K */ - {0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00}, /* L */ - {0xc6, 0xee, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0x00}, /* M */ - {0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00}, /* N */ - {0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* O */ - {0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0x00}, /* P */ - {0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x06}, /* Q */ - {0xfc, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xc6, 0x00}, /* R */ - {0x78, 0xcc, 0x60, 0x30, 0x18, 0xcc, 0x78, 0x00}, /* S */ - {0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00}, /* T */ - {0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* U */ - {0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00}, /* V */ - {0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00}, /* W */ - {0xc6, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0xc6, 0x00}, /* X */ - {0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00}, /* Y */ - {0xfe, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xfe, 0x00}, /* Z */ - {0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00}, /* [ */ - {0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x00}, /* \ */ - {0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00}, /* ] */ - {0x00, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00}, /* ^ */ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}, /* _ */ - {0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ` */ - {0x00, 0x00, 0x7c, 0x06, 0x7e, 0xc6, 0x7e, 0x00}, /* a */ - {0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xe6, 0xdc, 0x00}, /* b */ - {0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0x7e, 0x00}, /* c */ - {0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xce, 0x76, 0x00}, /* d */ - {0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0x7e, 0x00}, /* e */ - {0x1e, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x30, 0x00}, /* f */ - {0x00, 0x00, 0x7e, 0xc6, 0xce, 0x76, 0x06, 0x7c}, /* g */ - {0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00}, /* */ - {0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00}, /* i */ - {0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0xf0}, /* j */ - {0xc0, 0xc0, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0x00}, /* k */ - {0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00}, /* l */ - {0x00, 0x00, 0xcc, 0xfe, 0xd6, 0xc6, 0xc6, 0x00}, /* m */ - {0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00}, /* n */ - {0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* o */ - {0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xe6, 0xdc, 0xc0}, /* p */ - {0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xce, 0x76, 0x06}, /* q */ - {0x00, 0x00, 0x6e, 0x70, 0x60, 0x60, 0x60, 0x00}, /* r */ - {0x00, 0x00, 0x7c, 0xc0, 0x7c, 0x06, 0xfc, 0x00}, /* s */ - {0x30, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x1c, 0x00}, /* t */ - {0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00}, /* u */ - {0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00}, /* v */ - {0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xfe, 0x6c, 0x00}, /* w */ - {0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00}, /* x */ - {0x00, 0x00, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0x7c}, /* y */ - {0x00, 0x00, 0xfc, 0x18, 0x30, 0x60, 0xfc, 0x00}, /* z */ - {0x0e, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0e, 0x00}, /* { */ - {0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, /* | */ - {0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00}, /* } */ - {0x00, 0x00, 0x70, 0x9a, 0x0e, 0x00, 0x00, 0x00}, /* ~ */ - {0x00, 0x00, 0x18, 0x3c, 0x66, 0xff, 0x00, 0x00} /* Ascii 127 */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* Ascii 0 */ + {0x3c, 0x42, 0xa5, 0x81, 0xbd, 0x42, 0x3c, 0x00}, /* Ascii 1 */ + {0x3c, 0x7e, 0xdb, 0xff, 0xc3, 0x7e, 0x3c, 0x00}, /* Ascii 2 */ + {0x00, 0xee, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* Ascii 3 */ + {0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* Ascii 4 */ + {0x00, 0x3c, 0x18, 0xff, 0xff, 0x08, 0x18, 0x00}, /* Ascii 5 */ + {0x10, 0x38, 0x7c, 0xfe, 0xfe, 0x10, 0x38, 0x00}, /* Ascii 6 */ + {0x00, 0x00, 0x18, 0x3c, 0x18, 0x00, 0x00, 0x00}, /* Ascii 7 */ + {0xff, 0xff, 0xe7, 0xc3, 0xe7, 0xff, 0xff, 0xff}, /* Ascii 8 */ + {0x00, 0x3c, 0x42, 0x81, 0x81, 0x42, 0x3c, 0x00}, /* Ascii 9 */ + {0xff, 0xc3, 0xbd, 0x7e, 0x7e, 0xbd, 0xc3, 0xff}, /* Ascii 10 */ + {0x1f, 0x07, 0x0d, 0x7c, 0xc6, 0xc6, 0x7c, 0x00}, /* Ascii 11 */ + {0x00, 0x7e, 0xc3, 0xc3, 0x7e, 0x18, 0x7e, 0x18}, /* Ascii 12 */ + {0x04, 0x06, 0x07, 0x04, 0x04, 0xfc, 0xf8, 0x00}, /* Ascii 13 */ + {0x0c, 0x0a, 0x0d, 0x0b, 0xf9, 0xf9, 0x1f, 0x1f}, /* Ascii 14 */ + {0x00, 0x92, 0x7c, 0x44, 0xc6, 0x7c, 0x92, 0x00}, /* Ascii 15 */ + {0x00, 0x00, 0x60, 0x78, 0x7e, 0x78, 0x60, 0x00}, /* Ascii 16 */ + {0x00, 0x00, 0x06, 0x1e, 0x7e, 0x1e, 0x06, 0x00}, /* Ascii 17 */ + {0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x18}, /* Ascii 18 */ + {0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00}, /* Ascii 19 */ + {0xff, 0xb6, 0x76, 0x36, 0x36, 0x36, 0x36, 0x00}, /* Ascii 20 */ + {0x7e, 0xc1, 0xdc, 0x22, 0x22, 0x1f, 0x83, 0x7e}, /* Ascii 21 */ + {0x00, 0x00, 0x00, 0x7e, 0x7e, 0x00, 0x00, 0x00}, /* Ascii 22 */ + {0x18, 0x7e, 0x18, 0x18, 0x7e, 0x18, 0x00, 0xff}, /* Ascii 23 */ + {0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00}, /* Ascii 24 */ + {0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x00}, /* Ascii 25 */ + {0x00, 0x04, 0x06, 0xff, 0x06, 0x04, 0x00, 0x00}, /* Ascii 26 */ + {0x00, 0x20, 0x60, 0xff, 0x60, 0x20, 0x00, 0x00}, /* Ascii 27 */ + {0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xff, 0x00}, /* Ascii 28 */ + {0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00}, /* Ascii 29 */ + {0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x00, 0x00}, /* Ascii 30 */ + {0x00, 0x00, 0x00, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* Ascii 31 */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* */ + {0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x00}, /* ! */ + {0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* " */ + {0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00}, /* # */ + {0x10, 0x7c, 0xd2, 0x7c, 0x86, 0x7c, 0x10, 0x00}, /* $ */ + {0xf0, 0x96, 0xfc, 0x18, 0x3e, 0x72, 0xde, 0x00}, /* % */ + {0x30, 0x48, 0x30, 0x78, 0xce, 0xcc, 0x78, 0x00}, /* & */ + {0x0c, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ' */ + {0x10, 0x60, 0xc0, 0xc0, 0xc0, 0x60, 0x10, 0x00}, /* ( */ + {0x10, 0x0c, 0x06, 0x06, 0x06, 0x0c, 0x10, 0x00}, /* ) */ + {0x00, 0x54, 0x38, 0xfe, 0x38, 0x54, 0x00, 0x00}, /* * */ + {0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00}, /* + */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x70}, /* , */ + {0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00}, /* - */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00}, /* . */ + {0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x00}, /* / */ + {0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* 0 */ + {0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x3c, 0x00}, /* 1 */ + {0x7c, 0xc6, 0x06, 0x0c, 0x30, 0x60, 0xfe, 0x00}, /* 2 */ + {0x7c, 0xc6, 0x06, 0x3c, 0x06, 0xc6, 0x7c, 0x00}, /* 3 */ + {0x0e, 0x1e, 0x36, 0x66, 0xfe, 0x06, 0x06, 0x00}, /* 4 */ + {0xfe, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0xfc, 0x00}, /* 5 */ + {0x7c, 0xc6, 0xc0, 0xfc, 0xc6, 0xc6, 0x7c, 0x00}, /* 6 */ + {0xfe, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x60, 0x00}, /* 7 */ + {0x7c, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0x7c, 0x00}, /* 8 */ + {0x7c, 0xc6, 0xc6, 0x7e, 0x06, 0xc6, 0x7c, 0x00}, /* 9 */ + {0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00}, /* : */ + {0x00, 0x30, 0x00, 0x00, 0x00, 0x30, 0x20, 0x00}, /* }, */ + {0x00, 0x1c, 0x30, 0x60, 0x30, 0x1c, 0x00, 0x00}, /* < */ + {0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00}, /* = */ + {0x00, 0x70, 0x18, 0x0c, 0x18, 0x70, 0x00, 0x00}, /* > */ + {0x7c, 0xc6, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00}, /* ? */ + {0x7c, 0x82, 0x9a, 0xaa, 0xaa, 0x9e, 0x7c, 0x00}, /* @ */ + {0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x00}, /* A */ + {0xfc, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xfc, 0x00}, /* B */ + {0x7c, 0xc6, 0xc6, 0xc0, 0xc0, 0xc6, 0x7c, 0x00}, /* C */ + {0xf8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0xf8, 0x00}, /* D */ + {0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xfe, 0x00}, /* E */ + {0xfe, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0x00}, /* F */ + {0x7c, 0xc6, 0xc0, 0xce, 0xc6, 0xc6, 0x7e, 0x00}, /* G */ + {0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00}, /* H */ + {0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00}, /* I */ + {0x1e, 0x06, 0x06, 0x06, 0xc6, 0xc6, 0x7c, 0x00}, /* J */ + {0xc6, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0xc6, 0x00}, /* K */ + {0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0x00}, /* L */ + {0xc6, 0xee, 0xfe, 0xd6, 0xc6, 0xc6, 0xc6, 0x00}, /* M */ + {0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00}, /* N */ + {0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* O */ + {0xfc, 0xc6, 0xc6, 0xfc, 0xc0, 0xc0, 0xc0, 0x00}, /* P */ + {0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x06}, /* Q */ + {0xfc, 0xc6, 0xc6, 0xfc, 0xc6, 0xc6, 0xc6, 0x00}, /* R */ + {0x78, 0xcc, 0x60, 0x30, 0x18, 0xcc, 0x78, 0x00}, /* S */ + {0xfc, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00}, /* T */ + {0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* U */ + {0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00}, /* V */ + {0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00}, /* W */ + {0xc6, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0xc6, 0x00}, /* X */ + {0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00}, /* Y */ + {0xfe, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xfe, 0x00}, /* Z */ + {0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00}, /* [ */ + {0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x00}, /* \ */ + {0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00}, /* ] */ + {0x00, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00}, /* ^ */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}, /* _ */ + {0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ` */ + {0x00, 0x00, 0x7c, 0x06, 0x7e, 0xc6, 0x7e, 0x00}, /* a */ + {0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xe6, 0xdc, 0x00}, /* b */ + {0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0x7e, 0x00}, /* c */ + {0x06, 0x06, 0x7e, 0xc6, 0xc6, 0xce, 0x76, 0x00}, /* d */ + {0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0x7e, 0x00}, /* e */ + {0x1e, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x30, 0x00}, /* f */ + {0x00, 0x00, 0x7e, 0xc6, 0xce, 0x76, 0x06, 0x7c}, /* g */ + {0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00}, /* */ + {0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3c, 0x00}, /* i */ + {0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0xf0}, /* j */ + {0xc0, 0xc0, 0xcc, 0xd8, 0xf0, 0xd8, 0xcc, 0x00}, /* k */ + {0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00}, /* l */ + {0x00, 0x00, 0xcc, 0xfe, 0xd6, 0xc6, 0xc6, 0x00}, /* m */ + {0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x00}, /* n */ + {0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0x00}, /* o */ + {0x00, 0x00, 0xfc, 0xc6, 0xc6, 0xe6, 0xdc, 0xc0}, /* p */ + {0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xce, 0x76, 0x06}, /* q */ + {0x00, 0x00, 0x6e, 0x70, 0x60, 0x60, 0x60, 0x00}, /* r */ + {0x00, 0x00, 0x7c, 0xc0, 0x7c, 0x06, 0xfc, 0x00}, /* s */ + {0x30, 0x30, 0x7c, 0x30, 0x30, 0x30, 0x1c, 0x00}, /* t */ + {0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x00}, /* u */ + {0x00, 0x00, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00}, /* v */ + {0x00, 0x00, 0xc6, 0xc6, 0xd6, 0xfe, 0x6c, 0x00}, /* w */ + {0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00}, /* x */ + {0x00, 0x00, 0xc6, 0xc6, 0xce, 0x76, 0x06, 0x7c}, /* y */ + {0x00, 0x00, 0xfc, 0x18, 0x30, 0x60, 0xfc, 0x00}, /* z */ + {0x0e, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0e, 0x00}, /* { */ + {0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, /* | */ + {0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00}, /* } */ + {0x00, 0x00, 0x70, 0x9a, 0x0e, 0x00, 0x00, 0x00}, /* ~ */ + {0x00, 0x00, 0x18, 0x3c, 0x66, 0xff, 0x00, 0x00} /* Ascii 127 */ }; /*@=charint@*/ -void -GifDrawText8x8(SavedImage *Image, - const int x, const int y, - const char *legend, - const int color) -{ - int i, j; - const char *cp; +void GifDrawText8x8(SavedImage *Image, const int x, const int y, + const char *legend, const int color) { + int i, j; + const char *cp; - for (i = 0; i < GIF_FONT_HEIGHT; i++) { - int base = Image->ImageDesc.Width * (y + i) + x; + for (i = 0; i < GIF_FONT_HEIGHT; i++) { + int base = Image->ImageDesc.Width * (y + i) + x; - for (cp = legend; *cp; cp++) - for (j = 0; j < GIF_FONT_WIDTH; j++) { - if (GifAsciiTable8x8[(short)(*cp)][i] & (1 << (GIF_FONT_WIDTH - j))) - Image->RasterBits[base] = color; - base++; - } - } + for (cp = legend; *cp; cp++) { + for (j = 0; j < GIF_FONT_WIDTH; j++) { + if (GifAsciiTable8x8[(short)(*cp)][i] & + (1 << (GIF_FONT_WIDTH - j))) { + Image->RasterBits[base] = color; + } + base++; + } + } + } } -void -GifDrawBox(SavedImage *Image, - const int x, const int y, - const int w, const int d, - const int color) -{ - int j, base = Image->ImageDesc.Width * y + x; +void GifDrawBox(SavedImage *Image, const int x, const int y, const int w, + const int d, const int color) { + int j, base = Image->ImageDesc.Width * y + x; - for (j = 0; j < w; j++) - Image->RasterBits[base + j] = - Image->RasterBits[base + (d * Image->ImageDesc.Width) + j] = color; + for (j = 0; j < w; j++) { + Image->RasterBits[base + j] = + Image->RasterBits[base + (d * Image->ImageDesc.Width) + j] = + color; + } - for (j = 0; j < d; j++) - Image->RasterBits[base + j * Image->ImageDesc.Width] = - Image->RasterBits[base + j * Image->ImageDesc.Width + w] = color; + for (j = 0; j < d; j++) { + Image->RasterBits[base + j * Image->ImageDesc.Width] = + Image->RasterBits[base + j * Image->ImageDesc.Width + w] = + color; + } } -void -GifDrawRectangle(SavedImage *Image, - const int x, const int y, - const int w, const int d, - const int color) -{ - unsigned char *bp = Image->RasterBits + Image->ImageDesc.Width * y + x; - int i; +void GifDrawRectangle(SavedImage *Image, const int x, const int y, const int w, + const int d, const int color) { + unsigned char *bp = Image->RasterBits + Image->ImageDesc.Width * y + x; + int i; - for (i = 0; i < d; i++) - memset(bp + (i * Image->ImageDesc.Width), color, (size_t)w); + for (i = 0; i < d; i++) { + memset(bp + (i * Image->ImageDesc.Width), color, (size_t)w); + } } -void -GifDrawBoxedText8x8(SavedImage *Image, - const int x, const int y, - const char *legend, - const int border, - const int bg, const int fg) -{ - int j = 0, LineCount = 0, TextWidth = 0; - const char *cp; - char *dup; +void GifDrawBoxedText8x8(SavedImage *Image, const int x, const int y, + const char *legend, const int border, const int bg, + const int fg) { + int j = 0, LineCount = 0, TextWidth = 0; + const char *cp; + char *dup; - /* compute size of text to box */ - for (cp = legend; *cp; cp++) - if (*cp == '\r') { - if (j > TextWidth) - TextWidth = j; - j = 0; - LineCount++; - } else if (*cp != '\t') - ++j; - LineCount++; /* count last line */ - if (j > TextWidth) /* last line might be longer than any previous */ - TextWidth = j; + /* compute size of text to box */ + for (cp = legend; *cp; cp++) { + if (*cp == '\r') { + if (j > TextWidth) { + TextWidth = j; + } + j = 0; + LineCount++; + } else if (*cp != '\t') { + ++j; + } + } + LineCount++; /* count last line */ + if (j > TextWidth) { /* last line might be longer than any previous */ + TextWidth = j; + } - /* draw the text */ - dup = malloc(strlen(legend)+1); - /* FIXME: should return bad status, but that would require API change */ - if (dup != NULL) { - int i = 0; - /* fill the box */ - GifDrawRectangle(Image, x + 1, y + 1, - border + TextWidth * GIF_FONT_WIDTH + border - 1, - border + LineCount * GIF_FONT_HEIGHT + border - 1, bg); - (void)strcpy(dup, (char *)legend); - char *lasts; - cp = strtok_r(dup, "\r\n", &lasts); - do { - int leadspace = 0; + /* draw the text */ + dup = malloc(strlen(legend) + 1); + /* FIXME: should return bad status, but that would require API change */ + if (dup != NULL) { + int i = 0; + /* fill the box */ + GifDrawRectangle( + Image, x + 1, y + 1, + border + TextWidth * GIF_FONT_WIDTH + border - 1, + border + LineCount * GIF_FONT_HEIGHT + border - 1, bg); + (void)strcpy(dup, (char *)legend); + char *lasts; + cp = strtok_r(dup, "\r\n", &lasts); + do { + int leadspace = 0; - if (cp[0] == '\t') - leadspace = (TextWidth - strlen(++cp)) / 2; + if (cp[0] == '\t') { + leadspace = (TextWidth - strlen(++cp)) / 2; + } - GifDrawText8x8(Image, x + border + (leadspace * GIF_FONT_WIDTH), - y + border + (GIF_FONT_HEIGHT * i++), cp, fg); - cp = strtok_r(NULL, "\r\n", &lasts); - } while (cp); - (void)free((void *)dup); + GifDrawText8x8( + Image, x + border + (leadspace * GIF_FONT_WIDTH), + y + border + (GIF_FONT_HEIGHT * i++), cp, fg); + cp = strtok_r(NULL, "\r\n", &lasts); + } while (cp); + (void)free((void *)dup); - /* outline the box */ - GifDrawBox(Image, x, y, border + TextWidth * GIF_FONT_WIDTH + border, - border + LineCount * GIF_FONT_HEIGHT + border, fg); - } + /* outline the box */ + GifDrawBox(Image, x, y, + border + TextWidth * GIF_FONT_WIDTH + border, + border + LineCount * GIF_FONT_HEIGHT + border, fg); + } } /* end */ diff --git a/gif_hash.c b/gif_hash.c index ed7fd90..ad777cd 100644 --- a/gif_hash.c +++ b/gif_hash.c @@ -9,98 +9,98 @@ gif_hash.c -- module to support the following operations: This module is used to hash the GIF codes during encoding. +SPDX-License-Identifier: MIT + *****************************************************************************/ -#include -#include #include +#include #include +#include #include -#include "gif_lib.h" #include "gif_hash.h" +#include "gif_lib.h" #include "gif_lib_private.h" /* #define DEBUG_HIT_RATE Debug number of misses per hash Insert/Exists. */ -#ifdef DEBUG_HIT_RATE -static long NumberOfTests = 0, - NumberOfMisses = 0; -#endif /* DEBUG_HIT_RATE */ +#ifdef DEBUG_HIT_RATE +static long NumberOfTests = 0, NumberOfMisses = 0; +#endif /* DEBUG_HIT_RATE */ static int KeyItem(uint32_t Item); /****************************************************************************** Initialize HashTable - allocate the memory needed and clear it. * ******************************************************************************/ -GifHashTableType *_InitHashTable(void) -{ - GifHashTableType *HashTable; +GifHashTableType *_InitHashTable(void) { + GifHashTableType *HashTable; - if ((HashTable = (GifHashTableType *) malloc(sizeof(GifHashTableType))) - == NULL) - return NULL; + if ((HashTable = (GifHashTableType *)malloc( + sizeof(GifHashTableType))) == NULL) { + return NULL; + } - _ClearHashTable(HashTable); + _ClearHashTable(HashTable); - return HashTable; + return HashTable; } /****************************************************************************** Routine to clear the HashTable to an empty state. * This part is a little machine depended. Use the commented part otherwise. * ******************************************************************************/ -void _ClearHashTable(GifHashTableType *HashTable) -{ - memset(HashTable -> HTable, 0xFF, HT_SIZE * sizeof(uint32_t)); +void _ClearHashTable(GifHashTableType *HashTable) { + memset(HashTable->HTable, 0xFF, HT_SIZE * sizeof(uint32_t)); } /****************************************************************************** Routine to insert a new Item into the HashTable. The data is assumed to be * new one. * ******************************************************************************/ -void _InsertHashTable(GifHashTableType *HashTable, uint32_t Key, int Code) -{ - int HKey = KeyItem(Key); - uint32_t *HTable = HashTable -> HTable; +void _InsertHashTable(GifHashTableType *HashTable, uint32_t Key, int Code) { + int HKey = KeyItem(Key); + uint32_t *HTable = HashTable->HTable; #ifdef DEBUG_HIT_RATE NumberOfTests++; NumberOfMisses++; #endif /* DEBUG_HIT_RATE */ - while (HT_GET_KEY(HTable[HKey]) != 0xFFFFFL) { + while (HT_GET_KEY(HTable[HKey]) != 0xFFFFFL) { #ifdef DEBUG_HIT_RATE - NumberOfMisses++; + NumberOfMisses++; #endif /* DEBUG_HIT_RATE */ - HKey = (HKey + 1) & HT_KEY_MASK; - } - HTable[HKey] = HT_PUT_KEY(Key) | HT_PUT_CODE(Code); + HKey = (HKey + 1) & HT_KEY_MASK; + } + HTable[HKey] = HT_PUT_KEY(Key) | HT_PUT_CODE(Code); } /****************************************************************************** Routine to test if given Key exists in HashTable and if so returns its code * Returns the Code if key was found, -1 if not. * ******************************************************************************/ -int _ExistsHashTable(GifHashTableType *HashTable, uint32_t Key) -{ - int HKey = KeyItem(Key); - uint32_t *HTable = HashTable -> HTable, HTKey; +int _ExistsHashTable(GifHashTableType *HashTable, uint32_t Key) { + int HKey = KeyItem(Key); + uint32_t *HTable = HashTable->HTable, HTKey; #ifdef DEBUG_HIT_RATE NumberOfTests++; NumberOfMisses++; #endif /* DEBUG_HIT_RATE */ - while ((HTKey = HT_GET_KEY(HTable[HKey])) != 0xFFFFFL) { + while ((HTKey = HT_GET_KEY(HTable[HKey])) != 0xFFFFFL) { #ifdef DEBUG_HIT_RATE - NumberOfMisses++; + NumberOfMisses++; #endif /* DEBUG_HIT_RATE */ - if (Key == HTKey) return HT_GET_CODE(HTable[HKey]); - HKey = (HKey + 1) & HT_KEY_MASK; - } + if (Key == HTKey) { + return HT_GET_CODE(HTable[HKey]); + } + HKey = (HKey + 1) & HT_KEY_MASK; + } - return -1; + return -1; } /****************************************************************************** @@ -110,22 +110,19 @@ int _ExistsHashTable(GifHashTableType *HashTable, uint32_t Key) Because the average hit ratio is only 2 (2 hash references per entry), * evaluating more complex keys (such as twin prime keys) does not worth it! * ******************************************************************************/ -static int KeyItem(uint32_t Item) -{ - return ((Item >> 12) ^ Item) & HT_KEY_MASK; +static int KeyItem(uint32_t Item) { + return ((Item >> 12) ^ Item) & HT_KEY_MASK; } -#ifdef DEBUG_HIT_RATE +#ifdef DEBUG_HIT_RATE /****************************************************************************** Debugging routine to print the hit ratio - number of times the hash table * was tested per operation. This routine was used to test the KeyItem routine * ******************************************************************************/ -void HashTablePrintHitRatio(void) -{ - printf("Hash Table Hit Ratio is %ld/%ld = %ld%%.\n", - NumberOfMisses, NumberOfTests, - NumberOfMisses * 100 / NumberOfTests); +void HashTablePrintHitRatio(void) { + printf("Hash Table Hit Ratio is %ld/%ld = %ld%%.\n", NumberOfMisses, + NumberOfTests, NumberOfMisses * 100 / NumberOfTests); } -#endif /* DEBUG_HIT_RATE */ +#endif /* DEBUG_HIT_RATE */ /* end */ diff --git a/gif_hash.h b/gif_hash.h index ac20a43..e393d80 100644 --- a/gif_hash.h +++ b/gif_hash.h @@ -2,31 +2,35 @@ gif_hash.h - magfic constants and declarations for GIF LZW +SPDX-License-Identifier: MIT + ******************************************************************************/ #ifndef _GIF_HASH_H_ #define _GIF_HASH_H_ +#ifndef _WIN32 #include +#endif /* _WIN32 */ #include -#define HT_SIZE 8192 /* 12bits = 4096 or twice as big! */ -#define HT_KEY_MASK 0x1FFF /* 13bits keys */ -#define HT_KEY_NUM_BITS 13 /* 13bits keys */ -#define HT_MAX_KEY 8191 /* 13bits - 1, maximal code possible */ -#define HT_MAX_CODE 4095 /* Biggest code possible in 12 bits. */ +#define HT_SIZE 8192 /* 12bits = 4096 or twice as big! */ +#define HT_KEY_MASK 0x1FFF /* 13bits keys */ +#define HT_KEY_NUM_BITS 13 /* 13bits keys */ +#define HT_MAX_KEY 8191 /* 13bits - 1, maximal code possible */ +#define HT_MAX_CODE 4095 /* Biggest code possible in 12 bits. */ /* The 32 bits of the long are divided into two parts for the key & code: */ /* 1. The code is 12 bits as our compression algorithm is limited to 12bits */ /* 2. The key is 12 bits Prefix code + 8 bit new char or 20 bits. */ /* The key is the upper 20 bits. The code is the lower 12. */ -#define HT_GET_KEY(l) (l >> 12) -#define HT_GET_CODE(l) (l & 0x0FFF) -#define HT_PUT_KEY(l) (l << 12) -#define HT_PUT_CODE(l) (l & 0x0FFF) +#define HT_GET_KEY(l) (l >> 12) +#define HT_GET_CODE(l) (l & 0x0FFF) +#define HT_PUT_KEY(l) (l << 12) +#define HT_PUT_CODE(l) (l & 0x0FFF) typedef struct GifHashTableType { - uint32_t HTable[HT_SIZE]; + uint32_t HTable[HT_SIZE]; } GifHashTableType; GifHashTableType *_InitHashTable(void); diff --git a/gif_lib.h b/gif_lib.h index 9575e84..b05c9ff 100644 --- a/gif_lib.h +++ b/gif_lib.h @@ -1,7 +1,9 @@ /****************************************************************************** - + gif_lib.h - service library for decoding and encoding GIF images - + +SPDX-License-Identifier: MIT + *****************************************************************************/ #ifndef _GIF_LIB_H_ @@ -12,20 +14,20 @@ extern "C" { #endif /* __cplusplus */ #define GIFLIB_MAJOR 5 -#define GIFLIB_MINOR 1 -#define GIFLIB_RELEASE 9 +#define GIFLIB_MINOR 2 +#define GIFLIB_RELEASE 2 -#define GIF_ERROR 0 -#define GIF_OK 1 +#define GIF_ERROR 0 +#define GIF_OK 1 -#include #include +#include -#define GIF_STAMP "GIFVER" /* First chars in file - GIF stamp. */ +#define GIF_STAMP "GIFVER" /* First chars in file - GIF stamp. */ #define GIF_STAMP_LEN sizeof(GIF_STAMP) - 1 -#define GIF_VERSION_POS 3 /* Version first character in stamp. */ -#define GIF87_STAMP "GIF87a" /* First chars in file - GIF stamp. */ -#define GIF89_STAMP "GIF89a" /* First chars in file - GIF stamp. */ +#define GIF_VERSION_POS 3 /* Version first character in stamp. */ +#define GIF87_STAMP "GIF87a" /* First chars in file - GIF stamp. */ +#define GIF89_STAMP "GIF89a" /* First chars in file - GIF stamp. */ typedef unsigned char GifPixelType; typedef unsigned char *GifRowType; @@ -34,88 +36,88 @@ typedef unsigned int GifPrefixType; typedef int GifWord; typedef struct GifColorType { - GifByteType Red, Green, Blue; + GifByteType Red, Green, Blue; } GifColorType; typedef struct ColorMapObject { - int ColorCount; - int BitsPerPixel; - bool SortFlag; - GifColorType *Colors; /* on malloc(3) heap */ + int ColorCount; + int BitsPerPixel; + bool SortFlag; + GifColorType *Colors; /* on malloc(3) heap */ } ColorMapObject; typedef struct GifImageDesc { - GifWord Left, Top, Width, Height; /* Current image dimensions. */ - bool Interlace; /* Sequential/Interlaced lines. */ - ColorMapObject *ColorMap; /* The local color map */ + GifWord Left, Top, Width, Height; /* Current image dimensions. */ + bool Interlace; /* Sequential/Interlaced lines. */ + ColorMapObject *ColorMap; /* The local color map */ } GifImageDesc; typedef struct ExtensionBlock { - int ByteCount; - GifByteType *Bytes; /* on malloc(3) heap */ - int Function; /* The block function code */ -#define CONTINUE_EXT_FUNC_CODE 0x00 /* continuation subblock */ -#define COMMENT_EXT_FUNC_CODE 0xfe /* comment */ -#define GRAPHICS_EXT_FUNC_CODE 0xf9 /* graphics control (GIF89) */ -#define PLAINTEXT_EXT_FUNC_CODE 0x01 /* plaintext */ -#define APPLICATION_EXT_FUNC_CODE 0xff /* application block (GIF89) */ + int ByteCount; + GifByteType *Bytes; /* on malloc(3) heap */ + int Function; /* The block function code */ +#define CONTINUE_EXT_FUNC_CODE 0x00 /* continuation subblock */ +#define COMMENT_EXT_FUNC_CODE 0xfe /* comment */ +#define GRAPHICS_EXT_FUNC_CODE 0xf9 /* graphics control (GIF89) */ +#define PLAINTEXT_EXT_FUNC_CODE 0x01 /* plaintext */ +#define APPLICATION_EXT_FUNC_CODE 0xff /* application block (GIF89) */ } ExtensionBlock; typedef struct SavedImage { - GifImageDesc ImageDesc; - GifByteType *RasterBits; /* on malloc(3) heap */ - int ExtensionBlockCount; /* Count of extensions before image */ - ExtensionBlock *ExtensionBlocks; /* Extensions before image */ + GifImageDesc ImageDesc; + GifByteType *RasterBits; /* on malloc(3) heap */ + int ExtensionBlockCount; /* Count of extensions before image */ + ExtensionBlock *ExtensionBlocks; /* Extensions before image */ } SavedImage; typedef struct GifFileType { - GifWord SWidth, SHeight; /* Size of virtual canvas */ - GifWord SColorResolution; /* How many colors can we generate? */ - GifWord SBackGroundColor; /* Background color for virtual canvas */ - GifByteType AspectByte; /* Used to compute pixel aspect ratio */ - ColorMapObject *SColorMap; /* Global colormap, NULL if nonexistent. */ - int ImageCount; /* Number of current image (both APIs) */ - GifImageDesc Image; /* Current image (low-level API) */ - SavedImage *SavedImages; /* Image sequence (high-level API) */ - int ExtensionBlockCount; /* Count extensions past last image */ - ExtensionBlock *ExtensionBlocks; /* Extensions past last image */ - int Error; /* Last error condition reported */ - void *UserData; /* hook to attach user data (TVT) */ - void *Private; /* Don't mess with this! */ + GifWord SWidth, SHeight; /* Size of virtual canvas */ + GifWord SColorResolution; /* How many colors can we generate? */ + GifWord SBackGroundColor; /* Background color for virtual canvas */ + GifByteType AspectByte; /* Used to compute pixel aspect ratio */ + ColorMapObject *SColorMap; /* Global colormap, NULL if nonexistent. */ + int ImageCount; /* Number of current image (both APIs) */ + GifImageDesc Image; /* Current image (low-level API) */ + SavedImage *SavedImages; /* Image sequence (high-level API) */ + int ExtensionBlockCount; /* Count extensions past last image */ + ExtensionBlock *ExtensionBlocks; /* Extensions past last image */ + int Error; /* Last error condition reported */ + void *UserData; /* hook to attach user data (TVT) */ + void *Private; /* Don't mess with this! */ } GifFileType; -#define GIF_ASPECT_RATIO(n) ((n)+15.0/64.0) +#define GIF_ASPECT_RATIO(n) ((n) + 15.0 / 64.0) typedef enum { - UNDEFINED_RECORD_TYPE, - SCREEN_DESC_RECORD_TYPE, - IMAGE_DESC_RECORD_TYPE, /* Begin with ',' */ - EXTENSION_RECORD_TYPE, /* Begin with '!' */ - TERMINATE_RECORD_TYPE /* Begin with ';' */ + UNDEFINED_RECORD_TYPE, + SCREEN_DESC_RECORD_TYPE, + IMAGE_DESC_RECORD_TYPE, /* Begin with ',' */ + EXTENSION_RECORD_TYPE, /* Begin with '!' */ + TERMINATE_RECORD_TYPE /* Begin with ';' */ } GifRecordType; /* func type to read gif data from arbitrary sources (TVT) */ -typedef int (*InputFunc) (GifFileType *, GifByteType *, int); +typedef int (*InputFunc)(GifFileType *, GifByteType *, int); /* func type to write gif data to arbitrary targets. * Returns count of bytes written. (MRB) */ -typedef int (*OutputFunc) (GifFileType *, const GifByteType *, int); +typedef int (*OutputFunc)(GifFileType *, const GifByteType *, int); /****************************************************************************** GIF89 structures ******************************************************************************/ typedef struct GraphicsControlBlock { - int DisposalMode; -#define DISPOSAL_UNSPECIFIED 0 /* No disposal specified. */ -#define DISPOSE_DO_NOT 1 /* Leave image in place */ -#define DISPOSE_BACKGROUND 2 /* Set area too background color */ -#define DISPOSE_PREVIOUS 3 /* Restore to previous content */ - bool UserInputFlag; /* User confirmation required before disposal */ - int DelayTime; /* pre-display delay in 0.01sec units */ - int TransparentColor; /* Palette index for transparency, -1 if none */ -#define NO_TRANSPARENT_COLOR -1 + int DisposalMode; +#define DISPOSAL_UNSPECIFIED 0 /* No disposal specified. */ +#define DISPOSE_DO_NOT 1 /* Leave image in place */ +#define DISPOSE_BACKGROUND 2 /* Set area too background color */ +#define DISPOSE_PREVIOUS 3 /* Restore to previous content */ + bool UserInputFlag; /* User confirmation required before disposal */ + int DelayTime; /* pre-display delay in 0.01sec units */ + int TransparentColor; /* Palette index for transparency, -1 if none */ +#define NO_TRANSPARENT_COLOR -1 } GraphicsControlBlock; /****************************************************************************** @@ -127,49 +129,44 @@ GifFileType *EGifOpenFileName(const char *GifFileName, const bool GifTestExistence, int *Error); GifFileType *EGifOpenFileHandle(const int GifFileHandle, int *Error); GifFileType *EGifOpen(void *userPtr, OutputFunc writeFunc, int *Error); -int EGifSpew(GifFileType * GifFile); +int EGifSpew(GifFileType *GifFile); const char *EGifGetGifVersion(GifFileType *GifFile); /* new in 5.x */ int EGifCloseFile(GifFileType *GifFile, int *ErrorCode); -#define E_GIF_SUCCEEDED 0 -#define E_GIF_ERR_OPEN_FAILED 1 /* And EGif possible errors. */ -#define E_GIF_ERR_WRITE_FAILED 2 -#define E_GIF_ERR_HAS_SCRN_DSCR 3 -#define E_GIF_ERR_HAS_IMAG_DSCR 4 -#define E_GIF_ERR_NO_COLOR_MAP 5 -#define E_GIF_ERR_DATA_TOO_BIG 6 +#define E_GIF_SUCCEEDED 0 +#define E_GIF_ERR_OPEN_FAILED 1 /* And EGif possible errors. */ +#define E_GIF_ERR_WRITE_FAILED 2 +#define E_GIF_ERR_HAS_SCRN_DSCR 3 +#define E_GIF_ERR_HAS_IMAG_DSCR 4 +#define E_GIF_ERR_NO_COLOR_MAP 5 +#define E_GIF_ERR_DATA_TOO_BIG 6 #define E_GIF_ERR_NOT_ENOUGH_MEM 7 -#define E_GIF_ERR_DISK_IS_FULL 8 -#define E_GIF_ERR_CLOSE_FAILED 9 -#define E_GIF_ERR_NOT_WRITEABLE 10 +#define E_GIF_ERR_DISK_IS_FULL 8 +#define E_GIF_ERR_CLOSE_FAILED 9 +#define E_GIF_ERR_NOT_WRITEABLE 10 /* These are legacy. You probably do not want to call them directly */ -int EGifPutScreenDesc(GifFileType *GifFile, - const int GifWidth, const int GifHeight, - const int GifColorRes, +int EGifPutScreenDesc(GifFileType *GifFile, const int GifWidth, + const int GifHeight, const int GifColorRes, const int GifBackGround, const ColorMapObject *GifColorMap); -int EGifPutImageDesc(GifFileType *GifFile, - const int GifLeft, const int GifTop, - const int GifWidth, const int GifHeight, - const bool GifInterlace, +int EGifPutImageDesc(GifFileType *GifFile, const int GifLeft, const int GifTop, + const int GifWidth, const int GifHeight, + const bool GifInterlace, const ColorMapObject *GifColorMap); void EGifSetGifVersion(GifFileType *GifFile, const bool gif89); -int EGifPutLine(GifFileType *GifFile, GifPixelType *GifLine, - int GifLineLen); +int EGifPutLine(GifFileType *GifFile, GifPixelType *GifLine, int GifLineLen); int EGifPutPixel(GifFileType *GifFile, const GifPixelType GifPixel); int EGifPutComment(GifFileType *GifFile, const char *GifComment); int EGifPutExtensionLeader(GifFileType *GifFile, const int GifExtCode); -int EGifPutExtensionBlock(GifFileType *GifFile, - const int GifExtLen, const void *GifExtension); +int EGifPutExtensionBlock(GifFileType *GifFile, const int GifExtLen, + const void *GifExtension); int EGifPutExtensionTrailer(GifFileType *GifFile); -int EGifPutExtension(GifFileType *GifFile, const int GifExtCode, - const int GifExtLen, - const void *GifExtension); +int EGifPutExtension(GifFileType *GifFile, const int GifExtCode, + const int GifExtLen, const void *GifExtension); int EGifPutCode(GifFileType *GifFile, int GifCodeSize, const GifByteType *GifCodeBlock); -int EGifPutCodeNext(GifFileType *GifFile, - const GifByteType *GifCodeBlock); +int EGifPutCodeNext(GifFileType *GifFile, const GifByteType *GifCodeBlock); /****************************************************************************** GIF decoding routines @@ -178,24 +175,25 @@ int EGifPutCodeNext(GifFileType *GifFile, /* Main entry points */ GifFileType *DGifOpenFileName(const char *GifFileName, int *Error); GifFileType *DGifOpenFileHandle(int GifFileHandle, int *Error); -int DGifSlurp(GifFileType * GifFile); -GifFileType *DGifOpen(void *userPtr, InputFunc readFunc, int *Error); /* new one (TVT) */ - int DGifCloseFile(GifFileType * GifFile, int *ErrorCode); - -#define D_GIF_SUCCEEDED 0 -#define D_GIF_ERR_OPEN_FAILED 101 /* And DGif possible errors. */ -#define D_GIF_ERR_READ_FAILED 102 -#define D_GIF_ERR_NOT_GIF_FILE 103 -#define D_GIF_ERR_NO_SCRN_DSCR 104 -#define D_GIF_ERR_NO_IMAG_DSCR 105 -#define D_GIF_ERR_NO_COLOR_MAP 106 -#define D_GIF_ERR_WRONG_RECORD 107 -#define D_GIF_ERR_DATA_TOO_BIG 108 +int DGifSlurp(GifFileType *GifFile); +GifFileType *DGifOpen(void *userPtr, InputFunc readFunc, + int *Error); /* new one (TVT) */ +int DGifCloseFile(GifFileType *GifFile, int *ErrorCode); + +#define D_GIF_SUCCEEDED 0 +#define D_GIF_ERR_OPEN_FAILED 101 /* And DGif possible errors. */ +#define D_GIF_ERR_READ_FAILED 102 +#define D_GIF_ERR_NOT_GIF_FILE 103 +#define D_GIF_ERR_NO_SCRN_DSCR 104 +#define D_GIF_ERR_NO_IMAG_DSCR 105 +#define D_GIF_ERR_NO_COLOR_MAP 106 +#define D_GIF_ERR_WRONG_RECORD 107 +#define D_GIF_ERR_DATA_TOO_BIG 108 #define D_GIF_ERR_NOT_ENOUGH_MEM 109 -#define D_GIF_ERR_CLOSE_FAILED 110 -#define D_GIF_ERR_NOT_READABLE 111 -#define D_GIF_ERR_IMAGE_DEFECT 112 -#define D_GIF_ERR_EOF_TOO_SOON 113 +#define D_GIF_ERR_CLOSE_FAILED 110 +#define D_GIF_ERR_NOT_READABLE 111 +#define D_GIF_ERR_IMAGE_DEFECT 112 +#define D_GIF_ERR_EOF_TOO_SOON 113 /* These are legacy. You probably do not want to call them directly */ int DGifGetScreenDesc(GifFileType *GifFile); @@ -213,20 +211,10 @@ int DGifGetCodeNext(GifFileType *GifFile, GifByteType **GifCodeBlock); int DGifGetLZCodes(GifFileType *GifFile, int *GifCode); const char *DGifGetGifVersion(GifFileType *GifFile); - -/****************************************************************************** - Color table quantization (deprecated) -******************************************************************************/ -int GifQuantizeBuffer(unsigned int Width, unsigned int Height, - int *ColorMapSize, GifByteType * RedInput, - GifByteType * GreenInput, GifByteType * BlueInput, - GifByteType * OutputBuffer, - GifColorType * OutputColorMap); - /****************************************************************************** Error handling and reporting. ******************************************************************************/ -extern const char *GifErrorString(int ErrorCode); /* new in 2012 - ESR */ +extern const char *GifErrorString(int ErrorCode); /* new in 2012 - ESR */ /***************************************************************************** Everything below this point is new after version 1.2, supporting `slurp @@ -238,26 +226,26 @@ extern const char *GifErrorString(int ErrorCode); /* new in 2012 - ESR */ ******************************************************************************/ extern ColorMapObject *GifMakeMapObject(int ColorCount, - const GifColorType *ColorMap); + const GifColorType *ColorMap); extern void GifFreeMapObject(ColorMapObject *Object); extern ColorMapObject *GifUnionColorMap(const ColorMapObject *ColorIn1, - const ColorMapObject *ColorIn2, - GifPixelType ColorTransIn2[]); + const ColorMapObject *ColorIn2, + GifPixelType ColorTransIn2[]); extern int GifBitSize(int n); /****************************************************************************** - Support for the in-core structures allocation (slurp mode). + Support for the in-core structures allocation (slurp mode). ******************************************************************************/ -extern void GifApplyTranslation(SavedImage *Image, GifPixelType Translation[]); +extern void GifApplyTranslation(SavedImage *Image, + const GifPixelType Translation[]); extern int GifAddExtensionBlock(int *ExtensionBlock_Count, - ExtensionBlock **ExtensionBlocks, - int Function, - unsigned int Len, unsigned char ExtData[]); + ExtensionBlock **ExtensionBlocks, int Function, + unsigned int Len, unsigned char ExtData[]); extern void GifFreeExtensions(int *ExtensionBlock_Count, - ExtensionBlock **ExtensionBlocks); + ExtensionBlock **ExtensionBlocks); extern SavedImage *GifMakeSavedImage(GifFileType *GifFile, - const SavedImage *CopyFrom); + const SavedImage *CopyFrom); extern void GifFreeSavedImages(GifFileType *GifFile); /****************************************************************************** @@ -265,42 +253,36 @@ extern void GifFreeSavedImages(GifFileType *GifFile); ******************************************************************************/ int DGifExtensionToGCB(const size_t GifExtensionLength, - const GifByteType *GifExtension, - GraphicsControlBlock *GCB); + const GifByteType *GifExtension, + GraphicsControlBlock *GCB); size_t EGifGCBToExtension(const GraphicsControlBlock *GCB, - GifByteType *GifExtension); + GifByteType *GifExtension); -int DGifSavedExtensionToGCB(GifFileType *GifFile, - int ImageIndex, - GraphicsControlBlock *GCB); -int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB, - GifFileType *GifFile, - int ImageIndex); +int DGifSavedExtensionToGCB(GifFileType *GifFile, int ImageIndex, + GraphicsControlBlock *GCB); +int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB, + GifFileType *GifFile, int ImageIndex); /****************************************************************************** - The library's internal utility font + The library's internal utility font ******************************************************************************/ -#define GIF_FONT_WIDTH 8 +#define GIF_FONT_WIDTH 8 #define GIF_FONT_HEIGHT 8 extern const unsigned char GifAsciiTable8x8[][GIF_FONT_WIDTH]; -extern void GifDrawText8x8(SavedImage *Image, - const int x, const int y, - const char *legend, const int color); +extern void GifDrawText8x8(SavedImage *Image, const int x, const int y, + const char *legend, const int color); -extern void GifDrawBox(SavedImage *Image, - const int x, const int y, - const int w, const int d, const int color); +extern void GifDrawBox(SavedImage *Image, const int x, const int y, const int w, + const int d, const int color); -extern void GifDrawRectangle(SavedImage *Image, - const int x, const int y, - const int w, const int d, const int color); +extern void GifDrawRectangle(SavedImage *Image, const int x, const int y, + const int w, const int d, const int color); -extern void GifDrawBoxedText8x8(SavedImage *Image, - const int x, const int y, - const char *legend, - const int border, const int bg, const int fg); +extern void GifDrawBoxedText8x8(SavedImage *Image, const int x, const int y, + const char *legend, const int border, + const int bg, const int fg); #ifdef __cplusplus } diff --git a/gif_lib_private.h b/gif_lib_private.h index 6fe4b8b..19578d4 100644 --- a/gif_lib_private.h +++ b/gif_lib_private.h @@ -2,60 +2,64 @@ gif_lib_private.h - internal giflib routines and structures +SPDX-License-Identifier: MIT + ****************************************************************************/ #ifndef _GIF_LIB_PRIVATE_H #define _GIF_LIB_PRIVATE_H -#include "gif_lib.h" #include "gif_hash.h" +#include "gif_lib.h" #ifndef SIZE_MAX - #define SIZE_MAX UINTPTR_MAX +#define SIZE_MAX UINTPTR_MAX #endif -#define EXTENSION_INTRODUCER 0x21 -#define DESCRIPTOR_INTRODUCER 0x2c -#define TERMINATOR_INTRODUCER 0x3b +#define EXTENSION_INTRODUCER 0x21 +#define DESCRIPTOR_INTRODUCER 0x2c +#define TERMINATOR_INTRODUCER 0x3b -#define LZ_MAX_CODE 4095 /* Biggest code possible in 12 bits. */ -#define LZ_BITS 12 +#define LZ_MAX_CODE 4095 /* Biggest code possible in 12 bits. */ +#define LZ_BITS 12 -#define FLUSH_OUTPUT 4096 /* Impossible code, to signal flush. */ -#define FIRST_CODE 4097 /* Impossible code, to signal first. */ -#define NO_SUCH_CODE 4098 /* Impossible code, to signal empty. */ +#define FLUSH_OUTPUT 4096 /* Impossible code, to signal flush. */ +#define FIRST_CODE 4097 /* Impossible code, to signal first. */ +#define NO_SUCH_CODE 4098 /* Impossible code, to signal empty. */ -#define FILE_STATE_WRITE 0x01 -#define FILE_STATE_SCREEN 0x02 -#define FILE_STATE_IMAGE 0x04 -#define FILE_STATE_READ 0x08 +#define FILE_STATE_WRITE 0x01 +#define FILE_STATE_SCREEN 0x02 +#define FILE_STATE_IMAGE 0x04 +#define FILE_STATE_READ 0x08 -#define IS_READABLE(Private) (Private->FileState & FILE_STATE_READ) -#define IS_WRITEABLE(Private) (Private->FileState & FILE_STATE_WRITE) +#define IS_READABLE(Private) (Private->FileState & FILE_STATE_READ) +#define IS_WRITEABLE(Private) (Private->FileState & FILE_STATE_WRITE) typedef struct GifFilePrivateType { - GifWord FileState, FileHandle, /* Where all this data goes to! */ - BitsPerPixel, /* Bits per pixel (Codes uses at least this + 1). */ - ClearCode, /* The CLEAR LZ code. */ - EOFCode, /* The EOF LZ code. */ - RunningCode, /* The next code algorithm can generate. */ - RunningBits, /* The number of bits required to represent RunningCode. */ - MaxCode1, /* 1 bigger than max. possible code, in RunningBits bits. */ - LastCode, /* The code before the current code. */ - CrntCode, /* Current algorithm code. */ - StackPtr, /* For character stack (see below). */ - CrntShiftState; /* Number of bits in CrntShiftDWord. */ - unsigned long CrntShiftDWord; /* For bytes decomposition into codes. */ - unsigned long PixelCount; /* Number of pixels in image. */ - FILE *File; /* File as stream. */ - InputFunc Read; /* function to read gif input (TVT) */ - OutputFunc Write; /* function to write gif output (MRB) */ - GifByteType Buf[256]; /* Compressed input is buffered here. */ - GifByteType Stack[LZ_MAX_CODE]; /* Decoded pixels are stacked here. */ - GifByteType Suffix[LZ_MAX_CODE + 1]; /* So we can trace the codes. */ - GifPrefixType Prefix[LZ_MAX_CODE + 1]; - GifHashTableType *HashTable; - bool gif89; + GifWord FileState, FileHandle, /* Where all this data goes to! */ + BitsPerPixel, /* Bits per pixel (Codes uses at least this + 1). */ + ClearCode, /* The CLEAR LZ code. */ + EOFCode, /* The EOF LZ code. */ + RunningCode, /* The next code algorithm can generate. */ + RunningBits, /* The number of bits required to represent + RunningCode. */ + MaxCode1, /* 1 bigger than max. possible code, in RunningBits bits. + */ + LastCode, /* The code before the current code. */ + CrntCode, /* Current algorithm code. */ + StackPtr, /* For character stack (see below). */ + CrntShiftState; /* Number of bits in CrntShiftDWord. */ + unsigned long CrntShiftDWord; /* For bytes decomposition into codes. */ + unsigned long PixelCount; /* Number of pixels in image. */ + FILE *File; /* File as stream. */ + InputFunc Read; /* function to read gif input (TVT) */ + OutputFunc Write; /* function to write gif output (MRB) */ + GifByteType Buf[256]; /* Compressed input is buffered here. */ + GifByteType Stack[LZ_MAX_CODE]; /* Decoded pixels are stacked here. */ + GifByteType Suffix[LZ_MAX_CODE + 1]; /* So we can trace the codes. */ + GifPrefixType Prefix[LZ_MAX_CODE + 1]; + GifHashTableType *HashTable; + bool gif89; } GifFilePrivateType; #ifndef HAVE_REALLOCARRAY diff --git a/gifalloc.c b/gifalloc.c index 8ef6d7b..47c6539 100644 --- a/gifalloc.c +++ b/gifalloc.c @@ -2,417 +2,424 @@ GIF construction tools +SPDX-License-Identifier: MIT + ****************************************************************************/ -#include #include +#include #include #include "gif_lib.h" #include "gif_lib_private.h" -#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) /****************************************************************************** - Miscellaneous utility functions + Miscellaneous utility functions ******************************************************************************/ /* return smallest bitfield size n will fit in */ -int -GifBitSize(int n) -{ - register int i; - - for (i = 1; i <= 8; i++) - if ((1 << i) >= n) - break; - return (i); +int GifBitSize(int n) { + register int i; + + for (i = 1; i <= 8; i++) { + if ((1 << i) >= n) { + break; + } + } + return (i); } /****************************************************************************** - Color map object functions + Color map object functions ******************************************************************************/ /* * Allocate a color map of given size; initialize with contents of * ColorMap if that pointer is non-NULL. */ -ColorMapObject * -GifMakeMapObject(int ColorCount, const GifColorType *ColorMap) -{ - ColorMapObject *Object; - - /*** FIXME: Our ColorCount has to be a power of two. Is it necessary to - * make the user know that or should we automatically round up instead? */ - if (ColorCount != (1 << GifBitSize(ColorCount))) { - return ((ColorMapObject *) NULL); - } - - Object = (ColorMapObject *)malloc(sizeof(ColorMapObject)); - if (Object == (ColorMapObject *) NULL) { - return ((ColorMapObject *) NULL); - } - - Object->Colors = (GifColorType *)calloc(ColorCount, sizeof(GifColorType)); - if (Object->Colors == (GifColorType *) NULL) { - free(Object); - return ((ColorMapObject *) NULL); - } - - Object->ColorCount = ColorCount; - Object->BitsPerPixel = GifBitSize(ColorCount); - Object->SortFlag = false; - - if (ColorMap != NULL) { - memcpy((char *)Object->Colors, - (char *)ColorMap, ColorCount * sizeof(GifColorType)); - } - - return (Object); +ColorMapObject *GifMakeMapObject(int ColorCount, const GifColorType *ColorMap) { + ColorMapObject *Object; + + /*** FIXME: Our ColorCount has to be a power of two. Is it necessary to + * make the user know that or should we automatically round up instead? + */ + if (ColorCount != (1 << GifBitSize(ColorCount))) { + return ((ColorMapObject *)NULL); + } + + Object = (ColorMapObject *)malloc(sizeof(ColorMapObject)); + if (Object == (ColorMapObject *)NULL) { + return ((ColorMapObject *)NULL); + } + + Object->Colors = + (GifColorType *)calloc(ColorCount, sizeof(GifColorType)); + if (Object->Colors == (GifColorType *)NULL) { + free(Object); + return ((ColorMapObject *)NULL); + } + + Object->ColorCount = ColorCount; + Object->BitsPerPixel = GifBitSize(ColorCount); + Object->SortFlag = false; + + if (ColorMap != NULL) { + memcpy((char *)Object->Colors, (char *)ColorMap, + ColorCount * sizeof(GifColorType)); + } + + return (Object); } /******************************************************************************* -Free a color map object + Free a color map object *******************************************************************************/ -void -GifFreeMapObject(ColorMapObject *Object) -{ - if (Object != NULL) { - (void)free(Object->Colors); - (void)free(Object); - } +void GifFreeMapObject(ColorMapObject *Object) { + if (Object != NULL) { + (void)free(Object->Colors); + (void)free(Object); + } } #ifdef DEBUG -void -DumpColorMap(ColorMapObject *Object, - FILE * fp) -{ - if (Object != NULL) { - int i, j, Len = Object->ColorCount; - - for (i = 0; i < Len; i += 4) { - for (j = 0; j < 4 && j < Len; j++) { - (void)fprintf(fp, "%3d: %02x %02x %02x ", i + j, - Object->Colors[i + j].Red, - Object->Colors[i + j].Green, - Object->Colors[i + j].Blue); - } - (void)fprintf(fp, "\n"); - } - } +void DumpColorMap(ColorMapObject *Object, FILE *fp) { + if (Object != NULL) { + int i, j, Len = Object->ColorCount; + + for (i = 0; i < Len; i += 4) { + for (j = 0; j < 4 && j < Len; j++) { + (void)fprintf(fp, "%3d: %02x %02x %02x ", + i + j, Object->Colors[i + j].Red, + Object->Colors[i + j].Green, + Object->Colors[i + j].Blue); + } + (void)fprintf(fp, "\n"); + } + } } #endif /* DEBUG */ /******************************************************************************* - Compute the union of two given color maps and return it. If result can't + Compute the union of two given color maps and return it. If result can't fit into 256 colors, NULL is returned, the allocated union otherwise. ColorIn1 is copied as is to ColorUnion, while colors from ColorIn2 are copied iff they didn't exist before. ColorTransIn2 maps the old ColorIn2 into the ColorUnion color map table./ *******************************************************************************/ -ColorMapObject * -GifUnionColorMap(const ColorMapObject *ColorIn1, - const ColorMapObject *ColorIn2, - GifPixelType ColorTransIn2[]) -{ - int i, j, CrntSlot, RoundUpTo, NewGifBitSize; - ColorMapObject *ColorUnion; - - /* - * We don't worry about duplicates within either color map; if - * the caller wants to resolve those, he can perform unions - * with an empty color map. - */ - - /* Allocate table which will hold the result for sure. */ - ColorUnion = GifMakeMapObject(MAX(ColorIn1->ColorCount, - ColorIn2->ColorCount) * 2, NULL); - - if (ColorUnion == NULL) - return (NULL); - - /* - * Copy ColorIn1 to ColorUnion. - */ - for (i = 0; i < ColorIn1->ColorCount; i++) - ColorUnion->Colors[i] = ColorIn1->Colors[i]; - CrntSlot = ColorIn1->ColorCount; - - /* - * Potentially obnoxious hack: - * - * Back CrntSlot down past all contiguous {0, 0, 0} slots at the end - * of table 1. This is very useful if your display is limited to - * 16 colors. - */ - while (ColorIn1->Colors[CrntSlot - 1].Red == 0 - && ColorIn1->Colors[CrntSlot - 1].Green == 0 - && ColorIn1->Colors[CrntSlot - 1].Blue == 0) - CrntSlot--; - - /* Copy ColorIn2 to ColorUnion (use old colors if they exist): */ - for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) { - /* Let's see if this color already exists: */ - for (j = 0; j < ColorIn1->ColorCount; j++) - if (memcmp (&ColorIn1->Colors[j], &ColorIn2->Colors[i], - sizeof(GifColorType)) == 0) - break; - - if (j < ColorIn1->ColorCount) - ColorTransIn2[i] = j; /* color exists in Color1 */ - else { - /* Color is new - copy it to a new slot: */ - ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i]; - ColorTransIn2[i] = CrntSlot++; - } - } - - if (CrntSlot > 256) { - GifFreeMapObject(ColorUnion); - return ((ColorMapObject *) NULL); - } - - NewGifBitSize = GifBitSize(CrntSlot); - RoundUpTo = (1 << NewGifBitSize); - - if (RoundUpTo != ColorUnion->ColorCount) { - register GifColorType *Map = ColorUnion->Colors; - - /* - * Zero out slots up to next power of 2. - * We know these slots exist because of the way ColorUnion's - * start dimension was computed. - */ - for (j = CrntSlot; j < RoundUpTo; j++) - Map[j].Red = Map[j].Green = Map[j].Blue = 0; - - /* perhaps we can shrink the map? */ - if (RoundUpTo < ColorUnion->ColorCount) { - GifColorType *new_map = (GifColorType *)reallocarray(Map, - RoundUpTo, sizeof(GifColorType)); - if( new_map == NULL ) { - GifFreeMapObject(ColorUnion); - return ((ColorMapObject *) NULL); - } - ColorUnion->Colors = new_map; - } - } - - ColorUnion->ColorCount = RoundUpTo; - ColorUnion->BitsPerPixel = NewGifBitSize; - - return (ColorUnion); +ColorMapObject *GifUnionColorMap(const ColorMapObject *ColorIn1, + const ColorMapObject *ColorIn2, + GifPixelType ColorTransIn2[]) { + int i, j, CrntSlot, RoundUpTo, NewGifBitSize; + ColorMapObject *ColorUnion; + + /* + * We don't worry about duplicates within either color map; if + * the caller wants to resolve those, he can perform unions + * with an empty color map. + */ + + /* Allocate table which will hold the result for sure. */ + ColorUnion = GifMakeMapObject( + MAX(ColorIn1->ColorCount, ColorIn2->ColorCount) * 2, NULL); + + if (ColorUnion == NULL) { + return (NULL); + } + + /* + * Copy ColorIn1 to ColorUnion. + */ + for (i = 0; i < ColorIn1->ColorCount; i++) { + ColorUnion->Colors[i] = ColorIn1->Colors[i]; + } + CrntSlot = ColorIn1->ColorCount; + + /* + * Potentially obnoxious hack: + * + * Back CrntSlot down past all contiguous {0, 0, 0} slots at the end + * of table 1. This is very useful if your display is limited to + * 16 colors. + */ + while (ColorIn1->Colors[CrntSlot - 1].Red == 0 && + ColorIn1->Colors[CrntSlot - 1].Green == 0 && + ColorIn1->Colors[CrntSlot - 1].Blue == 0) { + CrntSlot--; + } + + /* Copy ColorIn2 to ColorUnion (use old colors if they exist): */ + for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) { + /* Let's see if this color already exists: */ + for (j = 0; j < ColorIn1->ColorCount; j++) { + if (memcmp(&ColorIn1->Colors[j], &ColorIn2->Colors[i], + sizeof(GifColorType)) == 0) { + break; + } + } + + if (j < ColorIn1->ColorCount) { + ColorTransIn2[i] = j; /* color exists in Color1 */ + } else { + /* Color is new - copy it to a new slot: */ + ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i]; + ColorTransIn2[i] = CrntSlot++; + } + } + + if (CrntSlot > 256) { + GifFreeMapObject(ColorUnion); + return ((ColorMapObject *)NULL); + } + + NewGifBitSize = GifBitSize(CrntSlot); + RoundUpTo = (1 << NewGifBitSize); + + if (RoundUpTo != ColorUnion->ColorCount) { + register GifColorType *Map = ColorUnion->Colors; + + /* + * Zero out slots up to next power of 2. + * We know these slots exist because of the way ColorUnion's + * start dimension was computed. + */ + for (j = CrntSlot; j < RoundUpTo; j++) { + Map[j].Red = Map[j].Green = Map[j].Blue = 0; + } + + /* perhaps we can shrink the map? */ + if (RoundUpTo < ColorUnion->ColorCount) { + GifColorType *new_map = (GifColorType *)reallocarray( + Map, RoundUpTo, sizeof(GifColorType)); + if (new_map == NULL) { + GifFreeMapObject(ColorUnion); + return ((ColorMapObject *)NULL); + } + ColorUnion->Colors = new_map; + } + } + + ColorUnion->ColorCount = RoundUpTo; + ColorUnion->BitsPerPixel = NewGifBitSize; + + return (ColorUnion); } /******************************************************************************* Apply a given color translation to the raster bits of an image *******************************************************************************/ -void -GifApplyTranslation(SavedImage *Image, GifPixelType Translation[]) -{ - register int i; - register int RasterSize = Image->ImageDesc.Height * Image->ImageDesc.Width; - - for (i = 0; i < RasterSize; i++) - Image->RasterBits[i] = Translation[Image->RasterBits[i]]; +void GifApplyTranslation(SavedImage *Image, const GifPixelType Translation[]) { + register int i; + register int RasterSize = + Image->ImageDesc.Height * Image->ImageDesc.Width; + + for (i = 0; i < RasterSize; i++) { + Image->RasterBits[i] = Translation[Image->RasterBits[i]]; + } } /****************************************************************************** - Extension record functions + Extension record functions ******************************************************************************/ -int -GifAddExtensionBlock(int *ExtensionBlockCount, - ExtensionBlock **ExtensionBlocks, - int Function, - unsigned int Len, - unsigned char ExtData[]) -{ - ExtensionBlock *ep; - - if (*ExtensionBlocks == NULL) - *ExtensionBlocks=(ExtensionBlock *)malloc(sizeof(ExtensionBlock)); - else { - ExtensionBlock* ep_new = (ExtensionBlock *)reallocarray - (*ExtensionBlocks, (*ExtensionBlockCount + 1), - sizeof(ExtensionBlock)); - if( ep_new == NULL ) - return (GIF_ERROR); - *ExtensionBlocks = ep_new; - } - - if (*ExtensionBlocks == NULL) - return (GIF_ERROR); - - ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++]; - - ep->Function = Function; - ep->ByteCount=Len; - ep->Bytes = (GifByteType *)malloc(ep->ByteCount); - if (ep->Bytes == NULL) - return (GIF_ERROR); - - if (ExtData != NULL) { - memcpy(ep->Bytes, ExtData, Len); - } - - return (GIF_OK); +int GifAddExtensionBlock(int *ExtensionBlockCount, + ExtensionBlock **ExtensionBlocks, int Function, + unsigned int Len, unsigned char ExtData[]) { + ExtensionBlock *ep; + + if (*ExtensionBlocks == NULL) { + *ExtensionBlocks = + (ExtensionBlock *)malloc(sizeof(ExtensionBlock)); + } else { + ExtensionBlock *ep_new = (ExtensionBlock *)reallocarray( + *ExtensionBlocks, (*ExtensionBlockCount + 1), + sizeof(ExtensionBlock)); + if (ep_new == NULL) { + return (GIF_ERROR); + } + *ExtensionBlocks = ep_new; + } + + if (*ExtensionBlocks == NULL) { + return (GIF_ERROR); + } + + ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++]; + + ep->Function = Function; + ep->ByteCount = Len; + ep->Bytes = (GifByteType *)malloc(ep->ByteCount); + if (ep->Bytes == NULL) { + return (GIF_ERROR); + } + + if (ExtData != NULL) { + memcpy(ep->Bytes, ExtData, Len); + } + + return (GIF_OK); } -void -GifFreeExtensions(int *ExtensionBlockCount, - ExtensionBlock **ExtensionBlocks) -{ - ExtensionBlock *ep; - - if (*ExtensionBlocks == NULL) - return; - - for (ep = *ExtensionBlocks; - ep < (*ExtensionBlocks + *ExtensionBlockCount); - ep++) - (void)free((char *)ep->Bytes); - (void)free((char *)*ExtensionBlocks); - *ExtensionBlocks = NULL; - *ExtensionBlockCount = 0; +void GifFreeExtensions(int *ExtensionBlockCount, + ExtensionBlock **ExtensionBlocks) { + ExtensionBlock *ep; + + if (*ExtensionBlocks == NULL) { + return; + } + + for (ep = *ExtensionBlocks; + ep < (*ExtensionBlocks + *ExtensionBlockCount); ep++) { + (void)free((char *)ep->Bytes); + } + (void)free((char *)*ExtensionBlocks); + *ExtensionBlocks = NULL; + *ExtensionBlockCount = 0; } /****************************************************************************** - Image block allocation functions + Image block allocation functions ******************************************************************************/ /* Private Function: * Frees the last image in the GifFile->SavedImages array */ -void -FreeLastSavedImage(GifFileType *GifFile) -{ - SavedImage *sp; - - if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) - return; - - /* Remove one SavedImage from the GifFile */ - GifFile->ImageCount--; - sp = &GifFile->SavedImages[GifFile->ImageCount]; - - /* Deallocate its Colormap */ - if (sp->ImageDesc.ColorMap != NULL) { - GifFreeMapObject(sp->ImageDesc.ColorMap); - sp->ImageDesc.ColorMap = NULL; - } - - /* Deallocate the image data */ - if (sp->RasterBits != NULL) - free((char *)sp->RasterBits); - - /* Deallocate any extensions */ - GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks); - - /*** FIXME: We could realloc the GifFile->SavedImages structure but is - * there a point to it? Saves some memory but we'd have to do it every - * time. If this is used in GifFreeSavedImages then it would be inefficient - * (The whole array is going to be deallocated.) If we just use it when - * we want to free the last Image it's convenient to do it here. - */ +void FreeLastSavedImage(GifFileType *GifFile) { + SavedImage *sp; + + if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) { + return; + } + + /* Remove one SavedImage from the GifFile */ + GifFile->ImageCount--; + sp = &GifFile->SavedImages[GifFile->ImageCount]; + + /* Deallocate its Colormap */ + if (sp->ImageDesc.ColorMap != NULL) { + GifFreeMapObject(sp->ImageDesc.ColorMap); + sp->ImageDesc.ColorMap = NULL; + } + + /* Deallocate the image data */ + if (sp->RasterBits != NULL) { + free((char *)sp->RasterBits); + } + + /* Deallocate any extensions */ + GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks); + + /*** FIXME: We could realloc the GifFile->SavedImages structure but is + * there a point to it? Saves some memory but we'd have to do it every + * time. If this is used in GifFreeSavedImages then it would be + * inefficient (The whole array is going to be deallocated.) If we just + * use it when we want to free the last Image it's convenient to do it + * here. + */ } /* - * Append an image block to the SavedImages array + * Append an image block to the SavedImages array */ -SavedImage * -GifMakeSavedImage(GifFileType *GifFile, const SavedImage *CopyFrom) -{ - if (GifFile->SavedImages == NULL) - GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage)); - else { - SavedImage* newSavedImages = (SavedImage *)reallocarray(GifFile->SavedImages, - (GifFile->ImageCount + 1), sizeof(SavedImage)); - if( newSavedImages == NULL) - return ((SavedImage *)NULL); - GifFile->SavedImages = newSavedImages; - } - if (GifFile->SavedImages == NULL) - return ((SavedImage *)NULL); - else { - SavedImage *sp = &GifFile->SavedImages[GifFile->ImageCount++]; - - if (CopyFrom != NULL) { - memcpy((char *)sp, CopyFrom, sizeof(SavedImage)); - - /* - * Make our own allocated copies of the heap fields in the - * copied record. This guards against potential aliasing - * problems. - */ - - /* first, the local color map */ - if (CopyFrom->ImageDesc.ColorMap != NULL) { - sp->ImageDesc.ColorMap = GifMakeMapObject( - CopyFrom->ImageDesc.ColorMap->ColorCount, - CopyFrom->ImageDesc.ColorMap->Colors); - if (sp->ImageDesc.ColorMap == NULL) { - FreeLastSavedImage(GifFile); - return (SavedImage *)(NULL); - } - } - - /* next, the raster */ - sp->RasterBits = (unsigned char *)reallocarray(NULL, - (CopyFrom->ImageDesc.Height * - CopyFrom->ImageDesc.Width), - sizeof(GifPixelType)); - if (sp->RasterBits == NULL) { - FreeLastSavedImage(GifFile); - return (SavedImage *)(NULL); - } - memcpy(sp->RasterBits, CopyFrom->RasterBits, - sizeof(GifPixelType) * CopyFrom->ImageDesc.Height * - CopyFrom->ImageDesc.Width); - - /* finally, the extension blocks */ - if (CopyFrom->ExtensionBlocks != NULL) { - sp->ExtensionBlocks = (ExtensionBlock *)reallocarray(NULL, - CopyFrom->ExtensionBlockCount, - sizeof(ExtensionBlock)); - if (sp->ExtensionBlocks == NULL) { - FreeLastSavedImage(GifFile); - return (SavedImage *)(NULL); - } - memcpy(sp->ExtensionBlocks, CopyFrom->ExtensionBlocks, - sizeof(ExtensionBlock) * CopyFrom->ExtensionBlockCount); - } - } - else { - memset((char *)sp, '\0', sizeof(SavedImage)); - } - - return (sp); - } +SavedImage *GifMakeSavedImage(GifFileType *GifFile, + const SavedImage *CopyFrom) { + // cppcheck-suppress ctunullpointer + if (GifFile->SavedImages == NULL) { + GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage)); + } else { + SavedImage *newSavedImages = (SavedImage *)reallocarray( + GifFile->SavedImages, (GifFile->ImageCount + 1), + sizeof(SavedImage)); + if (newSavedImages == NULL) { + return ((SavedImage *)NULL); + } + GifFile->SavedImages = newSavedImages; + } + if (GifFile->SavedImages == NULL) { + return ((SavedImage *)NULL); + } else { + SavedImage *sp = &GifFile->SavedImages[GifFile->ImageCount++]; + + if (CopyFrom != NULL) { + memcpy((char *)sp, CopyFrom, sizeof(SavedImage)); + + /* + * Make our own allocated copies of the heap fields in + * the copied record. This guards against potential + * aliasing problems. + */ + + /* first, the local color map */ + if (CopyFrom->ImageDesc.ColorMap != NULL) { + sp->ImageDesc.ColorMap = GifMakeMapObject( + CopyFrom->ImageDesc.ColorMap->ColorCount, + CopyFrom->ImageDesc.ColorMap->Colors); + if (sp->ImageDesc.ColorMap == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + } + + /* next, the raster */ + sp->RasterBits = (unsigned char *)reallocarray( + NULL, + (CopyFrom->ImageDesc.Height * + CopyFrom->ImageDesc.Width), + sizeof(GifPixelType)); + if (sp->RasterBits == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + memcpy(sp->RasterBits, CopyFrom->RasterBits, + sizeof(GifPixelType) * + CopyFrom->ImageDesc.Height * + CopyFrom->ImageDesc.Width); + + /* finally, the extension blocks */ + if (CopyFrom->ExtensionBlocks != NULL) { + sp->ExtensionBlocks = + (ExtensionBlock *)reallocarray( + NULL, CopyFrom->ExtensionBlockCount, + sizeof(ExtensionBlock)); + if (sp->ExtensionBlocks == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + memcpy(sp->ExtensionBlocks, + CopyFrom->ExtensionBlocks, + sizeof(ExtensionBlock) * + CopyFrom->ExtensionBlockCount); + } + } else { + memset((char *)sp, '\0', sizeof(SavedImage)); + } + + return (sp); + } } -void -GifFreeSavedImages(GifFileType *GifFile) -{ - SavedImage *sp; - - if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) { - return; - } - for (sp = GifFile->SavedImages; - sp < GifFile->SavedImages + GifFile->ImageCount; sp++) { - if (sp->ImageDesc.ColorMap != NULL) { - GifFreeMapObject(sp->ImageDesc.ColorMap); - sp->ImageDesc.ColorMap = NULL; - } - - if (sp->RasterBits != NULL) - free((char *)sp->RasterBits); - - GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks); - } - free((char *)GifFile->SavedImages); - GifFile->SavedImages = NULL; +void GifFreeSavedImages(GifFileType *GifFile) { + SavedImage *sp; + + if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) { + return; + } + for (sp = GifFile->SavedImages; + sp < GifFile->SavedImages + GifFile->ImageCount; sp++) { + if (sp->ImageDesc.ColorMap != NULL) { + GifFreeMapObject(sp->ImageDesc.ColorMap); + sp->ImageDesc.ColorMap = NULL; + } + + if (sp->RasterBits != NULL) { + free((char *)sp->RasterBits); + } + + GifFreeExtensions(&sp->ExtensionBlockCount, + &sp->ExtensionBlocks); + } + free((char *)GifFile->SavedImages); + GifFile->SavedImages = NULL; } /* end */ diff --git a/gifbg.c b/gifbg.c index e909e82..698678c 100644 --- a/gifbg.c +++ b/gifbg.c @@ -2,346 +2,388 @@ gifbg - generate a test-pattern GIF +SPDX-License-Identifier: MIT + *****************************************************************************/ -#include #include -#include #include +#include #include +#include -#include "gif_lib.h" #include "getarg.h" +#include "gif_lib.h" + +#define PROGRAM_NAME "gifbg" + +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 350 + +#define DEFAULT_COLOR_RED 0 +#define DEFAULT_COLOR_GREEN 0 +#define DEFAULT_COLOR_BLUE 255 + +#define DEFAULT_MIN_INTENSITY 10 /* In percent. */ +#define DEFAULT_MAX_INTENSITY 100 -#define PROGRAM_NAME "gifbg" - -#define DEFAULT_WIDTH 640 -#define DEFAULT_HEIGHT 350 - -#define DEFAULT_COLOR_RED 0 -#define DEFAULT_COLOR_GREEN 0 -#define DEFAULT_COLOR_BLUE 255 - -#define DEFAULT_MIN_INTENSITY 10 /* In percent. */ -#define DEFAULT_MAX_INTENSITY 100 - -#define DEFAULT_NUM_LEVELS 16 /* Number of colors to gen in image. */ - -#define DIR_NONE 0 /* Direction the levels can be changed: */ -#define DIR_TOP 1 -#define DIR_TOP_RIGHT 2 -#define DIR_RIGHT 3 -#define DIR_BOT_RIGHT 4 -#define DIR_BOT 5 -#define DIR_BOT_LEFT 6 -#define DIR_LEFT 7 -#define DIR_TOP_LEFT 8 - -#define DEFAULT_DIR "T" /* TOP (North) direction. */ - -static char - *VersionStr = - PROGRAM_NAME - VERSION_COOKIE - " Gershon Elber, " - __DATE__ ", " __TIME__ "\n" - "(C) Copyright 1989 Gershon Elber.\n"; -static char - *CtrlStr = - PROGRAM_NAME - " v%- d%-Dir!s l%-#Lvls!d c%-R|G|B!d!d!d m%-MinI!d M%-MaxI!d s%-W|H!d!d h%-"; - -static int - MaximumIntensity = DEFAULT_MAX_INTENSITY, /* In percent. */ - MinimumIntensity = DEFAULT_MIN_INTENSITY, - NumLevels = DEFAULT_NUM_LEVELS, - ImageWidth = DEFAULT_WIDTH, - ImageHeight = DEFAULT_HEIGHT, - Direction; -static unsigned int - RedColor = DEFAULT_COLOR_RED, - GreenColor = DEFAULT_COLOR_GREEN, - BlueColor = DEFAULT_COLOR_BLUE; +#define DEFAULT_NUM_LEVELS 16 /* Number of colors to gen in image. */ + +#define DIR_NONE 0 /* Direction the levels can be changed: */ +#define DIR_TOP 1 +#define DIR_TOP_RIGHT 2 +#define DIR_RIGHT 3 +#define DIR_BOT_RIGHT 4 +#define DIR_BOT 5 +#define DIR_BOT_LEFT 6 +#define DIR_LEFT 7 +#define DIR_TOP_LEFT 8 + +#define DEFAULT_DIR "T" /* TOP (North) direction. */ + +static char *VersionStr = PROGRAM_NAME VERSION_COOKIE + " Gershon Elber, " __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char *CtrlStr = PROGRAM_NAME " v%- d%-Dir!s l%-#Lvls!d c%-R|G|B!d!d!d " + "m%-MinI!d M%-MaxI!d s%-W|H!d!d h%-"; + +static int MaximumIntensity = DEFAULT_MAX_INTENSITY, /* In percent. */ + MinimumIntensity = DEFAULT_MIN_INTENSITY, NumLevels = DEFAULT_NUM_LEVELS, + ImageWidth = DEFAULT_WIDTH, ImageHeight = DEFAULT_HEIGHT, Direction; +static unsigned int RedColor = DEFAULT_COLOR_RED, + GreenColor = DEFAULT_COLOR_GREEN, + BlueColor = DEFAULT_COLOR_BLUE; static void QuitGifError(GifFileType *GifFile); /****************************************************************************** Interpret the command line and scan the given GIF file. ******************************************************************************/ -int main(int argc, char **argv) -{ - int i, l, LevelWidth, LogNumLevels, ErrorCode, Count = 0; - bool Error, FlipDir, DoAllMaximum = false, - DirectionFlag = false, LevelsFlag = false, ColorFlag = false, - MinFlag = false, MaxFlag = false, SizeFlag = false, HelpFlag = false; - GifPixelType Color; - char *DirectionStr = DEFAULT_DIR; - GifRowType Line; - ColorMapObject *ColorMap; - GifFileType *GifFile; - - if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, - &DirectionFlag, &DirectionStr, &LevelsFlag, &NumLevels, - &ColorFlag, &RedColor, &GreenColor, &BlueColor, - &MinFlag, &MinimumIntensity, &MaxFlag, &MaximumIntensity, - &SizeFlag, &ImageWidth, &ImageHeight, - &HelpFlag)) != false) { - GAPrintErrMsg(Error); - GAPrintHowTo(CtrlStr); - exit(EXIT_FAILURE); - } - - if (HelpFlag) { - (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); - GAPrintHowTo(CtrlStr); - exit(EXIT_SUCCESS); - } - - /* Make sure intensities are in the right range: */ - if (MinimumIntensity < 0 || MinimumIntensity > 100 || - MaximumIntensity < 0 || MaximumIntensity > 100) - GIF_EXIT("Intensities (-m or -M options) are not in [0..100] range (percent)."); - - /* Convert DirectionStr to our local representation: */ - Direction = DIR_NONE; - FlipDir = false; - /* Make sure it's upper case. */ - for (i = 0; i < (int)strlen(DirectionStr); i++) - if (islower(DirectionStr[i])) - DirectionStr[i] = toupper(DirectionStr[i]); - - switch(DirectionStr[0]) { +int main(int argc, char **argv) { + int i, l, LevelWidth, LogNumLevels, ErrorCode, Count = 0; + bool Error, FlipDir, DoAllMaximum = false, DirectionFlag = false, + LevelsFlag = false, ColorFlag = false, + MinFlag = false, MaxFlag = false, SizeFlag = false, + HelpFlag = false, GifNoisyPrint; + GifPixelType Color; + char *DirectionStr = DEFAULT_DIR; + GifRowType Line; + ColorMapObject *ColorMap; + GifFileType *GifFile; + + if ((Error = GAGetArgs( + argc, argv, CtrlStr, &GifNoisyPrint, &DirectionFlag, + &DirectionStr, &LevelsFlag, &NumLevels, &ColorFlag, &RedColor, + &GreenColor, &BlueColor, &MinFlag, &MinimumIntensity, &MaxFlag, + &MaximumIntensity, &SizeFlag, &ImageWidth, &ImageHeight, + &HelpFlag)) != false) { + GAPrintErrMsg(Error); + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); + } + + /* Make sure intensities are in the right range: */ + if (MinimumIntensity < 0 || MinimumIntensity > 100 || + MaximumIntensity < 0 || MaximumIntensity > 100) { + GIF_EXIT("Intensities (-m or -M options) are not in [0..100] " + "range (percent)."); + } + + /* Convert DirectionStr to our local representation: */ + Direction = DIR_NONE; + FlipDir = false; + /* Make sure it's upper case. */ + for (i = 0; i < (int)strlen(DirectionStr); i++) { + if (islower(DirectionStr[i])) { + DirectionStr[i] = toupper(DirectionStr[i]); + } + } + + switch (DirectionStr[0]) { case 'T': /* Top or North */ case 'N': - if (strlen(DirectionStr) < 2) - Direction = DIR_TOP; - else - switch(DirectionStr[1]) { - case 'R': - case 'E': - Direction = DIR_TOP_RIGHT; - break; - case 'L': - case 'W': - Direction = DIR_TOP_LEFT; - FlipDir = true; - break; + if (strlen(DirectionStr) < 2) { + Direction = DIR_TOP; + } else { + switch (DirectionStr[1]) { + case 'R': + case 'E': + Direction = DIR_TOP_RIGHT; + break; + case 'L': + case 'W': + Direction = DIR_TOP_LEFT; + FlipDir = true; + break; + } } - break; + break; case 'R': /* Right or East */ case 'E': - Direction = DIR_RIGHT; - break; + Direction = DIR_RIGHT; + break; case 'B': /* Bottom or South */ case 'S': - if (strlen(DirectionStr) < 2) { - Direction = DIR_BOT; - FlipDir = true; - } - else - switch(DirectionStr[1]) { - case 'R': - case 'E': - Direction = DIR_BOT_RIGHT; - break; - case 'L': - case 'W': - Direction = DIR_BOT_LEFT; + if (strlen(DirectionStr) < 2) { + Direction = DIR_BOT; FlipDir = true; - break; + } else { + switch (DirectionStr[1]) { + case 'R': + case 'E': + Direction = DIR_BOT_RIGHT; + break; + case 'L': + case 'W': + Direction = DIR_BOT_LEFT; + FlipDir = true; + break; + } } - break; + break; case 'L': /* Left or West */ case 'W': - Direction = DIR_LEFT; - FlipDir = true; - break; - } - if (Direction == DIR_NONE) - GIF_EXIT("Direction requested (-d option) is weird!"); - - /* We are going to handle only TOP, TOP_RIGHT, RIGHT, BOT_RIGHT so flip */ - /* the complement cases (TOP <-> BOT for example) by flipping the */ - /* Color i with color (NumLevels - i - 1). */ - if (FlipDir) { - switch (Direction) { - case DIR_BOT: - Direction = DIR_TOP; - break; - case DIR_BOT_LEFT: - Direction = DIR_TOP_RIGHT; - break; - case DIR_LEFT: - Direction = DIR_RIGHT; - break; - case DIR_TOP_LEFT: - Direction = DIR_BOT_RIGHT; + Direction = DIR_LEFT; + FlipDir = true; break; } - } - - /* If binary mask is requested (special case): */ - if (MinimumIntensity == 100 && MaximumIntensity == 100 && NumLevels == 2) { - MinimumIntensity = 0; - DoAllMaximum = true; - Direction = DIR_RIGHT; - } - - /* Make sure colors are in the right range: */ - if (RedColor > 255 || GreenColor > 255 || BlueColor > 255) - GIF_EXIT("Colors are not in the ragne [0..255]."); - - /* Make sure number of levels is power of 2 (up to 8 bits per pixel). */ - for (i = 1; i < 8; i++) if (NumLevels == (1 << i)) break; - if (i == 8) GIF_EXIT("#Lvls (-l option) is not power of 2."); - LogNumLevels = i; - - /* Open stdout for the output file: */ - if ((GifFile = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } - - /* Dump out screen description with given size and generated color map: */ - if ((ColorMap = GifMakeMapObject(NumLevels, NULL)) == NULL) - GIF_EXIT("Failed to allocate memory required, aborted."); - - for (i = 1; i <= NumLevels; i++) { - /* Ratio will be in the range of 0..100 for required intensity: */ - unsigned int Ratio = (MaximumIntensity * (i * (256 / NumLevels)) + - MinimumIntensity * ((NumLevels - i) * (256 / NumLevels))) / - 256; - ColorMap->Colors[i-1].Red = (RedColor * Ratio) / 100; - ColorMap->Colors[i-1].Green = (GreenColor * Ratio) / 100; - ColorMap->Colors[i-1].Blue = (BlueColor * Ratio) / 100; - } - if (EGifPutScreenDesc(GifFile, - ImageWidth, ImageHeight, LogNumLevels, 0, ColorMap) - == GIF_ERROR) - QuitGifError(GifFile); - - /* Dump out the image descriptor: */ - if (EGifPutImageDesc(GifFile, - 0, 0, ImageWidth, ImageHeight, false, NULL) == GIF_ERROR) - QuitGifError(GifFile); - - GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]: ", - PROGRAM_NAME, GifFile->Image.Left, GifFile->Image.Top, - GifFile->Image.Width, GifFile->Image.Height); - - /* Allocate one scan line twice as big as image is, as we are going to */ - /* shift along it, while we dump the scan lines: */ - if ((Line = (GifRowType) malloc(sizeof(GifPixelType) * ImageWidth * 2)) == NULL) - GIF_EXIT("Failed to allocate memory required, aborted."); - - if (Direction == DIR_TOP) { - int LevelHeight; - /* We must evaluate the line each time level is changing: */ - LevelHeight = ImageHeight / NumLevels; - for (Color = NumLevels, i = l = 0; i < ImageHeight; i++) { - if (i == l) { - int j; - /* Time to update the line to next color level: */ - if (Color != 0) Color--; - for (j = 0; j < ImageWidth; j++) - Line[j] = (FlipDir ? NumLevels - Color - 1 : Color); - l += LevelHeight; - } - if (EGifPutLine(GifFile, Line, ImageWidth) == GIF_ERROR) - QuitGifError(GifFile); - GifQprintf("\b\b\b\b%-4d", Count++); + if (Direction == DIR_NONE) { + GIF_EXIT("Direction requested (-d option) is weird!"); } - } - else if (Direction == DIR_RIGHT) { - /* We pre-prepare the scan lines as going from color zero to maximum */ - /* color and dump the same scan line Height times: */ - /* Note this case should handle the Boolean Mask special case. */ - LevelWidth = ImageWidth / NumLevels; - if (DoAllMaximum) { - /* Special case - do all in maximum color: */ - for (i = 0; i < ImageWidth; i++) Line[i] = 1; + + /* We are going to handle only TOP, TOP_RIGHT, RIGHT, BOT_RIGHT so flip + */ + /* the complement cases (TOP <-> BOT for example) by flipping the + */ + /* Color i with color (NumLevels - i - 1). */ + if (FlipDir) { + switch (Direction) { + case DIR_BOT: + Direction = DIR_TOP; + break; + case DIR_BOT_LEFT: + Direction = DIR_TOP_RIGHT; + break; + case DIR_LEFT: + Direction = DIR_RIGHT; + break; + case DIR_TOP_LEFT: + Direction = DIR_BOT_RIGHT; + break; + } } - else { - for (Color = i = 0, l = LevelWidth; i < ImageWidth; i++, l--) { - if (l == 0) { - l = LevelWidth; - if (Color < NumLevels - 1) Color++; + + /* If binary mask is requested (special case): */ + if (MinimumIntensity == 100 && MaximumIntensity == 100 && + NumLevels == 2) { + MinimumIntensity = 0; + DoAllMaximum = true; + Direction = DIR_RIGHT; + } + + /* Make sure colors are in the right range: */ + if (RedColor > 255 || GreenColor > 255 || BlueColor > 255) { + GIF_EXIT("Colors are not in the ragne [0..255]."); + } + + /* Make sure number of levels is power of 2 (up to 8 bits per pixel). */ + for (i = 1; i < 8; i++) { + if (NumLevels == (1 << i)) { + break; } - Line[i] = (FlipDir ? NumLevels - Color - 1 : Color); - } } + if (i == 8) { + GIF_EXIT("#Lvls (-l option) is not power of 2."); + } + LogNumLevels = i; - for (i = 0; i < ImageHeight; i++) { - /* coverity[uninit_use_in_call] */ - if (EGifPutLine(GifFile, Line, ImageWidth) == GIF_ERROR) - QuitGifError(GifFile); - GifQprintf("\b\b\b\b%-4d", Count++); + /* Open stdout for the output file: */ + if ((GifFile = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); } - } - else { - int Accumulator, StartX, StepX; - /* We are in one of the TOP_RIGHT, BOT_RIGHT cases: we will */ - /* initialize the Line with its double ImageWidth length from the */ - /* minimum intensity to the maximum intensity and shift along it */ - /* while we go along the image height. */ - LevelWidth = ImageWidth * 2 / NumLevels; - for (Color = i = 0, l = LevelWidth; i < ImageWidth * 2; i++, l--) { - if (l == 0) { - l = LevelWidth; - if (Color < NumLevels - 1) Color++; - } - Line[i] = (FlipDir ? NumLevels - Color - 1 : Color); + + /* Dump out screen description with given size and generated color map: + */ + if ((ColorMap = GifMakeMapObject(NumLevels, NULL)) == NULL) { + GIF_EXIT("Failed to allocate memory required, aborted."); } - /* We need to implement a DDA to know how much to shift Line while */ - /* we go down along image height. we set the parameters for it now: */ - Accumulator = 0; - switch(Direction) { - case DIR_TOP_RIGHT: - StartX = ImageWidth; - StepX = -1; - break; - case DIR_BOT_RIGHT: - default: - StartX = 0; - StepX = 1; - break; + + for (i = 1; i <= NumLevels; i++) { + /* Ratio will be in the range of 0..100 for required intensity: + */ + unsigned int Ratio = + (MaximumIntensity * (i * (256 / NumLevels)) + + MinimumIntensity * ((NumLevels - i) * (256 / NumLevels))) / + 256; + ColorMap->Colors[i - 1].Red = (RedColor * Ratio) / 100; + ColorMap->Colors[i - 1].Green = (GreenColor * Ratio) / 100; + ColorMap->Colors[i - 1].Blue = (BlueColor * Ratio) / 100; + } + if (EGifPutScreenDesc(GifFile, ImageWidth, ImageHeight, LogNumLevels, 0, + ColorMap) == GIF_ERROR) { + QuitGifError(GifFile); } - /* Time to dump information out: */ - for (i = 0; i < ImageHeight; i++) { - if (EGifPutLine(GifFile, &Line[StartX], ImageWidth) == GIF_ERROR) + /* Dump out the image descriptor: */ + if (EGifPutImageDesc(GifFile, 0, 0, ImageWidth, ImageHeight, false, + NULL) == GIF_ERROR) { QuitGifError(GifFile); - GifQprintf("\b\b\b\b%-4d", Count++); - if ((Accumulator += ImageWidth) > ImageHeight) { - while (Accumulator > ImageHeight) { - Accumulator -= ImageHeight; - StartX += StepX; + } + + GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]: ", PROGRAM_NAME, + GifFile->Image.Left, GifFile->Image.Top, + GifFile->Image.Width, GifFile->Image.Height); + + /* Allocate one scan line twice as big as image is, as we are going to + */ + /* shift along it, while we dump the scan lines: */ + if ((Line = (GifRowType)malloc(sizeof(GifPixelType) * ImageWidth * + 2)) == NULL) { + GIF_EXIT("Failed to allocate memory required, aborted."); + } + + if (Direction == DIR_TOP) { + int LevelHeight; + /* We must evaluate the line each time level is changing: */ + LevelHeight = ImageHeight / NumLevels; + for (Color = NumLevels, i = l = 0; i < ImageHeight; i++) { + if (i == l) { + int j; + /* Time to update the line to next color level: + */ + if (Color != 0) { + Color--; + } + for (j = 0; j < ImageWidth; j++) { + Line[j] = + (FlipDir ? NumLevels - Color - 1 + : Color); + } + l += LevelHeight; + } + if (EGifPutLine(GifFile, Line, ImageWidth) == + GIF_ERROR) { + QuitGifError(GifFile); + } + GifQprintf("\b\b\b\b%-4d", Count++); + } + } else if (Direction == DIR_RIGHT) { + /* We pre-prepare the scan lines as going from color zero to + * maximum */ + /* color and dump the same scan line Height times: + */ + /* Note this case should handle the Boolean Mask special case. + */ + LevelWidth = ImageWidth / NumLevels; + if (DoAllMaximum) { + /* Special case - do all in maximum color: */ + for (i = 0; i < ImageWidth; i++) { + Line[i] = 1; + } + } else { + for (Color = i = 0, l = LevelWidth; i < ImageWidth; + i++, l--) { + if (l == 0) { + l = LevelWidth; + if (Color < NumLevels - 1) { + Color++; + } + } + Line[i] = + (FlipDir ? NumLevels - Color - 1 : Color); + } + } + + for (i = 0; i < ImageHeight; i++) { + /* coverity[uninit_use_in_call] */ + if (EGifPutLine(GifFile, Line, ImageWidth) == + GIF_ERROR) { + QuitGifError(GifFile); + } + GifQprintf("\b\b\b\b%-4d", Count++); + } + } else { + int Accumulator, StartX, StepX; + /* We are in one of the TOP_RIGHT, BOT_RIGHT cases: we will */ + /* initialize the Line with its double ImageWidth length from + * the */ + /* minimum intensity to the maximum intensity and shift along it + */ + /* while we go along the image height. */ + LevelWidth = ImageWidth * 2 / NumLevels; + for (Color = i = 0, l = LevelWidth; i < ImageWidth * 2; + i++, l--) { + if (l == 0) { + l = LevelWidth; + if (Color < NumLevels - 1) { + Color++; + } + } + Line[i] = (FlipDir ? NumLevels - Color - 1 : Color); + } + /* We need to implement a DDA to know how much to shift Line + * while */ + /* we go down along image height. we set the parameters for it + * now: */ + Accumulator = 0; + switch (Direction) { + case DIR_TOP_RIGHT: + StartX = ImageWidth; + StepX = -1; + break; + case DIR_BOT_RIGHT: + default: + StartX = 0; + StepX = 1; + break; + } + + /* Time to dump information out: */ + for (i = 0; i < ImageHeight; i++) { + if (EGifPutLine(GifFile, &Line[StartX], ImageWidth) == + GIF_ERROR) { + QuitGifError(GifFile); + } + GifQprintf("\b\b\b\b%-4d", Count++); + if ((Accumulator += ImageWidth) > ImageHeight) { + while (Accumulator > ImageHeight) { + Accumulator -= ImageHeight; + StartX += StepX; + } + if (Direction < 0) { + Direction = 0; + } + if (Direction > ImageWidth) { + Direction = ImageWidth; + } + } } - if (Direction < 0) Direction = 0; - if (Direction > ImageWidth) Direction = ImageWidth; - } } - } - if (EGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) - { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } + if (EGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } - return 0; + return 0; } /****************************************************************************** Close output file (if open), and exit. ******************************************************************************/ -static void QuitGifError(GifFileType *GifFile) -{ - if (GifFile != NULL) { - PrintGifError(GifFile->Error); - EGifCloseFile(GifFile, NULL); - } - exit(EXIT_FAILURE); +static void QuitGifError(GifFileType *GifFile) { + if (GifFile != NULL) { + PrintGifError(GifFile->Error); + EGifCloseFile(GifFile, NULL); + } + exit(EXIT_FAILURE); } /* end */ diff --git a/gifbuild.c b/gifbuild.c index 7076830..e8b9b0c 100644 --- a/gifbuild.c +++ b/gifbuild.c @@ -2,937 +2,1020 @@ gifbuild - dump GIF data in a textual format, or undump it to a GIF +SPDX-License-Identifier: MIT + *****************************************************************************/ -#include -#include -#include -#include #include #include +#include +#include +#include -#include "gif_lib.h" #include "getarg.h" +#include "gif_lib.h" + +#define PROGRAM_NAME "gifbuild" -#define PROGRAM_NAME "gifbuild" - -static char - *VersionStr = - PROGRAM_NAME - VERSION_COOKIE - " Eric Raymond, " - __DATE__ ", " __TIME__ "\n" - "(C) Copyright 1992 Eric Raymond.\n"; -static char - *CtrlStr = - PROGRAM_NAME - " v%- d%- t%-Characters!s h%- GifFile(s)!*s"; - -static char KeyLetters[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:<=>?@[\\]^_`{|}~"; -#define PRINTABLES (sizeof(KeyLetters) - 1) - -static void Icon2Gif(char *FileName, FILE *txtin, int fdout); -static void Gif2Icon(char *FileName, - int fdin, int fdout, - char NameTable[]); +static char *VersionStr = PROGRAM_NAME VERSION_COOKIE + " Eric Raymond, " __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1992 Eric Raymond.\n"; +static char *CtrlStr = + PROGRAM_NAME " v%- d%- t%-Characters!s h%- GifFile(s)!*s"; + +static char KeyLetters[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO" + "PQRSTUVWXYZ!\"#$%&'()*+,-./:<=>?@[\\]^_`{|}~"; +#define PRINTABLES (sizeof(KeyLetters) - 1) + +static void Icon2Gif(char *FileName, FILE *txtin, bool, int fdout); +static void Gif2Icon(char *FileName, int fdin, int fdout, char NameTable[]); static int EscapeString(char *cp, char *tp); /****************************************************************************** Main sequence ******************************************************************************/ -int main(int argc, char **argv) -{ - int NumFiles; - bool Error, DisasmFlag = false, HelpFlag = false, TextLineFlag = false; - char **FileNames = NULL; - char *TextLines[1]; - - if ((Error = GAGetArgs(argc, argv, CtrlStr, - &GifNoisyPrint, &DisasmFlag, &TextLineFlag, &TextLines[0], - &HelpFlag, &NumFiles, &FileNames)) != false) { - GAPrintErrMsg(Error); - GAPrintHowTo(CtrlStr); - exit(EXIT_FAILURE); - } - - if (HelpFlag) { - (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); - GAPrintHowTo(CtrlStr); - exit(EXIT_SUCCESS); - } - - if (!DisasmFlag && NumFiles > 1) { - GIF_MESSAGE("Error in command line parsing - one text input please."); - GAPrintHowTo(CtrlStr); - exit(EXIT_FAILURE); - } - - if (!DisasmFlag && TextLineFlag) { - GIF_MESSAGE("Error in command line parsing - -t invalid without -d."); - GAPrintHowTo(CtrlStr); - exit(EXIT_FAILURE); - } - - - if (NumFiles == 0) - { - if (DisasmFlag) - Gif2Icon("Stdin", 0, 1, TextLineFlag ? TextLines[0] : KeyLetters); - else - Icon2Gif("Stdin", stdin, 1); - } - else - { - int i; - for (i = 0; i < NumFiles; i++) - { - FILE *fp; - - if ((fp = fopen(FileNames[i], "r")) == (FILE *)NULL) - { - (void) fprintf(stderr, "Can't open %s\n", FileNames[i]); +int main(int argc, char **argv) { + int NumFiles; + bool Error, DisasmFlag = false, HelpFlag = false, TextLineFlag = false, + GifNoisyPrint = false; + char **FileNames = NULL; + char *TextLines[1]; + + if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &DisasmFlag, + &TextLineFlag, &TextLines[0], &HelpFlag, + &NumFiles, &FileNames)) != false) { + GAPrintErrMsg(Error); + GAPrintHowTo(CtrlStr); exit(EXIT_FAILURE); - } - - if (DisasmFlag) - { - printf("#\n# GIF information from %s\n", FileNames[i]); - Gif2Icon(FileNames[i], -1, 1, TextLineFlag ? TextLines[0] : KeyLetters); - } - else - { - Icon2Gif(FileNames[i], fp, 1); - } - - (void) fclose(fp); } - } - return 0; + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); + } + + if (!DisasmFlag && NumFiles > 1) { + GIF_MESSAGE( + "Error in command line parsing - one text input please."); + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (!DisasmFlag && TextLineFlag) { + GIF_MESSAGE( + "Error in command line parsing - -t invalid without -d."); + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (NumFiles == 0) { + if (DisasmFlag) + Gif2Icon("Stdin", 0, 1, + TextLineFlag ? TextLines[0] : KeyLetters); + else + Icon2Gif("Stdin", stdin, GifNoisyPrint, 1); + } else { + int i; + for (i = 0; i < NumFiles; i++) { + FILE *fp; + + if ((fp = fopen(FileNames[i], "r")) == (FILE *)NULL) { + (void)fprintf(stderr, "Can't open %s\n", + FileNames[i]); + exit(EXIT_FAILURE); + } + + if (DisasmFlag) { + printf("#\n# GIF information from %s\n", + FileNames[i]); + Gif2Icon(FileNames[i], -1, 1, + TextLineFlag ? TextLines[0] + : KeyLetters); + } else { + Icon2Gif(FileNames[i], fp, GifNoisyPrint, 1); + } + + (void)fclose(fp); + } + } + + return 0; } /****************************************************************************** Parse image directives ******************************************************************************/ -#define PARSE_ERROR(str) (void) fprintf(stderr,"%s:%d: %s\n",FileName,LineNum,str); +#define PARSE_ERROR(str) \ + (void)fprintf(stderr, "%s:%d: %s\n", FileName, LineNum, str); + +static void Icon2Gif(char *FileName, FILE *txtin, bool GifNoisyPrint, + int fdout) { + unsigned int ColorMapSize = 0; + GifColorType GlobalColorMap[256], LocalColorMap[256], + *ColorMap = GlobalColorMap; + char GlobalColorKeys[PRINTABLES], LocalColorKeys[PRINTABLES], + *KeyTable = GlobalColorKeys; + bool SortFlag = false; + unsigned int ExtCode, intval; + int red, green, blue, n; + char buf[BUFSIZ * 2], InclusionFile[64]; + GifFileType *GifFileOut; + SavedImage *NewImage = NULL; + int LeadingExtensionBlockCount = 0; + ExtensionBlock *LeadingExtensionBlocks = NULL; + int ErrorCode, LineNum = 0; + + if ((GifFileOut = EGifOpenFileHandle(fdout, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } -static void Icon2Gif(char *FileName, FILE *txtin, int fdout) -{ - unsigned int ColorMapSize = 0; - GifColorType GlobalColorMap[256], LocalColorMap[256], - *ColorMap = GlobalColorMap; - char GlobalColorKeys[PRINTABLES], LocalColorKeys[PRINTABLES], - *KeyTable = GlobalColorKeys; - bool SortFlag = false; - unsigned int ExtCode, intval; - int red, green, blue, n; - char buf[BUFSIZ * 2], InclusionFile[64]; - GifFileType *GifFileOut; - SavedImage *NewImage = NULL; - int LeadingExtensionBlockCount = 0; - ExtensionBlock *LeadingExtensionBlocks = NULL; - int ErrorCode, LineNum = 0; - - if ((GifFileOut = EGifOpenFileHandle(fdout, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } - - /* OK, interpret directives */ - /* coverity[tainted_data_transitive] */ - while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) - { - char *cp; - - ++LineNum; + /* OK, interpret directives */ + /* coverity[tainted_data_transitive] */ + while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) { + char *cp; - /* - * Skip lines consisting only of whitespace and comments - */ - for (cp = buf; isspace((int)(*cp)); cp++) - continue; - if (*cp == '#' || *cp == '\0') - continue; + ++LineNum; - /* - * If there's a trailing comment, nuke it and all preceding whitespace. - * But preserve the EOL. - */ - if ((cp = strchr(buf, '#')) && (cp == strrchr(cp, '#'))) - { - while (isspace((int)(*--cp))) - continue; - *++cp = '\n'; - *++cp = '\0'; - } + /* + * Skip lines consisting only of whitespace and comments + */ + for (cp = buf; isspace((int)(*cp)); cp++) { + continue; + } + if (*cp == '#' || *cp == '\0') { + continue; + } - /* - * Explicit header declarations - */ + /* + * If there's a trailing comment, nuke it and all preceding + * whitespace. But preserve the EOL. + */ + if ((cp = strchr(buf, '#')) && (cp == strrchr(cp, '#'))) { + while (isspace((int)(*--cp))) { + continue; + } + *++cp = '\n'; + *++cp = '\0'; + } - if (sscanf(buf, "screen width %d\n", &GifFileOut->SWidth) == 1) - continue; + /* + * Explicit header declarations + */ - else if (sscanf(buf, "screen height %d\n", &GifFileOut->SHeight) == 1) - continue; + if (sscanf(buf, "screen width %d\n", &GifFileOut->SWidth) == + 1) { + continue; + } - else if (sscanf(buf, "screen colors %d\n", &n) == 1) - { - int ResBits = GifBitSize(n); + else if (sscanf(buf, "screen height %d\n", + &GifFileOut->SHeight) == 1) { + continue; + } - if (n > 256 || n < 0 || n != (1 << ResBits)) - { - PARSE_ERROR("Invalid color resolution value."); - exit(EXIT_FAILURE); - } + else if (sscanf(buf, "screen colors %d\n", &n) == 1) { + int ResBits = GifBitSize(n); - GifFileOut->SColorResolution = ResBits; - continue; - } + if (n > 256 || n < 0 || n != (1 << ResBits)) { + PARSE_ERROR("Invalid color resolution value."); + exit(EXIT_FAILURE); + } - else if (sscanf(buf, - "screen background %d\n", - &GifFileOut->SBackGroundColor) == 1) - continue; + GifFileOut->SColorResolution = ResBits; + continue; + } - else if (sscanf(buf, "pixel aspect byte %u\n", &intval) == 1) { - GifFileOut->AspectByte = (GifByteType)(intval & 0xff); - continue; - } + else if (sscanf(buf, "screen background %d\n", + &GifFileOut->SBackGroundColor) == 1) { + continue; + } - /* - * Color table parsing - */ + else if (sscanf(buf, "pixel aspect byte %u\n", &intval) == 1) { + GifFileOut->AspectByte = (GifByteType)(intval & 0xff); + continue; + } - else if (strcmp(buf, "screen map\n") == 0) - { - if (GifFileOut->SColorMap != NULL) - { - PARSE_ERROR("You've already declared a global color map."); - exit(EXIT_FAILURE); - } + /* + * Color table parsing + */ - ColorMapSize = 0; - ColorMap = GlobalColorMap; - SortFlag = false; - KeyTable = GlobalColorKeys; - memset(GlobalColorKeys, '\0', sizeof(GlobalColorKeys)); - } + else if (strcmp(buf, "screen map\n") == 0) { + if (GifFileOut->SColorMap != NULL) { + PARSE_ERROR("You've already declared a global " + "color map."); + exit(EXIT_FAILURE); + } - else if (strcmp(buf, "image map\n") == 0) - { - if (NewImage == NULL) - { - PARSE_ERROR("No previous image declaration."); - exit(EXIT_FAILURE); - } + ColorMapSize = 0; + ColorMap = GlobalColorMap; + SortFlag = false; + KeyTable = GlobalColorKeys; + memset(GlobalColorKeys, '\0', sizeof(GlobalColorKeys)); + } - ColorMapSize = 0; - ColorMap = LocalColorMap; - KeyTable = LocalColorKeys; - memset(LocalColorKeys, '\0', sizeof(LocalColorKeys)); - } + else if (strcmp(buf, "image map\n") == 0) { + if (NewImage == NULL) { + PARSE_ERROR("No previous image declaration."); + exit(EXIT_FAILURE); + } - else if (sscanf(buf, " rgb %d %d %d is %c", - &red, &green, &blue, &KeyTable[ColorMapSize]) == 4) - { - ColorMap[ColorMapSize].Red = red; - ColorMap[ColorMapSize].Green = green; - ColorMap[ColorMapSize].Blue = blue; - ColorMapSize++; - } + ColorMapSize = 0; + ColorMap = LocalColorMap; + KeyTable = LocalColorKeys; + memset(LocalColorKeys, '\0', sizeof(LocalColorKeys)); + } - else if (sscanf(buf, " rgb %d %d %d", &red, &green, &blue) == 3) - { - ColorMap[ColorMapSize].Red = red; - ColorMap[ColorMapSize].Green = green; - ColorMap[ColorMapSize].Blue = blue; - ColorMapSize++; - } + else if (sscanf(buf, " rgb %d %d %d is %c", &red, &green, + &blue, &KeyTable[ColorMapSize]) == 4) { + if (ColorMapSize >= 256) { + PARSE_ERROR("Too many color entries."); + exit(EXIT_FAILURE); + } + ColorMap[ColorMapSize].Red = red; + ColorMap[ColorMapSize].Green = green; + ColorMap[ColorMapSize].Blue = blue; + ColorMapSize++; + } - else if (strcmp(buf, " sort flag on\n") == 0) - SortFlag = true; + else if (sscanf(buf, " rgb %d %d %d", &red, &green, &blue) == + 3) { + if (ColorMapSize >= 256) { + PARSE_ERROR("Too many color entries."); + exit(EXIT_FAILURE); + } + ColorMap[ColorMapSize].Red = red; + ColorMap[ColorMapSize].Green = green; + ColorMap[ColorMapSize].Blue = blue; + ColorMapSize++; + } - else if (strcmp(buf, " sort flag off\n") == 0) - SortFlag = false; + else if (strcmp(buf, " sort flag on\n") == 0) { + SortFlag = true; + } - else if (strcmp(buf, "end\n") == 0) - { - ColorMapObject *NewMap; + else if (strcmp(buf, " sort flag off\n") == 0) { + SortFlag = false; + } - NewMap = GifMakeMapObject(1 << GifBitSize(ColorMapSize), ColorMap); - if (NewMap == (ColorMapObject *)NULL) - { - PARSE_ERROR("Out of memory while allocating new color map."); - exit(EXIT_FAILURE); - } + else if (strcmp(buf, "end\n") == 0) { + ColorMapObject *NewMap; - NewMap->SortFlag = SortFlag; + NewMap = GifMakeMapObject(1 << GifBitSize(ColorMapSize), + ColorMap); + if (NewMap == (ColorMapObject *)NULL) { + PARSE_ERROR("Out of memory while allocating " + "new color map."); + exit(EXIT_FAILURE); + } - if (NewImage) - NewImage->ImageDesc.ColorMap = NewMap; - else - GifFileOut->SColorMap = NewMap; - } + NewMap->SortFlag = SortFlag; - /* GIF inclusion */ - /* ugly magic number is because scanf has no */ - else if (sscanf(buf, "include %63s", InclusionFile) == 1) - { - int ErrorCode; - bool DoTranslation; - GifPixelType Translation[256]; + if (NewImage) { + NewImage->ImageDesc.ColorMap = NewMap; + } else { + GifFileOut->SColorMap = NewMap; + } + } - GifFileType *Inclusion; - SavedImage *CopyFrom; + /* GIF inclusion */ + /* ugly magic number is because scanf has no */ + else if (sscanf(buf, "include %63s", InclusionFile) == 1) { + bool DoTranslation; + GifPixelType Translation[256]; - if ((Inclusion = DGifOpenFileName(InclusionFile, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } - - if (DGifSlurp(Inclusion) == GIF_ERROR) - { - PARSE_ERROR("Inclusion read failed."); - if (Inclusion != NULL) { - PrintGifError(Inclusion->Error); - DGifCloseFile(Inclusion, NULL); - } - if (GifFileOut != NULL) { - EGifCloseFile(GifFileOut, NULL); - }; - exit(EXIT_FAILURE); - } + GifFileType *Inclusion; + SavedImage *CopyFrom; - //cppcheck-suppress nullPointerRedundantCheck - if ((DoTranslation = (GifFileOut->SColorMap!=(ColorMapObject*)NULL))) - { - ColorMapObject *UnionMap; + if ((Inclusion = DGifOpenFileName( + InclusionFile, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } - //cppcheck-suppress nullPointerRedundantCheck - UnionMap = GifUnionColorMap(GifFileOut->SColorMap, Inclusion->SColorMap, Translation); + if (DGifSlurp(Inclusion) == GIF_ERROR) { + PARSE_ERROR("Inclusion read failed."); + // cppcheck-suppress knownConditionTrueFalse + if (Inclusion != NULL) { + PrintGifError(Inclusion->Error); + DGifCloseFile(Inclusion, NULL); + } + if (GifFileOut != NULL) { + EGifCloseFile(GifFileOut, NULL); + }; + exit(EXIT_FAILURE); + } - if (UnionMap == NULL) - { - PARSE_ERROR("Inclusion failed --- global map conflict."); - //cppcheck-suppress nullPointerRedundantCheck - PrintGifError(GifFileOut->Error); - if (Inclusion != NULL) DGifCloseFile(Inclusion, NULL); - if (GifFileOut != NULL) EGifCloseFile(GifFileOut, NULL); - exit(EXIT_FAILURE); - } - - GifFreeMapObject(GifFileOut->SColorMap); - GifFileOut->SColorMap = UnionMap; - } - - //cppcheck-suppress nullPointerRedundantCheck - for (CopyFrom = Inclusion->SavedImages; - //cppcheck-suppress nullPointerRedundantCheck - CopyFrom < Inclusion->SavedImages + Inclusion->ImageCount; - CopyFrom++) - { - SavedImage *NewImage; - if ((NewImage = GifMakeSavedImage(GifFileOut, CopyFrom)) == NULL) - { - PARSE_ERROR("Inclusion failed --- out of memory."); - //cppcheck-suppress nullPointerRedundantCheck - PrintGifError(GifFileOut->Error); - if (Inclusion != NULL) DGifCloseFile(Inclusion, NULL); - if (GifFileOut != NULL) EGifCloseFile(GifFileOut, NULL); - exit(EXIT_FAILURE); - } - else if (DoTranslation) - GifApplyTranslation(NewImage, Translation); - - GifQprintf( - "%s: Image %d at (%d, %d) [%dx%d]: from %s\n", - PROGRAM_NAME, GifFileOut->ImageCount, - NewImage->ImageDesc.Left, NewImage->ImageDesc.Top, - NewImage->ImageDesc.Width, NewImage->ImageDesc.Height, - InclusionFile); - } - - (void) DGifCloseFile(Inclusion, NULL); - } + // cppcheck-suppress nullPointerRedundantCheck + if ((DoTranslation = (GifFileOut->SColorMap != + (ColorMapObject *)NULL))) { + ColorMapObject *UnionMap; + + UnionMap = GifUnionColorMap( + // cppcheck-suppress nullPointerRedundantCheck + GifFileOut->SColorMap, Inclusion->SColorMap, + Translation); + + if (UnionMap == NULL) { + PARSE_ERROR("Inclusion failed --- " + "global map conflict."); + // cppcheck-suppress nullPointerRedundantCheck + PrintGifError(GifFileOut->Error); + // cppcheck-suppress knownConditionTrueFalse + if (Inclusion != NULL) { + DGifCloseFile(Inclusion, NULL); + } + if (GifFileOut != NULL) { + EGifCloseFile(GifFileOut, NULL); + } + exit(EXIT_FAILURE); + } + + GifFreeMapObject(GifFileOut->SColorMap); + GifFileOut->SColorMap = UnionMap; + } - /* - * Extension blocks. - */ - else if (strcmp(buf, "comment\n") == 0) - { - int bc = 0; - while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) - if (strcmp(buf, "end\n") == 0) - break; - else - { - int Len; - - buf[strlen(buf) - 1] = '\0'; - Len = EscapeString(buf, buf); - if (GifAddExtensionBlock(&LeadingExtensionBlockCount, - &LeadingExtensionBlocks, - bc++ == CONTINUE_EXT_FUNC_CODE ? COMMENT_EXT_FUNC_CODE : 0, - Len, - (unsigned char *)buf) == GIF_ERROR) { - PARSE_ERROR("out of memory while adding comment block."); - exit(EXIT_FAILURE); - } - } - } - else if (strcmp(buf, "plaintext\n") == 0) - { - int bc = 0; - while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) - if (strcmp(buf, "end\n") == 0) - break; - else - { - int Len; - - buf[strlen(buf) - 1] = '\0'; - Len = EscapeString(buf, buf); - if (GifAddExtensionBlock(&LeadingExtensionBlockCount, - &LeadingExtensionBlocks, - bc++ == CONTINUE_EXT_FUNC_CODE ? PLAINTEXT_EXT_FUNC_CODE : 0, - Len, - (unsigned char *)buf) == GIF_ERROR) { - PARSE_ERROR("out of memory while adding plaintext block."); - exit(EXIT_FAILURE); - } + for (CopyFrom = Inclusion->SavedImages; + CopyFrom < + Inclusion->SavedImages + Inclusion->ImageCount; + CopyFrom++) { + if ((NewImage = GifMakeSavedImage( + GifFileOut, CopyFrom)) == NULL) { + PARSE_ERROR("Inclusion failed --- out " + "of memory."); + // cppcheck-suppress nullPointerRedundantCheck + PrintGifError(GifFileOut->Error); + // cppcheck-suppress knownConditionTrueFalse + if (Inclusion != NULL) { + DGifCloseFile(Inclusion, NULL); + } + if (GifFileOut != NULL) { + EGifCloseFile(GifFileOut, NULL); + } + exit(EXIT_FAILURE); + } else if (DoTranslation) { + GifApplyTranslation(NewImage, + Translation); + } + + GifQprintf("%s: Image %d at (%d, %d) [%dx%d]: " + "from %s\n", + PROGRAM_NAME, GifFileOut->ImageCount, + NewImage->ImageDesc.Left, + NewImage->ImageDesc.Top, + NewImage->ImageDesc.Width, + NewImage->ImageDesc.Height, + InclusionFile); + } + + (void)DGifCloseFile(Inclusion, NULL); } - } - else if (strcmp(buf, "graphics control\n") == 0) - { - GraphicsControlBlock gcb; - size_t Len; - - memset(&gcb, '\0', sizeof(gcb)); - gcb.TransparentColor = NO_TRANSPARENT_COLOR; - while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) - if (strcmp(buf, "end\n") == 0) - break; - else - { - char *tp = buf; - while (isspace(*tp)) - tp++; - if (sscanf(tp, "disposal mode %d\n", &gcb.DisposalMode)) - continue; - if (strcmp(tp, "user input flag on\n") == 0) { - gcb.UserInputFlag = true; - continue; - } - if (strcmp(tp, "user input flag off\n") == 0) { - gcb.UserInputFlag = false; - continue; - } - if (sscanf(tp, "delay %d\n", &gcb.DelayTime)) - continue; - if (sscanf(tp, "transparent index %d\n", - &gcb.TransparentColor)) - continue; - (void) fputs(tp, stderr); - PARSE_ERROR("unrecognized directive in GCB block."); - exit(EXIT_FAILURE); - } - Len = EGifGCBToExtension(&gcb, (GifByteType *)buf); - if (GifAddExtensionBlock(&LeadingExtensionBlockCount, - &LeadingExtensionBlocks, - GRAPHICS_EXT_FUNC_CODE, - Len, - (unsigned char *)buf) == GIF_ERROR) { - PARSE_ERROR("out of memory while adding GCB."); - exit(EXIT_FAILURE); - } + /* + * Extension blocks. + */ + else if (strcmp(buf, "comment\n") == 0) { + int bc = 0; + while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) { + if (strcmp(buf, "end\n") == 0) { + break; + } else { + int Len; + + buf[strlen(buf) - 1] = '\0'; + Len = EscapeString(buf, buf); + if (GifAddExtensionBlock( + &LeadingExtensionBlockCount, + &LeadingExtensionBlocks, + bc++ == CONTINUE_EXT_FUNC_CODE + ? COMMENT_EXT_FUNC_CODE + : 0, + Len, (unsigned char *)buf) == + GIF_ERROR) { + PARSE_ERROR( + "out of memory while " + "adding comment block."); + exit(EXIT_FAILURE); + } + } + } + } else if (strcmp(buf, "plaintext\n") == 0) { + int bc = 0; + while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) { + if (strcmp(buf, "end\n") == 0) { + break; + } else { + int Len; + + buf[strlen(buf) - 1] = '\0'; + Len = EscapeString(buf, buf); + if (GifAddExtensionBlock( + &LeadingExtensionBlockCount, + &LeadingExtensionBlocks, + bc++ == CONTINUE_EXT_FUNC_CODE + ? PLAINTEXT_EXT_FUNC_CODE + : 0, + Len, (unsigned char *)buf) == + GIF_ERROR) { + PARSE_ERROR( + "out of memory while " + "adding plaintext block."); + exit(EXIT_FAILURE); + } + } + } + } else if (strcmp(buf, "graphics control\n") == 0) { + GraphicsControlBlock gcb; + size_t Len; + + memset(&gcb, '\0', sizeof(gcb)); + gcb.TransparentColor = NO_TRANSPARENT_COLOR; + while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) { + if (strcmp(buf, "end\n") == 0) { + break; + } else { + char *tp = buf; + + while (isspace(*tp)) { + tp++; + } + if (sscanf(tp, "disposal mode %d\n", + &gcb.DisposalMode)) { + continue; + } + if (strcmp(tp, + "user input flag on\n") == + 0) { + gcb.UserInputFlag = true; + continue; + } + if (strcmp(tp, + "user input flag off\n") == + 0) { + gcb.UserInputFlag = false; + continue; + } + if (sscanf(tp, "delay %d\n", + &gcb.DelayTime)) { + continue; + } + if (sscanf(tp, "transparent index %d\n", + &gcb.TransparentColor)) { + continue; + } + (void)fputs(tp, stderr); + PARSE_ERROR("unrecognized directive in " + "GCB block."); + exit(EXIT_FAILURE); + } + } + Len = EGifGCBToExtension(&gcb, (GifByteType *)buf); + if (GifAddExtensionBlock( + &LeadingExtensionBlockCount, + &LeadingExtensionBlocks, GRAPHICS_EXT_FUNC_CODE, + Len, (unsigned char *)buf) == GIF_ERROR) { + PARSE_ERROR("out of memory while adding GCB."); + exit(EXIT_FAILURE); + } - } - else if (sscanf(buf, "netscape loop %u", &intval)) - { - unsigned char params[3] = {1, 0, 0}; - /* Create a Netscape 2.0 loop block */ - if (GifAddExtensionBlock(&LeadingExtensionBlockCount, - &LeadingExtensionBlocks, - APPLICATION_EXT_FUNC_CODE, - 11, - (unsigned char *)"NETSCAPE2.0")==GIF_ERROR) { - PARSE_ERROR("out of memory while adding loop block."); - exit(EXIT_FAILURE); - } - params[1] = (intval & 0xff); - params[2] = (intval >> 8) & 0xff; - if (GifAddExtensionBlock(&LeadingExtensionBlockCount, - &LeadingExtensionBlocks, - 0, sizeof(params), params) == GIF_ERROR) { - PARSE_ERROR("out of memory while adding loop continuation."); - exit(EXIT_FAILURE); - } - - } - else if (sscanf(buf, "extension %x", &ExtCode)) - { - int bc = 0; - while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) - if (strcmp(buf, "end\n") == 0) - break; - else - { - int Len; - - buf[strlen(buf) - 1] = '\0'; - Len = EscapeString(buf, buf); - if (GifAddExtensionBlock(&LeadingExtensionBlockCount, - &LeadingExtensionBlocks, - bc++ == CONTINUE_EXT_FUNC_CODE ? ExtCode : 0, - Len, - (unsigned char *)buf) == GIF_ERROR) { - PARSE_ERROR("out of memory while adding extension block."); - exit(EXIT_FAILURE); - } - } - } + } else if (sscanf(buf, "netscape loop %u", &intval)) { + unsigned char params[3] = {1, 0, 0}; + /* Create a Netscape 2.0 loop block */ + if (GifAddExtensionBlock( + &LeadingExtensionBlockCount, + &LeadingExtensionBlocks, + APPLICATION_EXT_FUNC_CODE, 11, + (unsigned char *)"NETSCAPE2.0") == GIF_ERROR) { + PARSE_ERROR( + "out of memory while adding loop block."); + exit(EXIT_FAILURE); + } + params[1] = (intval & 0xff); + params[2] = (intval >> 8) & 0xff; + if (GifAddExtensionBlock(&LeadingExtensionBlockCount, + &LeadingExtensionBlocks, 0, + sizeof(params), + params) == GIF_ERROR) { + PARSE_ERROR("out of memory while adding loop " + "continuation."); + exit(EXIT_FAILURE); + } - /* - * Explicit image declarations - */ + } else if (sscanf(buf, "extension %x", &ExtCode)) { + int bc = 0; + while (fgets(buf, sizeof(buf), txtin) != (char *)NULL) { + if (strcmp(buf, "end\n") == 0) { + break; + } else { + int Len; + + buf[strlen(buf) - 1] = '\0'; + Len = EscapeString(buf, buf); + if (GifAddExtensionBlock( + &LeadingExtensionBlockCount, + &LeadingExtensionBlocks, + bc++ == CONTINUE_EXT_FUNC_CODE + ? ExtCode + : 0, + Len, (unsigned char *)buf) == + GIF_ERROR) { + PARSE_ERROR( + "out of memory while " + "adding extension block."); + exit(EXIT_FAILURE); + } + } + } + } - else if (strcmp(buf, "image\n") == 0) - { - if ((NewImage = GifMakeSavedImage(GifFileOut, NULL)) == (SavedImage *)NULL) - { - PARSE_ERROR("Out of memory while allocating image block."); - exit(EXIT_FAILURE); - } + /* + * Explicit image declarations + */ - /* use global table unless user specifies a local one */ - ColorMap = GlobalColorMap; - KeyTable = GlobalColorKeys; + else if (strcmp(buf, "image\n") == 0) { + if ((NewImage = GifMakeSavedImage(GifFileOut, NULL)) == + (SavedImage *)NULL) { + PARSE_ERROR("Out of memory while allocating " + "image block."); + exit(EXIT_FAILURE); + } - /* connect leading extension blocks */ - NewImage->ExtensionBlockCount = LeadingExtensionBlockCount; - NewImage->ExtensionBlocks = LeadingExtensionBlocks; - LeadingExtensionBlockCount = 0; - LeadingExtensionBlocks = NULL; - } + /* use global table unless user specifies a local one */ + ColorMap = GlobalColorMap; + KeyTable = GlobalColorKeys; - /* - * Nothing past this point is valid unless we've seen a previous - * image declaration. - */ - else if (NewImage == (SavedImage *)NULL) - { - (void) fputs(buf, stderr); - PARSE_ERROR("Syntax error in header block."); - exit(EXIT_FAILURE); - } + /* connect leading extension blocks */ + NewImage->ExtensionBlockCount = + LeadingExtensionBlockCount; + NewImage->ExtensionBlocks = LeadingExtensionBlocks; + LeadingExtensionBlockCount = 0; + LeadingExtensionBlocks = NULL; + } - /* - * Accept image attributes - */ - else if (sscanf(buf, "image top %d\n", &NewImage->ImageDesc.Top) == 1) - continue; + /* + * Nothing past this point is valid unless we've seen a previous + * image declaration. + */ + else if (NewImage == (SavedImage *)NULL) { + (void)fputs(buf, stderr); + PARSE_ERROR("Syntax error in header block."); + exit(EXIT_FAILURE); + } - else if (sscanf(buf, "image left %d\n", &NewImage->ImageDesc.Left)== 1) - continue; + /* + * Accept image attributes + */ + else if (sscanf(buf, "image top %d\n", + &NewImage->ImageDesc.Top) == 1) { + continue; + } - else if (strcmp(buf, "image interlaced\n") == 0) - { - NewImage->ImageDesc.Interlace = true; - continue; - } + else if (sscanf(buf, "image left %d\n", + &NewImage->ImageDesc.Left) == 1) { + continue; + } - else if (sscanf(buf, - "image bits %d by %d", - &NewImage->ImageDesc.Width, - &NewImage->ImageDesc.Height) == 2) - { - int i, j; - static GifPixelType *Raster, *cp; - int c; - bool hex = (strstr(buf, "hex") != NULL); - - /* coverity[overflow_sink] */ - if ((Raster = (GifPixelType *) malloc(sizeof(GifPixelType) * NewImage->ImageDesc.Width * NewImage->ImageDesc.Height)) - == NULL) { - PARSE_ERROR("Failed to allocate raster block, aborted."); - exit(EXIT_FAILURE); - } + else if (strcmp(buf, "image interlaced\n") == 0) { + NewImage->ImageDesc.Interlace = true; + continue; + } - GifQprintf("%s: Image %d at (%d, %d) [%dx%d]: ", - PROGRAM_NAME, GifFileOut->ImageCount, - NewImage->ImageDesc.Left, NewImage->ImageDesc.Top, - NewImage->ImageDesc.Width, NewImage->ImageDesc.Height); + else if (sscanf(buf, "image bits %d by %d", + &NewImage->ImageDesc.Width, + &NewImage->ImageDesc.Height) == 2) { + int i, j; + static GifPixelType *Raster; + int c; + bool hex = (strstr(buf, "hex") != NULL); + + /* coverity[overflow_sink] */ + if ((Raster = (GifPixelType *)malloc( + sizeof(GifPixelType) * + NewImage->ImageDesc.Width * + NewImage->ImageDesc.Height)) == NULL) { + PARSE_ERROR("Failed to allocate raster block, " + "aborted."); + exit(EXIT_FAILURE); + } - cp = Raster; - for (i = 0; i < NewImage->ImageDesc.Height; i++) { + GifQprintf("%s: Image %d at (%d, %d) [%dx%d]: ", + PROGRAM_NAME, GifFileOut->ImageCount, + NewImage->ImageDesc.Left, + NewImage->ImageDesc.Top, + NewImage->ImageDesc.Width, + NewImage->ImageDesc.Height); + + GifByteType *tp = Raster; + for (i = 0; i < NewImage->ImageDesc.Height; i++) { + + char *dp; + + for (j = 0; j < NewImage->ImageDesc.Width; + j++) { + if ((c = fgetc(txtin)) == EOF) { + PARSE_ERROR("input file ended " + "prematurely."); + exit(EXIT_FAILURE); + } else if (c == '\n') { + --j; + ++LineNum; + } else if (isspace(c)) { + --j; + } else if (hex) { + const static char *hexdigits = + "0123456789ABCDEF"; + unsigned char hi, lo; + dp = strchr(hexdigits, + toupper(c)); + if (dp == NULL) { + PARSE_ERROR( + "Invalid hex high " + "byte."); + exit(EXIT_FAILURE); + } + hi = (dp - hexdigits); + if ((c = fgetc(txtin)) == EOF) { + PARSE_ERROR( + "input file ended " + "prematurely."); + exit(EXIT_FAILURE); + } + dp = strchr(hexdigits, + toupper(c)); + if (dp == NULL) { + PARSE_ERROR( + "Invalid hex low " + "byte."); + exit(EXIT_FAILURE); + } + lo = (dp - hexdigits); + *tp++ = (hi << 4) | lo; + } else if ((dp = strchr(KeyTable, c))) { + *tp++ = (dp - KeyTable); + } else { + PARSE_ERROR( + "Invalid ASCII pixel key."); + exit(EXIT_FAILURE); + } + } + + if (GifNoisyPrint) { + fprintf(stderr, "\b\b\b\b%-4d", i); + } + } - char *dp; + if (GifNoisyPrint) { + putc('\n', stderr); + } - for (j = 0; j < NewImage->ImageDesc.Width; j++) - if ((c = fgetc(txtin)) == EOF) { - PARSE_ERROR("input file ended prematurely."); - exit(EXIT_FAILURE); - } - else if (c == '\n') - { - --j; - ++LineNum; - } - else if (isspace(c)) - --j; - else if (hex) - { - const static char *hexdigits = "0123456789ABCDEF"; - unsigned char hi, lo; - dp = strchr(hexdigits, toupper(c)); - if (dp == NULL) { - PARSE_ERROR("Invalid hex high byte."); - exit(EXIT_FAILURE); - } - hi = (dp - hexdigits); - if ((c = fgetc(txtin)) == EOF) { - PARSE_ERROR("input file ended prematurely."); - exit(EXIT_FAILURE); - } - dp = strchr(hexdigits, toupper(c)); - if (dp == NULL) { - PARSE_ERROR("Invalid hex low byte."); - exit(EXIT_FAILURE); - } - lo = (dp - hexdigits); - *cp++ = (hi << 4) | lo; - } - else if ((dp = strchr(KeyTable, c))) - *cp++ = (dp - KeyTable); - else { - PARSE_ERROR("Invalid ASCII pixel key."); + NewImage->RasterBits = (unsigned char *)Raster; + } else { + (void)fputs(buf, stderr); + PARSE_ERROR("Syntax error in image description."); exit(EXIT_FAILURE); - } - - if (GifNoisyPrint) - fprintf(stderr, "\b\b\b\b%-4d", i); - } + } + } - if (GifNoisyPrint) - putc('\n', stderr); + /* connect trailing extension blocks */ + GifFileOut->ExtensionBlockCount = LeadingExtensionBlockCount; + GifFileOut->ExtensionBlocks = LeadingExtensionBlocks; + // LeadingExtensionBlockCount = 0; + LeadingExtensionBlocks = NULL; - NewImage->RasterBits = (unsigned char *) Raster; - } - else - { - (void) fputs(buf, stderr); - PARSE_ERROR("Syntax error in image description."); - exit(EXIT_FAILURE); - } - } - - /* connect trailing extension blocks */ - GifFileOut->ExtensionBlockCount = LeadingExtensionBlockCount; - GifFileOut->ExtensionBlocks = LeadingExtensionBlocks; - //LeadingExtensionBlockCount = 0; - LeadingExtensionBlocks = NULL; - - EGifSpew(GifFileOut); + EGifSpew(GifFileOut); } static void VisibleDumpBuffer(GifByteType *buf, int len) /* Visibilize a given string */ { - GifByteType *cp; - - for (cp = buf; cp < buf + len; cp++) - { - if (isprint((int)(*cp)) || *cp == ' ') - putchar(*cp); - else if (*cp == '\n') - { - putchar('\\'); putchar('n'); - } - else if (*cp == '\r') - { - putchar('\\'); putchar('r'); - } - else if (*cp == '\b') - { - putchar('\\'); putchar('b'); - } - else if (*cp < ' ') - { - putchar('\\'); putchar('^'); putchar('@' + *cp); + GifByteType *cp; + + for (cp = buf; cp < buf + len; cp++) { + if (isprint((int)(*cp)) || *cp == ' ') { + putchar(*cp); + } else if (*cp == '\n') { + putchar('\\'); + putchar('n'); + } else if (*cp == '\r') { + putchar('\\'); + putchar('r'); + } else if (*cp == '\b') { + putchar('\\'); + putchar('b'); + } else if (*cp < ' ') { + putchar('\\'); + putchar('^'); + putchar('@' + *cp); + } else { + printf("\\0x%02x", *cp); + } } - else - printf("\\0x%02x", *cp); - } } -static void DumpExtensions(GifFileType *GifFileOut, - int ExtensionBlockCount, - ExtensionBlock *ExtensionBlocks) -{ - ExtensionBlock *ep; - - for (ep = ExtensionBlocks; - ep < ExtensionBlocks + ExtensionBlockCount; - ep++) { - bool last = (ep - ExtensionBlocks == (ExtensionBlockCount - 1)); - if (ep->Function == COMMENT_EXT_FUNC_CODE) { - printf("comment\n"); - VisibleDumpBuffer(ep->Bytes, ep->ByteCount); - putchar('\n'); - while (!last && ep[1].Function == CONTINUE_EXT_FUNC_CODE) { - ++ep; - last = (ep - ExtensionBlocks == (ExtensionBlockCount - 1)); - VisibleDumpBuffer(ep->Bytes, ep->ByteCount); - putchar('\n'); - } - printf("end\n\n"); +static void DumpExtensions(GifFileType *GifFileOut, int ExtensionBlockCount, + ExtensionBlock *ExtensionBlocks) { + ExtensionBlock *ep; + + for (ep = ExtensionBlocks; ep < ExtensionBlocks + ExtensionBlockCount; + ep++) { + bool last = (ep - ExtensionBlocks == (ExtensionBlockCount - 1)); + if (ep->Function == COMMENT_EXT_FUNC_CODE) { + printf("comment\n"); + VisibleDumpBuffer(ep->Bytes, ep->ByteCount); + putchar('\n'); + while (!last && + ep[1].Function == CONTINUE_EXT_FUNC_CODE) { + ++ep; + last = (ep - ExtensionBlocks == + (ExtensionBlockCount - 1)); + VisibleDumpBuffer(ep->Bytes, ep->ByteCount); + putchar('\n'); + } + printf("end\n\n"); + } else if (ep->Function == PLAINTEXT_EXT_FUNC_CODE) { + printf("plaintext\n"); + VisibleDumpBuffer(ep->Bytes, ep->ByteCount); + putchar('\n'); + while (!last && + ep[1].Function == CONTINUE_EXT_FUNC_CODE) { + ++ep; + last = (ep - ExtensionBlocks == + (ExtensionBlockCount - 1)); + VisibleDumpBuffer(ep->Bytes, ep->ByteCount); + putchar('\n'); + } + printf("end\n\n"); + } else if (ep->Function == GRAPHICS_EXT_FUNC_CODE) { + GraphicsControlBlock gcb; + printf("graphics control\n"); + if (DGifExtensionToGCB(ep->ByteCount, ep->Bytes, + &gcb) == GIF_ERROR) { + GIF_MESSAGE("invalid graphics control block"); + exit(EXIT_FAILURE); + } + printf("\tdisposal mode %d\n", gcb.DisposalMode); + printf("\tuser input flag %s\n", + gcb.UserInputFlag ? "on" : "off"); + printf("\tdelay %d\n", gcb.DelayTime); + printf("\ttransparent index %d\n", + gcb.TransparentColor); + printf("end\n\n"); + } else if (!last && ep->Function == APPLICATION_EXT_FUNC_CODE && + ep->ByteCount >= 11 && (ep + 1)->ByteCount >= 3 && + memcmp(ep->Bytes, "NETSCAPE2.0", 11) == 0) { + unsigned char *params = (++ep)->Bytes; + unsigned int loopcount = params[1] | (params[2] << 8); + printf("netscape loop %u\n\n", loopcount); + } else { + printf("extension 0x%02x\n", ep->Function); + VisibleDumpBuffer(ep->Bytes, ep->ByteCount); + while (!last && + ep[1].Function == CONTINUE_EXT_FUNC_CODE) { + ++ep; + last = (ep - ExtensionBlocks == + (ExtensionBlockCount - 1)); + VisibleDumpBuffer(ep->Bytes, ep->ByteCount); + putchar('\n'); + } + printf("end\n\n"); + } } - else if (ep->Function == PLAINTEXT_EXT_FUNC_CODE) { - printf("plaintext\n"); - VisibleDumpBuffer(ep->Bytes, ep->ByteCount); - putchar('\n'); - while (!last && ep[1].Function == CONTINUE_EXT_FUNC_CODE) { - ++ep; - last = (ep - ExtensionBlocks == (ExtensionBlockCount - 1)); - VisibleDumpBuffer(ep->Bytes, ep->ByteCount); - putchar('\n'); - } - printf("end\n\n"); +} + +static void Gif2Icon(char *FileName, int fdin, int fdout, char NameTable[]) { + int ErrorCode, im, i, j, ColorCount = 0; + GifFileType *GifFile; + + if (fdin == -1) { + if ((GifFile = DGifOpenFileName(FileName, &ErrorCode)) == + NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + } else { + /* Use stdin instead: */ + if ((GifFile = DGifOpenFileHandle(fdin, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } } - else if (ep->Function == GRAPHICS_EXT_FUNC_CODE) - { - GraphicsControlBlock gcb; - printf("graphics control\n"); - if (DGifExtensionToGCB(ep->ByteCount, ep->Bytes, &gcb) == GIF_ERROR) { - GIF_MESSAGE("invalid graphics control block"); + + if (DGifSlurp(GifFile) == GIF_ERROR) { + PrintGifError(GifFile->Error); exit(EXIT_FAILURE); - } - printf("\tdisposal mode %d\n", gcb.DisposalMode); - printf("\tuser input flag %s\n", - gcb.UserInputFlag ? "on" : "off"); - printf("\tdelay %d\n", gcb.DelayTime); - printf("\ttransparent index %d\n", gcb.TransparentColor); - printf("end\n\n"); } - else if (!last - && ep->Function == APPLICATION_EXT_FUNC_CODE - && ep->ByteCount >= 11 - && (ep+1)->ByteCount >= 3 - && memcmp(ep->Bytes, "NETSCAPE2.0", 11) == 0) { - unsigned char *params = (++ep)->Bytes; - unsigned int loopcount = params[1] | (params[2] << 8); - printf("netscape loop %u\n\n", loopcount); + + printf("screen width %d\nscreen height %d\n", GifFile->SWidth, + GifFile->SHeight); + + printf( + "screen colors %d\nscreen background %d\npixel aspect byte %u\n\n", + 1 << GifFile->SColorResolution, GifFile->SBackGroundColor, + (unsigned)GifFile->AspectByte); + + if (GifFile->SColorMap) { + printf("screen map\n"); + + printf("\tsort flag %s\n", + GifFile->SColorMap->SortFlag ? "on" : "off"); + + for (i = 0; i < GifFile->SColorMap->ColorCount; i++) { + if (GifFile->SColorMap->ColorCount < PRINTABLES) { + printf("\trgb %03d %03d %03d is %c\n", + GifFile->SColorMap->Colors[i].Red, + GifFile->SColorMap->Colors[i].Green, + GifFile->SColorMap->Colors[i].Blue, + NameTable[i]); + } else { + printf("\trgb %03d %03d %03d\n", + GifFile->SColorMap->Colors[i].Red, + GifFile->SColorMap->Colors[i].Green, + GifFile->SColorMap->Colors[i].Blue); + } + } + printf("end\n\n"); } - else { - printf("extension 0x%02x\n", ep->Function); - VisibleDumpBuffer(ep->Bytes, ep->ByteCount); - while (!last && ep[1].Function == CONTINUE_EXT_FUNC_CODE) { - ++ep; - last = (ep - ExtensionBlocks == (ExtensionBlockCount - 1)); - VisibleDumpBuffer(ep->Bytes, ep->ByteCount); + + for (im = 0; im < GifFile->ImageCount; im++) { + SavedImage *image = &GifFile->SavedImages[im]; + + DumpExtensions(GifFile, image->ExtensionBlockCount, + image->ExtensionBlocks); + + printf("image # %d\nimage left %d\nimage top %d\n", im + 1, + image->ImageDesc.Left, image->ImageDesc.Top); + if (image->ImageDesc.Interlace) { + printf("image interlaced\n"); + } + + if (image->ImageDesc.ColorMap) { + printf("image map\n"); + + printf("\tsort flag %s\n", + image->ImageDesc.ColorMap->SortFlag ? "on" + : "off"); + + if (image->ImageDesc.ColorMap->ColorCount < + PRINTABLES) { + for (i = 0; + i < image->ImageDesc.ColorMap->ColorCount; + i++) { + printf( + "\trgb %03d %03d %03d is %c\n", + image->ImageDesc.ColorMap->Colors[i] + .Red, + image->ImageDesc.ColorMap->Colors[i] + .Green, + image->ImageDesc.ColorMap->Colors[i] + .Blue, + NameTable[i]); + } + } else { + for (i = 0; + i < image->ImageDesc.ColorMap->ColorCount; + i++) { + printf( + "\trgb %03d %03d %03d\n", + image->ImageDesc.ColorMap->Colors[i] + .Red, + image->ImageDesc.ColorMap->Colors[i] + .Green, + image->ImageDesc.ColorMap->Colors[i] + .Blue); + } + } + printf("end\n\n"); + } + + /* one of these conditions has to be true */ + if (image->ImageDesc.ColorMap) { + ColorCount = image->ImageDesc.ColorMap->ColorCount; + } else if (GifFile->SColorMap) { + ColorCount = GifFile->SColorMap->ColorCount; + } + + if (ColorCount < PRINTABLES) { + printf("image bits %d by %d\n", image->ImageDesc.Width, + image->ImageDesc.Height); + } else { + printf("image bits %d by %d hex\n", + image->ImageDesc.Width, image->ImageDesc.Height); + } + for (i = 0; i < image->ImageDesc.Height; i++) { + for (j = 0; j < image->ImageDesc.Width; j++) { + GifByteType ch = + image->RasterBits + [i * image->ImageDesc.Width + j]; + if (ColorCount < PRINTABLES && + ch < PRINTABLES) { + putchar(NameTable[ch]); + } else { + printf("%02x", ch); + } + } + putchar('\n'); + } putchar('\n'); - } - printf("end\n\n"); } - } -} -static void Gif2Icon(char *FileName, - int fdin, int fdout, - char NameTable[]) -{ - int ErrorCode, im, i, j, ColorCount = 0; - GifFileType *GifFile; + DumpExtensions(GifFile, GifFile->ExtensionBlockCount, + GifFile->ExtensionBlocks); - if (fdin == -1) { - if ((GifFile = DGifOpenFileName(FileName, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } - } - else { - /* Use stdin instead: */ - if ((GifFile = DGifOpenFileHandle(fdin, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); + /* Tell EMACS this is a picture... */ + printf("# The following sets edit modes for GNU EMACS\n"); + printf("# Local "); /* ...break this up, so that EMACS doesn't */ + printf("Variables:\n"); /* get confused when visiting *this* file! */ + printf("# mode:picture\n"); + printf("# truncate-lines:t\n"); + printf("# End:\n"); + + if (fdin == -1) { + (void)printf("# End of %s dump\n", FileName); } - } - - if (DGifSlurp(GifFile) == GIF_ERROR) { - PrintGifError(GifFile->Error); - exit(EXIT_FAILURE); - } - - printf("screen width %d\nscreen height %d\n", - GifFile->SWidth, GifFile->SHeight); - - printf("screen colors %d\nscreen background %d\npixel aspect byte %u\n\n", - 1 << GifFile->SColorResolution, - GifFile->SBackGroundColor, - (unsigned)GifFile->AspectByte); - - if (GifFile->SColorMap) - { - printf("screen map\n"); - - printf("\tsort flag %s\n", GifFile->SColorMap->SortFlag ? "on" : "off"); - - for (i = 0; i < GifFile->SColorMap->ColorCount; i++) - if (GifFile->SColorMap->ColorCount < PRINTABLES) - printf("\trgb %03d %03d %03d is %c\n", - GifFile->SColorMap ->Colors[i].Red, - GifFile->SColorMap ->Colors[i].Green, - GifFile->SColorMap ->Colors[i].Blue, - NameTable[i]); - else - printf("\trgb %03d %03d %03d\n", - GifFile->SColorMap ->Colors[i].Red, - GifFile->SColorMap ->Colors[i].Green, - GifFile->SColorMap ->Colors[i].Blue); - printf("end\n\n"); - } - - for (im = 0; im < GifFile->ImageCount; im++) { - SavedImage *image = &GifFile->SavedImages[im]; - - DumpExtensions(GifFile, - image->ExtensionBlockCount, image->ExtensionBlocks); - - printf("image # %d\nimage left %d\nimage top %d\n", - im+1, image->ImageDesc.Left, image->ImageDesc.Top); - if (image->ImageDesc.Interlace) - printf("image interlaced\n"); - - if (image->ImageDesc.ColorMap) - { - printf("image map\n"); - - printf("\tsort flag %s\n", - image->ImageDesc.ColorMap->SortFlag ? "on" : "off"); - - if (image->ImageDesc.ColorMap->ColorCount < PRINTABLES) - for (i = 0; i < image->ImageDesc.ColorMap->ColorCount; i++) - printf("\trgb %03d %03d %03d is %c\n", - image->ImageDesc.ColorMap ->Colors[i].Red, - image->ImageDesc.ColorMap ->Colors[i].Green, - image->ImageDesc.ColorMap ->Colors[i].Blue, - NameTable[i]); - else - for (i = 0; i < image->ImageDesc.ColorMap->ColorCount; i++) - printf("\trgb %03d %03d %03d\n", - image->ImageDesc.ColorMap ->Colors[i].Red, - image->ImageDesc.ColorMap ->Colors[i].Green, - image->ImageDesc.ColorMap ->Colors[i].Blue); - printf("end\n\n"); + + /* + * Sanity checks. + */ + + /* check that the background color isn't garbage (SF bug #87) */ + if (GifFile->SBackGroundColor < 0 || + (GifFile->SColorMap && + GifFile->SBackGroundColor >= GifFile->SColorMap->ColorCount)) { + fprintf(stderr, "gifbuild: background color invalid for screen " + "colormap.\n"); } - /* one of these conditions has to be true */ - if (image->ImageDesc.ColorMap) - ColorCount = image->ImageDesc.ColorMap->ColorCount; - else if (GifFile->SColorMap) - ColorCount = GifFile->SColorMap->ColorCount; - - if (ColorCount < PRINTABLES) - printf("image bits %d by %d\n", - image->ImageDesc.Width, image->ImageDesc.Height); - else - printf("image bits %d by %d hex\n", - image->ImageDesc.Width, image->ImageDesc.Height); - for (i = 0; i < image->ImageDesc.Height; i++) { - for (j = 0; j < image->ImageDesc.Width; j++) { - GifByteType ch = image->RasterBits[i*image->ImageDesc.Width + j]; - if (ColorCount < PRINTABLES && ch < PRINTABLES) - putchar(NameTable[ch]); - else - printf("%02x", ch); - } - putchar('\n'); + if (DGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); } - putchar('\n'); - } - - DumpExtensions(GifFile, - GifFile->ExtensionBlockCount, GifFile->ExtensionBlocks); - - /* Tell EMACS this is a picture... */ - printf("# The following sets edit modes for GNU EMACS\n"); - printf("# Local "); /* ...break this up, so that EMACS doesn't */ - printf("Variables:\n"); /* get confused when visiting *this* file! */ - printf("# mode:picture\n"); - printf("# truncate-lines:t\n"); - printf("# End:\n"); - - if (fdin == -1) - (void) printf("# End of %s dump\n", FileName); - - - /* - * Sanity checks. - */ - - /* check that the background color isn't garbage (SF bug #87) */ - if (GifFile->SBackGroundColor < 0 - || GifFile->SBackGroundColor >= GifFile->SColorMap->ColorCount) { - fprintf(stderr, "gifbuild: background color invalid for screen colormap.\n"); - } - - if (DGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } } static int EscapeString(char *cp, char *tp) /* process standard C-style escape sequences in a string */ { - char *StartAddr = tp; - - while (*cp) - { - int cval = 0; - - if (*cp == '\\' && strchr("0123456789xX", cp[1])) - { - int dcount = 0; - - if (*++cp == 'x' || *cp == 'X') { - char *dp, *hex = "00112233445566778899aAbBcCdDeEfF"; - for (++cp; (dp = strchr(hex, *cp)) && (dcount++ < 2); cp++) - cval = (cval * 16) + (dp - hex) / 2; - } else if (*cp == '0') - while (strchr("01234567",*cp) != (char*)NULL && (dcount++ < 3)) - cval = (cval * 8) + (*cp++ - '0'); - else - while ((strchr("0123456789",*cp)!=(char*)NULL)&&(dcount++ < 3)) - cval = (cval * 10) + (*cp++ - '0'); - } - else if (*cp == '\\') /* C-style character escapes */ - { - switch (*++cp) - { - case '\\': cval = '\\'; break; - case 'n': cval = '\n'; break; - case 't': cval = '\t'; break; - case 'b': cval = '\b'; break; - case 'r': cval = '\r'; break; - default: cval = *cp; - } - cp++; - } - else if (*cp == '^') /* expand control-character syntax */ - { - cval = (*++cp & 0x1f); - cp++; + char *StartAddr = tp; + + while (*cp) { + int cval = 0; + + if (*cp == '\\' && strchr("0123456789xX", cp[1])) { + int dcount = 0; + + if (*++cp == 'x' || *cp == 'X') { + char *dp, + *hex = "00112233445566778899aAbBcCdDeEfF"; + for (++cp; + (dp = strchr(hex, *cp)) && (dcount++ < 2); + cp++) { + cval = (cval * 16) + (dp - hex) / 2; + } + } else if (*cp == '0') { + while (strchr("01234567", *cp) != + (char *)NULL && + (dcount++ < 3)) { + cval = (cval * 8) + (*cp++ - '0'); + } + } else { + while ((strchr("0123456789", *cp) != + (char *)NULL) && + (dcount++ < 3)) { + cval = (cval * 10) + (*cp++ - '0'); + } + } + } else if (*cp == '\\') /* C-style character escapes */ + { + switch (*++cp) { + case '\\': + cval = '\\'; + break; + case 'n': + cval = '\n'; + break; + case 't': + cval = '\t'; + break; + case 'b': + cval = '\b'; + break; + case 'r': + cval = '\r'; + break; + default: + cval = *cp; + } + cp++; + } else if (*cp == '^') /* expand control-character syntax */ + { + cval = (*++cp & 0x1f); + cp++; + } else { + cval = *cp++; + } + *tp++ = cval; } - else - cval = *cp++; - *tp++ = cval; - } - return(tp - StartAddr); + return (tp - StartAddr); } /* end */ - diff --git a/gifclrmp.c b/gifclrmp.c index 7a684a8..2ddcb72 100644 --- a/gifclrmp.c +++ b/gifclrmp.c @@ -2,45 +2,36 @@ gifclrmap - extract colormaps from GIF images +SPDX-License-Identifier: MIT + *****************************************************************************/ -#include -#include +#include #include -#include +#include #include +#include #include -#include +#include -#include "gif_lib.h" #include "getarg.h" +#include "gif_lib.h" + +#define PROGRAM_NAME "gifclrmp" + +static char *VersionStr = PROGRAM_NAME VERSION_COOKIE + " Gershon Elber, " __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char *CtrlStr = + PROGRAM_NAME " v%- s%- t%-TranslationFile!s l%-ColorMapFile!s g%-Gamma!F " + "i%-Image#!d h%- GifFile!*s"; -#define PROGRAM_NAME "gifclrmp" - -static char - *VersionStr = - PROGRAM_NAME - VERSION_COOKIE - " Gershon Elber, " - __DATE__ ", " __TIME__ "\n" - "(C) Copyright 1989 Gershon Elber.\n"; -static char - *CtrlStr = - PROGRAM_NAME - " v%- s%- t%-TranslationFile!s l%-ColorMapFile!s g%-Gamma!F i%-Image#!d h%- GifFile!*s"; - -static bool - SaveFlag = false, - TranslateFlag = false, - LoadFlag = false, - GammaFlag = false; -static - double Gamma = 1.0; -static - FILE *ColorFile = NULL; - FILE *TranslateFile = NULL; -static - GifPixelType Translation[256]; +static bool SaveFlag = false, TranslateFlag = false, LoadFlag = false, + GammaFlag = false; +static double Gamma = 1.0; +static FILE *ColorFile = NULL; +FILE *TranslateFile = NULL; +static GifPixelType Translation[256]; static ColorMapObject *ModifyColorMap(ColorMapObject *ColorMap); static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut); @@ -48,312 +39,377 @@ static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut); /****************************************************************************** Interpret the command line and scan the given GIF file. ******************************************************************************/ -int main(int argc, char **argv) -{ - int NumFiles, ExtCode, CodeSize, ImageNum = 0, - ImageN, HasGIFOutput, ErrorCode; - bool Error, ImageNFlag = false, HelpFlag = false; - GifRecordType RecordType; - GifByteType *Extension, *CodeBlock; - char **FileName = NULL, *ColorFileName, *TranslateFileName; - GifFileType *GifFileIn = NULL, *GifFileOut = NULL; - - if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &SaveFlag, - &TranslateFlag, &TranslateFileName, - &LoadFlag, &ColorFileName, - &GammaFlag, &Gamma, &ImageNFlag, &ImageN, - &HelpFlag, &NumFiles, &FileName)) != false || - (NumFiles > 1 && !HelpFlag)) { - if (Error) - GAPrintErrMsg(Error); - else if (NumFiles > 1) - GIF_MESSAGE("Error in command line parsing - one GIF file please."); - GAPrintHowTo(CtrlStr); - exit(EXIT_FAILURE); - } - - if (HelpFlag) { - (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); - GAPrintHowTo(CtrlStr); - exit(EXIT_SUCCESS); - } - - if (SaveFlag + LoadFlag + GammaFlag + TranslateFlag > 1) - GIF_EXIT("Can not handle more than one of -s -l, -t, or -g at the same time."); - - /* Default action is to dump colormaps */ - if (!SaveFlag && !LoadFlag && !GammaFlag && !TranslateFlag) - SaveFlag = true; +int main(int argc, char **argv) { + int NumFiles, ExtCode, CodeSize, ImageNum = 0, ImageN, HasGIFOutput, + ErrorCode; + bool Error, ImageNFlag = false, HelpFlag = false, GifNoisyPrint = false; + GifRecordType RecordType; + GifByteType *Extension, *CodeBlock; + char **FileName = NULL, *ColorFileName, *TranslateFileName; + GifFileType *GifFileIn = NULL, *GifFileOut = NULL; + + if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &SaveFlag, + &TranslateFlag, &TranslateFileName, &LoadFlag, + &ColorFileName, &GammaFlag, &Gamma, &ImageNFlag, + &ImageN, &HelpFlag, &NumFiles, &FileName)) != + false || + (NumFiles > 1 && !HelpFlag)) { + if (Error) { + GAPrintErrMsg(Error); + } else if (NumFiles > 1) { + GIF_MESSAGE("Error in command line parsing - one GIF " + "file please."); + } + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } - if (NumFiles == 1) { - if ((GifFileIn = DGifOpenFileName(*FileName, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); } - } - else { - /* Use stdin instead: */ - if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); + + if (SaveFlag + LoadFlag + GammaFlag + TranslateFlag > 1) { + GIF_EXIT("Can not handle more than one of -s -l, -t, or -g at " + "the same time."); } - } - - if (SaveFlag) { - /* We are dumping out the color map as text file to stdout: */ - ColorFile = stdout; - } - else { - if (TranslateFlag) { - /* We are loading new color map from specified file: */ - if ((TranslateFile = fopen(TranslateFileName, "rt")) == NULL) - GIF_EXIT("Failed to open specified color translation file."); + + /* Default action is to dump colormaps */ + if (!SaveFlag && !LoadFlag && !GammaFlag && !TranslateFlag) { + SaveFlag = true; } - if (LoadFlag) { - /* We are loading new color map from specified file: */ - if ((ColorFile = fopen(ColorFileName, "rt")) == NULL) - GIF_EXIT("Failed to open specified color map file."); + if (NumFiles == 1) { + if ((GifFileIn = DGifOpenFileName(*FileName, &ErrorCode)) == + NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + } else { + /* Use stdin instead: */ + if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } } - } - if ((HasGIFOutput = (LoadFlag || TranslateFlag || GammaFlag)) != 0) { - /* Open stdout for GIF output file: */ - if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); + if (SaveFlag) { + /* We are dumping out the color map as text file to stdout: */ + ColorFile = stdout; + } else { + if (TranslateFlag) { + /* We are loading new color map from specified file: */ + if ((TranslateFile = fopen(TranslateFileName, "rt")) == + NULL) { + GIF_EXIT("Failed to open specified color " + "translation file."); + } + } + + if (LoadFlag) { + /* We are loading new color map from specified file: */ + if ((ColorFile = fopen(ColorFileName, "rt")) == NULL) { + GIF_EXIT( + "Failed to open specified color map file."); + } + } } - } - - if (!ImageNFlag) { - /* We are supposed to modify the screen color map, so do it: */ - if (!GifFileIn->SColorMap) - GIF_EXIT("No colormap to modify"); - GifFileIn->SColorMap = ModifyColorMap(GifFileIn->SColorMap); - if (!HasGIFOutput) { - /* We can quit here, as we have the color map: */ - DGifCloseFile(GifFileIn, NULL); - fclose(ColorFile); - exit(EXIT_SUCCESS); + + if ((HasGIFOutput = (LoadFlag || TranslateFlag || GammaFlag)) != 0) { + /* Open stdout for GIF output file: */ + if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } } - } - /* And dump out its new possible repositioned screen information: */ - if (HasGIFOutput) - if (EGifPutScreenDesc(GifFileOut, - GifFileIn->SWidth, GifFileIn->SHeight, - GifFileIn->SColorResolution, GifFileIn->SBackGroundColor, - GifFileIn->SColorMap) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - - /* Scan the content of the GIF file and load the image(s) in: */ - do { - if (DGifGetRecordType(GifFileIn, &RecordType) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - - switch (RecordType) { - case IMAGE_DESC_RECORD_TYPE: - if (DGifGetImageDesc(GifFileIn) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - if ((++ImageNum == ImageN) && ImageNFlag) { - /* We are suppose to modify this image color map, do it: */ - GifFileIn->SColorMap =ModifyColorMap(GifFileIn->SColorMap); - if (!HasGIFOutput) { + + if (!ImageNFlag) { + /* We are supposed to modify the screen color map, so do it: */ + if (!GifFileIn->SColorMap) { + GIF_EXIT("No colormap to modify"); + } + GifFileIn->SColorMap = ModifyColorMap(GifFileIn->SColorMap); + if (!HasGIFOutput) { /* We can quit here, as we have the color map: */ DGifCloseFile(GifFileIn, NULL); fclose(ColorFile); exit(EXIT_SUCCESS); - } } - if (HasGIFOutput) - if (EGifPutImageDesc(GifFileOut, - GifFileIn->Image.Left, GifFileIn->Image.Top, - GifFileIn->Image.Width, GifFileIn->Image.Height, - GifFileIn->Image.Interlace, - GifFileIn->Image.ColorMap) == GIF_ERROR) + } + /* And dump out its new possible repositioned screen information: */ + if (HasGIFOutput) { + if (EGifPutScreenDesc(GifFileOut, GifFileIn->SWidth, + GifFileIn->SHeight, + GifFileIn->SColorResolution, + GifFileIn->SBackGroundColor, + GifFileIn->SColorMap) == GIF_ERROR) { QuitGifError(GifFileIn, GifFileOut); + } + } - if (!TranslateFlag || (ImageNFlag && (ImageN != ImageNum))) - { - /* Now read image itself in decoded form as we don't */ - /* really care what we have there, and this is much */ - /* faster. */ - if (DGifGetCode(GifFileIn, &CodeSize, &CodeBlock) == GIF_ERROR) + /* Scan the content of the GIF file and load the image(s) in: */ + do { + if (DGifGetRecordType(GifFileIn, &RecordType) == GIF_ERROR) { QuitGifError(GifFileIn, GifFileOut); - if (HasGIFOutput) - if (EGifPutCode(GifFileOut, CodeSize, CodeBlock) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - while (CodeBlock != NULL) { - if (DGifGetCodeNext(GifFileIn, &CodeBlock) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - if (HasGIFOutput) - if (EGifPutCodeNext(GifFileOut, CodeBlock) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - } } - else /* we need to mung pixels intices */ - { - int i; - register GifPixelType *cp; - - GifPixelType *Line - = (GifPixelType *) malloc(GifFileIn->Image.Width * - sizeof(GifPixelType)); - for (i = 0; i < GifFileIn->Image.Height; i++) { - if (DGifGetLine(GifFileIn, Line,GifFileIn->Image.Width) - == GIF_ERROR) { - QuitGifError(GifFileIn, GifFileOut); - } - /* translation step goes here */ - for (cp = Line; cp < Line+GifFileIn->Image.Width; cp++) - *cp = Translation[*cp]; + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFileIn) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + if ((++ImageNum == ImageN) && ImageNFlag) { + /* We are suppose to modify this image color + * map, do it: */ + GifFileIn->SColorMap = + ModifyColorMap(GifFileIn->SColorMap); + if (!HasGIFOutput) { + /* We can quit here, as we have the + * color map: */ + DGifCloseFile(GifFileIn, NULL); + fclose(ColorFile); + exit(EXIT_SUCCESS); + } + } + if (HasGIFOutput) { + if (EGifPutImageDesc( + GifFileOut, GifFileIn->Image.Left, + GifFileIn->Image.Top, + GifFileIn->Image.Width, + GifFileIn->Image.Height, + GifFileIn->Image.Interlace, + GifFileIn->Image.ColorMap) == + GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + } - if (EGifPutLine(GifFileOut, - Line, GifFileIn->Image.Width) - == GIF_ERROR) { - QuitGifError(GifFileIn, GifFileOut); + if (!TranslateFlag || + (ImageNFlag && (ImageN != ImageNum))) { + /* Now read image itself in decoded form as we + * don't */ + /* really care what we have there, and this is + * much */ + /* faster. + */ + if (DGifGetCode(GifFileIn, &CodeSize, + &CodeBlock) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + if (HasGIFOutput) { + if (EGifPutCode(GifFileOut, CodeSize, + CodeBlock) == + GIF_ERROR) { + QuitGifError(GifFileIn, + GifFileOut); + } + } + while (CodeBlock != NULL) { + if (DGifGetCodeNext(GifFileIn, + &CodeBlock) == + GIF_ERROR) { + QuitGifError(GifFileIn, + GifFileOut); + } + if (HasGIFOutput) { + if (EGifPutCodeNext( + GifFileOut, + CodeBlock) == + GIF_ERROR) { + QuitGifError( + GifFileIn, + GifFileOut); + } + } + } + } else /* we need to mung pixels intices */ + { + int i; + register GifPixelType *cp; + + GifPixelType *Line = (GifPixelType *)malloc( + GifFileIn->Image.Width * + sizeof(GifPixelType)); + for (i = 0; i < GifFileIn->Image.Height; i++) { + if (DGifGetLine( + GifFileIn, Line, + GifFileIn->Image.Width) == + GIF_ERROR) { + QuitGifError(GifFileIn, + GifFileOut); + } + + /* translation step goes here */ + for (cp = Line; + cp < Line + GifFileIn->Image.Width; + cp++) { + *cp = Translation[*cp]; + } + + if (EGifPutLine( + GifFileOut, Line, + GifFileIn->Image.Width) == + GIF_ERROR) { + QuitGifError(GifFileIn, + GifFileOut); + } + } + free((char *)Line); } - } - free((char *) Line); - } - break; - case EXTENSION_RECORD_TYPE: - assert(GifFileOut != NULL); /* might pacify Coverity */ - /* pass through extension records */ - if (DGifGetExtension(GifFileIn, &ExtCode, &Extension) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - if (Extension == NULL) - break; - if (EGifPutExtensionLeader(GifFileOut, ExtCode) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - if (EGifPutExtensionBlock(GifFileOut, - Extension[0], - Extension + 1) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - while (Extension != NULL) { - if (DGifGetExtensionNext(GifFileIn, &Extension)==GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - if (Extension != NULL) - if (EGifPutExtensionBlock(GifFileOut, - Extension[0], - Extension + 1) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); + break; + case EXTENSION_RECORD_TYPE: + assert(GifFileOut != NULL); /* might pacify Coverity */ + /* pass through extension records */ + if (DGifGetExtension(GifFileIn, &ExtCode, &Extension) == + GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + if (Extension == NULL) { + break; + } + if (EGifPutExtensionLeader(GifFileOut, ExtCode) == + GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + if (EGifPutExtensionBlock(GifFileOut, Extension[0], + Extension + 1) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + while (Extension != NULL) { + if (DGifGetExtensionNext( + GifFileIn, &Extension) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + if (Extension != NULL) { + if (EGifPutExtensionBlock( + GifFileOut, Extension[0], + Extension + 1) == GIF_ERROR) { + QuitGifError(GifFileIn, + GifFileOut); + } + } + } + if (EGifPutExtensionTrailer(GifFileOut) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + break; + case TERMINATE_RECORD_TYPE: + break; + default: /* Should be trapped by DGifGetRecordType. */ + break; } - if (EGifPutExtensionTrailer(GifFileOut) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - break; - case TERMINATE_RECORD_TYPE: - break; - default: /* Should be trapped by DGifGetRecordType. */ - break; - } - } - while (RecordType != TERMINATE_RECORD_TYPE); + } while (RecordType != TERMINATE_RECORD_TYPE); - if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) - { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } - if (HasGIFOutput) - if (EGifCloseFile(GifFileOut, &ErrorCode) == GIF_ERROR) - { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); + if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + if (HasGIFOutput) { + if (EGifCloseFile(GifFileOut, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } } - return 0; + return 0; } /****************************************************************************** Modify the given colormap according to global variables setting. ******************************************************************************/ -static ColorMapObject *ModifyColorMap(ColorMapObject *ColorMap) -{ - int i, Dummy, Red, Green, Blue; - - if (SaveFlag) { - /* Save this color map to ColorFile: */ - for (i = 0; i < ColorMap->ColorCount; i++) - fprintf(ColorFile, "%3d %3d %3d %3d\n", i, - ColorMap->Colors[i].Red, - ColorMap->Colors[i].Green, - ColorMap->Colors[i].Blue); - return(ColorMap); - } - else if (LoadFlag) { - /* Read the color map in ColorFile into this color map: */ - for (i = 0; i < ColorMap->ColorCount; i++) { - if (feof(ColorFile)) - GIF_EXIT("Color file to load color map from, too small."); - if (fscanf(ColorFile, "%3d %3d %3d %3d\n", &Dummy, &Red, &Green, &Blue) == 4) { - ColorMap->Colors[i].Red = Red; - ColorMap->Colors[i].Green = Green; - ColorMap->Colors[i].Blue = Blue; - } - } - return(ColorMap); - } - else if (GammaFlag) { - /* Apply gamma correction to this color map: */ - double Gamma1 = 1.0 / Gamma; - for (i = 0; i < ColorMap->ColorCount; i++) { - ColorMap->Colors[i].Red = - ((int) (255 * pow(ColorMap->Colors[i].Red / 255.0, Gamma1))); - ColorMap->Colors[i].Green = - ((int) (255 * pow(ColorMap->Colors[i].Green / 255.0, Gamma1))); - ColorMap->Colors[i].Blue = - ((int) (255 * pow(ColorMap->Colors[i].Blue / 255.0, Gamma1))); - } - return(ColorMap); - } - else if (TranslateFlag) { - ColorMapObject *NewMap; - int Max = 0; - - /* Read the translation table in TranslateFile: */ - for (i = 0; i < ColorMap->ColorCount; i++) { - int tmp; - if (feof(TranslateFile)) - GIF_EXIT("Color file to load color map from, too small."); - if (fscanf(TranslateFile, "%3d %3d\n", &Dummy, &tmp) == 2) { - Translation[i] = tmp & 0xff; - if (Translation[i] > Max) - Max = Translation[i]; - } - } +static ColorMapObject *ModifyColorMap(ColorMapObject *ColorMap) { + int i, Dummy, Red, Green, Blue; + + if (SaveFlag) { + /* Save this color map to ColorFile: */ + for (i = 0; i < ColorMap->ColorCount; i++) { + fprintf(ColorFile, "%3d %3d %3d %3d\n", i, + ColorMap->Colors[i].Red, + ColorMap->Colors[i].Green, + ColorMap->Colors[i].Blue); + } + return (ColorMap); + } else if (LoadFlag) { + /* Read the color map in ColorFile into this color map: */ + for (i = 0; i < ColorMap->ColorCount; i++) { + if (feof(ColorFile)) { + GIF_EXIT("Color file to load color map from, " + "too small."); + } + if (fscanf(ColorFile, "%3d %3d %3d %3d\n", &Dummy, &Red, + &Green, &Blue) == 4) { + ColorMap->Colors[i].Red = Red; + ColorMap->Colors[i].Green = Green; + ColorMap->Colors[i].Blue = Blue; + } + } + return (ColorMap); + } else if (GammaFlag) { + /* Apply gamma correction to this color map: */ + double Gamma1 = 1.0 / Gamma; + for (i = 0; i < ColorMap->ColorCount; i++) { + ColorMap->Colors[i].Red = + ((int)(255 * pow(ColorMap->Colors[i].Red / 255.0, + Gamma1))); + ColorMap->Colors[i].Green = + ((int)(255 * pow(ColorMap->Colors[i].Green / 255.0, + Gamma1))); + ColorMap->Colors[i].Blue = + ((int)(255 * pow(ColorMap->Colors[i].Blue / 255.0, + Gamma1))); + } + return (ColorMap); + } else if (TranslateFlag) { + ColorMapObject *NewMap; + int Max = 0; + + /* Read the translation table in TranslateFile: */ + for (i = 0; i < ColorMap->ColorCount; i++) { + int tmp; + if (feof(TranslateFile)) { + GIF_EXIT("Color file to load color map from, " + "too small."); + } + if (fscanf(TranslateFile, "%3d %3d\n", &Dummy, &tmp) == + 2) { + Translation[i] = tmp & 0xff; + if (Translation[i] > Max) { + Max = Translation[i]; + } + } + } - if ((NewMap = GifMakeMapObject(1 << GifBitSize(Max+1), NULL)) == NULL) - GIF_EXIT("Out of memory while allocating color map!"); + if ((NewMap = GifMakeMapObject(1 << GifBitSize(Max + 1), + NULL)) == NULL) { + GIF_EXIT("Out of memory while allocating color map!"); + } - /* Apply the translation; we'll do it to the pixels, too */ - for (i = 0; i < ColorMap->ColorCount; i++) { - NewMap->Colors[i] = ColorMap->Colors[Translation[i]]; + /* Apply the translation; we'll do it to the pixels, too */ + for (i = 0; i < ColorMap->ColorCount; i++) { + NewMap->Colors[i] = ColorMap->Colors[Translation[i]]; + } + + return (NewMap); + } else { + GIF_EXIT("Nothing to do!"); + return (ColorMap); } - - return(NewMap); - } - else - { - GIF_EXIT("Nothing to do!"); - return(ColorMap); - } } /****************************************************************************** Close both input and output file (if open), and exit. ******************************************************************************/ -static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut) -{ - if (GifFileIn != NULL) { - PrintGifError(GifFileIn->Error); - EGifCloseFile(GifFileIn, NULL); - } - if (GifFileOut != NULL) { - PrintGifError(GifFileOut->Error); - EGifCloseFile(GifFileOut, NULL); - } - exit(EXIT_FAILURE); +static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut) { + if (GifFileIn != NULL) { + PrintGifError(GifFileIn->Error); + EGifCloseFile(GifFileIn, NULL); + } + if (GifFileOut != NULL) { + PrintGifError(GifFileOut->Error); + EGifCloseFile(GifFileOut, NULL); + } + exit(EXIT_FAILURE); } /* end */ diff --git a/gifcolor.c b/gifcolor.c index f00ad38..017f554 100644 --- a/gifcolor.c +++ b/gifcolor.c @@ -2,163 +2,166 @@ gifcolor - generate color test-pattern GIFs +SPDX-License-Identifier: MIT + *****************************************************************************/ -#include -#include #include -#include #include +#include +#include +#include -#include "gif_lib.h" #include "getarg.h" +#include "gif_lib.h" -#define PROGRAM_NAME "gifcolor" +#define PROGRAM_NAME "gifcolor" -#define LINE_LEN 40 -#define IMAGEWIDTH LINE_LEN*GIF_FONT_WIDTH +#define LINE_LEN 40 +#define IMAGEWIDTH LINE_LEN *GIF_FONT_WIDTH -static char - *VersionStr = - PROGRAM_NAME - VERSION_COOKIE - " Gershon Elber, " - __DATE__ ", " __TIME__ "\n" - "(C) Copyright 1989 Gershon Elber.\n"; -static char - *CtrlStr = PROGRAM_NAME " v%- b%-Background!d h%-"; +static char *VersionStr = PROGRAM_NAME VERSION_COOKIE + " Gershon Elber, " __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char *CtrlStr = PROGRAM_NAME " v%- b%-Background!d h%-"; static int BackGround = 0; static void QuitGifError(GifFileType *GifFile); static void GenRasterTextLine(GifRowType *RasterBuffer, char *TextLine, - int BufferWidth, int ForeGroundIndex); + int BufferWidth, int ForeGroundIndex); /****************************************************************************** Interpret the command line and generate the given GIF file. ******************************************************************************/ -int main(int argc, char **argv) -{ - int i, j, l, GifNoisyPrint, ColorMapSize, ErrorCode; - bool Error, BackGroundFlag = false, HelpFlag = false; - char Line[LINE_LEN]; - GifRowType RasterBuffer[GIF_FONT_HEIGHT]; - ColorMapObject *ColorMap; - GifFileType *GifFile; - GifColorType ScratchMap[256]; - int red, green, blue; - - if ((Error = GAGetArgs(argc, argv, CtrlStr, - &GifNoisyPrint, - &BackGroundFlag, &BackGround, - &HelpFlag)) != false) { - GAPrintErrMsg(Error); - GAPrintHowTo(CtrlStr); - exit(EXIT_FAILURE); - } - - if (HelpFlag) { - (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); - GAPrintHowTo(CtrlStr); - exit(EXIT_SUCCESS); - } - - /* Allocate the raster buffer for GIF_FONT_HEIGHT scan lines. */ - for (i = 0; i < GIF_FONT_HEIGHT; i++) - { - if ((RasterBuffer[i] = (GifRowType) malloc(sizeof(GifPixelType) * - IMAGEWIDTH)) == NULL) - GIF_EXIT("Failed to allocate memory required, aborted."); - } - - /* Open stdout for the output file: */ - if ((GifFile = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } - - /* Read the color map in ColorFile into this color map: */ - ColorMapSize = 0; - while (fscanf(stdin, - "%*3d %3d %3d %3d\n", - &red, &green, &blue) == 3) { - ScratchMap[ColorMapSize].Red = red; - ScratchMap[ColorMapSize].Green = green; - ScratchMap[ColorMapSize].Blue = blue; - ColorMapSize++; +int main(int argc, char **argv) { + int i, j, l, GifNoisyPrint, ColorMapSize, ErrorCode; + bool Error, BackGroundFlag = false, HelpFlag = false; + char Line[LINE_LEN]; + GifRowType RasterBuffer[GIF_FONT_HEIGHT]; + ColorMapObject *ColorMap; + GifFileType *GifFile; + GifColorType ScratchMap[256]; + int red, green, blue; + + if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, + &BackGroundFlag, &BackGround, &HelpFlag)) != + false) { + GAPrintErrMsg(Error); + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); + } + + /* Allocate the raster buffer for GIF_FONT_HEIGHT scan lines. */ + for (i = 0; i < GIF_FONT_HEIGHT; i++) { + if ((RasterBuffer[i] = (GifRowType)malloc( + sizeof(GifPixelType) * IMAGEWIDTH)) == NULL) { + GIF_EXIT( + "Failed to allocate memory required, aborted."); + } } - if ((ColorMap = GifMakeMapObject(1 << GifBitSize(ColorMapSize), ScratchMap)) == NULL) - GIF_EXIT("Failed to allocate memory required, aborted."); - - if (EGifPutScreenDesc(GifFile, - IMAGEWIDTH, ColorMapSize * GIF_FONT_HEIGHT, - GifBitSize(ColorMapSize), - BackGround, ColorMap) == GIF_ERROR) - QuitGifError(GifFile); - - /* Dump out the image descriptor: */ - if (EGifPutImageDesc(GifFile, - 0, 0, IMAGEWIDTH, ColorMapSize * GIF_FONT_HEIGHT, false, NULL) == GIF_ERROR) - QuitGifError(GifFile); - - GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]: ", - PROGRAM_NAME, GifFile->Image.Left, GifFile->Image.Top, - GifFile->Image.Width, GifFile->Image.Height); - - for (i = l = 0; i < ColorMap->ColorCount; i++) { - (void)snprintf(Line, sizeof(Line), - "Color %-3d: [%-3d, %-3d, %-3d] ", i, - ColorMap->Colors[i].Red, - ColorMap->Colors[i].Green, - ColorMap->Colors[i].Blue); - GenRasterTextLine(RasterBuffer, Line, IMAGEWIDTH, i); - for (j = 0; j < GIF_FONT_HEIGHT; j++) { - if (EGifPutLine(GifFile, RasterBuffer[j], IMAGEWIDTH) == GIF_ERROR) + /* Open stdout for the output file: */ + if ((GifFile = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + /* Read the color map in ColorFile into this color map: */ + ColorMapSize = 0; + while (fscanf(stdin, "%*3d %3d %3d %3d\n", &red, &green, &blue) == 3) { + if (ColorMapSize < 256) { + ScratchMap[ColorMapSize].Red = red; + ScratchMap[ColorMapSize].Green = green; + ScratchMap[ColorMapSize].Blue = blue; + ColorMapSize++; + } else { + GIF_EXIT("Too many color map triples, aborting."); + } + } + + if ((ColorMap = GifMakeMapObject(1 << GifBitSize(ColorMapSize), + ScratchMap)) == NULL) { + GIF_EXIT("Failed to allocate memory required, aborted."); + } + + if (EGifPutScreenDesc( + GifFile, IMAGEWIDTH, ColorMapSize * GIF_FONT_HEIGHT, + GifBitSize(ColorMapSize), BackGround, ColorMap) == GIF_ERROR) { QuitGifError(GifFile); - GifQprintf("\b\b\b\b%-4d", l++); } - } - if (EGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) - { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } + /* Dump out the image descriptor: */ + if (EGifPutImageDesc(GifFile, 0, 0, IMAGEWIDTH, + ColorMapSize * GIF_FONT_HEIGHT, false, + NULL) == GIF_ERROR) { + QuitGifError(GifFile); + } + + GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]: ", PROGRAM_NAME, + GifFile->Image.Left, GifFile->Image.Top, + GifFile->Image.Width, GifFile->Image.Height); + + for (i = l = 0; i < ColorMap->ColorCount; i++) { + (void)snprintf( + Line, sizeof(Line), "Color %-3d: [%-3d, %-3d, %-3d] ", i, + ColorMap->Colors[i].Red, ColorMap->Colors[i].Green, + ColorMap->Colors[i].Blue); + GenRasterTextLine(RasterBuffer, Line, IMAGEWIDTH, i); + for (j = 0; j < GIF_FONT_HEIGHT; j++) { + if (EGifPutLine(GifFile, RasterBuffer[j], IMAGEWIDTH) == + GIF_ERROR) { + QuitGifError(GifFile); + } + GifQprintf("\b\b\b\b%-4d", l++); + } + } - return 0; + if (EGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + return 0; } /****************************************************************************** Close output file (if open), and exit. ******************************************************************************/ static void GenRasterTextLine(GifRowType *RasterBuffer, char *TextLine, - int BufferWidth, int ForeGroundIndex) -{ - unsigned char Byte, Mask; - int i, j, k, CharPosX, Len = strlen(TextLine); - - for (i = 0; i < BufferWidth; i++) - for (j = 0; j < GIF_FONT_HEIGHT; j++) RasterBuffer[j][i] = BackGround; - - for (i = CharPosX = 0; i < Len; i++, CharPosX += GIF_FONT_WIDTH) { - unsigned char c = TextLine[i]; - for (j = 0; j < GIF_FONT_HEIGHT; j++) { - Byte = GifAsciiTable8x8[(unsigned short)c][j]; - for (k = 0, Mask = 128; k < GIF_FONT_WIDTH; k++, Mask >>= 1) - if (Byte & Mask) - RasterBuffer[j][CharPosX + k] = ForeGroundIndex; + int BufferWidth, int ForeGroundIndex) { + unsigned char Byte, Mask; + int i, j, k, CharPosX, Len = strlen(TextLine); + + for (i = 0; i < BufferWidth; i++) + for (j = 0; j < GIF_FONT_HEIGHT; j++) + RasterBuffer[j][i] = BackGround; + + for (i = CharPosX = 0; i < Len; i++, CharPosX += GIF_FONT_WIDTH) { + unsigned char c = TextLine[i]; + for (j = 0; j < GIF_FONT_HEIGHT; j++) { + Byte = GifAsciiTable8x8[(unsigned short)c][j]; + for (k = 0, Mask = 128; k < GIF_FONT_WIDTH; + k++, Mask >>= 1) + if (Byte & Mask) + RasterBuffer[j][CharPosX + k] = + ForeGroundIndex; + } } - } } /****************************************************************************** Close output file (if open), and exit. ******************************************************************************/ -static void QuitGifError(GifFileType *GifFile) -{ - if (GifFile != NULL) { - PrintGifError(GifFile->Error); - EGifCloseFile(GifFile, NULL); - } - exit(EXIT_FAILURE); +static void QuitGifError(GifFileType *GifFile) { + if (GifFile != NULL) { + PrintGifError(GifFile->Error); + EGifCloseFile(GifFile, NULL); + } + exit(EXIT_FAILURE); } diff --git a/gifecho.c b/gifecho.c index dc78cb5..8eb005e 100644 --- a/gifecho.c +++ b/gifecho.c @@ -2,206 +2,227 @@ gifecho - generate a GIF from ASCII text +SPDX-License-Identifier: MIT + *****************************************************************************/ -#include -#include #include -#include #include +#include +#include +#include -#include "gif_lib.h" #include "getarg.h" +#include "gif_lib.h" -#define PROGRAM_NAME "gifecho" +#define PROGRAM_NAME "gifecho" -#define MAX_NUM_TEXT_LINES 100 /* Maximum number of lines in file. */ +#define MAX_NUM_TEXT_LINES 100 /* Maximum number of lines in file. */ -#define LINE_LEN 256 /* Maximum length of one text line. */ +#define LINE_LEN 256 /* Maximum length of one text line. */ -#define DEFAULT_FG_INDEX 1 /* Text foreground index. */ +#define DEFAULT_FG_INDEX 1 /* Text foreground index. */ -#define DEFAULT_COLOR_RED 255 /* Text foreground color. */ -#define DEFAULT_COLOR_GREEN 255 -#define DEFAULT_COLOR_BLUE 255 +#define DEFAULT_COLOR_RED 255 /* Text foreground color. */ +#define DEFAULT_COLOR_GREEN 255 +#define DEFAULT_COLOR_BLUE 255 -static char - *VersionStr = - PROGRAM_NAME - VERSION_COOKIE - " Gershon Elber, " - __DATE__ ", " __TIME__ "\n" - "(C) Copyright 1989 Gershon Elber.\n"; -static char - *CtrlStr = - PROGRAM_NAME - " v%- s%-ClrMapSize!d f%-FGClr!d c%-R|G|B!d!d!d t%-\"Text\"!s h%-"; +static char *VersionStr = PROGRAM_NAME VERSION_COOKIE + " Gershon Elber, " __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char *CtrlStr = PROGRAM_NAME + " v%- s%-ClrMapSize!d f%-FGClr!d c%-R|G|B!d!d!d t%-\"Text\"!s h%-"; -static unsigned int - RedColor = DEFAULT_COLOR_RED, - GreenColor = DEFAULT_COLOR_GREEN, - BlueColor = DEFAULT_COLOR_BLUE; +static unsigned int RedColor = DEFAULT_COLOR_RED, + GreenColor = DEFAULT_COLOR_GREEN, + BlueColor = DEFAULT_COLOR_BLUE; static void QuitGifError(GifFileType *GifFile); static void GenRasterTextLine(GifRowType *RasterBuffer, char *TextLine, - int BufferWidth, int ForeGroundIndex); + int BufferWidth, int ForeGroundIndex); /****************************************************************************** Interpret the command line and generate the given GIF file. ******************************************************************************/ -int main(int argc, char **argv) -{ - int i, j, l, ImageWidth, ImageHeight, NumOfLines, LogNumLevels, - ErrorCode, NumLevels, ColorMapSize = 1, - ForeGroundIndex = DEFAULT_FG_INDEX; - bool Error, ClrMapSizeFlag = false, ForeGroundFlag = false, - TextLineFlag = false, HelpFlag = false, ColorFlag = false; - char *TextLines[MAX_NUM_TEXT_LINES]; - GifRowType RasterBuffer[GIF_FONT_HEIGHT]; - ColorMapObject *ColorMap; - GifFileType *GifFile; - - if ((Error = GAGetArgs(argc, argv, CtrlStr, - &GifNoisyPrint, &ClrMapSizeFlag, &ColorMapSize, - &ForeGroundFlag, &ForeGroundIndex, - &ColorFlag, &RedColor, &GreenColor, &BlueColor, - &TextLineFlag, &TextLines[0], - &HelpFlag)) != false) { - GAPrintErrMsg(Error); - GAPrintHowTo(CtrlStr); - exit(EXIT_FAILURE); - } - - if (HelpFlag) { - (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); - GAPrintHowTo(CtrlStr); - exit(EXIT_SUCCESS); - } - - if (ForeGroundIndex > 255 || ForeGroundIndex < 1) - GIF_EXIT("Foregound (-f) should be in the range 1..255, aborted."); - - if (ColorMapSize > 8 || ColorMapSize < 1) - GIF_EXIT("ColorMapSize (-s) should be in the range 1..8, aborted."); - - if (TextLineFlag) { - NumOfLines = 1; - ImageHeight = GIF_FONT_HEIGHT; - ImageWidth = GIF_FONT_WIDTH * strlen(TextLines[0]); - } - else { - char Line[LINE_LEN]; - NumOfLines = l = 0; - while (fgets(Line, LINE_LEN - 1, stdin)) { - for (i = strlen(Line); i > 0 && Line[i-1] <= ' '; i--); - Line[i] = 0; - if (l < i) l = i; - TextLines[NumOfLines++] = strdup(Line); - if (NumOfLines == MAX_NUM_TEXT_LINES) - GIF_EXIT("Input file has too many lines, aborted."); - } - if (NumOfLines == 0) - GIF_EXIT("No input text, aborted."); - ImageHeight = GIF_FONT_HEIGHT * NumOfLines; - ImageWidth = GIF_FONT_WIDTH * l; - } - - /* Allocate the raster buffer for GIF_FONT_HEIGHT scan lines (one text line). */ - for (i = 0; i < GIF_FONT_HEIGHT; i++) - if ((RasterBuffer[i] = (GifRowType) malloc(sizeof(GifPixelType) * - ImageWidth)) == NULL) - GIF_EXIT("Failed to allocate memory required, aborted."); - - /* Open stdout for the output file: */ - if ((GifFile = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } - - /* Dump out screen description with given size and generated color map: */ - for (LogNumLevels = 1, NumLevels = 2; - NumLevels < ForeGroundIndex; - LogNumLevels++, NumLevels <<= 1); - if (NumLevels < (1 << ColorMapSize)) { - NumLevels = (1 << ColorMapSize); - LogNumLevels = ColorMapSize; - } - - if ((ColorMap = GifMakeMapObject(NumLevels, NULL)) == NULL) - GIF_EXIT("Failed to allocate memory required, aborted."); - - for (i = 0; i < NumLevels; i++) - ColorMap->Colors[i].Red = ColorMap->Colors[i].Green = ColorMap->Colors[i].Blue = 0; - ColorMap->Colors[ForeGroundIndex].Red = RedColor; - ColorMap->Colors[ForeGroundIndex].Green = GreenColor; - ColorMap->Colors[ForeGroundIndex].Blue = BlueColor; - - if (EGifPutScreenDesc(GifFile, - ImageWidth, ImageHeight, LogNumLevels, 0, ColorMap) - == GIF_ERROR) - QuitGifError(GifFile); - - /* Dump out the image descriptor: */ - if (EGifPutImageDesc(GifFile, - 0, 0, ImageWidth, ImageHeight, false, NULL) == GIF_ERROR) - QuitGifError(GifFile); - - GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]: ", - PROGRAM_NAME, GifFile->Image.Left, GifFile->Image.Top, - GifFile->Image.Width, GifFile->Image.Height); - - for (i = l = 0; i < NumOfLines; i++) { - GenRasterTextLine(RasterBuffer, TextLines[i], ImageWidth, - ForeGroundIndex); - for (j = 0; j < GIF_FONT_HEIGHT; j++) { - if (EGifPutLine(GifFile, RasterBuffer[j], ImageWidth) == GIF_ERROR) +int main(int argc, char **argv) { + int i, j, l, ImageWidth, ImageHeight, NumOfLines, LogNumLevels, + ErrorCode, NumLevels, ColorMapSize = 1, + ForeGroundIndex = DEFAULT_FG_INDEX; + bool Error, ClrMapSizeFlag = false, ForeGroundFlag = false, + TextLineFlag = false, HelpFlag = false, ColorFlag = false, + GifNoisyPrint = false; + char *TextLines[MAX_NUM_TEXT_LINES]; + GifRowType RasterBuffer[GIF_FONT_HEIGHT]; + ColorMapObject *ColorMap; + GifFileType *GifFile; + + if ((Error = + GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &ClrMapSizeFlag, + &ColorMapSize, &ForeGroundFlag, &ForeGroundIndex, + &ColorFlag, &RedColor, &GreenColor, &BlueColor, + &TextLineFlag, &TextLines[0], &HelpFlag)) != false) { + GAPrintErrMsg(Error); + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); + } + + if (ForeGroundIndex > 255 || ForeGroundIndex < 1) { + GIF_EXIT( + "Foregound (-f) should be in the range 1..255, aborted."); + } + + if (ColorMapSize > 8 || ColorMapSize < 1) { + GIF_EXIT( + "ColorMapSize (-s) should be in the range 1..8, aborted."); + } + + if (TextLineFlag) { + NumOfLines = 1; + ImageHeight = GIF_FONT_HEIGHT; + ImageWidth = GIF_FONT_WIDTH * strlen(TextLines[0]); + } else { + char Line[LINE_LEN]; + NumOfLines = l = 0; + while (fgets(Line, LINE_LEN - 1, stdin)) { + for (i = strlen(Line); i > 0 && Line[i - 1] <= ' '; + i--) { + ; + } + Line[i] = 0; + if (l < i) { + l = i; + } + TextLines[NumOfLines++] = strdup(Line); + if (NumOfLines == MAX_NUM_TEXT_LINES) { + GIF_EXIT( + "Input file has too many lines, aborted."); + } + } + if (NumOfLines == 0) { + GIF_EXIT("No input text, aborted."); + } + ImageHeight = GIF_FONT_HEIGHT * NumOfLines; + ImageWidth = GIF_FONT_WIDTH * l; + } + + /* Allocate the raster buffer for GIF_FONT_HEIGHT scan lines (one text + * line). */ + for (i = 0; i < GIF_FONT_HEIGHT; i++) { + if ((RasterBuffer[i] = (GifRowType)malloc( + sizeof(GifPixelType) * ImageWidth)) == NULL) { + GIF_EXIT( + "Failed to allocate memory required, aborted."); + } + } + + /* Open stdout for the output file: */ + if ((GifFile = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + /* Dump out screen description with given size and generated color map: + */ + for (LogNumLevels = 1, NumLevels = 2; NumLevels < ForeGroundIndex; + LogNumLevels++, NumLevels <<= 1) { + ; + } + if (NumLevels < (1 << ColorMapSize)) { + NumLevels = (1 << ColorMapSize); + LogNumLevels = ColorMapSize; + } + + if ((ColorMap = GifMakeMapObject(NumLevels, NULL)) == NULL) { + GIF_EXIT("Failed to allocate memory required, aborted."); + } + + for (i = 0; i < NumLevels; i++) { + ColorMap->Colors[i].Red = ColorMap->Colors[i].Green = + ColorMap->Colors[i].Blue = 0; + } + ColorMap->Colors[ForeGroundIndex].Red = RedColor; + ColorMap->Colors[ForeGroundIndex].Green = GreenColor; + ColorMap->Colors[ForeGroundIndex].Blue = BlueColor; + + if (EGifPutScreenDesc(GifFile, ImageWidth, ImageHeight, LogNumLevels, 0, + ColorMap) == GIF_ERROR) { QuitGifError(GifFile); - GifQprintf("\b\b\b\b%-4d", l++); } - } - if (EGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) - { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } + /* Dump out the image descriptor: */ + if (EGifPutImageDesc(GifFile, 0, 0, ImageWidth, ImageHeight, false, + NULL) == GIF_ERROR) { + QuitGifError(GifFile); + } + + GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]: ", PROGRAM_NAME, + GifFile->Image.Left, GifFile->Image.Top, + GifFile->Image.Width, GifFile->Image.Height); + + for (i = l = 0; i < NumOfLines; i++) { + GenRasterTextLine(RasterBuffer, TextLines[i], ImageWidth, + ForeGroundIndex); + for (j = 0; j < GIF_FONT_HEIGHT; j++) { + if (EGifPutLine(GifFile, RasterBuffer[j], ImageWidth) == + GIF_ERROR) { + QuitGifError(GifFile); + } + GifQprintf("\b\b\b\b%-4d", l++); + } + } - return 0; + if (EGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + return 0; } /****************************************************************************** Generate raster bits corresponding to given text ******************************************************************************/ static void GenRasterTextLine(GifRowType *RasterBuffer, char *TextLine, - int BufferWidth, int ForeGroundIndex) -{ - unsigned char Byte, Mask; - int i, j, k, CharPosX, Len = strlen(TextLine); - - for (i = 0; i < BufferWidth; i++) - for (j = 0; j < GIF_FONT_HEIGHT; j++) RasterBuffer[j][i] = 0; - - for (i = CharPosX = 0; i < Len; i++, CharPosX += GIF_FONT_WIDTH) { - unsigned char c = TextLine[i]; - for (j = 0; j < GIF_FONT_HEIGHT; j++) { - Byte = GifAsciiTable8x8[(unsigned short)c][j]; - for (k = 0, Mask = 128; k < GIF_FONT_WIDTH; k++, Mask >>= 1) - if (Byte & Mask) - RasterBuffer[j][CharPosX + k] = ForeGroundIndex; - } - } + int BufferWidth, int ForeGroundIndex) { + unsigned char Byte, Mask; + int i, j, k, CharPosX, Len = strlen(TextLine); + + for (i = 0; i < BufferWidth; i++) { + for (j = 0; j < GIF_FONT_HEIGHT; j++) { + RasterBuffer[j][i] = 0; + } + } + + for (i = CharPosX = 0; i < Len; i++, CharPosX += GIF_FONT_WIDTH) { + unsigned char c = TextLine[i]; + for (j = 0; j < GIF_FONT_HEIGHT; j++) { + Byte = GifAsciiTable8x8[(unsigned short)c][j]; + for (k = 0, Mask = 128; k < GIF_FONT_WIDTH; + k++, Mask >>= 1) { + if (Byte & Mask) { + RasterBuffer[j][CharPosX + k] = + ForeGroundIndex; + } + } + } + } } /****************************************************************************** -* Close output file (if open), and exit. -******************************************************************************/ -static void QuitGifError(GifFileType *GifFile) -{ - if (GifFile != NULL) { - PrintGifError(GifFile->Error); - EGifCloseFile(GifFile, NULL); - } - exit(EXIT_FAILURE); + * Close output file (if open), and exit. + ******************************************************************************/ +static void QuitGifError(GifFileType *GifFile) { + if (GifFile != NULL) { + PrintGifError(GifFile->Error); + EGifCloseFile(GifFile, NULL); + } + exit(EXIT_FAILURE); } /* end */ diff --git a/giffilter.c b/giffilter.c index e10829a..98c2af2 100644 --- a/giffilter.c +++ b/giffilter.c @@ -1,6 +1,6 @@ /****************************************************************************** - -giffilter.c - skeleton file for generic GIF `filter' program + +giffilter.c - skeleton file for generic GIF `filter' program Sequentially read GIF records from stdin, process them, send them out. Most of the junk above `int main' isn't needed for the skeleton, but @@ -16,137 +16,158 @@ copy an image from a much more (or much *less*) memory-limited system; your compression may use more (or fewer) bits. The uncompressed rasters should, however, be identical (you can check this with gifbuild -d). +SPDX-License-Identifier: MIT + ******************************************************************************/ +#include #include #include #include -#include #include "getarg.h" #include "gif_lib.h" -#define PROGRAM_NAME "giffilter" +#define PROGRAM_NAME "giffilter" /****************************************************************************** Close both input and output file (if open), and exit. ******************************************************************************/ -static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut) -{ - if (GifFileIn != NULL) { - PrintGifError(GifFileIn->Error); - EGifCloseFile(GifFileIn, NULL); - } - if (GifFileOut != NULL) { - PrintGifError(GifFileOut->Error); - EGifCloseFile(GifFileOut, NULL); - } - exit(EXIT_FAILURE); +static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut) { + if (GifFileIn != NULL) { + PrintGifError(GifFileIn->Error); + EGifCloseFile(GifFileIn, NULL); + } + if (GifFileOut != NULL) { + PrintGifError(GifFileOut->Error); + EGifCloseFile(GifFileOut, NULL); + } + exit(EXIT_FAILURE); } /****************************************************************************** Main sequence ******************************************************************************/ -int main(int argc, char **argv) -{ - GifFileType *GifFileIn = NULL, *GifFileOut = NULL; - GifRecordType RecordType; - int CodeSize, ExtCode, ErrorCode; - GifByteType *CodeBlock, *Extension; - - /* - * Command-line processing goes here. - */ - - /* Use stdin as input (note this also read screen descriptor in: */ - if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } +int main(int argc, char **argv) { + GifFileType *GifFileIn = NULL, *GifFileOut = NULL; + GifRecordType RecordType; + int CodeSize, ExtCode, ErrorCode; + GifByteType *CodeBlock, *Extension; + + /* + * Command-line processing goes here. + */ + + /* Use stdin as input (note this also read screen descriptor in: */ + if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } - /* Use the stdout as output: */ - if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } - - /* And dump out its screen information: */ - if (EGifPutScreenDesc(GifFileOut, - GifFileIn->SWidth, GifFileIn->SHeight, - GifFileIn->SColorResolution, GifFileIn->SBackGroundColor, - GifFileIn->SColorMap) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - - /* Scan the content of the input GIF file and load the image(s) in: */ - do { - if (DGifGetRecordType(GifFileIn, &RecordType) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - - switch (RecordType) { - case IMAGE_DESC_RECORD_TYPE: - if (DGifGetImageDesc(GifFileIn) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - /* Put image descriptor to out file: */ - if (EGifPutImageDesc(GifFileOut, - GifFileIn->Image.Left, GifFileIn->Image.Top, - GifFileIn->Image.Width, GifFileIn->Image.Height, - GifFileIn->Image.Interlace, - GifFileIn->Image.ColorMap) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - - /* Now read image itself in decoded form as we dont really */ - /* care what we have there, and this is much faster. */ - if (DGifGetCode(GifFileIn, &CodeSize, &CodeBlock) == GIF_ERROR || - EGifPutCode(GifFileOut, CodeSize, CodeBlock) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - while (CodeBlock != NULL) { - if (DGifGetCodeNext(GifFileIn, &CodeBlock) == GIF_ERROR || - EGifPutCodeNext(GifFileOut, CodeBlock) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - } - break; - case EXTENSION_RECORD_TYPE: - /* pass through extension records */ - if (DGifGetExtension(GifFileIn, &ExtCode, &Extension) == GIF_ERROR || Extension == NULL) - QuitGifError(GifFileIn, GifFileOut); - if (EGifPutExtensionLeader(GifFileOut, ExtCode) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - if (EGifPutExtensionBlock(GifFileOut, - Extension[0], - Extension + 1) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - while (Extension != NULL) { - if (DGifGetExtensionNext(GifFileIn, &Extension)==GIF_ERROR) + /* Use the stdout as output: */ + if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + /* And dump out its screen information: */ + if (EGifPutScreenDesc(GifFileOut, GifFileIn->SWidth, GifFileIn->SHeight, + GifFileIn->SColorResolution, + GifFileIn->SBackGroundColor, + GifFileIn->SColorMap) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + + /* Scan the content of the input GIF file and load the image(s) in: */ + do { + if (DGifGetRecordType(GifFileIn, &RecordType) == GIF_ERROR) { QuitGifError(GifFileIn, GifFileOut); - if (Extension != NULL) - if (EGifPutExtensionBlock(GifFileOut, - Extension[0], - Extension + 1) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); } - if (EGifPutExtensionTrailer(GifFileOut) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - break; - case TERMINATE_RECORD_TYPE: - break; - default: /* Should be trapped by DGifGetRecordType */ - break; - } - } - while (RecordType != TERMINATE_RECORD_TYPE); - if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) - { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } - if (EGifCloseFile(GifFileOut, &ErrorCode) == GIF_ERROR) - { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFileIn) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + /* Put image descriptor to out file: */ + if (EGifPutImageDesc( + GifFileOut, GifFileIn->Image.Left, + GifFileIn->Image.Top, GifFileIn->Image.Width, + GifFileIn->Image.Height, + GifFileIn->Image.Interlace, + GifFileIn->Image.ColorMap) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + + /* Now read image itself in decoded form as we dont + * really */ + /* care what we have there, and this is much faster. + */ + if (DGifGetCode(GifFileIn, &CodeSize, &CodeBlock) == + GIF_ERROR || + EGifPutCode(GifFileOut, CodeSize, CodeBlock) == + GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + while (CodeBlock != NULL) { + if (DGifGetCodeNext(GifFileIn, &CodeBlock) == + GIF_ERROR || + EGifPutCodeNext(GifFileOut, CodeBlock) == + GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + } + break; + case EXTENSION_RECORD_TYPE: + /* pass through extension records */ + if (DGifGetExtension(GifFileIn, &ExtCode, &Extension) == + GIF_ERROR || + Extension == NULL) { + QuitGifError(GifFileIn, GifFileOut); + } + if (EGifPutExtensionLeader(GifFileOut, ExtCode) == + GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + if (EGifPutExtensionBlock(GifFileOut, Extension[0], + Extension + 1) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + while (Extension != NULL) { + if (DGifGetExtensionNext( + GifFileIn, &Extension) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + if (Extension != NULL) { + if (EGifPutExtensionBlock( + GifFileOut, Extension[0], + Extension + 1) == GIF_ERROR) { + QuitGifError(GifFileIn, + GifFileOut); + } + } + } + if (EGifPutExtensionTrailer(GifFileOut) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + break; + case TERMINATE_RECORD_TYPE: + break; + default: /* Should be trapped by DGifGetRecordType */ + break; + } + } while (RecordType != TERMINATE_RECORD_TYPE); + + if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + if (EGifCloseFile(GifFileOut, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } - return 0; + return 0; } /* end */ diff --git a/giffix.c b/giffix.c index 6764bab..e7a79a8 100644 --- a/giffix.c +++ b/giffix.c @@ -2,215 +2,243 @@ giffix - attempt to fix a truncated GIF +SPDX-License-Identifier: MIT + *****************************************************************************/ -#include -#include #include -#include #include +#include +#include +#include -#include "gif_lib.h" #include "getarg.h" +#include "gif_lib.h" -#define PROGRAM_NAME "giffix" +#define PROGRAM_NAME "giffix" -static char - *VersionStr = - PROGRAM_NAME - VERSION_COOKIE - " Gershon Elber, " - __DATE__ ", " __TIME__ "\n" - "(C) Copyright 1989 Gershon Elber.\n"; -static char - *CtrlStr = - PROGRAM_NAME - " v%- h%- GifFile!*s"; +static char *VersionStr = PROGRAM_NAME VERSION_COOKIE + " Gershon Elber, " __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char *CtrlStr = PROGRAM_NAME " v%- h%- GifFile!*s"; static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut); /****************************************************************************** Interpret the command line and scan the given GIF file. ******************************************************************************/ -int main(int argc, char **argv) -{ - int i, j, NumFiles, ExtCode, Row, Col, Width, Height, ErrorCode, - DarkestColor = 0, ColorIntens = 10000; - bool Error, HelpFlag = false; - GifRecordType RecordType; - GifByteType *Extension; - char **FileName = NULL; - GifRowType LineBuffer; - ColorMapObject *ColorMap; - GifFileType *GifFileIn = NULL, *GifFileOut = NULL; - int ImageNum = 0; - - if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &HelpFlag, - &NumFiles, &FileName)) != false || - (NumFiles > 1 && !HelpFlag)) { - if (Error) - GAPrintErrMsg(Error); - else if (NumFiles > 1) - GIF_MESSAGE("Error in command line parsing - one GIF file please."); - GAPrintHowTo(CtrlStr); - exit(EXIT_FAILURE); - } - - if (HelpFlag) { - (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); - GAPrintHowTo(CtrlStr); - exit(EXIT_SUCCESS); - } - - if (NumFiles == 1) { - if ((GifFileIn = DGifOpenFileName(*FileName, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); +int main(int argc, char **argv) { + int i, j, NumFiles, ExtCode, Row, Col, Width, Height, ErrorCode, + DarkestColor = 0, ColorIntens = 10000; + bool Error, HelpFlag = false, GifNoisyPrint = false; + GifRecordType RecordType; + GifByteType *Extension; + char **FileName = NULL; + GifRowType LineBuffer; + ColorMapObject *ColorMap; + GifFileType *GifFileIn = NULL, *GifFileOut = NULL; + int ImageNum = 0; + + if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &HelpFlag, + &NumFiles, &FileName)) != false || + (NumFiles > 1 && !HelpFlag)) { + if (Error) { + GAPrintErrMsg(Error); + } else if (NumFiles > 1) { + GIF_MESSAGE("Error in command line parsing - one GIF " + "file please."); + } + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); } - } - else - { - /* Use stdin instead: */ - if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); } - } - /* Open stdout for the output file: */ - if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } - - /* Dump out exactly same screen information: */ - /* coverity[var_deref_op] */ - if (EGifPutScreenDesc(GifFileOut, - GifFileIn->SWidth, GifFileIn->SHeight, - GifFileIn->SColorResolution, GifFileIn->SBackGroundColor, - GifFileIn->SColorMap) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - - if ((LineBuffer = (GifRowType) malloc(GifFileIn->SWidth)) == NULL) - GIF_EXIT("Failed to allocate memory required, aborted."); - - /* Scan the content of the GIF file and load the image(s) in: */ - do { - if (DGifGetRecordType(GifFileIn, &RecordType) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - - switch (RecordType) { - case IMAGE_DESC_RECORD_TYPE: - if (DGifGetImageDesc(GifFileIn) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - if (GifFileIn->Image.Interlace) - GIF_EXIT("Cannot fix interlaced images."); - - Row = GifFileIn->Image.Top; /* Image Position relative to Screen. */ - Col = GifFileIn->Image.Left; - Width = GifFileIn->Image.Width; - Height = GifFileIn->Image.Height; - GifQprintf("\n%s: Image %d at (%d, %d) [%dx%d]: ", - PROGRAM_NAME, ++ImageNum, Col, Row, Width, Height); - if (Width > GifFileIn->SWidth) - GIF_EXIT("Image is wider than total"); - - /* Put the image descriptor to out file: */ - if (EGifPutImageDesc(GifFileOut, Col, Row, Width, Height, - false, GifFileIn->Image.ColorMap) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - - /* Find the darkest color in color map to use as a filler. */ - ColorMap = (GifFileIn->Image.ColorMap ? GifFileIn->Image.ColorMap : - GifFileIn->SColorMap); - for (i = 0; i < ColorMap->ColorCount; i++) { - j = ((int) ColorMap->Colors[i].Red) * 30 + - ((int) ColorMap->Colors[i].Green) * 59 + - ((int) ColorMap->Colors[i].Blue) * 11; - if (j < ColorIntens) { - ColorIntens = j; - DarkestColor = i; - } + if (NumFiles == 1) { + if ((GifFileIn = DGifOpenFileName(*FileName, &ErrorCode)) == + NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); } - - /* Load the image, and dump it. */ - for (i = 0; i < Height; i++) { - GifQprintf("\b\b\b\b%-4d", i); - if (DGifGetLine(GifFileIn, LineBuffer, Width) - == GIF_ERROR) break; - if (EGifPutLine(GifFileOut, LineBuffer, Width) - == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); + } else { + /* Use stdin instead: */ + if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); } + } + + /* Open stdout for the output file: */ + if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } - if (i < Height) { - fprintf(stderr,"\nFollowing error occurred (and ignored):"); - PrintGifError(GifFileIn->Error); + /* Dump out exactly same screen information: */ + /* coverity[var_deref_op] */ + if (EGifPutScreenDesc(GifFileOut, GifFileIn->SWidth, GifFileIn->SHeight, + GifFileIn->SColorResolution, + GifFileIn->SBackGroundColor, + GifFileIn->SColorMap) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } - /* Fill in with the darkest color in color map. */ - for (j = 0; j < Width; j++) - LineBuffer[j] = DarkestColor; - for (; i < Height; i++) - if (EGifPutLine(GifFileOut, LineBuffer, Width) - == GIF_ERROR) QuitGifError(GifFileIn, GifFileOut); - } - break; - case EXTENSION_RECORD_TYPE: - /* pass through extension records */ - if (DGifGetExtension(GifFileIn, &ExtCode, &Extension) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - if (EGifPutExtensionLeader(GifFileOut, ExtCode) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - if (Extension != NULL) - if (EGifPutExtensionBlock(GifFileOut, - Extension[0], - Extension + 1) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - while (Extension != NULL) { - if (DGifGetExtensionNext(GifFileIn, &Extension)==GIF_ERROR) + if ((LineBuffer = (GifRowType)malloc(GifFileIn->SWidth)) == NULL) { + GIF_EXIT("Failed to allocate memory required, aborted."); + } + + /* Scan the content of the GIF file and load the image(s) in: */ + do { + if (DGifGetRecordType(GifFileIn, &RecordType) == GIF_ERROR) { QuitGifError(GifFileIn, GifFileOut); - if (Extension != NULL) - if (EGifPutExtensionBlock(GifFileOut, - Extension[0], - Extension + 1) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); } - if (EGifPutExtensionTrailer(GifFileOut) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - break; - case TERMINATE_RECORD_TYPE: - break; - default: /* Should be trapped by DGifGetRecordType. */ - break; - } - } - while (RecordType != TERMINATE_RECORD_TYPE); - if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } - if (EGifCloseFile(GifFileOut, &ErrorCode) == GIF_ERROR) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } - return 0; + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFileIn) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + if (GifFileIn->Image.Interlace) { + GIF_EXIT("Cannot fix interlaced images."); + } + + Row = GifFileIn->Image + .Top; /* Image Position relative to Screen. */ + Col = GifFileIn->Image.Left; + Width = GifFileIn->Image.Width; + Height = GifFileIn->Image.Height; + GifQprintf("\n%s: Image %d at (%d, %d) [%dx%d]: ", + PROGRAM_NAME, ++ImageNum, Col, Row, Width, + Height); + if (Width > GifFileIn->SWidth) { + GIF_EXIT("Image is wider than total"); + } + + /* Put the image descriptor to out file: */ + if (EGifPutImageDesc( + GifFileOut, Col, Row, Width, Height, false, + GifFileIn->Image.ColorMap) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + + /* Find the darkest color in color map to use as a + * filler. */ + ColorMap = (GifFileIn->Image.ColorMap + ? GifFileIn->Image.ColorMap + : GifFileIn->SColorMap); + for (i = 0; i < ColorMap->ColorCount; i++) { + j = ((int)ColorMap->Colors[i].Red) * 30 + + ((int)ColorMap->Colors[i].Green) * 59 + + ((int)ColorMap->Colors[i].Blue) * 11; + if (j < ColorIntens) { + ColorIntens = j; + DarkestColor = i; + } + } + + /* Load the image, and dump it. */ + for (i = 0; i < Height; i++) { + GifQprintf("\b\b\b\b%-4d", i); + if (DGifGetLine(GifFileIn, LineBuffer, Width) == + GIF_ERROR) { + break; + } + if (EGifPutLine(GifFileOut, LineBuffer, + Width) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + } + + if (i < Height) { + fprintf(stderr, "\nFollowing error occurred " + "(and ignored):"); + PrintGifError(GifFileIn->Error); + + /* Fill in with the darkest color in color map. + */ + for (j = 0; j < Width; j++) { + LineBuffer[j] = DarkestColor; + } + for (; i < Height; i++) { + if (EGifPutLine(GifFileOut, LineBuffer, + Width) == GIF_ERROR) { + QuitGifError(GifFileIn, + GifFileOut); + } + } + } + break; + case EXTENSION_RECORD_TYPE: + /* pass through extension records */ + if (DGifGetExtension(GifFileIn, &ExtCode, &Extension) == + GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + if (EGifPutExtensionLeader(GifFileOut, ExtCode) == + GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + if (Extension != NULL) { + if (EGifPutExtensionBlock( + GifFileOut, Extension[0], + Extension + 1) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + } + while (Extension != NULL) { + if (DGifGetExtensionNext( + GifFileIn, &Extension) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + if (Extension != NULL) { + if (EGifPutExtensionBlock( + GifFileOut, Extension[0], + Extension + 1) == GIF_ERROR) { + QuitGifError(GifFileIn, + GifFileOut); + } + } + } + if (EGifPutExtensionTrailer(GifFileOut) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + break; + case TERMINATE_RECORD_TYPE: + break; + default: /* Should be trapped by DGifGetRecordType. */ + break; + } + } while (RecordType != TERMINATE_RECORD_TYPE); + + if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + if (EGifCloseFile(GifFileOut, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + return 0; } /****************************************************************************** Close both input and output file (if open), and exit. ******************************************************************************/ -static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut) -{ - fprintf(stderr, "\nFollowing unrecoverable error occurred:"); - if (GifFileIn != NULL) { - PrintGifError(GifFileIn->Error); - EGifCloseFile(GifFileIn, NULL); - } - if (GifFileOut != NULL) { - PrintGifError(GifFileOut->Error); - EGifCloseFile(GifFileOut, NULL); - } - exit(EXIT_FAILURE); +static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut) { + fprintf(stderr, "\nFollowing unrecoverable error occurred:"); + if (GifFileIn != NULL) { + PrintGifError(GifFileIn->Error); + EGifCloseFile(GifFileIn, NULL); + } + if (GifFileOut != NULL) { + PrintGifError(GifFileOut->Error); + EGifCloseFile(GifFileOut, NULL); + } + exit(EXIT_FAILURE); } /* end */ diff --git a/gifhisto.c b/gifhisto.c index 2e6bd7b..fb79ea0 100644 --- a/gifhisto.c +++ b/gifhisto.c @@ -2,256 +2,297 @@ gifhisto - make a color histogram from image color frequencies +SPDX-License-Identifier: MIT + *****************************************************************************/ -#include -#include #include -#include #include +#include +#include +#include -#include "gif_lib.h" #include "getarg.h" +#include "gif_lib.h" + +#define PROGRAM_NAME "gifhisto" -#define PROGRAM_NAME "gifhisto" - -#define DEFAULT_HISTO_WIDTH 100 /* Histogram image diemnsions. */ -#define DEFAULT_HISTO_HEIGHT 256 -#define HISTO_BITS_PER_PIXEL 2 /* Size of bitmap for histogram GIF. */ - -static char - *VersionStr = - PROGRAM_NAME - VERSION_COOKIE - " Gershon Elber, " - __DATE__ ", " __TIME__ "\n" - "(C) Copyright 1989 Gershon Elber.\n"; -static char - *CtrlStr = - PROGRAM_NAME - " v%- t%- s%-Width|Height!d!d n%-ImageNumber!d b%- h%- GifFile!*s"; - -static int - ImageWidth = DEFAULT_HISTO_WIDTH, - ImageHeight = DEFAULT_HISTO_HEIGHT, - ImageN = 1; -static GifColorType - HistoColorMap[] = { /* Constant bit map for histograms: */ - { 0, 0, 0 }, - { 255, 0, 0 }, - { 0, 255, 0 }, - { 0, 0, 255 } - }; +#define DEFAULT_HISTO_WIDTH 100 /* Histogram image diemnsions. */ +#define DEFAULT_HISTO_HEIGHT 256 +#define HISTO_BITS_PER_PIXEL 2 /* Size of bitmap for histogram GIF. */ + +static char *VersionStr = PROGRAM_NAME VERSION_COOKIE + " Gershon Elber, " __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char *CtrlStr = PROGRAM_NAME + " v%- t%- s%-Width|Height!d!d n%-ImageNumber!d b%- h%- GifFile!*s"; + +static int ImageWidth = DEFAULT_HISTO_WIDTH, ImageHeight = DEFAULT_HISTO_HEIGHT, + ImageN = 1; +static GifColorType HistoColorMap[] = {/* Constant bit map for histograms: */ + {0, 0, 0}, + {255, 0, 0}, + {0, 255, 0}, + {0, 0, 255}}; static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut); /****************************************************************************** Interpret the command line and scan the given GIF file. ******************************************************************************/ -int main(int argc, char **argv) -{ - int i, j, ErrorCode, NumFiles, ExtCode, CodeSize, NumColors = 2, ImageNum = 0; - bool Error, TextFlag = false, SizeFlag = false, - ImageNFlag = false, BackGroundFlag = false, HelpFlag = false; - long Histogram[256]; - GifRecordType RecordType; - GifByteType *Extension, *CodeBlock; - char **FileName = NULL; - GifRowType Line; - GifFileType *GifFileIn = NULL, *GifFileOut = NULL; - - /* Same image dimension vars for both Image & ImageN as only one allowed */ - if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, - &TextFlag, &SizeFlag, &ImageWidth, &ImageHeight, - &ImageNFlag, &ImageN, &BackGroundFlag, - &HelpFlag, &NumFiles, &FileName)) != false || - (NumFiles > 1 && !HelpFlag)) { - if (Error) - GAPrintErrMsg(Error); - else if (NumFiles > 1) - GIF_MESSAGE("Error in command line parsing - one GIF file please."); - GAPrintHowTo(CtrlStr); - exit(EXIT_FAILURE); - } - - if (HelpFlag) { - (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); - GAPrintHowTo(CtrlStr); - exit(EXIT_SUCCESS); - } - - if (NumFiles == 1) { - if ((GifFileIn = DGifOpenFileName(*FileName, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); +int main(int argc, char **argv) { + int i, j, ErrorCode, NumFiles, ExtCode, CodeSize, NumColors = 2, + ImageNum = 0; + bool Error, TextFlag = false, SizeFlag = false, ImageNFlag = false, + BackGroundFlag = false, HelpFlag = false, GifNoisyPrint; + long Histogram[256]; + GifRecordType RecordType; + GifByteType *Extension, *CodeBlock; + char **FileName = NULL; + GifRowType Line; + GifFileType *GifFileIn = NULL, *GifFileOut = NULL; + + /* Same image dimension vars for both Image & ImageN as only one allowed + */ + if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &TextFlag, + &SizeFlag, &ImageWidth, &ImageHeight, + &ImageNFlag, &ImageN, &BackGroundFlag, &HelpFlag, + &NumFiles, &FileName)) != false || + (NumFiles > 1 && !HelpFlag)) { + if (Error) { + GAPrintErrMsg(Error); + } else if (NumFiles > 1) { + GIF_MESSAGE("Error in command line parsing - one GIF " + "file please."); + } + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); } - } - else { - /* Use stdin instead: */ - if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); } - } - - for (i = 0; i < 256; i++) Histogram[i] = 0; /* Reset counters. */ - - /* Scan the content of the GIF file and load the image(s) in: */ - do { - if (DGifGetRecordType(GifFileIn, &RecordType) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - - switch (RecordType) { - case IMAGE_DESC_RECORD_TYPE: - if (DGifGetImageDesc(GifFileIn) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - - if (GifFileIn->Image.ColorMap) - NumColors = GifFileIn->Image.ColorMap->ColorCount; - else if (GifFileIn->SColorMap) - NumColors = GifFileIn->SColorMap->ColorCount; - else - GIF_EXIT("Neither Screen nor Image color map exists."); - - if ((ImageHeight / NumColors) * NumColors != ImageHeight) - GIF_EXIT("Image height specified not dividable by #colors."); - - if (++ImageNum == ImageN) { - /* This is the image we should make histogram for: */ - Line = (GifRowType) malloc(GifFileIn->Image.Width * - sizeof(GifPixelType)); - GifQprintf("\n%s: Image %d at (%d, %d) [%dx%d]: ", - PROGRAM_NAME, ImageNum, - GifFileIn->Image.Left, GifFileIn->Image.Top, - GifFileIn->Image.Width, GifFileIn->Image.Height); - - for (i = 0; i < GifFileIn->Image.Height; i++) { - if (DGifGetLine(GifFileIn, Line, GifFileIn->Image.Width) - == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - for (j = 0; j < GifFileIn->Image.Width; j++) - Histogram[Line[j]]++; - GifQprintf("\b\b\b\b%-4d", i); - } - - free((char *) Line); + + if (NumFiles == 1) { + if ((GifFileIn = DGifOpenFileName(*FileName, &ErrorCode)) == + NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); } - else { - /* Skip the image: */ - /* Now read image itself in decoded form as we dont */ - /* really care what is there, and this is much faster. */ - if (DGifGetCode(GifFileIn, &CodeSize, &CodeBlock) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - while (CodeBlock != NULL) - if (DGifGetCodeNext(GifFileIn, &CodeBlock) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); + } else { + /* Use stdin instead: */ + if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); } - break; - case EXTENSION_RECORD_TYPE: - /* Skip any extension blocks in file: */ - if (DGifGetExtension(GifFileIn, &ExtCode, &Extension) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - - while (Extension != NULL) { - if (DGifGetExtensionNext(GifFileIn, &Extension) == GIF_ERROR) + } + + for (i = 0; i < 256; i++) { + Histogram[i] = 0; /* Reset counters. */ + } + /* Scan the content of the GIF file and load the image(s) in: */ + do { + if (DGifGetRecordType(GifFileIn, &RecordType) == GIF_ERROR) { QuitGifError(GifFileIn, GifFileOut); } - break; - case TERMINATE_RECORD_TYPE: - break; - default: /* Should be trapped by DGifGetRecordType. */ - break; - } - } - while (RecordType != TERMINATE_RECORD_TYPE); - /* We requested suppression of the background count: */ - if (BackGroundFlag) Histogram[GifFileIn->SBackGroundColor] = 0; + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFileIn) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + + if (GifFileIn->Image.ColorMap) { + NumColors = + GifFileIn->Image.ColorMap->ColorCount; + } else if (GifFileIn->SColorMap) { + NumColors = GifFileIn->SColorMap->ColorCount; + } else { + GIF_EXIT("Neither Screen nor Image color map " + "exists."); + } + + if ((ImageHeight / NumColors) * NumColors != + ImageHeight) { + GIF_EXIT("Image height specified not dividable " + "by #colors."); + } + + if (++ImageNum == ImageN) { + /* This is the image we should make histogram + * for: */ + Line = + (GifRowType)malloc(GifFileIn->Image.Width * + sizeof(GifPixelType)); + GifQprintf( + "\n%s: Image %d at (%d, %d) [%dx%d]: ", + PROGRAM_NAME, ImageNum, + GifFileIn->Image.Left, GifFileIn->Image.Top, + GifFileIn->Image.Width, + GifFileIn->Image.Height); + + for (i = 0; i < GifFileIn->Image.Height; i++) { + if (DGifGetLine( + GifFileIn, Line, + GifFileIn->Image.Width) == + GIF_ERROR) { + QuitGifError(GifFileIn, + GifFileOut); + } + for (j = 0; j < GifFileIn->Image.Width; + j++) { + Histogram[Line[j]]++; + } + GifQprintf("\b\b\b\b%-4d", i); + } + + free((char *)Line); + } else { + /* Skip the image: */ + /* Now read image itself in decoded form as we + * dont */ + /* really care what is there, and this is much + * faster. */ + if (DGifGetCode(GifFileIn, &CodeSize, + &CodeBlock) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + while (CodeBlock != NULL) { + if (DGifGetCodeNext(GifFileIn, + &CodeBlock) == + GIF_ERROR) { + QuitGifError(GifFileIn, + GifFileOut); + } + } + } + break; + case EXTENSION_RECORD_TYPE: + /* Skip any extension blocks in file: */ + if (DGifGetExtension(GifFileIn, &ExtCode, &Extension) == + GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + + while (Extension != NULL) { + if (DGifGetExtensionNext( + GifFileIn, &Extension) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + } + break; + case TERMINATE_RECORD_TYPE: + break; + default: /* Should be trapped by DGifGetRecordType. */ + break; + } + } while (RecordType != TERMINATE_RECORD_TYPE); - if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) - { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } - - - /* We may required to dump out the histogram as text file: */ - if (TextFlag) { - for (i = 0; i < NumColors; i++) - printf("%12ld %3d\n", Histogram[i], i); - } - else { - int Color, Count; - long Scaler; - /* Open stdout for the histogram output file: */ - if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); + /* We requested suppression of the background count: */ + if (BackGroundFlag) { + Histogram[GifFileIn->SBackGroundColor] = 0; } - /* Dump out screen descriptor to fit histogram dimensions: */ - if (EGifPutScreenDesc(GifFileOut, - ImageWidth, ImageHeight, HISTO_BITS_PER_PIXEL, 0, - GifMakeMapObject(4, HistoColorMap)) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - - /* Dump out image descriptor to fit histogram dimensions: */ - if (EGifPutImageDesc(GifFileOut, - 0, 0, ImageWidth, ImageHeight, false, NULL) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - - /* Prepare scan line for histogram file, and find scaler to scale */ - /* histogram to be between 0 and ImageWidth: */ - Line = (GifRowType) malloc(ImageWidth * sizeof(GifPixelType)); - for (Scaler = 0, i = 0; i < NumColors; i++) if (Histogram[i] > Scaler) - Scaler = Histogram[i]; - Scaler /= ImageWidth; - if (Scaler == 0) Scaler = 1; /* In case maximum is less than width. */ - - /* Dump out the image itself: */ - for (Count = ImageHeight, i = 0, Color = 1; i < NumColors; i++) { - int Size; - if ((Size = Histogram[i] / Scaler) > ImageWidth) Size = ImageWidth; - for (j = 0; j < Size; j++) - Line[j] = Color; - for (j = Size; j < ImageWidth; j++) - Line[j] = GifFileOut->SBackGroundColor; - - /* Move to next color: */ - if (++Color >= (1 << HISTO_BITS_PER_PIXEL)) Color = 1; - - /* Dump this histogram entry as many times as required: */ - for (j = 0; j < ImageHeight / NumColors; j++) { - if (EGifPutLine(GifFileOut, Line, ImageWidth) == GIF_ERROR) - QuitGifError(GifFileIn, GifFileOut); - GifQprintf("\b\b\b\b%-4d", Count--); - } + if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); } - if (EGifCloseFile(GifFileOut, &ErrorCode) == GIF_ERROR) - { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); + /* We may required to dump out the histogram as text file: */ + if (TextFlag) { + for (i = 0; i < NumColors; i++) { + printf("%12ld %3d\n", Histogram[i], i); + } + } else { + int Color, Count; + long Scaler; + /* Open stdout for the histogram output file: */ + if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + /* Dump out screen descriptor to fit histogram dimensions: */ + if (EGifPutScreenDesc(GifFileOut, ImageWidth, ImageHeight, + HISTO_BITS_PER_PIXEL, 0, + GifMakeMapObject(4, HistoColorMap)) == + GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + + /* Dump out image descriptor to fit histogram dimensions: */ + if (EGifPutImageDesc(GifFileOut, 0, 0, ImageWidth, ImageHeight, + false, NULL) == GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + + /* Prepare scan line for histogram file, and find scaler to + * scale */ + /* histogram to be between 0 and ImageWidth: */ + Line = (GifRowType)malloc(ImageWidth * sizeof(GifPixelType)); + for (Scaler = 0, i = 0; i < NumColors; i++) { + if (Histogram[i] > Scaler) { + Scaler = Histogram[i]; + } + } + Scaler /= ImageWidth; + if (Scaler == 0) { + Scaler = 1; /* In case maximum is less than width. */ + } + /* Dump out the image itself: */ + for (Count = ImageHeight, i = 0, Color = 1; i < NumColors; + i++) { + int Size; + if ((Size = Histogram[i] / Scaler) > ImageWidth) { + Size = ImageWidth; + } + for (j = 0; j < Size; j++) { + Line[j] = Color; + } + for (j = Size; j < ImageWidth; j++) { + Line[j] = GifFileOut->SBackGroundColor; + } + + /* Move to next color: */ + if (++Color >= (1 << HISTO_BITS_PER_PIXEL)) { + Color = 1; + } + + /* Dump this histogram entry as many times as required: + */ + for (j = 0; j < ImageHeight / NumColors; j++) { + if (EGifPutLine(GifFileOut, Line, ImageWidth) == + GIF_ERROR) { + QuitGifError(GifFileIn, GifFileOut); + } + GifQprintf("\b\b\b\b%-4d", Count--); + } + } + + if (EGifCloseFile(GifFileOut, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } } - } - return 0; + return 0; } /****************************************************************************** Close both input and output file (if open), and exit. ******************************************************************************/ -static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut) -{ - if (GifFileIn != NULL) { - PrintGifError(GifFileIn->Error); - EGifCloseFile(GifFileIn, NULL); - } - if (GifFileOut != NULL) { - PrintGifError(GifFileOut->Error); - EGifCloseFile(GifFileOut, NULL); - } - exit(EXIT_FAILURE); +static void QuitGifError(GifFileType *GifFileIn, GifFileType *GifFileOut) { + if (GifFileIn != NULL) { + PrintGifError(GifFileIn->Error); + EGifCloseFile(GifFileIn, NULL); + } + if (GifFileOut != NULL) { + PrintGifError(GifFileOut->Error); + EGifCloseFile(GifFileOut, NULL); + } + exit(EXIT_FAILURE); } /* end */ diff --git a/gifinto.c b/gifinto.c index 2b91714..07c85d3 100644 --- a/gifinto.c +++ b/gifinto.c @@ -2,14 +2,16 @@ gifinto - save GIF on stdin to file if size over set threshold +SPDX-License-Identifier: MIT + *****************************************************************************/ +#include #include +#include #include #include -#include #include -#include #ifdef _WIN32 #include @@ -17,51 +19,40 @@ gifinto - save GIF on stdin to file if size over set threshold #include #endif /* _WIN32 */ -#include "gif_lib.h" #include "getarg.h" +#include "gif_lib.h" -#define PROGRAM_NAME "gifinto" +#define PROGRAM_NAME "gifinto" -#define STRLEN 512 +#define STRLEN 512 -#define DEFAULT_MIN_FILE_SIZE 14 /* More than GIF stamp + screen desc. */ -#define DEFAULT_OUT_NAME "GifInto.Gif" -#define DEFAULT_TMP_NAME "TempInto.XXXXXX" +#define DEFAULT_MIN_FILE_SIZE 14 /* More than GIF stamp + screen desc. */ +#define DEFAULT_OUT_NAME "GifInto.Gif" +#define DEFAULT_TMP_NAME "TempInto.XXXXXX" -static char - *VersionStr = - PROGRAM_NAME - VERSION_COOKIE - " Gershon Elber, " - __DATE__ ", " __TIME__ "\n" - "(C) Copyright 1989 Gershon Elber.\n"; -static char - *CtrlStr = - PROGRAM_NAME - " v%- s%-MinFileSize!d h%- GifFile!*s"; +static char *VersionStr = PROGRAM_NAME VERSION_COOKIE + " Gershon Elber, " __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char *CtrlStr = PROGRAM_NAME " v%- s%-MinFileSize!d h%- GifFile!*s"; -static int - MinFileSize = DEFAULT_MIN_FILE_SIZE; +static int MinFileSize = DEFAULT_MIN_FILE_SIZE; #ifdef _WIN32 #include #include -int -mkstemp(char *tpl) -{ - int fd = -1; - char *p; - int e = errno; - - errno = 0; - p = _mktemp(tpl); - if (*p && errno == 0) - { - errno = e; - fd = _open(p, _O_RDWR | _O_CREAT | _O_EXCL | _O_BINARY, - _S_IREAD | _S_IWRITE); - } - return fd; +int mkstemp(char *tpl) { + int fd = -1; + char *p; + int e = errno; + + errno = 0; + p = _mktemp(tpl); + if (*p && errno == 0) { + errno = e; + fd = _open(p, _O_RDWR | _O_CREAT | _O_EXCL | _O_BINARY, + _S_IREAD | _S_IWRITE); + } + return fd; } #endif @@ -69,121 +60,138 @@ mkstemp(char *tpl) This is simply: read until EOF, then close the output, test its length, and if non zero then rename it. ******************************************************************************/ -int main(int argc, char **argv) -{ - int FD; - int NumFiles; - bool Error, MinSizeFlag = false, HelpFlag = false; - char **FileName = NULL, FoutTmpName[STRLEN+1], FullPath[STRLEN+1], *p; - FILE *Fin, *Fout; - - if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, - &MinSizeFlag, &MinFileSize, &HelpFlag, - &NumFiles, &FileName)) != false || - (NumFiles > 1 && !HelpFlag)) { - if (Error) - GAPrintErrMsg(Error); - else if (NumFiles != 1) - GIF_MESSAGE("Error in command line parsing - one GIF file please."); - GAPrintHowTo(CtrlStr); - exit(EXIT_FAILURE); - } - - if (HelpFlag) { - (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); - GAPrintHowTo(CtrlStr); - exit(EXIT_SUCCESS); - } - - /* Open the stdin in binary mode and increase its buffer size: */ +int main(int argc, char **argv) { + int FD; + int NumFiles; + bool Error, MinSizeFlag = false, HelpFlag = false, + GifNoisyPrint = false; + char **FileName = NULL, FoutTmpName[STRLEN + 1], FullPath[STRLEN + 1], + *p; + FILE *Fin, *Fout; + + if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, + &MinSizeFlag, &MinFileSize, &HelpFlag, &NumFiles, + &FileName)) != false || + (NumFiles > 1 && !HelpFlag)) { + if (Error) { + GAPrintErrMsg(Error); + } else if (NumFiles != 1) { + GIF_MESSAGE("Error in command line parsing - one GIF " + "file please."); + } + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); + } + + /* Open the stdin in binary mode and increase its buffer size: */ #ifdef _WIN32 - _setmode(0, O_BINARY); /* Make sure it is in binary mode. */ + _setmode(0, O_BINARY); /* Make sure it is in binary mode. */ #endif - Fin = fdopen(0, "rb"); /* Make it into a stream: */ - - if (Fin == NULL) - { - GIF_EXIT("Failed to open input."); - } - - /* Isolate the directory where our destination is, and set tmp file name */ - /* in the very same directory. This code is isecure because it creates */ - /* predictable names, but it's not worth the effort and risk to fix. */ - if ( *FileName == NULL ) GIF_EXIT("No valid Filename given."); - if ( strlen(*FileName) > STRLEN-1 ) GIF_EXIT("Filename too long."); - memset(FullPath, '\0', sizeof(FullPath)); - strncpy(FullPath, *FileName, STRLEN); - if ((p = strrchr(FullPath, '/')) != NULL || - (p = strrchr(FullPath, '\\')) != NULL) - p[1] = 0; - else if ((p = strrchr(FullPath, ':')) != NULL) - p[1] = 0; - else - FullPath[0] = 0; /* No directory or disk specified. */ - - if ( strlen(FullPath) > STRLEN-1 ) GIF_EXIT("Filename too long."); - strncpy(FoutTmpName, FullPath, STRLEN); /* First setup the Path */ - /* then add a name for the tempfile */ - if ( (strlen(FoutTmpName) + strlen(DEFAULT_TMP_NAME)) > STRLEN-1 ) GIF_EXIT("Filename too long."); - strcat(FoutTmpName, DEFAULT_TMP_NAME); + Fin = fdopen(0, "rb"); /* Make it into a stream: */ + + if (Fin == NULL) { + GIF_EXIT("Failed to open input."); + } + + /* Isolate the directory where our destination is, and set tmp file name + */ + /* in the very same directory. This code is isecure because it creates + */ + /* predictable names, but it's not worth the effort and risk to fix. */ + if (*FileName == NULL) { + GIF_EXIT("No valid Filename given."); + } + if (strlen(*FileName) > STRLEN - 1) { + GIF_EXIT("Filename too long."); + } + memset(FullPath, '\0', sizeof(FullPath)); + strncpy(FullPath, *FileName, STRLEN); + if ((p = strrchr(FullPath, '/')) != NULL || + (p = strrchr(FullPath, '\\')) != NULL) { + p[1] = 0; + } else if ((p = strrchr(FullPath, ':')) != NULL) { + p[1] = 0; + } else { + FullPath[0] = 0; /* No directory or disk specified. */ + } + if (strlen(FullPath) > STRLEN - 1) { + GIF_EXIT("Filename too long."); + } + strncpy(FoutTmpName, FullPath, STRLEN); /* First setup the Path */ + /* then add a name for the tempfile */ + if ((strlen(FoutTmpName) + strlen(DEFAULT_TMP_NAME)) > STRLEN - 1) { + GIF_EXIT("Filename too long."); + } + strcat(FoutTmpName, DEFAULT_TMP_NAME); #ifdef _WIN32 - char *tmpFN = _mktemp(FoutTmpName); - if (tmpFN) - FD = open(tmpFN, O_CREAT | O_EXCL | O_WRONLY); - else - FD = -1; + char *tmpFN = _mktemp(FoutTmpName); + if (tmpFN) { + FD = open(tmpFN, O_CREAT | O_EXCL | O_WRONLY); + } else { + FD = -1; + } #else - FD = mkstemp(FoutTmpName); /* returns filedescriptor */ + FD = mkstemp(FoutTmpName); /* returns filedescriptor */ #endif - if (FD == -1 ) - { - GIF_EXIT("Failed to open output."); - } - Fout = fdopen(FD, "wb"); /* returns a stream with FD */ - if (Fout == NULL ) - { - GIF_EXIT("Failed to open output."); - } - - while (1) { - int c = getc(Fin); - - if (feof(Fin)) - break; - if (putc(c, Fout) == EOF) - GIF_EXIT("Failed to write output."); - } - - fclose(Fin); - if (ftell(Fout) >= (long) MinFileSize) { - fclose(Fout); - unlink(*FileName); - if (rename(FoutTmpName, *FileName) != 0) { - char DefaultName[STRLEN+1]; - memset(DefaultName, '\0', sizeof(DefaultName)); - if ( (strlen(FullPath) + strlen(DEFAULT_OUT_NAME)) > STRLEN-1 ) GIF_EXIT("Filename too long."); - strncpy(DefaultName, FullPath, STRLEN); - strcat(DefaultName, DEFAULT_OUT_NAME); - if (rename(FoutTmpName, DefaultName) == 0) { - char s[STRLEN]; - snprintf(s, STRLEN, "Failed to rename out file - left as %s.", - DefaultName); - GIF_MESSAGE(s); - } - else { + if (FD == -1) { + GIF_EXIT("Failed to open output."); + } + Fout = fdopen(FD, "wb"); /* returns a stream with FD */ + if (Fout == NULL) { + GIF_EXIT("Failed to open output."); + } + + while (1) { + int c = getc(Fin); + + if (feof(Fin)) { + break; + } + if (putc(c, Fout) == EOF) { + GIF_EXIT("Failed to write output."); + } + } + + fclose(Fin); + if (ftell(Fout) >= (long)MinFileSize) { + fclose(Fout); + unlink(*FileName); + if (rename(FoutTmpName, *FileName) != 0) { + char DefaultName[STRLEN + 1]; + memset(DefaultName, '\0', sizeof(DefaultName)); + if ((strlen(FullPath) + strlen(DEFAULT_OUT_NAME)) > + STRLEN - 1) { + GIF_EXIT("Filename too long."); + } + strncpy(DefaultName, FullPath, STRLEN); + strcat(DefaultName, DEFAULT_OUT_NAME); + if (rename(FoutTmpName, DefaultName) == 0) { + char s[STRLEN]; + snprintf( + s, STRLEN, + "Failed to rename out file - left as %s.", + DefaultName); + GIF_MESSAGE(s); + } else { + unlink(FoutTmpName); + GIF_MESSAGE( + "Failed to rename out file - deleted."); + } + } + } else { + fclose(Fout); unlink(FoutTmpName); - GIF_MESSAGE("Failed to rename out file - deleted."); - } + GIF_MESSAGE("File too small - not renamed."); } - } - else { - fclose(Fout); - unlink(FoutTmpName); - GIF_MESSAGE("File too small - not renamed."); - } - - return 0; + + return 0; } /* end */ diff --git a/gifsponge.c b/gifsponge.c index 6e248d8..e018b9c 100644 --- a/gifsponge.c +++ b/gifsponge.c @@ -16,70 +16,75 @@ copy an image from a much more (or much *less*) memory-limited system; your compression may use more (or fewer) bits. The uncompressed rasters should, however, be identical (you can check this with gifbuild -d). +SPDX-License-Identifier: MIT + ****************************************************************************/ +#include #include #include #include -#include #include "getarg.h" #include "gif_lib.h" -#define PROGRAM_NAME "gifsponge" - -int main(int argc, char **argv) -{ - int i, ErrorCode; - GifFileType *GifFileIn, *GifFileOut = (GifFileType *)NULL; - - if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } - if (DGifSlurp(GifFileIn) == GIF_ERROR) { - PrintGifError(GifFileIn->Error); - exit(EXIT_FAILURE); - } - if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } - - /* - * Your operations on in-core structures go here. - * This code just copies the header and each image from the incoming file. - */ - GifFileOut->SWidth = GifFileIn->SWidth; - GifFileOut->SHeight = GifFileIn->SHeight; - GifFileOut->SColorResolution = GifFileIn->SColorResolution; - GifFileOut->SBackGroundColor = GifFileIn->SBackGroundColor; - if (GifFileIn->SColorMap) { - GifFileOut->SColorMap = GifMakeMapObject( - GifFileIn->SColorMap->ColorCount, - GifFileIn->SColorMap->Colors); - } else { - GifFileOut->SColorMap = NULL; - } - - for (i = 0; i < GifFileIn->ImageCount; i++) - (void) GifMakeSavedImage(GifFileOut, &GifFileIn->SavedImages[i]); - - /* - * Note: don't do DGifCloseFile early, as this will - * deallocate all the memory containing the GIF data! - * - * Further note: EGifSpew() doesn't try to validity-check any of this - * data; it's *your* responsibility to keep your changes consistent. - * Caveat hacker! - */ - if (EGifSpew(GifFileOut) == GIF_ERROR) - PrintGifError(GifFileOut->Error); - - if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) - PrintGifError(ErrorCode); - - return 0; +#define PROGRAM_NAME "gifsponge" + +int main(int argc, char **argv) { + int i, ErrorCode; + GifFileType *GifFileIn, *GifFileOut = (GifFileType *)NULL; + + if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + if (DGifSlurp(GifFileIn) == GIF_ERROR) { + PrintGifError(GifFileIn->Error); + exit(EXIT_FAILURE); + } + if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + /* + * Your operations on in-core structures go here. + * This code just copies the header and each image from the incoming + * file. + */ + GifFileOut->SWidth = GifFileIn->SWidth; + GifFileOut->SHeight = GifFileIn->SHeight; + GifFileOut->SColorResolution = GifFileIn->SColorResolution; + GifFileOut->SBackGroundColor = GifFileIn->SBackGroundColor; + if (GifFileIn->SColorMap) { + GifFileOut->SColorMap = + GifMakeMapObject(GifFileIn->SColorMap->ColorCount, + GifFileIn->SColorMap->Colors); + } else { + GifFileOut->SColorMap = NULL; + } + + for (i = 0; i < GifFileIn->ImageCount; i++) { + (void)GifMakeSavedImage(GifFileOut, &GifFileIn->SavedImages[i]); + } + + /* + * Note: don't do DGifCloseFile early, as this will + * deallocate all the memory containing the GIF data! + * + * Further note: EGifSpew() doesn't try to validity-check any of this + * data; it's *your* responsibility to keep your changes consistent. + * Caveat hacker! + */ + if (EGifSpew(GifFileOut) == GIF_ERROR) { + PrintGifError(GifFileOut->Error); + } + + if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + } + + return 0; } /* end */ diff --git a/giftext.c b/giftext.c index bb12c7c..f085ed6 100644 --- a/giftext.c +++ b/giftext.c @@ -2,38 +2,34 @@ giftext - dump GIF pixels and metadata as text +SPDX-License-Identifier: MIT + *****************************************************************************/ -#include -#include #include #include #include +#include +#include #ifdef _WIN32 #include #endif /* _WIN32 */ -#include "gif_lib.h" #include "getarg.h" +#include "gif_lib.h" -#define PROGRAM_NAME "giftext" +#define PROGRAM_NAME "giftext" -#define MAKE_PRINTABLE(c) (isprint(c) ? (c) : ' ') +#define MAKE_PRINTABLE(c) (isprint(c) ? (c) : ' ') -static char - *VersionStr = - PROGRAM_NAME - VERSION_COOKIE - " Gershon Elber, " - __DATE__ ", " __TIME__ "\n" - "(C) Copyright 1989 Gershon Elber.\n"; -static char - *CtrlStr = - PROGRAM_NAME - " v%- c%- e%- z%- p%- r%- h%- GifFile!*s"; +static char *VersionStr = PROGRAM_NAME VERSION_COOKIE + " Gershon Elber, " __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char *CtrlStr = PROGRAM_NAME " v%- c%- e%- z%- p%- r%- h%- GifFile!*s"; -static void PrintCodeBlock(GifFileType *GifFile, GifByteType *CodeBlock, bool Reset); +static void PrintCodeBlock(GifFileType *GifFile, GifByteType *CodeBlock, + bool Reset); static void PrintPixelBlock(GifByteType *PixelBlock, int Len, bool Reset); static void PrintExtBlock(GifByteType *Extension, bool Reset); static void PrintLZCodes(GifFileType *GifFile); @@ -41,268 +37,320 @@ static void PrintLZCodes(GifFileType *GifFile); /****************************************************************************** Interpret the command line and scan the given GIF file. ******************************************************************************/ -int main(int argc, char **argv) -{ - int i, j, ExtCode, ErrorCode, CodeSize, NumFiles, Len, ImageNum = 1; - bool Error, - ColorMapFlag = false, EncodedFlag = false, LZCodesFlag = false, - PixelFlag = false, HelpFlag = false, RawFlag = false; - char *GifFileName, **FileName = NULL; - GifPixelType *Line; - GifRecordType RecordType; - GifByteType *CodeBlock, *Extension; - GifFileType *GifFile; - - if ((Error = GAGetArgs(argc, argv, CtrlStr, - &GifNoisyPrint, &ColorMapFlag, &EncodedFlag, - &LZCodesFlag, &PixelFlag, &RawFlag, &HelpFlag, - &NumFiles, &FileName)) != false || - (NumFiles > 1 && !HelpFlag)) { - if (Error) - GAPrintErrMsg(Error); - else if (NumFiles > 1) - GIF_MESSAGE("Error in command line parsing - one GIF file please."); - GAPrintHowTo(CtrlStr); - exit(EXIT_FAILURE); - } - - if (HelpFlag) { - (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); - GAPrintHowTo(CtrlStr); - exit(EXIT_SUCCESS); - } - - if (NumFiles == 1) { - GifFileName = *FileName; - if ((GifFile = DGifOpenFileName(*FileName, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); +int main(int argc, char **argv) { + int i, j, ExtCode, ErrorCode, CodeSize, NumFiles, Len, ImageNum = 1; + bool Error, ColorMapFlag = false, EncodedFlag = false, + LZCodesFlag = false, PixelFlag = false, HelpFlag = false, + RawFlag = false, GifNoisyPrint; + char *GifFileName, **FileName = NULL; + GifPixelType *Line; + GifRecordType RecordType; + GifByteType *CodeBlock, *Extension; + GifFileType *GifFile; + + if ((Error = + GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &ColorMapFlag, + &EncodedFlag, &LZCodesFlag, &PixelFlag, &RawFlag, + &HelpFlag, &NumFiles, &FileName)) != false || + (NumFiles > 1 && !HelpFlag)) { + if (Error) { + GAPrintErrMsg(Error); + } else if (NumFiles > 1) { + GIF_MESSAGE("Error in command line parsing - one GIF " + "file please."); + } + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); } - } - else { - /* Use stdin instead: */ - GifFileName = "Stdin"; - if ((GifFile = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); } - } - /* Because we write binary data - make sure no text will be written. */ - if (RawFlag) { - ColorMapFlag = EncodedFlag = LZCodesFlag = PixelFlag = false; -#ifdef _WIN32 - _setmode(1, O_BINARY); /* Make sure it is in binary mode. */ -#endif /* _WIN32 */ - } - else { - printf("\n%s:\n\n\tScreen Size - Width = %d, Height = %d.\n", - GifFileName, GifFile->SWidth, GifFile->SHeight); - printf("\tColorResolution = %d, BitsPerPixel = %d, BackGround = %d, Aspect = %d.\n", - GifFile->SColorResolution, - GifFile->SColorMap ? GifFile->SColorMap->BitsPerPixel : 0, - GifFile->SBackGroundColor, - GifFile->AspectByte); - if (GifFile->SColorMap) - printf("\tHas Global Color Map.\n\n"); - else - printf("\tNo Global Color Map.\n\n"); - if (ColorMapFlag && GifFile->SColorMap) { - printf("\tGlobal Color Map:\n"); - Len = GifFile->SColorMap->ColorCount; - printf("\tSort Flag: %s\n", - GifFile->SColorMap->SortFlag ? "on":"off"); - for (i = 0; i < Len; i+=4) { - for (j = 0; j < 4 && j < Len; j++) { - printf("%3d: %02xh %02xh %02xh ", i + j, - GifFile->SColorMap->Colors[i + j].Red, - GifFile->SColorMap->Colors[i + j].Green, - GifFile->SColorMap->Colors[i + j].Blue); + if (NumFiles == 1) { + GifFileName = *FileName; + if ((GifFile = DGifOpenFileName(*FileName, &ErrorCode)) == + NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + } else { + /* Use stdin instead: */ + GifFileName = "Stdin"; + if ((GifFile = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); } - printf("\n"); - } } - } - do { - if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) { - PrintGifError(GifFile->Error); - exit(EXIT_FAILURE); - } - switch (RecordType) { - case IMAGE_DESC_RECORD_TYPE: - if (DGifGetImageDesc(GifFile) == GIF_ERROR) { - PrintGifError(GifFile->Error); - exit(EXIT_FAILURE); + /* Because we write binary data - make sure no text will be written. */ + if (RawFlag) { + ColorMapFlag = EncodedFlag = LZCodesFlag = PixelFlag = false; +#ifdef _WIN32 + _setmode(1, O_BINARY); /* Make sure it is in binary mode. */ +#endif /* _WIN32 */ + } else { + printf("\n%s:\n\n\tScreen Size - Width = %d, Height = %d.\n", + GifFileName, GifFile->SWidth, GifFile->SHeight); + printf("\tColorResolution = %d, BitsPerPixel = %d, BackGround " + "= %d, Aspect = %d.\n", + GifFile->SColorResolution, + GifFile->SColorMap ? GifFile->SColorMap->BitsPerPixel + : 0, + GifFile->SBackGroundColor, GifFile->AspectByte); + if (GifFile->SColorMap) { + printf("\tHas Global Color Map.\n\n"); + } else { + printf("\tNo Global Color Map.\n\n"); } - if (!RawFlag) { - printf("\nImage #%d:\n\n\tImage Size - Left = %d, Top = %d, Width = %d, Height = %d.\n", - ImageNum++, GifFile->Image.Left, GifFile->Image.Top, - GifFile->Image.Width, GifFile->Image.Height); - printf("\tImage is %s", - GifFile->Image.Interlace ? "Interlaced" : - "Non Interlaced"); - if (GifFile->Image.ColorMap != NULL) - printf(", BitsPerPixel = %d.\n", - GifFile->Image.ColorMap->BitsPerPixel); - else - printf(".\n"); - if (GifFile->Image.ColorMap) - printf("\tImage Has Color Map.\n"); - else - printf("\tNo Image Color Map.\n"); - if (ColorMapFlag && GifFile->Image.ColorMap) { - printf("\tSort Flag: %s\n", - GifFile->Image.ColorMap->SortFlag ? "on":"off"); - Len = 1 << GifFile->Image.ColorMap->BitsPerPixel; - for (i = 0; i < Len; i+=4) { - for (j = 0; j < 4 && j < Len; j++) { - printf("%3d: %02xh %02xh %02xh ", i + j, - GifFile->Image.ColorMap->Colors[i + j].Red, - GifFile->Image.ColorMap->Colors[i + j].Green, - GifFile->Image.ColorMap->Colors[i + j].Blue); - } - printf("\n"); + if (ColorMapFlag && GifFile->SColorMap) { + printf("\tGlobal Color Map:\n"); + Len = GifFile->SColorMap->ColorCount; + printf("\tSort Flag: %s\n", + GifFile->SColorMap->SortFlag ? "on" : "off"); + for (i = 0; i < Len; i += 4) { + for (j = 0; j < 4 && j < Len; j++) { + printf("%3d: %02xh %02xh %02xh ", + i + j, + GifFile->SColorMap->Colors[i + j] + .Red, + GifFile->SColorMap->Colors[i + j] + .Green, + GifFile->SColorMap->Colors[i + j] + .Blue); + } + printf("\n"); } - } } + } - if (EncodedFlag) { - if (DGifGetCode(GifFile, &CodeSize, &CodeBlock) == GIF_ERROR) { + do { + if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) { PrintGifError(GifFile->Error); exit(EXIT_FAILURE); - } - printf("\nImage LZ compressed Codes (Code Size = %d):\n", - CodeSize); - PrintCodeBlock(GifFile, CodeBlock, true); - while (CodeBlock != NULL) { - if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR) { - PrintGifError(GifFile->Error); - exit(EXIT_FAILURE); - } - PrintCodeBlock(GifFile, CodeBlock, false); - } - } - else if (LZCodesFlag) { - PrintLZCodes(GifFile); } - else if (PixelFlag) { - Line = (GifPixelType *) malloc(GifFile->Image.Width * - sizeof(GifPixelType)); - for (i = 0; i < GifFile->Image.Height; i++) { - if (DGifGetLine(GifFile, Line, GifFile->Image.Width) - == GIF_ERROR) { - PrintGifError(GifFile->Error); - exit(EXIT_FAILURE); + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFile) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); } - PrintPixelBlock(Line, GifFile->Image.Width, i == 0); - } - PrintPixelBlock(NULL, GifFile->Image.Width, false); - free((char *) Line); - } - else if (RawFlag) { - Line = (GifPixelType *) malloc(GifFile->Image.Width * - sizeof(GifPixelType)); - for (i = 0; i < GifFile->Image.Height; i++) { - if (DGifGetLine(GifFile, Line, GifFile->Image.Width) - == GIF_ERROR) { - PrintGifError(GifFile->Error); - exit(EXIT_FAILURE); - } - fwrite(Line, 1, GifFile->Image.Width, stdout); - } - free((char *) Line); - } - else { - /* Skip the image: */ - if (DGifGetCode(GifFile, &CodeSize, &CodeBlock) == GIF_ERROR) { - PrintGifError(GifFile->Error); - exit(EXIT_FAILURE); - } - while (CodeBlock != NULL) { - if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR) { - PrintGifError(GifFile->Error); - exit(EXIT_FAILURE); + if (!RawFlag) { + printf( + "\nImage #%d:\n\n\tImage Size - Left = %d, " + "Top = %d, Width = %d, Height = %d.\n", + ImageNum++, GifFile->Image.Left, + GifFile->Image.Top, GifFile->Image.Width, + GifFile->Image.Height); + printf("\tImage is %s", GifFile->Image.Interlace + ? "Interlaced" + : "Non Interlaced"); + if (GifFile->Image.ColorMap != NULL) { + printf(", BitsPerPixel = %d.\n", + GifFile->Image.ColorMap + ->BitsPerPixel); + } else { + printf(".\n"); + } + if (GifFile->Image.ColorMap) { + printf("\tImage Has Color Map.\n"); + } else { + printf("\tNo Image Color Map.\n"); + } + if (ColorMapFlag && GifFile->Image.ColorMap) { + printf("\tSort Flag: %s\n", + GifFile->Image.ColorMap->SortFlag + ? "on" + : "off"); + Len = 1 << GifFile->Image.ColorMap + ->BitsPerPixel; + for (i = 0; i < Len; i += 4) { + for (j = 0; j < 4 && j < Len; + j++) { + printf( + "%3d: %02xh %02xh " + "%02xh ", + i + j, + GifFile->Image + .ColorMap + ->Colors[i + j] + .Red, + GifFile->Image + .ColorMap + ->Colors[i + j] + .Green, + GifFile->Image + .ColorMap + ->Colors[i + j] + .Blue); + } + printf("\n"); + } + } } - } - } - break; - case EXTENSION_RECORD_TYPE: - if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) { - PrintGifError(GifFile->Error); - exit(EXIT_FAILURE); - } - if (!RawFlag) { - putchar('\n'); - switch (ExtCode) - { - case COMMENT_EXT_FUNC_CODE: - printf("GIF89 comment"); - break; - case GRAPHICS_EXT_FUNC_CODE: - printf("GIF89 graphics control"); - break; - case PLAINTEXT_EXT_FUNC_CODE: - printf("GIF89 plaintext"); - break; - case APPLICATION_EXT_FUNC_CODE: - printf("GIF89 application block"); - break; - default: - printf("Extension record of unknown type"); + if (EncodedFlag) { + if (DGifGetCode(GifFile, &CodeSize, + &CodeBlock) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + printf("\nImage LZ compressed Codes (Code Size " + "= %d):\n", + CodeSize); + PrintCodeBlock(GifFile, CodeBlock, true); + while (CodeBlock != NULL) { + if (DGifGetCodeNext(GifFile, + &CodeBlock) == + GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + PrintCodeBlock(GifFile, CodeBlock, + false); + } + } else if (LZCodesFlag) { + PrintLZCodes(GifFile); + } else if (PixelFlag) { + Line = (GifPixelType *)malloc( + GifFile->Image.Width * + sizeof(GifPixelType)); + for (i = 0; i < GifFile->Image.Height; i++) { + if (DGifGetLine(GifFile, Line, + GifFile->Image.Width) == + GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + PrintPixelBlock( + Line, GifFile->Image.Width, i == 0); + } + PrintPixelBlock(NULL, GifFile->Image.Width, + false); + free((char *)Line); + } else if (RawFlag) { + Line = (GifPixelType *)malloc( + GifFile->Image.Width * + sizeof(GifPixelType)); + for (i = 0; i < GifFile->Image.Height; i++) { + if (DGifGetLine(GifFile, Line, + GifFile->Image.Width) == + GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + fwrite(Line, 1, GifFile->Image.Width, + stdout); + } + free((char *)Line); + } else { + /* Skip the image: */ + if (DGifGetCode(GifFile, &CodeSize, + &CodeBlock) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + while (CodeBlock != NULL) { + if (DGifGetCodeNext(GifFile, + &CodeBlock) == + GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + } + } break; - } - printf(" (Ext Code = %d [%c]):\n", - ExtCode, MAKE_PRINTABLE(ExtCode)); - PrintExtBlock(Extension, true); - - if (ExtCode == GRAPHICS_EXT_FUNC_CODE) { - GraphicsControlBlock gcb; - if (Extension == NULL) { - printf("Invalid extension block\n"); - GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; - PrintGifError(GifFile->Error); - exit(EXIT_FAILURE); + case EXTENSION_RECORD_TYPE: + if (DGifGetExtension(GifFile, &ExtCode, &Extension) == + GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); } - if (DGifExtensionToGCB(Extension[0], Extension+1, &gcb) == GIF_ERROR) { - PrintGifError(GifFile->Error); - exit(EXIT_FAILURE); + if (!RawFlag) { + putchar('\n'); + switch (ExtCode) { + case COMMENT_EXT_FUNC_CODE: + printf("GIF89 comment"); + break; + case GRAPHICS_EXT_FUNC_CODE: + printf("GIF89 graphics control"); + break; + case PLAINTEXT_EXT_FUNC_CODE: + printf("GIF89 plaintext"); + break; + case APPLICATION_EXT_FUNC_CODE: + printf("GIF89 application block"); + break; + default: + printf( + "Extension record of unknown type"); + break; + } + printf(" (Ext Code = %d [%c]):\n", ExtCode, + MAKE_PRINTABLE(ExtCode)); + PrintExtBlock(Extension, true); + + if (ExtCode == GRAPHICS_EXT_FUNC_CODE) { + GraphicsControlBlock gcb; + if (Extension == NULL) { + printf("Invalid extension " + "block\n"); + GifFile->Error = + D_GIF_ERR_IMAGE_DEFECT; + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + if (DGifExtensionToGCB( + Extension[0], Extension + 1, + &gcb) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + printf("\tDisposal Mode: %d\n", + gcb.DisposalMode); + printf("\tUser Input Flag: %d\n", + gcb.UserInputFlag); + printf("\tTransparency on: %s\n", + gcb.TransparentColor != -1 + ? "yes" + : "no"); + printf("\tDelayTime: %d\n", + gcb.DelayTime); + printf("\tTransparent Index: %d\n", + gcb.TransparentColor); + } } - printf("\tDisposal Mode: %d\n", gcb.DisposalMode); - printf("\tUser Input Flag: %d\n", gcb.UserInputFlag); - printf("\tTransparency on: %s\n", - gcb.TransparentColor != -1 ? "yes" : "no"); - printf("\tDelayTime: %d\n", gcb.DelayTime); - printf("\tTransparent Index: %d\n", gcb.TransparentColor); - } - } - for (;;) { - if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) { - PrintGifError(GifFile->Error); - exit(EXIT_FAILURE); - } - if (Extension == NULL) + for (;;) { + if (DGifGetExtensionNext(GifFile, &Extension) == + GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + if (Extension == NULL) { + break; + } + PrintExtBlock(Extension, false); + } + break; + case TERMINATE_RECORD_TYPE: + break; + default: /* Should be trapped by DGifGetRecordType */ break; - PrintExtBlock(Extension, false); } - break; - case TERMINATE_RECORD_TYPE: - break; - default: /* Should be trapped by DGifGetRecordType */ - break; - } - } - while (RecordType != TERMINATE_RECORD_TYPE); + } while (RecordType != TERMINATE_RECORD_TYPE); - if (DGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } + if (DGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } - if (!RawFlag) printf("\nGIF file terminated normally.\n"); + if (!RawFlag) { + printf("\nGIF file terminated normally.\n"); + } - return 0; + return 0; } /****************************************************************************** @@ -310,46 +358,56 @@ int main(int argc, char **argv) place). Save local information so printing can be performed continuously, or reset to start state if Reset. If CodeBlock is NULL, output is flushed ******************************************************************************/ -static void PrintCodeBlock(GifFileType *GifFile, GifByteType *CodeBlock, bool Reset) -{ - static int CrntPlace = 0; - static long CodeCount = 0; - int i, Len; - - if (Reset || CodeBlock == NULL) { - if (CodeBlock == NULL) { - long NumBytes = 0; - if (CrntPlace > 0) { - printf("\n"); - CodeCount += CrntPlace - 16; - } - if (GifFile->Image.ColorMap) - NumBytes = ((((long) GifFile->Image.Width) * GifFile->Image.Height) - * GifFile->Image.ColorMap->BitsPerPixel) / 8; - else if (GifFile->SColorMap != NULL) - NumBytes = ((((long) GifFile->Image.Width) * GifFile->Image.Height) - * GifFile->SColorMap->BitsPerPixel) / 8; - /* FIXME: What should the compression ratio be if no color table? */ - if (NumBytes > 0) { - int Percent = 100 * CodeCount / NumBytes; - printf("\nCompression ratio: %ld/%ld (%d%%).\n", - CodeCount, NumBytes, Percent); - } - return; +static void PrintCodeBlock(GifFileType *GifFile, GifByteType *CodeBlock, + bool Reset) { + static int CrntPlace = 0; + static long CodeCount = 0; + int i, Len; + + if (Reset || CodeBlock == NULL) { + if (CodeBlock == NULL) { + long NumBytes = 0; + if (CrntPlace > 0) { + printf("\n"); + CodeCount += CrntPlace - 16; + } + if (GifFile->Image.ColorMap) { + NumBytes = + ((((long)GifFile->Image.Width) * + GifFile->Image.Height) * + GifFile->Image.ColorMap->BitsPerPixel) / + 8; + } else if (GifFile->SColorMap != NULL) { + NumBytes = ((((long)GifFile->Image.Width) * + GifFile->Image.Height) * + GifFile->SColorMap->BitsPerPixel) / + 8; + } + /* FIXME: What should the compression ratio be if no + * color table? */ + if (NumBytes > 0) { + int Percent = 100 * CodeCount / NumBytes; + printf("\nCompression ratio: %ld/%ld (%d%%).\n", + CodeCount, NumBytes, Percent); + } + return; + } + CrntPlace = 0; + CodeCount = 0; } - CrntPlace = 0; - CodeCount = 0; - } - - Len = CodeBlock[0]; - for (i = 1; i <= Len; i++) { - if (CrntPlace == 0) { - printf("\n%05lxh: ", CodeCount); - CodeCount += 16; + + // cppcheck-suppress nullPointerRedundantCheck + Len = CodeBlock[0]; + for (i = 1; i <= Len; i++) { + if (CrntPlace == 0) { + printf("\n%05lxh: ", CodeCount); + CodeCount += 16; + } + (void)printf(" %02xh", CodeBlock[i]); + if (++CrntPlace >= 16) { + CrntPlace = 0; + } } - (void)printf(" %02xh", CodeBlock[i]); - if (++CrntPlace >= 16) CrntPlace = 0; - } } /****************************************************************************** @@ -357,44 +415,45 @@ static void PrintCodeBlock(GifFileType *GifFile, GifByteType *CodeBlock, bool Re place). Save local information so printing can be performed continuously, or reset to start state if Reset. If Extension is NULL, output is flushed ******************************************************************************/ -static void PrintExtBlock(GifByteType *Extension, bool Reset) -{ - static int CrntPlace = 0; - static long ExtCount = 0; - static char HexForm[49], AsciiForm[17]; - - if (Reset || Extension == NULL) { - if (Extension == NULL) { - if (CrntPlace > 0) { - HexForm[CrntPlace * 3] = 0; - AsciiForm[CrntPlace] = 0; - printf("\n%05lx: %-49s %-17s\n", ExtCount, HexForm, AsciiForm); - return; - } - else - printf("\n"); +static void PrintExtBlock(GifByteType *Extension, bool Reset) { + static int CrntPlace = 0; + static long ExtCount = 0; + static char HexForm[49], AsciiForm[17]; + + if (Reset || Extension == NULL) { + if (Extension == NULL) { + if (CrntPlace > 0) { + HexForm[CrntPlace * 3] = 0; + AsciiForm[CrntPlace] = 0; + printf("\n%05lx: %-49s %-17s\n", ExtCount, + HexForm, AsciiForm); + return; + } else { + printf("\n"); + } + } + CrntPlace = 0; + ExtCount = 0; } - CrntPlace = 0; - ExtCount = 0; - } - if (Extension != NULL) { - int i, Len; - Len = Extension[0]; - for (i = 1; i <= Len; i++) { - (void)snprintf(&HexForm[CrntPlace * 3], 3, - " %02x", Extension[i]); - (void)snprintf(&AsciiForm[CrntPlace], 3, - "%c", MAKE_PRINTABLE(Extension[i])); - if (++CrntPlace == 16) { - HexForm[CrntPlace * 3] = 0; - AsciiForm[CrntPlace] = 0; - printf("\n%05lx: %-49s %-17s", ExtCount, HexForm, AsciiForm); - ExtCount += 16; - CrntPlace = 0; - } + if (Extension != NULL) { + int i, Len; + Len = Extension[0]; + for (i = 1; i <= Len; i++) { + (void)snprintf(&HexForm[CrntPlace * 3], 3, " %02x", + Extension[i]); + (void)snprintf(&AsciiForm[CrntPlace], 3, "%c", + MAKE_PRINTABLE(Extension[i])); + if (++CrntPlace == 16) { + HexForm[CrntPlace * 3] = 0; + AsciiForm[CrntPlace] = 0; + printf("\n%05lx: %-49s %-17s", ExtCount, + HexForm, AsciiForm); + ExtCount += 16; + CrntPlace = 0; + } + } } - } } /****************************************************************************** @@ -402,63 +461,69 @@ static void PrintExtBlock(GifByteType *Extension, bool Reset) Save local information so printing can be performed continuously, or reset to start state if Reset. If PixelBlock is NULL, output is flushed ******************************************************************************/ -static void PrintPixelBlock(GifByteType *PixelBlock, int Len, bool Reset) -{ - static int CrntPlace = 0; - static long ExtCount = 0; - static char HexForm[49], AsciiForm[17]; - int i; - - if (Reset || PixelBlock == NULL) { - if (PixelBlock == NULL) { - if (CrntPlace > 0) { - HexForm[CrntPlace * 3] = 0; - AsciiForm[CrntPlace] = 0; - printf("\n%05lx: %-49s %-17s\n", ExtCount, HexForm, AsciiForm); - } - else - printf("\n"); +static void PrintPixelBlock(GifByteType *PixelBlock, int Len, bool Reset) { + static int CrntPlace = 0; + static long ExtCount = 0; + static char HexForm[49], AsciiForm[17]; + int i; + + if (Reset || PixelBlock == NULL) { + if (PixelBlock == NULL) { + if (CrntPlace > 0) { + HexForm[CrntPlace * 3] = 0; + AsciiForm[CrntPlace] = 0; + printf("\n%05lx: %-49s %-17s\n", ExtCount, + HexForm, AsciiForm); + } else { + printf("\n"); + } + } + CrntPlace = 0; + ExtCount = 0; + if (PixelBlock == NULL) { + return; + } } - CrntPlace = 0; - ExtCount = 0; - if (PixelBlock == NULL) return; - } - - for (i = 0; i < Len; i++) { - (void)snprintf(&HexForm[CrntPlace * 3], 3, - " %02x", PixelBlock[i]); - (void)snprintf(&AsciiForm[CrntPlace], 3, - "%c", MAKE_PRINTABLE(PixelBlock[i])); - if (++CrntPlace == 16) { - HexForm[CrntPlace * 3] = 0; - AsciiForm[CrntPlace] = 0; - printf("\n%05lx: %-49s %-17s", ExtCount, HexForm, AsciiForm); - ExtCount += 16; - CrntPlace = 0; + + for (i = 0; i < Len; i++) { + (void)snprintf(&HexForm[CrntPlace * 3], 3, " %02x", + PixelBlock[i]); + (void)snprintf(&AsciiForm[CrntPlace], 3, "%c", + MAKE_PRINTABLE(PixelBlock[i])); + if (++CrntPlace == 16) { + HexForm[CrntPlace * 3] = 0; + AsciiForm[CrntPlace] = 0; + printf("\n%05lx: %-49s %-17s", ExtCount, HexForm, + AsciiForm); + ExtCount += 16; + CrntPlace = 0; + } } - } } /****************************************************************************** Print the image as LZ codes (each 12bits), until EOF marker is reached. ******************************************************************************/ -static void PrintLZCodes(GifFileType *GifFile) -{ - int Code, CrntPlace = 0; - long CodeCount = 0; - - do { - if (CrntPlace == 0) printf("\n%05lx:", CodeCount); - if (DGifGetLZCodes(GifFile, &Code) == GIF_ERROR) { - PrintGifError(GifFile->Error); - exit(EXIT_FAILURE); - } - if (Code >= 0) - printf(" %03x", Code); /* EOF Code is returned as -1. */ - CodeCount++; - if (++CrntPlace >= 16) CrntPlace = 0; - } - while (Code >= 0); +static void PrintLZCodes(GifFileType *GifFile) { + int Code, CrntPlace = 0; + long CodeCount = 0; + + do { + if (CrntPlace == 0) { + printf("\n%05lx:", CodeCount); + } + if (DGifGetLZCodes(GifFile, &Code) == GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + if (Code >= 0) { + printf(" %03x", Code); /* EOF Code is returned as -1. */ + } + CodeCount++; + if (++CrntPlace >= 16) { + CrntPlace = 0; + } + } while (Code >= 0); } /* end */ diff --git a/giftool.c b/giftool.c index 21b9c48..fc068ce 100644 --- a/giftool.c +++ b/giftool.c @@ -2,576 +2,649 @@ giftool.c - GIF transformation tool. +SPDX-License-Identifier: MIT + ****************************************************************************/ +#include +#include #include #include #include -#include -#include -#include "getopt.h" #include "getarg.h" +#include "getopt.h" #include "gif_lib.h" -#define PROGRAM_NAME "giftool" +#define PROGRAM_NAME "giftool" -#define MAX_OPERATIONS 256 -#define MAX_IMAGES 2048 +#define MAX_OPERATIONS 256 +#define MAX_IMAGES 2048 -enum boolmode {numeric, onoff, tf, yesno}; +enum boolmode { numeric, onoff, tf, yesno }; -char *putbool(bool flag, enum boolmode mode) -{ - if (flag) - switch (mode) { - case numeric: return "1"; break; - case onoff: return "on"; break; - case tf: return "true"; break; - case yesno: return "yes"; break; - } - else - switch (mode) { - case numeric: return "0"; break; - case onoff: return "off"; break; - case tf: return "false"; break; - case yesno: return "no"; break; +char *putbool(bool flag, enum boolmode mode) { + if (flag) { + switch (mode) { + case numeric: + return "1"; + break; + case onoff: + return "on"; + break; + case tf: + return "true"; + break; + case yesno: + return "yes"; + break; + } + } else { + switch (mode) { + case numeric: + return "0"; + break; + case onoff: + return "off"; + break; + case tf: + return "false"; + break; + case yesno: + return "no"; + break; + } } - return "FAIL"; /* should never happen */ + return "FAIL"; /* should never happen */ } -bool getbool(char *from) -{ - struct valmap {char *name; bool val;} - boolnames[] = { - {"yes", true}, - {"on", true}, - {"1", true}, - {"t", true}, - {"no", false}, - {"off", false}, - {"0", false}, - {"f", false}, - {NULL, false}, - }, *sp; - - for (sp = boolnames; sp->name; sp++) - if (strcmp(sp->name, from) == 0) - return sp->val; - - (void)fprintf(stderr, - "giftool: %s is not a valid boolean argument.\n", - sp->name); - exit(EXIT_FAILURE); +bool getbool(char *from) { + struct valmap { + char *name; + bool val; + } boolnames[] = + { + {"yes", true}, {"on", true}, {"1", true}, + {"t", true}, {"no", false}, {"off", false}, + {"0", false}, {"f", false}, {NULL, false}, + }, + *sp; + + // cppcheck-suppress nullPointerRedundantCheck + for (sp = boolnames; sp->name; sp++) { + if (strcmp(sp->name, from) == 0) { + return sp->val; + } + } + + if (sp == NULL) { + (void)fprintf(stderr, + "giftool: %s is not a valid boolean argument.\n", + // cppcheck-suppress nullPointerRedundantCheck + sp->name); + } + exit(EXIT_FAILURE); } struct operation { - enum { - aspect, - delaytime, - background, - info, - interlace, - position, - screensize, - transparent, - userinput, - disposal, - } mode; - union { - GifByteType numerator; - int delay; - int color; - int dispose; - char *format; - bool flag; - struct { - int x, y; - } p; - }; + enum { + aspect, + delaytime, + background, + info, + interlace, + position, + screensize, + transparent, + userinput, + disposal, + } mode; + union { + GifByteType numerator; + int delay; + int color; + int dispose; + char *format; + bool flag; + struct { + int x, y; + } p; + }; }; -int main(int argc, char **argv) -{ - extern char *optarg; /* set by getopt */ - extern int optind; /* set by getopt */ - struct operation operations[MAX_OPERATIONS]; - struct operation *top = operations; - int selected[MAX_IMAGES], nselected = 0; - bool have_selection = false; - char *cp; - int i, status, ErrorCode; - GifFileType *GifFileIn, *GifFileOut = (GifFileType *)NULL; - struct operation *op; - - /* - * Gather operations from the command line. We use regular - * getopt(3) here rather than Gershom's argument getter because - * preserving the order of operations is important. - */ - while ((status = getopt(argc, argv, "a:b:d:f:i:n:p:s:u:x:")) != EOF) - { - if (top >= operations + MAX_OPERATIONS) { - (void)fprintf(stderr, "giftool: too many operations."); - exit(EXIT_FAILURE); - } +int main(int argc, char **argv) { + extern char *optarg; /* set by getopt */ + extern int optind; /* set by getopt */ + struct operation operations[MAX_OPERATIONS]; + struct operation *top = operations; + int selected[MAX_IMAGES], nselected = 0; + bool have_selection = false; + char *cp; + int i, status, ErrorCode; + GifFileType *GifFileIn, *GifFileOut = (GifFileType *)NULL; + struct operation *op; + + /* + * Gather operations from the command line. We use regular + * getopt(3) here rather than Gershom's argument getter because + * preserving the order of operations is important. + */ + while ((status = getopt(argc, argv, "a:b:d:f:i:n:p:s:u:x:")) != EOF) { + if (top >= operations + MAX_OPERATIONS) { + (void)fprintf(stderr, "giftool: too many operations."); + exit(EXIT_FAILURE); + } - switch (status) - { - case 'a': - top->mode = aspect; - top->numerator = (GifByteType)atoi(optarg); - break; - - case 'b': - top->mode = background; - top->color = atoi(optarg); - break; - - case 'd': - top->mode = delaytime; - top->delay = atoi(optarg); - break; - - case 'f': - top->mode = info; - top->format = optarg; - break; - - case 'i': - top->mode = interlace; - top->flag = getbool(optarg); - break; - - case 'n': - have_selection = true; - nselected = 0; - cp = optarg; - for (;;) - { - size_t span = strspn(cp, "0123456789"); + switch (status) { + case 'a': + top->mode = aspect; + top->numerator = (GifByteType)atoi(optarg); + break; + + case 'b': + top->mode = background; + top->color = atoi(optarg); + break; + + case 'd': + top->mode = delaytime; + top->delay = atoi(optarg); + break; + + case 'f': + top->mode = info; + top->format = optarg; + break; + + case 'i': + top->mode = interlace; + top->flag = getbool(optarg); + break; + + case 'n': + have_selection = true; + nselected = 0; + cp = optarg; + for (;;) { + size_t span = strspn(cp, "0123456789"); + + if (span > 0) { + selected[nselected++] = atoi(cp) - 1; + cp += span; + if (*cp == '\0') { + break; + } else if (*cp == ',') { + continue; + } + } + + (void)fprintf(stderr, + "giftool: bad selection.\n"); + exit(EXIT_FAILURE); + } + break; + + case 'p': + case 's': + if (status == 'p') { + top->mode = position; + } else { + top->mode = screensize; + } + cp = strchr(optarg, ','); + if (cp == NULL) { + (void)fprintf(stderr, "giftool: missing comma " + "in coordinate pair.\n"); + exit(EXIT_FAILURE); + } + top->p.x = atoi(optarg); + top->p.y = atoi(cp + 1); + if (top->p.x < 0 || top->p.y < 0) { + (void)fprintf( + stderr, "giftool: negative coordinate.\n"); + exit(EXIT_FAILURE); + } + break; + + case 'u': + top->mode = userinput; + top->flag = getbool(optarg); + break; + + case 'x': + top->mode = disposal; + top->dispose = atoi(optarg); + break; - if (span > 0) - { - selected[nselected++] = atoi(cp)-1; - cp += span; - if (*cp == '\0') + default: + fprintf(stderr, + "usage: giftool [-b color] [-d delay] [-iI] " + "[-t color] -[uU] [-x disposal]\n"); break; - else if (*cp == ',') - continue; } - (void) fprintf(stderr, "giftool: bad selection.\n"); + ++top; + } + + /* read in a GIF */ + if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); exit(EXIT_FAILURE); - } - break; - - case 'p': - case 's': - if (status == 'p') - top->mode = position; - else - top->mode = screensize; - cp = strchr(optarg, ','); - if (cp == NULL) - { - (void) fprintf(stderr, "giftool: missing comma in coordinate pair.\n"); + } + if (DGifSlurp(GifFileIn) == GIF_ERROR) { + PrintGifError(GifFileIn->Error); exit(EXIT_FAILURE); - } - top->p.x = atoi(optarg); - top->p.y = atoi(cp+1); - if (top->p.x < 0 || top->p.y < 0) - { - (void) fprintf(stderr, "giftool: negative coordinate.\n"); + } + if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); exit(EXIT_FAILURE); - } - break; - - case 'u': - top->mode = userinput; - top->flag = getbool(optarg); - break; - - case 'x': - top->mode = disposal; - top->dispose = atoi(optarg); - break; - - default: - fprintf(stderr, "usage: giftool [-b color] [-d delay] [-iI] [-t color] -[uU] [-x disposal]\n"); - break; } - ++top; - } + /* if the selection is defaulted, compute it; otherwise bounds-check it + */ + if (!have_selection) { + for (i = nselected = 0; i < GifFileIn->ImageCount; i++) { + selected[nselected++] = i; + } + } else { + for (i = 0; i < nselected; i++) { + if (selected[i] >= GifFileIn->ImageCount || + selected[i] < 0) { + (void)fprintf(stderr, "giftool: selection " + "index out of bounds.\n"); + exit(EXIT_FAILURE); + } + } + } - /* read in a GIF */ - if ((GifFileIn = DGifOpenFileHandle(0, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } - if (DGifSlurp(GifFileIn) == GIF_ERROR) { - PrintGifError(GifFileIn->Error); - exit(EXIT_FAILURE); - } - if ((GifFileOut = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } - - /* if the selection is defaulted, compute it; otherwise bounds-check it */ - if (!have_selection) - for (i = nselected = 0; i < GifFileIn->ImageCount; i++) - selected[nselected++] = i; - else - for (i = 0; i < nselected; i++) - if (selected[i] >= GifFileIn->ImageCount || selected[i] < 0) - { - (void) fprintf(stderr, - "giftool: selection index out of bounds.\n"); - exit(EXIT_FAILURE); - } - - /* perform the operations we've gathered */ - for (op = operations; op < top; op++) - switch (op->mode) - { - case background: - GifFileIn->SBackGroundColor = op->color; - break; - - case delaytime: - for (i = 0; i < nselected; i++) - { - GraphicsControlBlock gcb; - - DGifSavedExtensionToGCB(GifFileIn, selected[i], &gcb); - gcb.DelayTime = op->delay; - EGifGCBToSavedExtension(&gcb, GifFileIn, selected[i]); - } - break; - - case info: - for (i = 0; i < nselected; i++) { - SavedImage *ip = &GifFileIn->SavedImages[selected[i]]; - GraphicsControlBlock gcb; - for (cp = op->format; *cp; cp++) { - if (*cp == '\\') - { - char c; - switch (*++cp) - { - case 'b': - (void)putchar('\b'); - break; - case 'e': - (void)putchar(0x1b); - break; - case 'f': - (void)putchar('\f'); - break; - case 'n': - (void)putchar('\n'); - break; - case 'r': - (void)putchar('\r'); - break; - case 't': - (void)putchar('\t'); - break; - case 'v': - (void)putchar('\v'); - break; - case 'x': - switch (*++cp) { - case '0': - c = (char)0x00; - break; - case '1': - c = (char)0x10; - break; - case '2': - c = (char)0x20; - break; - case '3': - c = (char)0x30; - break; - case '4': - c = (char)0x40; - break; - case '5': - c = (char)0x50; - break; - case '6': - c = (char)0x60; - break; - case '7': - c = (char)0x70; - break; - case '8': - c = (char)0x80; - break; - case '9': - c = (char)0x90; - break; - case 'A': - case 'a': - c = (char)0xa0; - break; - case 'B': - case 'b': - c = (char)0xb0; - break; - case 'C': - case 'c': - c = (char)0xc0; - break; - case 'D': - case 'd': - c = (char)0xd0; - break; - case 'E': - case 'e': - c = (char)0xe0; - break; - case 'F': - case 'f': - c = (char)0xf0; - break; - default: - return -1; - } - switch (*++cp) { - case '0': - c += 0x00; - break; - case '1': - c += 0x01; - break; - case '2': - c += 0x02; - break; - case '3': - c += 0x03; - break; - case '4': - c += 0x04; - break; - case '5': - c += 0x05; - break; - case '6': - c += 0x06; - break; - case '7': - c += 0x07; - break; - case '8': - c += 0x08; - break; - case '9': - c += 0x09; - break; - case 'A': - case 'a': - c += 0x0a; - break; - case 'B': - case 'b': - c += 0x0b; - break; - case 'C': - case 'c': - c += 0x0c; - break; - case 'D': - case 'd': - c += 0x0d; - break; - case 'E': - case 'e': - c += 0x0e; - break; - case 'F': - case 'f': - c += 0x0f; - break; - default: - return -2; - } - putchar(c); - break; - default: - putchar(*cp); - break; + /* perform the operations we've gathered */ + for (op = operations; op < top; op++) { + switch (op->mode) { + case background: + GifFileIn->SBackGroundColor = op->color; + break; + + case delaytime: + for (i = 0; i < nselected; i++) { + GraphicsControlBlock gcb; + + DGifSavedExtensionToGCB(GifFileIn, selected[i], + &gcb); + gcb.DelayTime = op->delay; + EGifGCBToSavedExtension(&gcb, GifFileIn, + selected[i]); } - } - else if (*cp == '%') - { - enum boolmode boolfmt; - SavedImage *sp = &GifFileIn->SavedImages[i]; - - if (cp[1] == 't') { - boolfmt = tf; - ++cp; - } else if (cp[1] == 'o') { - boolfmt = onoff; - ++cp; - } else if (cp[1] == 'y') { - boolfmt = yesno; - ++cp; - } else if (cp[1] == '1') { - boolfmt = numeric; - ++cp; - } else - boolfmt = numeric; - - switch (*++cp) - { - case '%': - putchar('%'); - break; - case 'a': - (void)printf("%d", GifFileIn->AspectByte); - break; - case 'b': - (void)printf("%d", GifFileIn->SBackGroundColor); - break; - case 'd': - DGifSavedExtensionToGCB(GifFileIn, - selected[i], - &gcb); - (void)printf("%d", gcb.DelayTime); - break; - case 'h': - (void)printf("%d", ip->ImageDesc.Height); - break; - case 'n': - (void)printf("%d", selected[i]+1); - break; - case 'p': - (void)printf("%d,%d", - ip->ImageDesc.Left, ip->ImageDesc.Top); - break; - case 's': - (void)printf("%d,%d", - GifFileIn->SWidth, - GifFileIn->SHeight); - break; - case 'w': - (void)printf("%d", ip->ImageDesc.Width); - break; - case 't': - DGifSavedExtensionToGCB(GifFileIn, - selected[i], - &gcb); - (void)printf("%d", gcb.TransparentColor); - break; - case 'u': - DGifSavedExtensionToGCB(GifFileIn, - selected[i], - &gcb); - (void)printf("%s", putbool(gcb.UserInputFlag, boolfmt)); - break; - case 'v': - fputs(EGifGetGifVersion(GifFileIn), stdout); - break; - case 'x': - DGifSavedExtensionToGCB(GifFileIn, - selected[i], - &gcb); - (void)printf("%d", gcb.DisposalMode); - break; - case 'z': - (void) printf("%s", putbool(sp->ImageDesc.ColorMap && sp->ImageDesc.ColorMap->SortFlag, boolfmt)); - break; - default: - (void)fprintf(stderr, - "giftool: bad format %%%c\n", *cp); + break; + + case info: + for (i = 0; i < nselected; i++) { + SavedImage *ip = + &GifFileIn->SavedImages[selected[i]]; + GraphicsControlBlock gcb; + for (cp = op->format; *cp; cp++) { + if (*cp == '\\') { + char c; + switch (*++cp) { + case 'b': + (void)putchar('\b'); + break; + case 'e': + (void)putchar(0x1b); + break; + case 'f': + (void)putchar('\f'); + break; + case 'n': + (void)putchar('\n'); + break; + case 'r': + (void)putchar('\r'); + break; + case 't': + (void)putchar('\t'); + break; + case 'v': + (void)putchar('\v'); + break; + case 'x': + switch (*++cp) { + case '0': + c = (char)0x00; + break; + case '1': + c = (char)0x10; + break; + case '2': + c = (char)0x20; + break; + case '3': + c = (char)0x30; + break; + case '4': + c = (char)0x40; + break; + case '5': + c = (char)0x50; + break; + case '6': + c = (char)0x60; + break; + case '7': + c = (char)0x70; + break; + case '8': + c = (char)0x80; + break; + case '9': + c = (char)0x90; + break; + case 'A': + case 'a': + c = (char)0xa0; + break; + case 'B': + case 'b': + c = (char)0xb0; + break; + case 'C': + case 'c': + c = (char)0xc0; + break; + case 'D': + case 'd': + c = (char)0xd0; + break; + case 'E': + case 'e': + c = (char)0xe0; + break; + case 'F': + case 'f': + c = (char)0xf0; + break; + default: + return -1; + } + switch (*++cp) { + case '0': + c += 0x00; + break; + case '1': + c += 0x01; + break; + case '2': + c += 0x02; + break; + case '3': + c += 0x03; + break; + case '4': + c += 0x04; + break; + case '5': + c += 0x05; + break; + case '6': + c += 0x06; + break; + case '7': + c += 0x07; + break; + case '8': + c += 0x08; + break; + case '9': + c += 0x09; + break; + case 'A': + case 'a': + c += 0x0a; + break; + case 'B': + case 'b': + c += 0x0b; + break; + case 'C': + case 'c': + c += 0x0c; + break; + case 'D': + case 'd': + c += 0x0d; + break; + case 'E': + case 'e': + c += 0x0e; + break; + case 'F': + case 'f': + c += 0x0f; + break; + default: + return -2; + } + putchar(c); + break; + default: + putchar(*cp); + break; + } + } else if (*cp == '%') { + enum boolmode boolfmt; + SavedImage *sp = + &GifFileIn->SavedImages[i]; + + if (cp[1] == 't') { + boolfmt = tf; + ++cp; + } else if (cp[1] == 'o') { + boolfmt = onoff; + ++cp; + } else if (cp[1] == 'y') { + boolfmt = yesno; + ++cp; + } else if (cp[1] == '1') { + boolfmt = numeric; + ++cp; + } else { + boolfmt = numeric; + } + + switch (*++cp) { + case '%': + putchar('%'); + break; + case 'a': + (void)printf( + "%d", + GifFileIn + ->AspectByte); + break; + case 'b': + (void)printf( + "%d", + GifFileIn + ->SBackGroundColor); + break; + case 'd': + DGifSavedExtensionToGCB( + GifFileIn, + selected[i], &gcb); + (void)printf( + "%d", + gcb.DelayTime); + break; + case 'h': + (void)printf( + "%d", ip->ImageDesc + .Height); + break; + case 'n': + (void)printf( + "%d", + selected[i] + 1); + break; + case 'p': + (void)printf( + "%d,%d", + ip->ImageDesc.Left, + ip->ImageDesc.Top); + break; + case 's': + (void)printf( + "%d,%d", + GifFileIn->SWidth, + GifFileIn->SHeight); + break; + case 'w': + (void)printf( + "%d", ip->ImageDesc + .Width); + break; + case 't': + DGifSavedExtensionToGCB( + GifFileIn, + selected[i], &gcb); + (void)printf( + "%d", + gcb.TransparentColor); + break; + case 'u': + DGifSavedExtensionToGCB( + GifFileIn, + selected[i], &gcb); + (void)printf( + "%s", + putbool( + gcb.UserInputFlag, + boolfmt)); + break; + case 'v': + fputs(EGifGetGifVersion( + GifFileIn), + stdout); + break; + case 'x': + DGifSavedExtensionToGCB( + GifFileIn, + selected[i], &gcb); + (void)printf( + "%d", + gcb.DisposalMode); + break; + case 'z': + (void)printf( + "%s", + putbool( + sp->ImageDesc + .ColorMap && + sp->ImageDesc + .ColorMap + ->SortFlag, + boolfmt)); + break; + default: + (void)fprintf( + stderr, + "giftool: bad " + "format %%%c\n", + *cp); + } + } else { + (void)putchar(*cp); + } + } } - } - else - (void)putchar(*cp); - } - } - exit(EXIT_SUCCESS); - break; - - case interlace: - for (i = 0; i < nselected; i++) - GifFileIn->SavedImages[selected[i]].ImageDesc.Interlace = op->flag; - break; - - case position: - for (i = 0; i < nselected; i++) { - GifFileIn->SavedImages[selected[i]].ImageDesc.Left = op->p.x; - GifFileIn->SavedImages[selected[i]].ImageDesc.Top = op->p.y; - } - break; - - case screensize: - GifFileIn->SWidth = op->p.x; - GifFileIn->SHeight = op->p.y; - break; - - case transparent: - for (i = 0; i < nselected; i++) - { - GraphicsControlBlock gcb; + exit(EXIT_SUCCESS); + break; - DGifSavedExtensionToGCB(GifFileIn, selected[i], &gcb); - gcb.TransparentColor = op->color; - EGifGCBToSavedExtension(&gcb, GifFileIn, selected[i]); - } - break; + case interlace: + for (i = 0; i < nselected; i++) { + GifFileIn->SavedImages[selected[i]] + .ImageDesc.Interlace = op->flag; + } + break; - case userinput: - for (i = 0; i < nselected; i++) - { - GraphicsControlBlock gcb; + case position: + for (i = 0; i < nselected; i++) { + GifFileIn->SavedImages[selected[i]] + .ImageDesc.Left = op->p.x; + GifFileIn->SavedImages[selected[i]] + .ImageDesc.Top = op->p.y; + } + break; + + case screensize: + GifFileIn->SWidth = op->p.x; + GifFileIn->SHeight = op->p.y; + break; - DGifSavedExtensionToGCB(GifFileIn, selected[i], &gcb); - gcb.UserInputFlag = op->flag; - EGifGCBToSavedExtension(&gcb, GifFileIn, selected[i]); - } - break; + case transparent: + for (i = 0; i < nselected; i++) { + GraphicsControlBlock gcb; - case disposal: - for (i = 0; i < nselected; i++) - { - GraphicsControlBlock gcb; + DGifSavedExtensionToGCB(GifFileIn, selected[i], + &gcb); + gcb.TransparentColor = op->color; + EGifGCBToSavedExtension(&gcb, GifFileIn, + selected[i]); + } + break; + + case userinput: + for (i = 0; i < nselected; i++) { + GraphicsControlBlock gcb; + + DGifSavedExtensionToGCB(GifFileIn, selected[i], + &gcb); + gcb.UserInputFlag = op->flag; + EGifGCBToSavedExtension(&gcb, GifFileIn, + selected[i]); + } + break; + + case disposal: + for (i = 0; i < nselected; i++) { + GraphicsControlBlock gcb; + + DGifSavedExtensionToGCB(GifFileIn, selected[i], + &gcb); + gcb.DisposalMode = op->dispose; + EGifGCBToSavedExtension(&gcb, GifFileIn, + selected[i]); + } + break; - DGifSavedExtensionToGCB(GifFileIn, selected[i], &gcb); - gcb.DisposalMode = op->dispose; - EGifGCBToSavedExtension(&gcb, GifFileIn, selected[i]); - } - break; + default: + (void)fprintf(stderr, + "giftool: unknown operation mode\n"); + exit(EXIT_FAILURE); + } + } + + /* write out the results */ + GifFileOut->SWidth = GifFileIn->SWidth; + GifFileOut->SHeight = GifFileIn->SHeight; + GifFileOut->SColorResolution = GifFileIn->SColorResolution; + GifFileOut->SBackGroundColor = GifFileIn->SBackGroundColor; + if (GifFileIn->SColorMap != NULL) { + GifFileOut->SColorMap = + GifMakeMapObject(GifFileIn->SColorMap->ColorCount, + GifFileIn->SColorMap->Colors); + } + + for (i = 0; i < GifFileIn->ImageCount; i++) { + (void)GifMakeSavedImage(GifFileOut, &GifFileIn->SavedImages[i]); + } - default: - (void)fprintf(stderr, "giftool: unknown operation mode\n"); - exit(EXIT_FAILURE); + if (EGifSpew(GifFileOut) == GIF_ERROR) { + PrintGifError(GifFileOut->Error); + } else if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); } - /* write out the results */ - GifFileOut->SWidth = GifFileIn->SWidth; - GifFileOut->SHeight = GifFileIn->SHeight; - GifFileOut->SColorResolution = GifFileIn->SColorResolution; - GifFileOut->SBackGroundColor = GifFileIn->SBackGroundColor; - if (GifFileIn->SColorMap != NULL) - GifFileOut->SColorMap = GifMakeMapObject( - GifFileIn->SColorMap->ColorCount, - GifFileIn->SColorMap->Colors); - - for (i = 0; i < GifFileIn->ImageCount; i++) - (void) GifMakeSavedImage(GifFileOut, &GifFileIn->SavedImages[i]); - - if (EGifSpew(GifFileOut) == GIF_ERROR) - PrintGifError(GifFileOut->Error); - else if (DGifCloseFile(GifFileIn, &ErrorCode) == GIF_ERROR) - PrintGifError(ErrorCode); - - return 0; + return 0; } /* end */ diff --git a/gifwedge.c b/gifwedge.c index a8f587f..203d5da 100644 --- a/gifwedge.c +++ b/gifwedge.c @@ -2,140 +2,152 @@ gifwedge - create a GIF test pattern +SPDX-License-Identifier: MIT + *****************************************************************************/ -#include -#include #include -#include #include +#include +#include +#include -#include "gif_lib.h" #include "getarg.h" +#include "gif_lib.h" -#define PROGRAM_NAME "gifwedge" +#define PROGRAM_NAME "gifwedge" -#define DEFAULT_WIDTH 640 -#define DEFAULT_HEIGHT 350 +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 350 -#define DEFAULT_NUM_LEVELS 16 /* Number of colors to gen the image. */ +#define DEFAULT_NUM_LEVELS 16 /* Number of colors to gen the image. */ -static char - *VersionStr = - PROGRAM_NAME - VERSION_COOKIE - " Gershon Elber, " - __DATE__ ", " __TIME__ "\n" - "(C) Copyright 1989 Gershon Elber.\n"; -static char - *CtrlStr = - PROGRAM_NAME - " v%- l%-#Lvls!d s%-Width|Height!d!d h%-"; +static char *VersionStr = PROGRAM_NAME VERSION_COOKIE + " Gershon Elber, " __DATE__ ", " __TIME__ "\n" + "(C) Copyright 1989 Gershon Elber.\n"; +static char *CtrlStr = PROGRAM_NAME " v%- l%-#Lvls!d s%-Width|Height!d!d h%-"; -static int - NumLevels = DEFAULT_NUM_LEVELS, - ImageWidth = DEFAULT_WIDTH, - ImageHeight = DEFAULT_HEIGHT; +static int NumLevels = DEFAULT_NUM_LEVELS, ImageWidth = DEFAULT_WIDTH, + ImageHeight = DEFAULT_HEIGHT; /****************************************************************************** Interpret the command line and scan the given GIF file. ******************************************************************************/ -int main(int argc, char **argv) -{ - int i, j, l, c, LevelStep, LogNumLevels, ErrorCode, Count = 0; - bool Error, LevelsFlag = false, SizeFlag = false, HelpFlag = false; - GifRowType Line; - ColorMapObject *ColorMap; - GifFileType *GifFile; - - if ((Error = GAGetArgs(argc, argv, CtrlStr, - &GifNoisyPrint, &LevelsFlag, &NumLevels, - &SizeFlag, &ImageWidth, &ImageHeight, - &HelpFlag)) != false) { - GAPrintErrMsg(Error); - GAPrintHowTo(CtrlStr); - exit(EXIT_FAILURE); - } - - if (HelpFlag) { - (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); - GAPrintHowTo(CtrlStr); - exit(EXIT_SUCCESS); - } - - /* Make sure the number of levels is power of 2 (up to 32 levels.). */ - for (i = 1; i < 6; i++) if (NumLevels == (1 << i)) break; - if (i == 6) GIF_EXIT("#Lvls (-l option) is not power of 2 up to 32."); - LogNumLevels = i + 3; /* Multiple by 8 (see below). */ - LevelStep = 256 / NumLevels; - - /* Make sure the image dimension is a multiple of NumLevels horizontally */ - /* and 7 (White, Red, Green, Blue and Yellow Cyan Magenta) vertically. */ - ImageWidth = (ImageWidth / NumLevels) * NumLevels; - ImageHeight = (ImageHeight / 7) * 7; - - /* Open stdout for the output file: */ - if ((GifFile = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } - - /* Dump out screen description with given size and generated color map: */ - /* The color map has 7 NumLevels colors for White, Red, Green and then */ - /* The secondary colors Yellow Cyan and magenta. */ - if ((ColorMap = GifMakeMapObject(8 * NumLevels, NULL)) == NULL) - GIF_EXIT("Failed to allocate memory required, aborted."); - - for (i = 0; i < 8; i++) /* Set color map. */ - for (j = 0; j < NumLevels; j++) { - l = LevelStep * j; - c = i * NumLevels + j; - ColorMap->Colors[c].Red = (i == 0 || i == 1 || i == 4 || i == 6) * l; - ColorMap->Colors[c].Green = (i == 0 || i == 2 || i == 4 || i == 5) * l; - ColorMap->Colors[c].Blue = (i == 0 || i == 3 || i == 5 || i == 6) * l; +int main(int argc, char **argv) { + int i, j, l, c, LevelStep, LogNumLevels, ErrorCode, Count = 0; + bool Error, LevelsFlag = false, SizeFlag = false, HelpFlag = false, + GifNoisyPrint = false; + GifRowType Line; + ColorMapObject *ColorMap; + GifFileType *GifFile; + + if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint, &LevelsFlag, + &NumLevels, &SizeFlag, &ImageWidth, &ImageHeight, + &HelpFlag)) != false) { + GAPrintErrMsg(Error); + GAPrintHowTo(CtrlStr); + exit(EXIT_FAILURE); + } + + if (HelpFlag) { + (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR); + GAPrintHowTo(CtrlStr); + exit(EXIT_SUCCESS); + } + + /* Make sure the number of levels is power of 2 (up to 32 levels.). */ + for (i = 1; i < 6; i++) { + if (NumLevels == (1 << i)) { + break; + } + } + if (i == 6) { + GIF_EXIT("#Lvls (-l option) is not power of 2 up to 32."); + } + LogNumLevels = i + 3; /* Multiple by 8 (see below). */ + LevelStep = 256 / NumLevels; + + /* Make sure the image dimension is a multiple of NumLevels horizontally + */ + /* and 7 (White, Red, Green, Blue and Yellow Cyan Magenta) vertically. + */ + ImageWidth = (ImageWidth / NumLevels) * NumLevels; + ImageHeight = (ImageHeight / 7) * 7; + + /* Open stdout for the output file: */ + if ((GifFile = EGifOpenFileHandle(1, &ErrorCode)) == NULL) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } + + /* Dump out screen description with given size and generated color map: + */ + /* The color map has 7 NumLevels colors for White, Red, Green and then + */ + /* The secondary colors Yellow Cyan and magenta. */ + if ((ColorMap = GifMakeMapObject(8 * NumLevels, NULL)) == NULL) { + GIF_EXIT("Failed to allocate memory required, aborted."); + } + + for (i = 0; i < 8; i++) { /* Set color map. */ + for (j = 0; j < NumLevels; j++) { + l = LevelStep * j; + c = i * NumLevels + j; + ColorMap->Colors[c].Red = + (i == 0 || i == 1 || i == 4 || i == 6) * l; + ColorMap->Colors[c].Green = + (i == 0 || i == 2 || i == 4 || i == 5) * l; + ColorMap->Colors[c].Blue = + (i == 0 || i == 3 || i == 5 || i == 6) * l; + } + } + + if (EGifPutScreenDesc(GifFile, ImageWidth, ImageHeight, LogNumLevels, 0, + ColorMap) == GIF_ERROR) { + PrintGifError(GifFile->Error); } - if (EGifPutScreenDesc(GifFile, ImageWidth, ImageHeight, LogNumLevels, 0, ColorMap) == GIF_ERROR) { - PrintGifError(GifFile->Error); - } - - /* Dump out the image descriptor: */ - if (EGifPutImageDesc(GifFile, - 0, 0, ImageWidth, ImageHeight, - false, NULL) == GIF_ERROR) { - - PrintGifError(GifFile->Error); - exit(EXIT_FAILURE); - } - - GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]: ", - PROGRAM_NAME, GifFile->Image.Left, GifFile->Image.Top, - GifFile->Image.Width, GifFile->Image.Height); - - /* Allocate one scan line to be used for all image. */ - if ((Line = (GifRowType) malloc(sizeof(GifPixelType) * ImageWidth)) == NULL) - GIF_EXIT("Failed to allocate memory required, aborted."); - - /* Dump the pixels: */ - for (c = 0; c < 7; c++) { - for (i = 0, l = 0; i < NumLevels; i++) - for (j = 0; j < ImageWidth / NumLevels; j++) - Line[l++] = i + NumLevels * c; - for (i = 0; i < ImageHeight / 7; i++) { - if (EGifPutLine(GifFile, Line, ImageWidth) == GIF_ERROR) { + /* Dump out the image descriptor: */ + if (EGifPutImageDesc(GifFile, 0, 0, ImageWidth, ImageHeight, false, + NULL) == GIF_ERROR) { + PrintGifError(GifFile->Error); exit(EXIT_FAILURE); - } - GifQprintf("\b\b\b\b%-4d", Count++); } - } - if (EGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) { - PrintGifError(ErrorCode); - exit(EXIT_FAILURE); - } + GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]: ", PROGRAM_NAME, + GifFile->Image.Left, GifFile->Image.Top, + GifFile->Image.Width, GifFile->Image.Height); + + /* Allocate one scan line to be used for all image. */ + if ((Line = (GifRowType)malloc(sizeof(GifPixelType) * ImageWidth)) == + NULL) { + GIF_EXIT("Failed to allocate memory required, aborted."); + } + + /* Dump the pixels: */ + for (c = 0; c < 7; c++) { + for (i = 0, l = 0; i < NumLevels; i++) { + for (j = 0; j < ImageWidth / NumLevels; j++) { + Line[l++] = i + NumLevels * c; + } + } + for (i = 0; i < ImageHeight / 7; i++) { + if (EGifPutLine(GifFile, Line, ImageWidth) == + GIF_ERROR) { + PrintGifError(GifFile->Error); + exit(EXIT_FAILURE); + } + GifQprintf("\b\b\b\b%-4d", Count++); + } + } + + if (EGifCloseFile(GifFile, &ErrorCode) == GIF_ERROR) { + PrintGifError(ErrorCode); + exit(EXIT_FAILURE); + } - return 0; + return 0; } /* end */ diff --git a/openbsd-reallocarray.c b/openbsd-reallocarray.c index 47e80ef..5f5367c 100644 --- a/openbsd-reallocarray.c +++ b/openbsd-reallocarray.c @@ -1,38 +1,24 @@ -/* $OpenBSD: reallocarray.c,v 1.1 2014/05/08 21:43:49 deraadt Exp $ */ /* * Copyright (c) 2008 Otto Moerbeek - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * SPDX-License-Identifier: MIT */ -#include #include #include #include +#include #ifndef SIZE_MAX - #define SIZE_MAX UINTPTR_MAX +#define SIZE_MAX UINTPTR_MAX #endif /* * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW */ -#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) -void * -openbsd_reallocarray(void *optr, size_t nmemb, size_t size) -{ +void *openbsd_reallocarray(void *optr, size_t nmemb, size_t size) { if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && nmemb > 0 && SIZE_MAX / nmemb < size) { errno = ENOMEM; @@ -80,7 +66,8 @@ openbsd_reallocarray(void *optr, size_t nmemb, size_t size) * fuzzing on one platform may not detect zero-size allocation * problems on other platforms. */ - if (size == 0 || nmemb == 0) - return NULL; + if (size == 0 || nmemb == 0) { + return NULL; + } return realloc(optr, size * nmemb); } diff --git a/qprintf.c b/qprintf.c index 3f12781..77c0876 100644 --- a/qprintf.c +++ b/qprintf.c @@ -3,13 +3,14 @@ qprintf.c - module to emulate a printf with a possible quiet (disable mode.) A global variable GifNoisyPrint controls the printing of this routine - -*****************************************************************************/ +SPDX-License-Identifier: MIT + +*****************************************************************************/ -#include -#include #include +#include +#include #include "gif_lib.h" @@ -18,29 +19,28 @@ bool GifNoisyPrint = false; /***************************************************************************** Same as fprintf to stderr but with optional print. ******************************************************************************/ -void -GifQprintf(char *Format, ...) { - va_list ArgPtr; +void GifQprintf(char *Format, ...) { + va_list ArgPtr; - va_start(ArgPtr, Format); + va_start(ArgPtr, Format); - if (GifNoisyPrint) { - char Line[128]; - (void)vsnprintf(Line, sizeof(Line), Format, ArgPtr); - (void)fputs(Line, stderr); - } + if (GifNoisyPrint) { + char Line[128]; + (void)vsnprintf(Line, sizeof(Line), Format, ArgPtr); + (void)fputs(Line, stderr); + } - va_end(ArgPtr); + va_end(ArgPtr); } -void -PrintGifError(int ErrorCode) { - const char *Err = GifErrorString(ErrorCode); +void PrintGifError(int ErrorCode) { + const char *Err = GifErrorString(ErrorCode); - if (Err != NULL) - fprintf(stderr, "GIF-LIB error: %s.\n", Err); - else - fprintf(stderr, "GIF-LIB undefined error %d.\n", ErrorCode); + if (Err != NULL) { + fprintf(stderr, "GIF-LIB error: %s.\n", Err); + } else { + fprintf(stderr, "GIF-LIB undefined error %d.\n", ErrorCode); + } } /* end */ diff --git a/quantize.c b/quantize.c index b6de90d..1194876 100644 --- a/quantize.c +++ b/quantize.c @@ -9,36 +9,40 @@ and was removed in 4.2. Then it turned out some client apps were actually using it, so it was restored in 5.0. +SPDX-License-Identifier: MIT + ******************************************************************************/ -#include #include +#include + #include "gif_lib.h" #include "gif_lib_private.h" -#define ABS(x) ((x) > 0 ? (x) : (-(x))) +#define ABS(x) ((x) > 0 ? (x) : (-(x))) #define COLOR_ARRAY_SIZE 32768 #define BITS_PER_PRIM_COLOR 5 -#define MAX_PRIM_COLOR 0x1f +#define MAX_PRIM_COLOR 0x1f static int SortRGBAxis; typedef struct QuantizedColorType { - GifByteType RGB[3]; - GifByteType NewColorIndex; - long Count; - struct QuantizedColorType *Pnext; + GifByteType RGB[3]; + GifByteType NewColorIndex; + long Count; + struct QuantizedColorType *Pnext; } QuantizedColorType; typedef struct NewColorMapType { - GifByteType RGBMin[3], RGBWidth[3]; - unsigned int NumEntries; /* # of QuantizedColorType in linked list below */ - unsigned long Count; /* Total number of pixels in all the entries */ - QuantizedColorType *QuantizedColors; + GifByteType RGBMin[3], RGBWidth[3]; + unsigned int + NumEntries; /* # of QuantizedColorType in linked list below */ + unsigned long Count; /* Total number of pixels in all the entries */ + QuantizedColorType *QuantizedColors; } NewColorMapType; -static int SubdivColorMap(NewColorMapType * NewColorSubdiv, +static int SubdivColorMap(NewColorMapType *NewColorSubdiv, unsigned int ColorMapSize, unsigned int *NewColorMapSize); static int SortCmpRtn(const void *Entry1, const void *Entry2); @@ -55,136 +59,150 @@ static int SortCmpRtn(const void *Entry1, const void *Entry2); Also non of the parameter are allocated by this routine. This function returns GIF_OK if successful, GIF_ERROR otherwise. ******************************************************************************/ -int -GifQuantizeBuffer(unsigned int Width, - unsigned int Height, - int *ColorMapSize, - GifByteType * RedInput, - GifByteType * GreenInput, - GifByteType * BlueInput, - GifByteType * OutputBuffer, - GifColorType * OutputColorMap) { - - unsigned int Index, NumOfEntries; - int i, j, MaxRGBError[3]; - unsigned int NewColorMapSize; - long Red, Green, Blue; - NewColorMapType NewColorSubdiv[256]; - QuantizedColorType *ColorArrayEntries, *QuantizedColor; - - ColorArrayEntries = (QuantizedColorType *)malloc( - sizeof(QuantizedColorType) * COLOR_ARRAY_SIZE); - if (ColorArrayEntries == NULL) { - return GIF_ERROR; - } - - for (i = 0; i < COLOR_ARRAY_SIZE; i++) { - ColorArrayEntries[i].RGB[0] = i >> (2 * BITS_PER_PRIM_COLOR); - ColorArrayEntries[i].RGB[1] = (i >> BITS_PER_PRIM_COLOR) & - MAX_PRIM_COLOR; - ColorArrayEntries[i].RGB[2] = i & MAX_PRIM_COLOR; - ColorArrayEntries[i].Count = 0; - } - - /* Sample the colors and their distribution: */ - for (i = 0; i < (int)(Width * Height); i++) { - Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) << - (2 * BITS_PER_PRIM_COLOR)) + - ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) << - BITS_PER_PRIM_COLOR) + - (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR)); - ColorArrayEntries[Index].Count++; - } - - /* Put all the colors in the first entry of the color map, and call the - * recursive subdivision process. */ - for (i = 0; i < 256; i++) { - NewColorSubdiv[i].QuantizedColors = NULL; - NewColorSubdiv[i].Count = NewColorSubdiv[i].NumEntries = 0; - for (j = 0; j < 3; j++) { - NewColorSubdiv[i].RGBMin[j] = 0; - NewColorSubdiv[i].RGBWidth[j] = 255; - } - } - - /* Find the non empty entries in the color table and chain them: */ - for (i = 0; i < COLOR_ARRAY_SIZE; i++) - if (ColorArrayEntries[i].Count > 0) - break; - QuantizedColor = NewColorSubdiv[0].QuantizedColors = &ColorArrayEntries[i]; - NumOfEntries = 1; - while (++i < COLOR_ARRAY_SIZE) - if (ColorArrayEntries[i].Count > 0) { - QuantizedColor->Pnext = &ColorArrayEntries[i]; - QuantizedColor = &ColorArrayEntries[i]; - NumOfEntries++; - } - QuantizedColor->Pnext = NULL; - - NewColorSubdiv[0].NumEntries = NumOfEntries; /* Different sampled colors */ - NewColorSubdiv[0].Count = ((long)Width) * Height; /* Pixels */ - NewColorMapSize = 1; - if (SubdivColorMap(NewColorSubdiv, *ColorMapSize, &NewColorMapSize) != - GIF_OK) { - free((char *)ColorArrayEntries); - return GIF_ERROR; - } - if (NewColorMapSize < *ColorMapSize) { - /* And clear rest of color map: */ - for (i = NewColorMapSize; i < *ColorMapSize; i++) - OutputColorMap[i].Red = OutputColorMap[i].Green = - OutputColorMap[i].Blue = 0; - } - - /* Average the colors in each entry to be the color to be used in the - * output color map, and plug it into the output color map itself. */ - for (i = 0; i < NewColorMapSize; i++) { - if ((j = NewColorSubdiv[i].NumEntries) > 0) { - QuantizedColor = NewColorSubdiv[i].QuantizedColors; - Red = Green = Blue = 0; - while (QuantizedColor) { - QuantizedColor->NewColorIndex = i; - Red += QuantizedColor->RGB[0]; - Green += QuantizedColor->RGB[1]; - Blue += QuantizedColor->RGB[2]; - QuantizedColor = QuantizedColor->Pnext; - } - OutputColorMap[i].Red = (Red << (8 - BITS_PER_PRIM_COLOR)) / j; - OutputColorMap[i].Green = (Green << (8 - BITS_PER_PRIM_COLOR)) / j; - OutputColorMap[i].Blue = (Blue << (8 - BITS_PER_PRIM_COLOR)) / j; - } - } - - /* Finally scan the input buffer again and put the mapped index in the - * output buffer. */ - MaxRGBError[0] = MaxRGBError[1] = MaxRGBError[2] = 0; - for (i = 0; i < (int)(Width * Height); i++) { - Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) << - (2 * BITS_PER_PRIM_COLOR)) + - ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) << - BITS_PER_PRIM_COLOR) + - (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR)); - Index = ColorArrayEntries[Index].NewColorIndex; - OutputBuffer[i] = Index; - if (MaxRGBError[0] < ABS(OutputColorMap[Index].Red - RedInput[i])) - MaxRGBError[0] = ABS(OutputColorMap[Index].Red - RedInput[i]); - if (MaxRGBError[1] < ABS(OutputColorMap[Index].Green - GreenInput[i])) - MaxRGBError[1] = ABS(OutputColorMap[Index].Green - GreenInput[i]); - if (MaxRGBError[2] < ABS(OutputColorMap[Index].Blue - BlueInput[i])) - MaxRGBError[2] = ABS(OutputColorMap[Index].Blue - BlueInput[i]); - } +int GifQuantizeBuffer(unsigned int Width, unsigned int Height, + int *ColorMapSize, const GifByteType *RedInput, + const GifByteType *GreenInput, + const GifByteType *BlueInput, GifByteType *OutputBuffer, + GifColorType *OutputColorMap) { + + unsigned int Index, NumOfEntries; + int i, j, MaxRGBError[3]; + unsigned int NewColorMapSize; + long Red, Green, Blue; + NewColorMapType NewColorSubdiv[256]; + QuantizedColorType *ColorArrayEntries, *QuantizedColor; + + ColorArrayEntries = (QuantizedColorType *)malloc( + sizeof(QuantizedColorType) * COLOR_ARRAY_SIZE); + if (ColorArrayEntries == NULL) { + return GIF_ERROR; + } + + for (i = 0; i < COLOR_ARRAY_SIZE; i++) { + ColorArrayEntries[i].RGB[0] = i >> (2 * BITS_PER_PRIM_COLOR); + ColorArrayEntries[i].RGB[1] = + (i >> BITS_PER_PRIM_COLOR) & MAX_PRIM_COLOR; + ColorArrayEntries[i].RGB[2] = i & MAX_PRIM_COLOR; + ColorArrayEntries[i].Count = 0; + } + + /* Sample the colors and their distribution: */ + for (i = 0; i < (int)(Width * Height); i++) { + Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) + << (2 * BITS_PER_PRIM_COLOR)) + + ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) + << BITS_PER_PRIM_COLOR) + + (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR)); + ColorArrayEntries[Index].Count++; + } + + /* Put all the colors in the first entry of the color map, and call the + * recursive subdivision process. */ + for (i = 0; i < 256; i++) { + NewColorSubdiv[i].QuantizedColors = NULL; + NewColorSubdiv[i].Count = NewColorSubdiv[i].NumEntries = 0; + for (j = 0; j < 3; j++) { + NewColorSubdiv[i].RGBMin[j] = 0; + NewColorSubdiv[i].RGBWidth[j] = 255; + } + } + + /* Find the non empty entries in the color table and chain them: */ + for (i = 0; i < COLOR_ARRAY_SIZE; i++) { + if (ColorArrayEntries[i].Count > 0) { + break; + } + } + QuantizedColor = NewColorSubdiv[0].QuantizedColors = + &ColorArrayEntries[i]; + NumOfEntries = 1; + while (++i < COLOR_ARRAY_SIZE) { + if (ColorArrayEntries[i].Count > 0) { + QuantizedColor->Pnext = &ColorArrayEntries[i]; + QuantizedColor = &ColorArrayEntries[i]; + NumOfEntries++; + } + } + QuantizedColor->Pnext = NULL; + + NewColorSubdiv[0].NumEntries = + NumOfEntries; /* Different sampled colors */ + NewColorSubdiv[0].Count = ((long)Width) * Height; /* Pixels */ + NewColorMapSize = 1; + if (SubdivColorMap(NewColorSubdiv, *ColorMapSize, &NewColorMapSize) != + GIF_OK) { + free((char *)ColorArrayEntries); + return GIF_ERROR; + } + if (NewColorMapSize < *ColorMapSize) { + /* And clear rest of color map: */ + for (i = NewColorMapSize; i < *ColorMapSize; i++) { + OutputColorMap[i].Red = OutputColorMap[i].Green = + OutputColorMap[i].Blue = 0; + } + } + + /* Average the colors in each entry to be the color to be used in the + * output color map, and plug it into the output color map itself. */ + for (i = 0; i < NewColorMapSize; i++) { + if ((j = NewColorSubdiv[i].NumEntries) > 0) { + QuantizedColor = NewColorSubdiv[i].QuantizedColors; + Red = Green = Blue = 0; + while (QuantizedColor) { + QuantizedColor->NewColorIndex = i; + Red += QuantizedColor->RGB[0]; + Green += QuantizedColor->RGB[1]; + Blue += QuantizedColor->RGB[2]; + QuantizedColor = QuantizedColor->Pnext; + } + OutputColorMap[i].Red = + (Red << (8 - BITS_PER_PRIM_COLOR)) / j; + OutputColorMap[i].Green = + (Green << (8 - BITS_PER_PRIM_COLOR)) / j; + OutputColorMap[i].Blue = + (Blue << (8 - BITS_PER_PRIM_COLOR)) / j; + } + } + + /* Finally scan the input buffer again and put the mapped index in the + * output buffer. */ + MaxRGBError[0] = MaxRGBError[1] = MaxRGBError[2] = 0; + for (i = 0; i < (int)(Width * Height); i++) { + Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) + << (2 * BITS_PER_PRIM_COLOR)) + + ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) + << BITS_PER_PRIM_COLOR) + + (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR)); + Index = ColorArrayEntries[Index].NewColorIndex; + OutputBuffer[i] = Index; + if (MaxRGBError[0] < + ABS(OutputColorMap[Index].Red - RedInput[i])) { + MaxRGBError[0] = + ABS(OutputColorMap[Index].Red - RedInput[i]); + } + if (MaxRGBError[1] < + ABS(OutputColorMap[Index].Green - GreenInput[i])) { + MaxRGBError[1] = + ABS(OutputColorMap[Index].Green - GreenInput[i]); + } + if (MaxRGBError[2] < + ABS(OutputColorMap[Index].Blue - BlueInput[i])) { + MaxRGBError[2] = + ABS(OutputColorMap[Index].Blue - BlueInput[i]); + } + } #ifdef DEBUG - fprintf(stderr, - "Quantization L(0) errors: Red = %d, Green = %d, Blue = %d.\n", - MaxRGBError[0], MaxRGBError[1], MaxRGBError[2]); + fprintf(stderr, + "Quantization L(0) errors: Red = %d, Green = %d, Blue = %d.\n", + MaxRGBError[0], MaxRGBError[1], MaxRGBError[2]); #endif /* DEBUG */ - free((char *)ColorArrayEntries); + free((char *)ColorArrayEntries); - *ColorMapSize = NewColorMapSize; + *ColorMapSize = NewColorMapSize; - return GIF_OK; + return GIF_OK; } /****************************************************************************** @@ -193,138 +211,147 @@ GifQuantizeBuffer(unsigned int Width, The biggest cube in one dimension is subdivide unless it has only one entry. Returns GIF_ERROR if failed, otherwise GIF_OK. *******************************************************************************/ -static int -SubdivColorMap(NewColorMapType * NewColorSubdiv, - unsigned int ColorMapSize, - unsigned int *NewColorMapSize) { - - unsigned int i, j, Index = 0; - QuantizedColorType *QuantizedColor, **SortArray; - - while (ColorMapSize > *NewColorMapSize) { - /* Find candidate for subdivision: */ - long Sum, Count; - int MaxSize = -1; - unsigned int NumEntries, MinColor, MaxColor; - for (i = 0; i < *NewColorMapSize; i++) { - for (j = 0; j < 3; j++) { - if ((((int)NewColorSubdiv[i].RGBWidth[j]) > MaxSize) && - (NewColorSubdiv[i].NumEntries > 1)) { - MaxSize = NewColorSubdiv[i].RGBWidth[j]; - Index = i; - SortRGBAxis = j; - } - } - } - - if (MaxSize == -1) - return GIF_OK; - - /* Split the entry Index into two along the axis SortRGBAxis: */ - - /* Sort all elements in that entry along the given axis and split at - * the median. */ - SortArray = (QuantizedColorType **)malloc( - sizeof(QuantizedColorType *) * - NewColorSubdiv[Index].NumEntries); - if (SortArray == NULL) - return GIF_ERROR; - for (j = 0, QuantizedColor = NewColorSubdiv[Index].QuantizedColors; - j < NewColorSubdiv[Index].NumEntries && QuantizedColor != NULL; - j++, QuantizedColor = QuantizedColor->Pnext) - SortArray[j] = QuantizedColor; - - /* - * Because qsort isn't stable, this can produce differing - * results for the order of tuples depending on platform - * details of how qsort() is implemented. - * - * We mitigate this problem by sorting on all three axes rather - * than only the one specied by SortRGBAxis; that way the instability - * can only become an issue if there are multiple color indices - * referring to identical RGB tuples. Older versions of this - * sorted on only the one axis. - */ - qsort(SortArray, NewColorSubdiv[Index].NumEntries, - sizeof(QuantizedColorType *), SortCmpRtn); - - /* Relink the sorted list into one: */ - for (j = 0; j < NewColorSubdiv[Index].NumEntries - 1; j++) - SortArray[j]->Pnext = SortArray[j + 1]; - SortArray[NewColorSubdiv[Index].NumEntries - 1]->Pnext = NULL; - NewColorSubdiv[Index].QuantizedColors = QuantizedColor = SortArray[0]; - free((char *)SortArray); - - /* Now simply add the Counts until we have half of the Count: */ - Sum = NewColorSubdiv[Index].Count / 2 - QuantizedColor->Count; - NumEntries = 1; - Count = QuantizedColor->Count; - while (QuantizedColor->Pnext != NULL && - (Sum -= QuantizedColor->Pnext->Count) >= 0 && - QuantizedColor->Pnext->Pnext != NULL) { - QuantizedColor = QuantizedColor->Pnext; - NumEntries++; - Count += QuantizedColor->Count; - } - /* Save the values of the last color of the first half, and first - * of the second half so we can update the Bounding Boxes later. - * Also as the colors are quantized and the BBoxes are full 0..255, - * they need to be rescaled. - */ - MaxColor = QuantizedColor->RGB[SortRGBAxis]; /* Max. of first half */ - /* coverity[var_deref_op] */ - MinColor = QuantizedColor->Pnext->RGB[SortRGBAxis]; /* of second */ - MaxColor <<= (8 - BITS_PER_PRIM_COLOR); - MinColor <<= (8 - BITS_PER_PRIM_COLOR); - - /* Partition right here: */ - NewColorSubdiv[*NewColorMapSize].QuantizedColors = - QuantizedColor->Pnext; - QuantizedColor->Pnext = NULL; - NewColorSubdiv[*NewColorMapSize].Count = Count; - NewColorSubdiv[Index].Count -= Count; - NewColorSubdiv[*NewColorMapSize].NumEntries = - NewColorSubdiv[Index].NumEntries - NumEntries; - NewColorSubdiv[Index].NumEntries = NumEntries; - for (j = 0; j < 3; j++) { - NewColorSubdiv[*NewColorMapSize].RGBMin[j] = - NewColorSubdiv[Index].RGBMin[j]; - NewColorSubdiv[*NewColorMapSize].RGBWidth[j] = - NewColorSubdiv[Index].RGBWidth[j]; - } - NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] = - NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] + - NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] - MinColor; - NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] = MinColor; - - NewColorSubdiv[Index].RGBWidth[SortRGBAxis] = - MaxColor - NewColorSubdiv[Index].RGBMin[SortRGBAxis]; - - (*NewColorMapSize)++; - } - - return GIF_OK; +static int SubdivColorMap(NewColorMapType *NewColorSubdiv, + unsigned int ColorMapSize, + unsigned int *NewColorMapSize) { + + unsigned int i, j, Index = 0; + QuantizedColorType *QuantizedColor, **SortArray; + + while (ColorMapSize > *NewColorMapSize) { + /* Find candidate for subdivision: */ + long Sum, Count; + int MaxSize = -1; + unsigned int NumEntries, MinColor, MaxColor; + for (i = 0; i < *NewColorMapSize; i++) { + for (j = 0; j < 3; j++) { + if ((((int)NewColorSubdiv[i].RGBWidth[j]) > + MaxSize) && + (NewColorSubdiv[i].NumEntries > 1)) { + MaxSize = NewColorSubdiv[i].RGBWidth[j]; + Index = i; + SortRGBAxis = j; + } + } + } + + if (MaxSize == -1) { + return GIF_OK; + } + + /* Split the entry Index into two along the axis SortRGBAxis: */ + + /* Sort all elements in that entry along the given axis and + * split at the median. */ + SortArray = (QuantizedColorType **)malloc( + sizeof(QuantizedColorType *) * + NewColorSubdiv[Index].NumEntries); + if (SortArray == NULL) { + return GIF_ERROR; + } + for (j = 0, + QuantizedColor = NewColorSubdiv[Index].QuantizedColors; + j < NewColorSubdiv[Index].NumEntries && + QuantizedColor != NULL; + j++, QuantizedColor = QuantizedColor->Pnext) { + SortArray[j] = QuantizedColor; + } + + /* + * Because qsort isn't stable, this can produce differing + * results for the order of tuples depending on platform + * details of how qsort() is implemented. + * + * We mitigate this problem by sorting on all three axes rather + * than only the one specied by SortRGBAxis; that way the + * instability can only become an issue if there are multiple + * color indices referring to identical RGB tuples. Older + * versions of this sorted on only the one axis. + */ + qsort(SortArray, NewColorSubdiv[Index].NumEntries, + sizeof(QuantizedColorType *), SortCmpRtn); + + /* Relink the sorted list into one: */ + for (j = 0; j < NewColorSubdiv[Index].NumEntries - 1; j++) { + SortArray[j]->Pnext = SortArray[j + 1]; + } + SortArray[NewColorSubdiv[Index].NumEntries - 1]->Pnext = NULL; + NewColorSubdiv[Index].QuantizedColors = QuantizedColor = + SortArray[0]; + free((char *)SortArray); + + /* Now simply add the Counts until we have half of the Count: */ + Sum = NewColorSubdiv[Index].Count / 2 - QuantizedColor->Count; + NumEntries = 1; + Count = QuantizedColor->Count; + while (QuantizedColor->Pnext != NULL && + (Sum -= QuantizedColor->Pnext->Count) >= 0 && + QuantizedColor->Pnext->Pnext != NULL) { + QuantizedColor = QuantizedColor->Pnext; + NumEntries++; + Count += QuantizedColor->Count; + } + /* Save the values of the last color of the first half, and + * first of the second half so we can update the Bounding Boxes + * later. Also as the colors are quantized and the BBoxes are + * full 0..255, they need to be rescaled. + */ + MaxColor = + QuantizedColor->RGB[SortRGBAxis]; /* Max. of first half */ + /* coverity[var_deref_op] */ + MinColor = + // cppcheck-suppress nullPointerRedundantCheck + QuantizedColor->Pnext->RGB[SortRGBAxis]; /* of second */ + MaxColor <<= (8 - BITS_PER_PRIM_COLOR); + MinColor <<= (8 - BITS_PER_PRIM_COLOR); + + /* Partition right here: */ + NewColorSubdiv[*NewColorMapSize].QuantizedColors = + QuantizedColor->Pnext; + QuantizedColor->Pnext = NULL; + NewColorSubdiv[*NewColorMapSize].Count = Count; + NewColorSubdiv[Index].Count -= Count; + NewColorSubdiv[*NewColorMapSize].NumEntries = + NewColorSubdiv[Index].NumEntries - NumEntries; + NewColorSubdiv[Index].NumEntries = NumEntries; + for (j = 0; j < 3; j++) { + NewColorSubdiv[*NewColorMapSize].RGBMin[j] = + NewColorSubdiv[Index].RGBMin[j]; + NewColorSubdiv[*NewColorMapSize].RGBWidth[j] = + NewColorSubdiv[Index].RGBWidth[j]; + } + NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] = + NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] + + NewColorSubdiv[*NewColorMapSize].RGBWidth[SortRGBAxis] - + MinColor; + NewColorSubdiv[*NewColorMapSize].RGBMin[SortRGBAxis] = MinColor; + + NewColorSubdiv[Index].RGBWidth[SortRGBAxis] = + MaxColor - NewColorSubdiv[Index].RGBMin[SortRGBAxis]; + + (*NewColorMapSize)++; + } + + return GIF_OK; } /**************************************************************************** Routine called by qsort to compare two entries. -*****************************************************************************/ - -static int -SortCmpRtn(const void *Entry1, - const void *Entry2) { - QuantizedColorType *entry1 = (*((QuantizedColorType **) Entry1)); - QuantizedColorType *entry2 = (*((QuantizedColorType **) Entry2)); - - /* sort on all axes of the color space! */ - int hash1 = entry1->RGB[SortRGBAxis] * 256 * 256 - + entry1->RGB[(SortRGBAxis+1) % 3] * 256 - + entry1->RGB[(SortRGBAxis+2) % 3]; - int hash2 = entry2->RGB[SortRGBAxis] * 256 * 256 - + entry2->RGB[(SortRGBAxis+1) % 3] * 256 - + entry2->RGB[(SortRGBAxis+2) % 3]; - - return hash1 - hash2; + *****************************************************************************/ + +static int SortCmpRtn(const void *Entry1, const void *Entry2) { + QuantizedColorType *entry1 = (*((QuantizedColorType **)Entry1)); + QuantizedColorType *entry2 = (*((QuantizedColorType **)Entry2)); + + /* sort on all axes of the color space! */ + int hash1 = entry1->RGB[SortRGBAxis] * 256 * 256 + + entry1->RGB[(SortRGBAxis + 1) % 3] * 256 + + entry1->RGB[(SortRGBAxis + 2) % 3]; + int hash2 = entry2->RGB[SortRGBAxis] * 256 * 256 + + entry2->RGB[(SortRGBAxis + 1) % 3] * 256 + + entry2->RGB[(SortRGBAxis + 2) % 3]; + + return hash1 - hash2; } /* end */

    Step Action Index Stream New Code Table Row Code Stream
    0 Init