Snap for 10453563 from ef5735f51b1fd2a66aa1ad555dd5a8cbb904a088 to mainline-mediaprovider-release

Change-Id: I8d13a24ef10767cb7805ee8151463fb546802148
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..fa5a76c
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,6 @@
+.gitattributes export-ignore
+.gitignore export-ignore
+.mailmap export-ignore
+*.bat text eol=crlf
+*.pdf -text -diff
+*.ppm -text -diff
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6d6f03c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,56 @@
+*.l[ao]
+*.[ao]
+*.pc
+.DS_Store
+.deps
+.idea
+.libs
+.vscode
+/aclocal.m4
+/ar-lib
+/autom4te.cache
+/compile
+/config.*
+/configure
+/depcomp
+/dist
+/install-sh
+/libtool
+/ltmain.sh
+/missing
+/mkinstalldirs
+/stamp-h1
+Makefile
+Makefile.in
+examples/anim_diff
+examples/anim_dump
+examples/[cdv]webp
+examples/gif2webp
+examples/img2webp
+examples/webpinfo
+examples/webpmux
+src/webp/config.h*
+src/webp/stamp-h1
+/output
+/doc/output
+*.idb
+*.pdb
+/iosbuild
+/xcframeworkbuild
+/WebP*.*framework
+CMakeCache.txt
+CMakeFiles/
+cmake_install.cmake
+.gradle
+/build
+extras/get_disto
+extras/vwebp_sdl
+extras/webp_quality
+tests/fuzzer/advanced_api_fuzzer
+tests/fuzzer/animation_api_fuzzer
+tests/fuzzer/animdecoder_fuzzer
+tests/fuzzer/animencoder_fuzzer
+tests/fuzzer/demux_api_fuzzer
+tests/fuzzer/enc_dec_fuzzer
+tests/fuzzer/mux_demux_api_fuzzer
+tests/fuzzer/simple_api_fuzzer
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000..ddcf1a7
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,18 @@
+Johann Koenig <johann.koenig@duck.com>
+Johann Koenig <johann.koenig@duck.com> <johannkoenig@google.com>
+Mikołaj Zalewski <mikolajz@google.com>
+Pascal Massimino <pascal.massimino@gmail.com>
+Pascal Massimino <pascal.massimino@gmail.com> <skal@google.com>
+Vikas Arora <vikasa@google.com>
+<vikasa@google.com> <vikasa@gmail.com>
+<vikasa@google.com> <vikaas.arora@gmail.com>
+<slobodan.prijic@imgtec.com> <Slobodan.Prijic@imgtec.com>
+<vrabaud@google.com> <vincent.rabaud@gmail.com>
+Vincent Rabaud <vrabaud@google.com>
+Tamar Levy <tamar.levy@intel.com>
+<qrczak@google.com> <qrczak>
+Hui Su <huisu@google.com>
+James Zern <jzern@google.com>
+Roberto Alanis <alanisbaez@google.com>
+Brian Ledger <brianpl@google.com>
+Maryla Ustarroz-Calonge <maryla@google.com>
diff --git a/.pylintrc b/.pylintrc
new file mode 100644
index 0000000..4658b84
--- /dev/null
+++ b/.pylintrc
@@ -0,0 +1,441 @@
+# This Pylint rcfile contains a best-effort configuration to uphold the
+# best-practices and style described in the Google Python style guide:
+#   https://google.github.io/styleguide/pyguide.html
+#
+# Its canonical open-source location is:
+#   https://google.github.io/styleguide/pylintrc
+
+[MASTER]
+
+# Files or directories to be skipped. They should be base names, not paths.
+ignore=third_party
+
+# Files or directories matching the regex patterns are skipped. The regex
+# matches against base names, not paths.
+ignore-patterns=
+
+# Pickle collected data for later comparisons.
+persistent=no
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+# Use multiple processes to speed up Pylint.
+jobs=4
+
+# Allow loading of arbitrary C extensions. Extensions are imported into the
+# active Python interpreter and may run arbitrary code.
+unsafe-load-any-extension=no
+
+
+[MESSAGES CONTROL]
+
+# Only show warnings with the listed confidence levels. Leave empty to show
+# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
+confidence=
+
+# Enable the message, report, category or checker with the given id(s). You can
+# either give multiple identifier separated by comma (,) or put this option
+# multiple time (only on the command line, not in the configuration file where
+# it should appear only once). See also the "--disable" option for examples.
+#enable=
+
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifiers separated by comma (,) or put this
+# option multiple times (only on the command line, not in the configuration
+# file where it should appear only once).You can also use "--disable=all" to
+# disable everything first and then reenable specific checks. For example, if
+# you want to run only the similarities checker, you can use "--disable=all
+# --enable=similarities". If you want to run only the classes checker, but have
+# no Warning level messages displayed, use"--disable=all --enable=classes
+# --disable=W"
+disable=abstract-method,
+        apply-builtin,
+        arguments-differ,
+        attribute-defined-outside-init,
+        backtick,
+        bad-option-value,
+        basestring-builtin,
+        buffer-builtin,
+        c-extension-no-member,
+        consider-using-enumerate,
+        cmp-builtin,
+        cmp-method,
+        coerce-builtin,
+        coerce-method,
+        delslice-method,
+        div-method,
+        duplicate-code,
+        eq-without-hash,
+        execfile-builtin,
+        file-builtin,
+        filter-builtin-not-iterating,
+        fixme,
+        getslice-method,
+        global-statement,
+        hex-method,
+        idiv-method,
+        implicit-str-concat-in-sequence,
+        import-error,
+        import-self,
+        import-star-module-level,
+        inconsistent-return-statements,
+        input-builtin,
+        intern-builtin,
+        invalid-str-codec,
+        locally-disabled,
+        long-builtin,
+        long-suffix,
+        map-builtin-not-iterating,
+        misplaced-comparison-constant,
+        missing-function-docstring,
+        metaclass-assignment,
+        next-method-called,
+        next-method-defined,
+        no-absolute-import,
+        no-else-break,
+        no-else-continue,
+        no-else-raise,
+        no-else-return,
+        no-init,  # added
+        no-member,
+        no-name-in-module,
+        no-self-use,
+        nonzero-method,
+        oct-method,
+        old-division,
+        old-ne-operator,
+        old-octal-literal,
+        old-raise-syntax,
+        parameter-unpacking,
+        print-statement,
+        raising-string,
+        range-builtin-not-iterating,
+        raw_input-builtin,
+        rdiv-method,
+        reduce-builtin,
+        relative-import,
+        reload-builtin,
+        round-builtin,
+        setslice-method,
+        signature-differs,
+        standarderror-builtin,
+        suppressed-message,
+        sys-max-int,
+        too-few-public-methods,
+        too-many-ancestors,
+        too-many-arguments,
+        too-many-boolean-expressions,
+        too-many-branches,
+        too-many-instance-attributes,
+        too-many-locals,
+        too-many-nested-blocks,
+        too-many-public-methods,
+        too-many-return-statements,
+        too-many-statements,
+        trailing-newlines,
+        unichr-builtin,
+        unicode-builtin,
+        unnecessary-pass,
+        unpacking-in-except,
+        useless-else-on-loop,
+        useless-object-inheritance,
+        useless-suppression,
+        using-cmp-argument,
+        wrong-import-order,
+        xrange-builtin,
+        zip-builtin-not-iterating,
+
+
+[REPORTS]
+
+# Set the output format. Available formats are text, parseable, colorized, msvs
+# (visual studio) and html. You can also give a reporter class, eg
+# mypackage.mymodule.MyReporterClass.
+output-format=text
+
+# Put messages in a separate file for each module / package specified on the
+# command line instead of printing them on stdout. Reports (if any) will be
+# written in a file name "pylint_global.[txt|html]". This option is deprecated
+# and it will be removed in Pylint 2.0.
+files-output=no
+
+# Tells whether to display a full report or only the messages
+reports=no
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note). You have access to the variables errors warning, statement which
+# respectively contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (RP0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Template used to display messages. This is a python new-style format string
+# used to format the message information. See doc for all details
+#msg-template=
+
+
+[BASIC]
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=main,_,PRESUBMIT
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=
+
+# Colon-delimited sets of names that determine each other's naming style when
+# the name regexes allow several styles.
+name-group=
+
+# Include a hint for the correct naming format with invalid-name
+include-naming-hint=no
+
+# List of decorators that produce properties, such as abc.abstractproperty. Add
+# to this list to register other decorators that produce valid properties.
+property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl
+
+# Regular expression matching correct function names
+function-rgx=^(?:(?P<exempt>setUp|tearDown|setUpModule|tearDownModule)|(?P<camel_case>_?[A-Z][a-zA-Z0-9]*)|(?P<snake_case>_?[a-z][a-z0-9_]*))$
+
+# Regular expression matching correct variable names
+variable-rgx=^[a-z][a-z0-9_]*$
+
+# Regular expression matching correct constant names
+const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
+
+# Regular expression matching correct attribute names
+attr-rgx=^_{0,2}[a-z][a-z0-9_]*$
+
+# Regular expression matching correct argument names
+argument-rgx=^[a-z][a-z0-9_]*$
+
+# Regular expression matching correct class attribute names
+class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$
+
+# Regular expression matching correct inline iteration names
+inlinevar-rgx=^[a-z][a-z0-9_]*$
+
+# Regular expression matching correct class names
+class-rgx=^_?[A-Z][a-zA-Z0-9]*$
+
+# Regular expression matching correct module names
+module-rgx=^(_?[a-z][a-z0-9_]*|__init__)$
+
+# Regular expression matching correct method names
+method-rgx=(?x)^(?:(?P<exempt>_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P<camel_case>_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P<snake_case>_{0,2}[a-z][a-z0-9_]*))$
+
+# Regular expression which should only match function or class names that do
+# not require a docstring.
+no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$
+
+# Minimum line length for functions/classes that require docstrings, shorter
+# ones are exempt.
+docstring-min-length=10
+
+
+[TYPECHECK]
+
+# List of decorators that produce context managers, such as
+# contextlib.contextmanager. Add to this list to register other decorators that
+# produce valid context managers.
+contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# List of module names for which member attributes should not be checked
+# (useful for modules/projects where namespaces are manipulated during runtime
+# and thus existing member attributes cannot be deduced by static analysis. It
+# supports qualified module names, as well as Unix pattern matching.
+ignored-modules=
+
+# List of class names for which member attributes should not be checked (useful
+# for classes with dynamically set attributes). This supports the use of
+# qualified names.
+ignored-classes=optparse.Values,thread._local,_thread._local
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E1101 when accessed. Python regular
+# expressions are accepted.
+generated-members=
+
+
+[FORMAT]
+
+# Maximum number of characters on a single line.
+max-line-length=80
+
+# TODO(https://github.com/PyCQA/pylint/issues/3352): Direct pylint to exempt
+# lines made too long by directives to pytype.
+
+# Regexp for a line that is allowed to be longer than the limit.
+ignore-long-lines=(?x)(
+  ^\s*(\#\ )?<?https?://\S+>?$|
+  ^\s*(from\s+\S+\s+)?import\s+.+$)
+
+# Allow the body of an if to be on the same line as the test if there is no
+# else.
+single-line-if-stmt=yes
+
+# List of optional constructs for which whitespace checking is disabled. `dict-
+# separator` is used to allow tabulation in dicts, etc.: {1  : 1,\n222: 2}.
+# `trailing-comma` allows a space between comma and closing bracket: (a, ).
+# `empty-line` allows space-only lines.
+no-space-check=
+
+# Maximum number of lines in a module
+max-module-lines=99999
+
+# String used as indentation unit.  The internal Google style guide mandates 2
+# spaces.  Google's externaly-published style guide says 4, consistent with
+# PEP 8.  Here, we use 2 spaces, for conformity with many open-sourced Google
+# projects (like TensorFlow).
+indent-string='  '
+
+# Number of spaces of indent required inside a hanging  or continued line.
+indent-after-paren=4
+
+# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
+expected-line-ending-format=
+
+
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=TODO
+
+
+[STRING]
+
+# This flag controls whether inconsistent-quotes generates a warning when the
+# character used as a quote delimiter is used inconsistently within a module.
+check-quote-consistency=yes
+
+
+[VARIABLES]
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching the name of dummy variables (i.e. expectedly
+# not used).
+dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_)
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+# List of strings which can identify a callback function by name. A callback
+# name must start or end with one of those strings.
+callbacks=cb_,_cb
+
+# List of qualified module names which can have objects that can redefine
+# builtins.
+redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools
+
+
+[LOGGING]
+
+# Logging modules to check that the string format arguments are in logging
+# function parameter format
+logging-modules=logging,absl.logging,tensorflow.io.logging
+
+
+[SIMILARITIES]
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+# Ignore imports when computing similarities.
+ignore-imports=no
+
+
+[SPELLING]
+
+# Spelling dictionary name. Available dictionaries: none. To make it working
+# install python-enchant package.
+spelling-dict=
+
+# List of comma separated words that should not be checked.
+spelling-ignore-words=
+
+# A path to a file that contains private dictionary; one word per line.
+spelling-private-dict-file=
+
+# Tells whether to store unknown words to indicated private dictionary in
+# --spelling-private-dict-file option instead of raising a message.
+spelling-store-unknown-words=no
+
+
+[IMPORTS]
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,
+                   TERMIOS,
+                   Bastion,
+                   rexec,
+                   sets
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report RP0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report RP0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report RP0402 must
+# not be disabled)
+int-import-graph=
+
+# Force import order to recognize a module as part of the standard
+# compatibility libraries.
+known-standard-library=
+
+# Force import order to recognize a module as part of a third party library.
+known-third-party=enchant, absl
+
+# Analyse import fallback blocks. This can be used to support both Python 2 and
+# 3 compatible code, which means that the block might have code that exists
+# only in one or another interpreter, leading to false positives when analysed.
+analyse-fallback-blocks=no
+
+
+[CLASSES]
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,
+                      __new__,
+                      setUp
+
+# List of member names, which should be excluded from the protected access
+# warning.
+exclude-protected=_asdict,
+                  _fields,
+                  _replace,
+                  _source,
+                  _make
+
+# List of valid names for the first argument in a class method.
+valid-classmethod-first-arg=cls,
+                            class_
+
+# List of valid names for the first argument in a metaclass class method.
+valid-metaclass-classmethod-first-arg=mcs
+
+
+[EXCEPTIONS]
+
+# Exceptions that will emit a warning when being caught. Defaults to
+# "Exception"
+overgeneral-exceptions=StandardError,
+                       Exception,
+                       BaseException
diff --git a/.style.yapf b/.style.yapf
new file mode 100644
index 0000000..0be981a
--- /dev/null
+++ b/.style.yapf
@@ -0,0 +1,2 @@
+[style]
+based_on_style = yapf
\ No newline at end of file
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..2f0c537
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,58 @@
+Contributors:
+- Aidan O'Loan (aidanol at gmail dot com)
+- Alan Browning (browning at google dot com)
+- Alexandru Ardelean (ardeleanalex at gmail dot com)
+- Brian Ledger (brianpl at google dot com)
+- Charles Munger (clm at google dot com)
+- Cheng Yi (cyi at google dot com)
+- Christian Duvivier (cduvivier at google dot com)
+- Christopher Degawa (ccom at randomderp dot com)
+- Clement Courbet (courbet at google dot com)
+- Djordje Pesut (djordje dot pesut at imgtec dot com)
+- Frank Barchard (fbarchard at google dot com)
+- Hui Su (huisu at google dot com)
+- H. Vetinari (h dot vetinari at gmx dot com)
+- Ilya Kurdyukov (jpegqs at gmail dot com)
+- Ingvar Stepanyan (rreverser at google dot com)
+- James Zern (jzern at google dot com)
+- Jan Engelhardt (jengelh at medozas dot de)
+- Jehan (jehan at girinstud dot io)
+- Jeremy Maitin-Shepard (jbms at google dot com)
+- Johann Koenig (johann dot koenig at duck dot com)
+- Jovan Zelincevic (jovan dot zelincevic at imgtec dot com)
+- Jyrki Alakuijala (jyrki at google dot com)
+- Konstantin Ivlev (tomskside at gmail dot com)
+- Lode Vandevenne (lode at google dot com)
+- Lou Quillio (louquillio at google dot com)
+- Mans Rullgard (mans at mansr dot com)
+- Marcin Kowalczyk (qrczak at google dot com)
+- Martin Olsson (mnemo at minimum dot se)
+- Maryla Ustarroz-Calonge (maryla at google dot com)
+- Mikołaj Zalewski (mikolajz at google dot com)
+- Mislav Bradac (mislavm at google dot com)
+- Nico Weber (thakis at chromium dot org)
+- Noel Chromium (noel at chromium dot org)
+- Oliver Wolff (oliver dot wolff at qt dot io)
+- Owen Rodley (orodley at google dot com)
+- Parag Salasakar (img dot mips1 at gmail dot com)
+- Pascal Massimino (pascal dot massimino at gmail dot com)
+- Paweł Hajdan, Jr (phajdan dot jr at chromium dot org)
+- Pierre Joye (pierre dot php at gmail dot com)
+- Roberto Alanis (alanisbaez at google dot com)
+- Sam Clegg (sbc at chromium dot org)
+- Scott Hancher (seh at google dot com)
+- Scott LaVarnway (slavarnway at google dot com)
+- Scott Talbot (s at chikachow dot org)
+- Slobodan Prijic (slobodan dot prijic at imgtec dot com)
+- Somnath Banerjee (somnath dot banerjee at gmail dot com)
+- Sriraman Tallam (tmsriram at google dot com)
+- Tamar Levy (tamar dot levy at intel dot com)
+- Timothy Gu (timothygu99 at gmail dot com)
+- Urvang Joshi (urvang at google dot com)
+- Vikas Arora (vikasa at google dot com)
+- Vincent Rabaud (vrabaud at google dot com)
+- Vlad Tsyrklevich (vtsyrklevich at chromium dot org)
+- Wan-Teh Chang (wtc at google dot com)
+- Yang Zhang (yang dot zhang at arm dot com)
+- Yannis Guyon (yguyon at google dot com)
+- Zhi An Ng (zhin at chromium dot org)
diff --git a/Android.bp b/Android.bp
index e81468f..ace6a9e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -45,7 +45,7 @@
 cc_library_static {
     name: "webp-headers",
     host_supported: true,
-    export_include_dirs: ["include"],
+    export_include_dirs: ["src"],
     sdk_version: "9",
     target: {
         windows: {
@@ -60,6 +60,13 @@
     name: "libwebp-encode",
     host_supported: true,
     srcs: [
+        "sharpyuv/sharpyuv.c",
+        "sharpyuv/sharpyuv_cpu.c",
+        "sharpyuv/sharpyuv_csp.c",
+        "sharpyuv/sharpyuv_dsp.c",
+        "sharpyuv/sharpyuv_gamma.c",
+        "sharpyuv/sharpyuv_neon.c",
+        "sharpyuv/sharpyuv_sse2.c",
         "src/dsp/cost.c",
         "src/dsp/cost_mips32.c",
         "src/dsp/cost_mips_dsp_r2.c",
@@ -121,10 +128,10 @@
     },
 
     cflags: [
-        "-O2",
         "-DANDROID",
         "-DWEBP_SWAP_16BIT_CSP",
         "-DWEBP_USE_THREAD",
+        "-O2",
         "-Wall",
         "-Werror",
     ],
@@ -221,10 +228,10 @@
     },
 
     cflags: [
-        "-O2",
         "-DANDROID",
         "-DWEBP_SWAP_16BIT_CSP",
         "-DWEBP_USE_THREAD",
+        "-O2",
         "-Wall",
         "-Werror",
     ],
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..c7bcb0f
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,309 @@
+# Ignore this file during non-NDK builds.
+ifdef NDK_ROOT
+LOCAL_PATH := $(call my-dir)
+
+WEBP_CFLAGS := -Wall -DANDROID -DHAVE_MALLOC_H -DHAVE_PTHREAD -DWEBP_USE_THREAD
+WEBP_CFLAGS += -fvisibility=hidden
+
+ifeq ($(APP_OPTIM),release)
+  WEBP_CFLAGS += -finline-functions -ffast-math \
+                 -ffunction-sections -fdata-sections
+  ifeq ($(findstring clang,$(NDK_TOOLCHAIN_VERSION)),)
+    WEBP_CFLAGS += -frename-registers -s
+  endif
+endif
+
+# mips32 fails to build with clang from r14b
+# https://bugs.chromium.org/p/webp/issues/detail?id=343
+ifeq ($(findstring clang,$(NDK_TOOLCHAIN_VERSION)),clang)
+  ifeq ($(TARGET_ARCH),mips)
+    clang_version := $(shell $(TARGET_CC) --version)
+    ifneq ($(findstring clang version 3,$(clang_version)),)
+      WEBP_CFLAGS += -no-integrated-as
+    endif
+  endif
+endif
+
+ifneq ($(findstring armeabi-v7a, $(TARGET_ARCH_ABI)),)
+  # Setting LOCAL_ARM_NEON will enable -mfpu=neon which may cause illegal
+  # instructions to be generated for armv7a code. Instead target the neon code
+  # specifically.
+  NEON := c.neon
+  USE_CPUFEATURES := yes
+  WEBP_CFLAGS += -DHAVE_CPU_FEATURES_H
+else
+  NEON := c
+endif
+
+sharpyuv_srcs := \
+    sharpyuv/sharpyuv.c \
+    sharpyuv/sharpyuv_cpu.c \
+    sharpyuv/sharpyuv_csp.c \
+    sharpyuv/sharpyuv_dsp.c \
+    sharpyuv/sharpyuv_gamma.c \
+    sharpyuv/sharpyuv_neon.$(NEON) \
+    sharpyuv/sharpyuv_sse2.c \
+
+dec_srcs := \
+    src/dec/alpha_dec.c \
+    src/dec/buffer_dec.c \
+    src/dec/frame_dec.c \
+    src/dec/idec_dec.c \
+    src/dec/io_dec.c \
+    src/dec/quant_dec.c \
+    src/dec/tree_dec.c \
+    src/dec/vp8_dec.c \
+    src/dec/vp8l_dec.c \
+    src/dec/webp_dec.c \
+
+demux_srcs := \
+    src/demux/anim_decode.c \
+    src/demux/demux.c \
+
+dsp_dec_srcs := \
+    src/dsp/alpha_processing.c \
+    src/dsp/alpha_processing_mips_dsp_r2.c \
+    src/dsp/alpha_processing_neon.$(NEON) \
+    src/dsp/alpha_processing_sse2.c \
+    src/dsp/alpha_processing_sse41.c \
+    src/dsp/cpu.c \
+    src/dsp/dec.c \
+    src/dsp/dec_clip_tables.c \
+    src/dsp/dec_mips32.c \
+    src/dsp/dec_mips_dsp_r2.c \
+    src/dsp/dec_msa.c \
+    src/dsp/dec_neon.$(NEON) \
+    src/dsp/dec_sse2.c \
+    src/dsp/dec_sse41.c \
+    src/dsp/filters.c \
+    src/dsp/filters_mips_dsp_r2.c \
+    src/dsp/filters_msa.c \
+    src/dsp/filters_neon.$(NEON) \
+    src/dsp/filters_sse2.c \
+    src/dsp/lossless.c \
+    src/dsp/lossless_mips_dsp_r2.c \
+    src/dsp/lossless_msa.c \
+    src/dsp/lossless_neon.$(NEON) \
+    src/dsp/lossless_sse2.c \
+    src/dsp/lossless_sse41.c \
+    src/dsp/rescaler.c \
+    src/dsp/rescaler_mips32.c \
+    src/dsp/rescaler_mips_dsp_r2.c \
+    src/dsp/rescaler_msa.c \
+    src/dsp/rescaler_neon.$(NEON) \
+    src/dsp/rescaler_sse2.c \
+    src/dsp/upsampling.c \
+    src/dsp/upsampling_mips_dsp_r2.c \
+    src/dsp/upsampling_msa.c \
+    src/dsp/upsampling_neon.$(NEON) \
+    src/dsp/upsampling_sse2.c \
+    src/dsp/upsampling_sse41.c \
+    src/dsp/yuv.c \
+    src/dsp/yuv_mips32.c \
+    src/dsp/yuv_mips_dsp_r2.c \
+    src/dsp/yuv_neon.$(NEON) \
+    src/dsp/yuv_sse2.c \
+    src/dsp/yuv_sse41.c \
+
+dsp_enc_srcs := \
+    src/dsp/cost.c \
+    src/dsp/cost_mips32.c \
+    src/dsp/cost_mips_dsp_r2.c \
+    src/dsp/cost_neon.$(NEON) \
+    src/dsp/cost_sse2.c \
+    src/dsp/enc.c \
+    src/dsp/enc_mips32.c \
+    src/dsp/enc_mips_dsp_r2.c \
+    src/dsp/enc_msa.c \
+    src/dsp/enc_neon.$(NEON) \
+    src/dsp/enc_sse2.c \
+    src/dsp/enc_sse41.c \
+    src/dsp/lossless_enc.c \
+    src/dsp/lossless_enc_mips32.c \
+    src/dsp/lossless_enc_mips_dsp_r2.c \
+    src/dsp/lossless_enc_msa.c \
+    src/dsp/lossless_enc_neon.$(NEON) \
+    src/dsp/lossless_enc_sse2.c \
+    src/dsp/lossless_enc_sse41.c \
+    src/dsp/ssim.c \
+    src/dsp/ssim_sse2.c \
+
+enc_srcs := \
+    src/enc/alpha_enc.c \
+    src/enc/analysis_enc.c \
+    src/enc/backward_references_cost_enc.c \
+    src/enc/backward_references_enc.c \
+    src/enc/config_enc.c \
+    src/enc/cost_enc.c \
+    src/enc/filter_enc.c \
+    src/enc/frame_enc.c \
+    src/enc/histogram_enc.c \
+    src/enc/iterator_enc.c \
+    src/enc/near_lossless_enc.c \
+    src/enc/picture_enc.c \
+    src/enc/picture_csp_enc.c \
+    src/enc/picture_psnr_enc.c \
+    src/enc/picture_rescale_enc.c \
+    src/enc/picture_tools_enc.c \
+    src/enc/predictor_enc.c \
+    src/enc/quant_enc.c \
+    src/enc/syntax_enc.c \
+    src/enc/token_enc.c \
+    src/enc/tree_enc.c \
+    src/enc/vp8l_enc.c \
+    src/enc/webp_enc.c \
+
+mux_srcs := \
+    src/mux/anim_encode.c \
+    src/mux/muxedit.c \
+    src/mux/muxinternal.c \
+    src/mux/muxread.c \
+
+utils_dec_srcs := \
+    src/utils/bit_reader_utils.c \
+    src/utils/color_cache_utils.c \
+    src/utils/filters_utils.c \
+    src/utils/huffman_utils.c \
+    src/utils/quant_levels_dec_utils.c \
+    src/utils/random_utils.c \
+    src/utils/rescaler_utils.c \
+    src/utils/thread_utils.c \
+    src/utils/utils.c \
+
+utils_enc_srcs := \
+    src/utils/bit_writer_utils.c \
+    src/utils/huffman_encode_utils.c \
+    src/utils/quant_levels_utils.c \
+
+################################################################################
+# libwebpdecoder
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    $(dec_srcs) \
+    $(dsp_dec_srcs) \
+    $(utils_dec_srcs) \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)/src
+
+# prefer arm over thumb mode for performance gains
+LOCAL_ARM_MODE := arm
+
+ifeq ($(USE_CPUFEATURES),yes)
+  LOCAL_STATIC_LIBRARIES := cpufeatures
+endif
+
+LOCAL_MODULE := webpdecoder_static
+
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/COPYING $(LOCAL_PATH)/NOTICE $(LOCAL_PATH)/PATENTS
+include $(BUILD_STATIC_LIBRARY)
+
+ifeq ($(ENABLE_SHARED),1)
+include $(CLEAR_VARS)
+
+LOCAL_WHOLE_STATIC_LIBRARIES := webpdecoder_static
+
+LOCAL_MODULE := webpdecoder
+
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/COPYING $(LOCAL_PATH)/NOTICE $(LOCAL_PATH)/PATENTS
+include $(BUILD_SHARED_LIBRARY)
+endif  # ENABLE_SHARED=1
+
+################################################################################
+# libwebp
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    $(sharpyuv_srcs) \
+    $(dsp_enc_srcs) \
+    $(enc_srcs) \
+    $(utils_enc_srcs) \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)/src $(LOCAL_PATH)
+
+# prefer arm over thumb mode for performance gains
+LOCAL_ARM_MODE := arm
+
+LOCAL_WHOLE_STATIC_LIBRARIES := webpdecoder_static
+
+LOCAL_MODULE := webp
+
+ifeq ($(ENABLE_SHARED),1)
+  LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD
+  LOCAL_LICENSE_CONDITIONS := notice
+  LOCAL_NOTICE_FILE := $(LOCAL_PATH)/COPYING $(LOCAL_PATH)/NOTICE $(LOCAL_PATH)/PATENTS
+  include $(BUILD_SHARED_LIBRARY)
+else
+  include $(BUILD_STATIC_LIBRARY)
+endif
+
+################################################################################
+# libwebpdemux
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(demux_srcs)
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)/src
+
+# prefer arm over thumb mode for performance gains
+LOCAL_ARM_MODE := arm
+
+LOCAL_MODULE := webpdemux
+
+ifeq ($(ENABLE_SHARED),1)
+  LOCAL_SHARED_LIBRARIES := webp
+  LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD
+  LOCAL_LICENSE_CONDITIONS := notice
+  LOCAL_NOTICE_FILE := $(LOCAL_PATH)/COPYING $(LOCAL_PATH)/NOTICE $(LOCAL_PATH)/PATENTS
+  include $(BUILD_SHARED_LIBRARY)
+else
+  LOCAL_STATIC_LIBRARIES := webp
+  include $(BUILD_STATIC_LIBRARY)
+endif
+
+################################################################################
+# libwebpmux
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(mux_srcs)
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)/src
+
+# prefer arm over thumb mode for performance gains
+LOCAL_ARM_MODE := arm
+
+LOCAL_MODULE := webpmux
+
+ifeq ($(ENABLE_SHARED),1)
+  LOCAL_SHARED_LIBRARIES := webp
+  LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-BSD
+  LOCAL_LICENSE_CONDITIONS := notice
+  LOCAL_NOTICE_FILE := $(LOCAL_PATH)/COPYING $(LOCAL_PATH)/NOTICE $(LOCAL_PATH)/PATENTS
+  include $(BUILD_SHARED_LIBRARY)
+else
+  LOCAL_STATIC_LIBRARIES := webp
+  include $(BUILD_STATIC_LIBRARY)
+endif
+
+################################################################################
+
+WEBP_SRC_PATH := $(LOCAL_PATH)
+include $(WEBP_SRC_PATH)/imageio/Android.mk
+include $(WEBP_SRC_PATH)/examples/Android.mk
+
+ifeq ($(USE_CPUFEATURES),yes)
+  $(call import-module,android/cpufeatures)
+endif
+endif  # NDK_ROOT
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..f378db5
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,779 @@
+#  Copyright (c) 2020 Google LLC.
+#
+#  Use of this source code is governed by a BSD-style license
+#  that can be found in the LICENSE file in the root of the source
+#  tree. An additional intellectual property rights grant can be found
+#  in the file PATENTS.  All contributing project authors may
+#  be found in the AUTHORS file in the root of the source tree.
+
+if(APPLE)
+  cmake_minimum_required(VERSION 3.17)
+else()
+  cmake_minimum_required(VERSION 3.7)
+endif()
+
+if(POLICY CMP0072)
+  cmake_policy(SET CMP0072 NEW)
+endif()
+
+project(WebP C)
+
+# Options for coder / decoder executables.
+if(BUILD_SHARED_LIBS)
+  set(WEBP_LINK_STATIC_DEFAULT OFF)
+else()
+  set(WEBP_LINK_STATIC_DEFAULT ON)
+endif()
+option(WEBP_LINK_STATIC
+       "Link using static libraries. If OFF, use dynamic libraries."
+       ${WEBP_LINK_STATIC_DEFAULT})
+if(NOT EMSCRIPTEN)
+  # Disable SIMD on Emscripten by default, as it's a new unstable Wasm feature.
+  # Users can still explicitly opt-in to make a SIMD-enabled build.
+  set(WEBP_ENABLE_SIMD_DEFAULT ON)
+endif()
+option(WEBP_ENABLE_SIMD "Enable any SIMD optimization."
+       ${WEBP_ENABLE_SIMD_DEFAULT})
+option(WEBP_BUILD_ANIM_UTILS "Build animation utilities." ON)
+option(WEBP_BUILD_CWEBP "Build the cwebp command line tool." ON)
+option(WEBP_BUILD_DWEBP "Build the dwebp command line tool." ON)
+option(WEBP_BUILD_GIF2WEBP "Build the gif2webp conversion tool." ON)
+option(WEBP_BUILD_IMG2WEBP "Build the img2webp animation tool." ON)
+option(WEBP_BUILD_VWEBP "Build the vwebp viewer tool." ON)
+option(WEBP_BUILD_WEBPINFO "Build the webpinfo command line tool." ON)
+option(WEBP_BUILD_LIBWEBPMUX "Build the libwebpmux library." ON)
+option(WEBP_BUILD_WEBPMUX "Build the webpmux command line tool." ON)
+option(WEBP_BUILD_EXTRAS "Build extras." ON)
+option(WEBP_BUILD_WEBP_JS "Emscripten build of webp.js." OFF)
+option(WEBP_USE_THREAD "Enable threading support" ON)
+option(WEBP_NEAR_LOSSLESS "Enable near-lossless encoding" ON)
+option(WEBP_ENABLE_SWAP_16BIT_CSP "Enable byte swap for 16 bit colorspaces."
+       OFF)
+set(WEBP_BITTRACE "0" CACHE STRING "Bit trace mode (0=none, 1=bit, 2=bytes)")
+set_property(CACHE WEBP_BITTRACE PROPERTY STRINGS 0 1 2)
+
+if(WEBP_LINK_STATIC)
+  if(WIN32)
+    set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
+  else()
+    set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
+  endif()
+  set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+  # vwebp does not compile on Ubuntu with static libraries so disabling it for
+  # now.
+  set(WEBP_BUILD_VWEBP OFF)
+endif()
+
+# Option needed for handling Unicode file names on Windows.
+if(WIN32)
+  option(WEBP_UNICODE "Build Unicode executables." ON)
+endif()
+
+if(WEBP_BUILD_WEBP_JS)
+  set(WEBP_BUILD_ANIM_UTILS OFF)
+  set(WEBP_BUILD_CWEBP OFF)
+  set(WEBP_BUILD_DWEBP OFF)
+  set(WEBP_BUILD_GIF2WEBP OFF)
+  set(WEBP_BUILD_IMG2WEBP OFF)
+  set(WEBP_BUILD_VWEBP OFF)
+  set(WEBP_BUILD_WEBPINFO OFF)
+  set(WEBP_BUILD_WEBPMUX OFF)
+  set(WEBP_BUILD_EXTRAS OFF)
+  set(WEBP_USE_THREAD OFF)
+
+  if(WEBP_ENABLE_SIMD)
+    message("wasm2js does not support SIMD, disabling webp.js generation.")
+  endif()
+endif()
+
+set(SHARPYUV_DEP_LIBRARIES)
+set(SHARPYUV_DEP_INCLUDE_DIRS)
+set(WEBP_DEP_LIBRARIES)
+set(WEBP_DEP_INCLUDE_DIRS)
+
+if(NOT CMAKE_BUILD_TYPE)
+  set(CMAKE_BUILD_TYPE "Release"
+      CACHE STRING "Build type: Release, Debug, MinSizeRel or RelWithDebInfo"
+            FORCE)
+endif()
+
+# Include dependencies.
+include(cmake/deps.cmake)
+include(GNUInstallDirs)
+
+if(BUILD_SHARED_LIBS AND NOT DEFINED CMAKE_INSTALL_RPATH)
+  # Set the rpath to match autoconf/libtool behavior. Note this must be set
+  # before target creation.
+  set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
+endif()
+
+# ##############################################################################
+# Options.
+if(WEBP_ENABLE_SWAP_16BIT_CSP)
+  add_definitions(-DWEBP_SWAP_16BIT_CSP=1)
+endif()
+
+if(NOT WEBP_BITTRACE STREQUAL "0")
+  add_definitions(-DBITTRACE=${WEBP_BITTRACE})
+endif()
+
+if(WEBP_UNICODE)
+  # Windows recommends setting both UNICODE and _UNICODE.
+  add_definitions(-DUNICODE -D_UNICODE)
+endif()
+
+if(MSVC AND BUILD_SHARED_LIBS)
+  add_definitions(-DWEBP_DLL)
+endif()
+
+# pkg-config variables used by *.pc.in.
+set(prefix ${CMAKE_INSTALL_PREFIX})
+set(exec_prefix "\${prefix}")
+if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}")
+  set(libdir "${CMAKE_INSTALL_LIBDIR}")
+else()
+  set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
+endif()
+if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}")
+  set(includedir "${CMAKE_INSTALL_INCLUDEDIR}")
+else()
+  set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
+endif()
+set(PTHREAD_LIBS ${CMAKE_THREAD_LIBS_INIT})
+set(INSTALLED_LIBRARIES)
+
+if(MSVC)
+  # match the naming convention used by nmake
+  set(webp_libname_prefix "lib")
+  set(CMAKE_SHARED_LIBRARY_PREFIX "${webp_libname_prefix}")
+  set(CMAKE_IMPORT_LIBRARY_PREFIX "${webp_libname_prefix}")
+  set(CMAKE_STATIC_LIBRARY_PREFIX "${webp_libname_prefix}")
+endif()
+
+set(CMAKE_C_VISIBILITY_PRESET hidden)
+
+# ##############################################################################
+# Android only.
+if(ANDROID)
+  include_directories(${ANDROID_NDK}/sources/android/cpufeatures)
+  add_library(cpufeatures-webp STATIC
+              ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c)
+  list(APPEND INSTALLED_LIBRARIES cpufeatures-webp)
+  target_link_libraries(cpufeatures-webp dl)
+  set(SHARPYUV_DEP_LIBRARIES ${SHARPYUV_DEP_LIBRARIES} cpufeatures-webp)
+  set(WEBP_DEP_LIBRARIES ${WEBP_DEP_LIBRARIES} cpufeatures-webp)
+  set(cpufeatures_include_dir ${ANDROID_NDK}/sources/android/cpufeatures)
+  set(SHARPYUV_DEP_INCLUDE_DIRS ${SHARPYUV_DEP_INCLUDE_DIRS}
+                                ${cpufeatures_include_dir})
+  set(WEBP_DEP_INCLUDE_DIRS ${WEBP_DEP_INCLUDE_DIRS} ${cpufeatures_include_dir})
+  add_definitions(-DHAVE_CPU_FEATURES_H=1)
+  set(HAVE_CPU_FEATURES_H 1)
+else()
+  set(HAVE_CPU_FEATURES_H 0)
+endif()
+
+function(configure_pkg_config FILE)
+  configure_file("${CMAKE_CURRENT_SOURCE_DIR}/${FILE}.in"
+                 "${CMAKE_CURRENT_BINARY_DIR}/${FILE}" @ONLY)
+
+  if(HAVE_MATH_LIBRARY)
+    # MSVC doesn't have libm
+    file(READ ${CMAKE_CURRENT_BINARY_DIR}/${FILE} data)
+    string(REPLACE "-lm" "" data ${data})
+    file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${FILE} ${data})
+  endif()
+
+  install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${FILE}"
+          DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
+endfunction()
+
+# ##############################################################################
+# WebP source files. Read the Makefile.am to get the source files.
+
+# We expect the Makefiles to define the sources as defined in the first regex.
+# E.g.: libimagedec_la_SOURCES  = image_dec.c image_dec.h
+function(parse_Makefile_am FOLDER VAR SRC_REGEX)
+  file(READ ${FOLDER}/Makefile.am MAKEFILE_AM)
+  string(REGEX MATCHALL "${SRC_REGEX}_SOURCES[ ]*\\+?=[ ]+[0-9a-z\\._ ]*"
+               FILES_PER_LINE ${MAKEFILE_AM})
+  set(SRCS ${${VAR}})
+  foreach(FILES ${FILES_PER_LINE})
+    string(FIND ${FILES} "=" OFFSET)
+    math(EXPR OFFSET "${OFFSET} + 2")
+    string(SUBSTRING ${FILES} ${OFFSET} -1 FILES)
+    if(FILES)
+      string(REGEX MATCHALL "[0-9a-z\\._]+" FILES ${FILES})
+      foreach(FILE ${FILES})
+        list(APPEND SRCS ${FOLDER}/${FILE})
+      endforeach()
+    endif()
+  endforeach()
+  set(${VAR} ${SRCS} PARENT_SCOPE)
+endfunction()
+
+set(WEBP_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
+parse_makefile_am(${WEBP_SRC_DIR}/dec "WEBP_DEC_SRCS" "")
+parse_makefile_am(${WEBP_SRC_DIR}/demux "WEBP_DEMUX_SRCS" "")
+parse_makefile_am(${WEBP_SRC_DIR}/dsp "WEBP_DSP_COMMON_SRCS" "COMMON")
+parse_makefile_am(${WEBP_SRC_DIR}/dsp "WEBP_DSP_ENC_SRCS" "ENC")
+parse_makefile_am(${WEBP_SRC_DIR}/dsp "WEBP_DSP_ENC_SRCS" "dsp_[^ ]*")
+parse_makefile_am(${WEBP_SRC_DIR}/dsp "WEBP_DSP_DEC_SRCS" "decode_[^ ]*")
+parse_makefile_am(${WEBP_SRC_DIR}/enc "WEBP_ENC_SRCS" "")
+parse_makefile_am(${WEBP_SRC_DIR}/utils "WEBP_UTILS_COMMON_SRCS" "COMMON")
+parse_makefile_am(${WEBP_SRC_DIR}/utils "WEBP_UTILS_ENC_SRCS" "ENC")
+parse_makefile_am(${WEBP_SRC_DIR}/utils "WEBP_UTILS_DEC_SRCS" "decode_[^ ]*")
+
+# Remove the files specific to SIMD we don't use.
+foreach(FILE ${WEBP_SIMD_FILES_NOT_TO_INCLUDE})
+  list(REMOVE_ITEM WEBP_DSP_ENC_SRCS ${FILE})
+  list(REMOVE_ITEM WEBP_DSP_DEC_SRCS ${FILE})
+endforeach()
+
+# Generate the config.h file.
+configure_file(${CMAKE_CURRENT_LIST_DIR}/cmake/config.h.in
+               ${CMAKE_CURRENT_BINARY_DIR}/src/webp/config.h @ONLY)
+add_definitions(-DHAVE_CONFIG_H)
+
+# Set the version numbers.
+macro(set_version FILE TARGET_NAME NAME_IN_MAKEFILE)
+  file(READ ${CMAKE_CURRENT_SOURCE_DIR}/${FILE} SOURCE_FILE)
+  string(REGEX MATCH
+               "${NAME_IN_MAKEFILE}_la_LDFLAGS[^\n]* -version-info [0-9:]+" TMP
+               ${SOURCE_FILE})
+  string(REGEX MATCH "[0-9:]+" TMP ${TMP})
+  string(REGEX REPLACE ":" " " LT_VERSION ${TMP})
+
+  # See the libtool docs for more information:
+  # https://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info
+  #
+  # c=<current>, a=<age>, r=<revision>
+  #
+  # libtool generates a .so file as .so.[c-a].a.r, while -version-info c:r:a is
+  # passed to libtool.
+  #
+  # We set FULL = [c-a].a.r and MAJOR = [c-a].
+  separate_arguments(LT_VERSION)
+  list(GET LT_VERSION 0 LT_CURRENT)
+  list(GET LT_VERSION 1 LT_REVISION)
+  list(GET LT_VERSION 2 LT_AGE)
+  math(EXPR LT_CURRENT_MINUS_AGE "${LT_CURRENT} - ${LT_AGE}")
+
+  set_target_properties(
+    ${TARGET_NAME}
+    PROPERTIES VERSION ${LT_CURRENT_MINUS_AGE}.${LT_AGE}.${LT_REVISION}
+               SOVERSION ${LT_CURRENT_MINUS_AGE})
+  if(APPLE)
+    # For compatibility, set MACHO_COMPATIBILITY_VERSION and
+    # MACHO_CURRENT_VERSION to match libtool. These properties were introduced
+    # in 3.17:
+    # https://cmake.org/cmake/help/latest/prop_tgt/MACHO_COMPATIBILITY_VERSION.html
+    math(EXPR LIBWEBP_MACHO_COMPATIBILITY_VERSION "${LT_CURRENT} + 1")
+    set_target_properties(
+      ${TARGET_NAME}
+      PROPERTIES MACHO_COMPATIBILITY_VERSION
+                 ${LIBWEBP_MACHO_COMPATIBILITY_VERSION}
+                 MACHO_CURRENT_VERSION
+                 ${LIBWEBP_MACHO_COMPATIBILITY_VERSION}.${LT_REVISION})
+  endif()
+endmacro()
+
+# ##############################################################################
+# Build the webpdecoder library.
+
+# Creates a source file with an unused stub function in $CMAKE_BINARY_DIR and
+# adds it to the specified target. Currently used only with Xcode.
+#
+# See also:
+# https://cmake.org/cmake/help/v3.18/command/add_library.html#object-libraries
+# "Some native build systems (such as Xcode) may not like targets that have only
+# object files, so consider adding at least one real source file to any target
+# that references $<TARGET_OBJECTS:objlib>."
+function(libwebp_add_stub_file TARGET)
+  set(stub_source_dir "${CMAKE_BINARY_DIR}")
+  set(stub_source_file "${stub_source_dir}/libwebp_${TARGET}_stub.c")
+  set(stub_source_code
+      "// Generated file. DO NOT EDIT!\n"
+      "// C source file created for target ${TARGET}.\n"
+      "void libwebp_${TARGET}_stub_function(void)\;\n"
+      "void libwebp_${TARGET}_stub_function(void) {}\n")
+  file(WRITE "${stub_source_file}" ${stub_source_code})
+
+  target_sources(${TARGET} PRIVATE ${stub_source_file})
+endfunction()
+
+parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/sharpyuv "WEBP_SHARPYUV_SRCS" "")
+add_library(sharpyuv ${WEBP_SHARPYUV_SRCS})
+target_link_libraries(sharpyuv ${SHARPYUV_DEP_LIBRARIES})
+set_version(sharpyuv/Makefile.am sharpyuv sharpyuv)
+target_include_directories(
+  sharpyuv PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
+                   ${CMAKE_CURRENT_SOURCE_DIR}/src)
+set_target_properties(
+  sharpyuv
+  PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/sharpyuv/sharpyuv.h;\
+${CMAKE_CURRENT_SOURCE_DIR}/sharpyuv/sharpyuv_csp.h")
+configure_pkg_config("sharpyuv/libsharpyuv.pc")
+install(
+  TARGETS sharpyuv
+  EXPORT ${PROJECT_NAME}Targets
+  PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/webp/sharpyuv
+  INCLUDES
+  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+  ${CMAKE_INSTALL_INCLUDEDIR}/webp
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+if(MSVC)
+  # avoid security warnings for e.g., fopen() used in the examples.
+  add_definitions(-D_CRT_SECURE_NO_WARNINGS)
+else()
+  add_compile_options(-Wall)
+endif()
+include_directories(${WEBP_DEP_INCLUDE_DIRS})
+add_library(webpdecode OBJECT ${WEBP_DEC_SRCS})
+target_include_directories(webpdecode PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+                                              ${CMAKE_CURRENT_SOURCE_DIR})
+add_library(webpdspdecode OBJECT ${WEBP_DSP_COMMON_SRCS} ${WEBP_DSP_DEC_SRCS})
+target_include_directories(webpdspdecode PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+                                                 ${CMAKE_CURRENT_SOURCE_DIR})
+add_library(webputilsdecode OBJECT ${WEBP_UTILS_COMMON_SRCS}
+                                   ${WEBP_UTILS_DEC_SRCS})
+target_include_directories(webputilsdecode PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+                                                   ${CMAKE_CURRENT_SOURCE_DIR})
+add_library(
+  webpdecoder $<TARGET_OBJECTS:webpdecode> $<TARGET_OBJECTS:webpdspdecode>
+              $<TARGET_OBJECTS:webputilsdecode>)
+if(XCODE)
+  libwebp_add_stub_file(webpdecoder)
+endif()
+target_link_libraries(webpdecoder ${WEBP_DEP_LIBRARIES})
+target_include_directories(
+  webpdecoder PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
+  INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+            $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
+set_target_properties(
+  webpdecoder
+  PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/src/webp/decode.h;\
+${CMAKE_CURRENT_SOURCE_DIR}/src/webp/types.h")
+
+configure_pkg_config("src/libwebpdecoder.pc")
+
+# Build the webp library.
+add_library(webpencode OBJECT ${WEBP_ENC_SRCS})
+target_include_directories(
+  webpencode PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
+                     ${CMAKE_CURRENT_SOURCE_DIR}/src)
+add_library(webpdsp OBJECT ${WEBP_DSP_COMMON_SRCS} ${WEBP_DSP_DEC_SRCS}
+                           ${WEBP_DSP_ENC_SRCS})
+target_include_directories(webpdsp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+                                           ${CMAKE_CURRENT_SOURCE_DIR})
+add_library(webputils OBJECT ${WEBP_UTILS_COMMON_SRCS} ${WEBP_UTILS_DEC_SRCS}
+                             ${WEBP_UTILS_ENC_SRCS})
+target_include_directories(webputils PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+                                             ${CMAKE_CURRENT_SOURCE_DIR})
+add_library(webp $<TARGET_OBJECTS:webpdecode> $<TARGET_OBJECTS:webpdsp>
+                 $<TARGET_OBJECTS:webpencode> $<TARGET_OBJECTS:webputils>)
+target_link_libraries(webp sharpyuv)
+if(XCODE)
+  libwebp_add_stub_file(webp)
+endif()
+target_link_libraries(webp ${WEBP_DEP_LIBRARIES})
+target_include_directories(
+  webp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}
+  PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
+         $<INSTALL_INTERFACE:include>)
+set_target_properties(
+  webp
+  PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/src/webp/decode.h;\
+${CMAKE_CURRENT_SOURCE_DIR}/src/webp/encode.h;\
+${CMAKE_CURRENT_SOURCE_DIR}/src/webp/types.h")
+
+# Make sure the OBJECT libraries are built with position independent code (it is
+# not ON by default).
+set_target_properties(webpdecode webpdspdecode webputilsdecode webpencode
+                      webpdsp webputils PROPERTIES POSITION_INDEPENDENT_CODE ON)
+configure_pkg_config("src/libwebp.pc")
+
+# Build the webp demux library.
+add_library(webpdemux ${WEBP_DEMUX_SRCS})
+target_link_libraries(webpdemux webp)
+target_include_directories(
+  webpdemux PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}
+  PUBLIC $<INSTALL_INTERFACE:include>)
+set_target_properties(
+  webpdemux
+  PROPERTIES
+    PUBLIC_HEADER
+    "${CMAKE_CURRENT_SOURCE_DIR}/src/webp/decode.h;\
+${CMAKE_CURRENT_SOURCE_DIR}/src/webp/demux.h;\
+${CMAKE_CURRENT_SOURCE_DIR}/src/webp/mux_types.h;\
+${CMAKE_CURRENT_SOURCE_DIR}/src/webp/types.h")
+
+configure_pkg_config("src/demux/libwebpdemux.pc")
+
+set_version(src/Makefile.am webp webp)
+set_version(src/Makefile.am webpdecoder webpdecoder)
+set_version(src/demux/Makefile.am webpdemux webpdemux)
+file(READ ${CMAKE_CURRENT_SOURCE_DIR}/configure.ac CONFIGURE_FILE)
+string(REGEX MATCH "AC_INIT\\([^\n]*\\[[0-9\\.]+\\]" TMP ${CONFIGURE_FILE})
+string(REGEX MATCH "[0-9\\.]+" PROJECT_VERSION ${TMP})
+
+# Define the libraries to install.
+list(APPEND INSTALLED_LIBRARIES webpdecoder webp webpdemux)
+
+# Deal with SIMD. Change the compile flags for SIMD files we use.
+list(LENGTH WEBP_SIMD_FILES_TO_INCLUDE WEBP_SIMD_FILES_TO_INCLUDE_LENGTH)
+math(EXPR WEBP_SIMD_FILES_TO_INCLUDE_RANGE
+     "${WEBP_SIMD_FILES_TO_INCLUDE_LENGTH}-1")
+
+foreach(I_FILE RANGE ${WEBP_SIMD_FILES_TO_INCLUDE_RANGE})
+  list(GET WEBP_SIMD_FILES_TO_INCLUDE ${I_FILE} FILE)
+  list(GET WEBP_SIMD_FLAGS_TO_INCLUDE ${I_FILE} SIMD_COMPILE_FLAG)
+  set_source_files_properties(${FILE} PROPERTIES COMPILE_FLAGS
+                                                 ${SIMD_COMPILE_FLAG})
+endforeach()
+
+if(NOT WEBP_BUILD_LIBWEBPMUX)
+  set(WEBP_BUILD_GIF2WEBP OFF)
+  set(WEBP_BUILD_IMG2WEBP OFF)
+  set(WEBP_BUILD_WEBPMUX OFF)
+endif()
+
+if(WEBP_BUILD_GIF2WEBP AND NOT GIF_FOUND)
+  set(WEBP_BUILD_GIF2WEBP OFF)
+endif()
+
+if(WEBP_BUILD_ANIM_UTILS AND NOT GIF_FOUND)
+  set(WEBP_BUILD_ANIM_UTILS OFF)
+endif()
+
+# Build the executables if asked for.
+if(WEBP_BUILD_ANIM_UTILS
+   OR WEBP_BUILD_CWEBP
+   OR WEBP_BUILD_DWEBP
+   OR WEBP_BUILD_GIF2WEBP
+   OR WEBP_BUILD_IMG2WEBP
+   OR WEBP_BUILD_VWEBP
+   OR WEBP_BUILD_WEBPMUX
+   OR WEBP_BUILD_WEBPINFO)
+  # Example utility library.
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "EXAMPLEUTIL_SRCS"
+                    "example_util_[^ ]*")
+  list(APPEND EXAMPLEUTIL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/examples/stopwatch.h)
+  add_library(exampleutil STATIC ${EXAMPLEUTIL_SRCS})
+  target_include_directories(
+    exampleutil PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>)
+
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/imageio "IMAGEIOUTILS_SRCS"
+                    "imageio_util_[^ ]*")
+  add_library(imageioutil STATIC ${IMAGEIOUTILS_SRCS})
+  target_link_libraries(imageioutil webp)
+  target_link_libraries(exampleutil imageioutil)
+
+  # Image-decoding utility library.
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/imageio "IMAGEDEC_SRCS"
+                    "imagedec_[^ ]*")
+  add_library(imagedec STATIC ${IMAGEDEC_SRCS})
+  target_link_libraries(imagedec imageioutil webpdemux webp
+                        ${WEBP_DEP_IMG_LIBRARIES})
+
+  # Image-encoding utility library.
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/imageio "IMAGEENC_SRCS"
+                    "imageenc_[^ ]*")
+  add_library(imageenc STATIC ${IMAGEENC_SRCS})
+  target_link_libraries(imageenc imageioutil webp)
+
+  set_property(
+    TARGET exampleutil imageioutil imagedec imageenc
+    PROPERTY INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/src
+             ${CMAKE_CURRENT_BINARY_DIR}/src)
+endif()
+
+if(WEBP_BUILD_DWEBP)
+  # dwebp
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "DWEBP_SRCS" "dwebp")
+  add_executable(dwebp ${DWEBP_SRCS})
+  target_link_libraries(dwebp exampleutil imagedec imageenc)
+  target_include_directories(dwebp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
+  install(TARGETS dwebp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif()
+
+if(WEBP_BUILD_CWEBP)
+  # cwebp
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "CWEBP_SRCS" "cwebp")
+  add_executable(cwebp ${CWEBP_SRCS})
+  target_link_libraries(cwebp exampleutil imagedec webp)
+  target_include_directories(cwebp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src
+                                           ${CMAKE_CURRENT_SOURCE_DIR})
+  install(TARGETS cwebp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif()
+
+if(WEBP_BUILD_LIBWEBPMUX)
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/src/mux "WEBP_MUX_SRCS" "")
+  add_library(libwebpmux ${WEBP_MUX_SRCS})
+  target_link_libraries(libwebpmux webp)
+  target_include_directories(libwebpmux PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
+                                                ${CMAKE_CURRENT_SOURCE_DIR})
+  set_version(src/mux/Makefile.am libwebpmux webpmux)
+  set_target_properties(
+    libwebpmux
+    PROPERTIES PUBLIC_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/src/webp/mux.h;\
+${CMAKE_CURRENT_SOURCE_DIR}/src/webp/mux_types.h;\
+${CMAKE_CURRENT_SOURCE_DIR}/src/webp/types.h;")
+  set_target_properties(libwebpmux PROPERTIES OUTPUT_NAME webpmux)
+  list(APPEND INSTALLED_LIBRARIES libwebpmux)
+  configure_pkg_config("src/mux/libwebpmux.pc")
+endif()
+
+if(WEBP_BUILD_GIF2WEBP)
+  # gif2webp
+  include_directories(${WEBP_DEP_GIF_INCLUDE_DIRS})
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "GIF2WEBP_SRCS"
+                    "gif2webp")
+  add_executable(gif2webp ${GIF2WEBP_SRCS})
+  target_link_libraries(gif2webp exampleutil imageioutil webp libwebpmux
+                        ${WEBP_DEP_GIF_LIBRARIES})
+  target_include_directories(gif2webp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
+  install(TARGETS gif2webp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif()
+
+if(WEBP_BUILD_IMG2WEBP)
+  # img2webp
+  include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "IMG2WEBP_SRCS"
+                    "img2webp")
+  add_executable(img2webp ${IMG2WEBP_SRCS})
+  target_link_libraries(img2webp exampleutil imagedec imageioutil webp
+                        libwebpmux)
+  target_include_directories(img2webp PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
+  install(TARGETS img2webp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif()
+
+if(WEBP_BUILD_VWEBP)
+  # vwebp
+  find_package(GLUT)
+  if(GLUT_FOUND)
+    include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
+    parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "VWEBP_SRCS" "vwebp")
+    add_executable(vwebp ${VWEBP_SRCS})
+    target_link_libraries(
+      vwebp
+      ${OPENGL_LIBRARIES}
+      exampleutil
+      GLUT::GLUT
+      imageioutil
+      webp
+      webpdemux)
+    target_include_directories(
+      vwebp PRIVATE ${GLUT_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/src
+                    ${OPENGL_INCLUDE_DIR})
+    install(TARGETS vwebp RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+    if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
+      check_c_compiler_flag("-Wno-deprecated-declarations" HAS_NO_DEPRECATED)
+      if(HAS_NO_DEPRECATED)
+        target_compile_options(vwebp PRIVATE "-Wno-deprecated-declarations")
+      endif()
+    endif()
+  endif()
+endif()
+
+if(WEBP_BUILD_WEBPINFO)
+  # webpinfo
+  include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS})
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "WEBPINFO_SRCS"
+                    "webpinfo")
+  add_executable(webpinfo ${WEBPINFO_SRCS})
+  target_link_libraries(webpinfo exampleutil imageioutil)
+  target_include_directories(webpinfo PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src
+                                              ${CMAKE_CURRENT_SOURCE_DIR}/src)
+  install(TARGETS webpinfo RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif()
+
+if(WEBP_BUILD_WEBPMUX)
+  # webpmux
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "WEBPMUX_SRCS"
+                    "webpmux")
+  add_executable(webpmux ${WEBPMUX_SRCS})
+  target_link_libraries(webpmux exampleutil imageioutil libwebpmux webp)
+  target_include_directories(webpmux PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
+  install(TARGETS webpmux RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+endif()
+
+if(WEBP_BUILD_EXTRAS)
+  set(EXTRAS_MAKEFILE "${CMAKE_CURRENT_SOURCE_DIR}/extras")
+  parse_makefile_am(${EXTRAS_MAKEFILE} "WEBP_EXTRAS_SRCS" "libwebpextras_la")
+  parse_makefile_am(${EXTRAS_MAKEFILE} "GET_DISTO_SRCS" "get_disto")
+  parse_makefile_am(${EXTRAS_MAKEFILE} "WEBP_QUALITY_SRCS" "webp_quality")
+  parse_makefile_am(${EXTRAS_MAKEFILE} "VWEBP_SDL_SRCS" "vwebp_sdl")
+
+  # libextras
+  add_library(extras STATIC ${WEBP_EXTRAS_SRCS})
+  target_include_directories(
+    extras PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
+                   ${CMAKE_CURRENT_SOURCE_DIR}/src)
+
+  # get_disto
+  add_executable(get_disto ${GET_DISTO_SRCS})
+  target_link_libraries(get_disto imagedec)
+  target_include_directories(get_disto PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+                                               ${CMAKE_CURRENT_BINARY_DIR}/src)
+
+  # webp_quality
+  add_executable(webp_quality ${WEBP_QUALITY_SRCS})
+  target_link_libraries(webp_quality exampleutil imagedec extras)
+  target_include_directories(webp_quality PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+                                                  ${CMAKE_CURRENT_BINARY_DIR})
+
+  # vwebp_sdl
+  find_package(SDL)
+  if(WEBP_BUILD_VWEBP AND SDL_FOUND)
+    add_executable(vwebp_sdl ${VWEBP_SDL_SRCS})
+    target_link_libraries(vwebp_sdl ${SDL_LIBRARY} imageioutil webp)
+    target_include_directories(
+      vwebp_sdl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}
+                        ${CMAKE_CURRENT_BINARY_DIR}/src ${SDL_INCLUDE_DIR})
+    set(WEBP_HAVE_SDL 1)
+    target_compile_definitions(vwebp_sdl PUBLIC WEBP_HAVE_SDL)
+  endif()
+endif()
+
+if(WEBP_BUILD_WEBP_JS)
+  # wasm2js does not support SIMD.
+  if(NOT WEBP_ENABLE_SIMD)
+    # JavaScript version
+    add_executable(webp_js ${CMAKE_CURRENT_SOURCE_DIR}/extras/webp_to_sdl.c)
+    target_link_libraries(webp_js webpdecoder SDL)
+    target_include_directories(webp_js PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
+    set(WEBP_HAVE_SDL 1)
+    set_target_properties(
+      webp_js
+      PROPERTIES LINK_FLAGS "-sWASM=0 \
+         -sEXPORTED_FUNCTIONS=_WebPToSDL -sINVOKE_RUN=0 \
+         -sEXPORTED_RUNTIME_METHODS=cwrap")
+    set_target_properties(webp_js PROPERTIES OUTPUT_NAME webp)
+    target_compile_definitions(webp_js PUBLIC EMSCRIPTEN WEBP_HAVE_SDL)
+  endif()
+
+  # WASM version
+  add_executable(webp_wasm ${CMAKE_CURRENT_SOURCE_DIR}/extras/webp_to_sdl.c)
+  target_link_libraries(webp_wasm webpdecoder SDL)
+  target_include_directories(webp_wasm PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
+  set_target_properties(
+    webp_wasm
+    PROPERTIES LINK_FLAGS "-sWASM=1 \
+       -sEXPORTED_FUNCTIONS=_WebPToSDL -sINVOKE_RUN=0 \
+       -sEXPORTED_RUNTIME_METHODS=cwrap")
+  target_compile_definitions(webp_wasm PUBLIC EMSCRIPTEN WEBP_HAVE_SDL)
+
+  target_compile_definitions(webpdspdecode PUBLIC EMSCRIPTEN)
+endif()
+
+if(WEBP_BUILD_ANIM_UTILS)
+  # anim_diff
+  include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS} ${WEBP_DEP_GIF_INCLUDE_DIRS})
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "ANIM_DIFF_SRCS"
+                    "anim_diff")
+  add_executable(anim_diff ${ANIM_DIFF_SRCS})
+  target_link_libraries(
+    anim_diff
+    exampleutil
+    imagedec
+    imageenc
+    imageioutil
+    webp
+    webpdemux
+    ${WEBP_DEP_GIF_LIBRARIES})
+  target_include_directories(anim_diff PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
+
+  # anim_dump
+  include_directories(${WEBP_DEP_IMG_INCLUDE_DIRS} ${WEBP_DEP_GIF_INCLUDE_DIRS})
+  parse_makefile_am(${CMAKE_CURRENT_SOURCE_DIR}/examples "ANIM_DUMP_SRCS"
+                    "anim_dump")
+  add_executable(anim_dump ${ANIM_DUMP_SRCS})
+  target_link_libraries(
+    anim_dump
+    exampleutil
+    imagedec
+    imageenc
+    imageioutil
+    webp
+    webpdemux
+    ${WEBP_DEP_GIF_LIBRARIES})
+  target_include_directories(anim_dump PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
+endif()
+
+# Install the different headers and libraries.
+install(
+  TARGETS ${INSTALLED_LIBRARIES}
+  EXPORT ${PROJECT_NAME}Targets
+  PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/webp
+  INCLUDES
+  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+set(ConfigPackageLocation ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/cmake/)
+install(EXPORT ${PROJECT_NAME}Targets NAMESPACE ${PROJECT_NAME}::
+        DESTINATION ${ConfigPackageLocation})
+
+# Create the CMake version file.
+include(CMakePackageConfigHelpers)
+write_basic_package_version_file(
+  "${CMAKE_CURRENT_BINARY_DIR}/WebPConfigVersion.cmake"
+  VERSION ${PACKAGE_VERSION} COMPATIBILITY AnyNewerVersion)
+
+# Create the Config file.
+include(CMakePackageConfigHelpers)
+# Fix libwebpmux reference. The target name libwebpmux is used for compatibility
+# purposes, but the library mentioned in WebPConfig.cmake should be the
+# unprefixed version. Note string(...) can be replaced with list(TRANSFORM ...)
+# if cmake_minimum_required is >= 3.12.
+string(REGEX REPLACE "libwebpmux" "webpmux" INSTALLED_LIBRARIES
+                     "${INSTALLED_LIBRARIES}")
+
+if(MSVC)
+  # For compatibility with nmake, MSVC builds use a custom prefix (lib) that
+  # needs to be included in the library name.
+  string(REGEX REPLACE "[A-Za-z0-9_]+" "${CMAKE_STATIC_LIBRARY_PREFIX}\\0"
+                       INSTALLED_LIBRARIES "${INSTALLED_LIBRARIES}")
+endif()
+
+configure_package_config_file(
+  ${CMAKE_CURRENT_SOURCE_DIR}/cmake/WebPConfig.cmake.in
+  ${CMAKE_CURRENT_BINARY_DIR}/WebPConfig.cmake
+  INSTALL_DESTINATION ${ConfigPackageLocation})
+
+# Install the generated CMake files.
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/WebPConfigVersion.cmake"
+              "${CMAKE_CURRENT_BINARY_DIR}/WebPConfig.cmake"
+        DESTINATION ${ConfigPackageLocation})
+
+# Install the man pages.
+set(MAN_PAGES
+    cwebp.1
+    dwebp.1
+    gif2webp.1
+    img2webp.1
+    vwebp.1
+    webpmux.1
+    webpinfo.1)
+set(EXEC_BUILDS
+    "CWEBP"
+    "DWEBP"
+    "GIF2WEBP"
+    "IMG2WEBP"
+    "VWEBP"
+    "WEBPMUX"
+    "WEBPINFO")
+list(LENGTH MAN_PAGES MAN_PAGES_LENGTH)
+math(EXPR MAN_PAGES_RANGE "${MAN_PAGES_LENGTH} - 1")
+
+foreach(I_MAN RANGE ${MAN_PAGES_RANGE})
+  list(GET EXEC_BUILDS ${I_MAN} EXEC_BUILD)
+  if(WEBP_BUILD_${EXEC_BUILD})
+    list(GET MAN_PAGES ${I_MAN} MAN_PAGE)
+    install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/man/${MAN_PAGE}
+            DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 COMPONENT doc)
+  endif()
+endforeach()
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..7a73a30
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,29 @@
+# How to Contribute
+
+We'd love to accept your patches and contributions to this project. There are
+just a few small guidelines you need to follow.
+
+## Contributor License Agreement
+
+Contributions to this project must be accompanied by a Contributor License
+Agreement. You (or your employer) retain the copyright to your contribution;
+this simply gives us permission to use and redistribute your contributions as
+part of the project. Head over to <https://cla.developers.google.com/> to see
+your current agreements on file or to sign a new one.
+
+You generally only need to submit a CLA once, so if you've already submitted one
+(even if it was for a different project), you probably don't need to do it
+again.
+
+## Code reviews
+
+All submissions, including submissions by project members, require review. We
+use a [Gerrit](https://www.gerritcodereview.com) instance hosted at
+https://chromium-review.googlesource.com for this purpose. See the
+[WebM Project page](https://www.webmproject.org/code/contribute/submitting-patches/)
+for additional details.
+
+## Community Guidelines
+
+This project follows
+[Google's Open Source Community Guidelines](https://opensource.google.com/conduct/).
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..00ef617
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,4547 @@
+0ba77244 update NEWS
+e763eb1e bump version to 1.3.0
+2a8686fc update AUTHORS
+106a57c1 Merge "*/Android.mk: add a check for NDK_ROOT" into main
+c5e841c4 Merge "extras: WebpToSDL -> WebPToSDL" into main
+dbc30715 Merge "xcframeworkbuild.sh: bump MACOSX_CATALYST_MIN_VERSION" into main
+6fc1a9f9 */Android.mk: add a check for NDK_ROOT
+d3e151fc doc/api.md,webp_js/README.md: Webp -> WebP
+ed92a626 extras: WebpToSDL -> WebPToSDL
+6eb0189b xcframeworkbuild.sh: bump MACOSX_CATALYST_MIN_VERSION
+1d58575b CMake: align .pc variables with autoconf
+e5fe2cfc webp-lossless-bitstream-spec,cosmetics: reflow paragraphs
+0ceeeab9 webp-lossless-bitstream-spec: add amendment note
+607611cd Merge "webp-container-spec: normalize section title case" into main
+f853685e lossless: SUBTRACT_GREEN -> SUBTRACT_GREEN_TRANSFORM
+786497e4 webp-lossless-bitstream-spec: fix inv color txfm description
+c6ac672d webp-lossless-bitstream-spec: fix num_code_lengths check
+b5700efb webp-lossless-bitstream-spec,cosmetics: grammar/capitalization
+d8ed8c11 webp-container-spec: normalize section title case
+52ec0b8f Merge changes Ie975dbb5,Ifc8c93af,I6ca7c5d6,I2e8d66f5,I152477b8 into main
+5097ef62 webp-container-spec,cosmetics: grammar/capitalization
+e3ba2b1f webp-lossless-bitstream-spec,cosmetics: reflow abstract
+1e8e3ded webp-lossless-bitstream-spec: reword abstract re alpha
+017cb6fa webp-container-spec,cosmetics: normalize range syntax
+f6a4684b webp-lossless-bitstream-spec,cosmetics: normalize range syntax
+54ebd5a3 webp-lossless-bitstream-spec: limit dist map lut to 69 cols
+44741f9c webp-lossless-bitstream-spec: fix dist mapping example
+fad0ece7 pnmdec.c: use snprintf instead of sprintf
+3f73e8f7 sharpyuv: add SharpYuvGetVersion()
+ce2f2d66 SharpYuvConvert: fix a race on SharpYuvGetCPUInfo
+a458e308 sharpyuv_dsp.h: restore sharpyuv_cpu.h include
+9ba800a7 Merge changes Id72fbf3b,Ic59d23a2 into main
+979c0ebb sharpyuv: add SharpYuvGetCPUInfo
+8bab09a4 Merge "*.pc.in: rename lib_prefix to webp_libname_prefix" into main
+769387c5 cpu.c,cosmetics: fix a typo
+a02978c2 sharpyuv/Makefile.am+cmake: add missing -lm
+28aedcb9 *.pc.in: rename lib_prefix to webp_libname_prefix
+c42e6d5a configure.ac: export an empty lib_prefix variable
+dfc843aa Merge "*.pc.in: add lib prefix to lib names w/MSVC" into main
+2498209b *.pc.in: add lib prefix to lib names w/MSVC
+ac252b61 Merge "analysis_enc.c: fix a dead store warning" into main
+56944762 analysis_enc.c: fix a dead store warning
+d34f9b99 Merge "webp-lossless-bitstream-spec: convert BNF to ABNF" into main
+dc05b4db Merge changes I96bc063c,I45880467,If9e18e5a,I6ee938e4,I0a410b28, ... into main
+83270c7f webp-container-spec: add prose for rendering process
+73b19b64 webp-container-spec: note reserved fields MUST be ignored
+57101d3f webp-lossless-bitstream-spec: improve 'small' color table stmt
+dfd32e45 webp-container-spec: remove redundant sentence
+8a6185dd doc/webp-*: fix some punctuation, grammar
+72776530 webp-lossless-bitstream-spec: convert BNF to ABNF
+d992bb08 cmake: rename cpufeatures target to cpufeatures-webp
+3ed2b275 webp-container-spec: clarify background color note
+951c292d webp-container-spec: come too late -> out of order
+902dd787 webp-container-spec: prefer hex literals
+a8f6b5ee webp-container-spec: change SHOULD to MUST w/ANIM chunk
+1dc59435 webp-container-spec: add unknown fields MUST be ignored
+280a810f webp-container-spec: make padding byte=0 a MUST
+41f0bf68 webp-container-spec: update note on trailing data
+6bdd36db webp-container-spec: clarify Chunk Size is in bytes
+87e36c48 Merge "webp_js/README.md,cosmetics: reflow some lines" into main
+5b01f321 Merge "Update Windows makefile to build libsharpyuv library." into main
+19b1a71c webp_js/README.md,cosmetics: reflow some lines
+780db756 Update Windows makefile to build libsharpyuv library.
+e407d4b3 CMakeLists.txt: replace GLUT_glut_LIBRARY w/GLUT::GLUT
+abf73d62 Merge "WebPConfig.cmake.in: add find_dependency(Threads)" into main
+25807fb4 Merge "cmake: restore compatibility with cmake < 3.12" into main
+5dbc4bfa WebPConfig.cmake.in: add find_dependency(Threads)
+b2a175dd Merge "Update wasm instructions." into main
+cb90f76b Update wasm instructions.
+02d15258 cmake: restore compatibility with cmake < 3.12
+5ba046e2 CMake: add_definitions -> add_compile_options
+e68765af dsp,neon: use vaddv in a few more places
+e8f83de2 Set libsharpyuv include dir to 'webp' subdirectory.
+15a91ab1 cmake,cosmetics: apply cmake-format
+0dd49d1a CMakeLists.txt: set @ONLY in configure_file() calls
+62b1bfe8 Merge changes I2877e7bb,I777cad70,I15af7d1a,I686e6740,If10538a9, ... into main
+95c8fe5f Merge changes Iecea3603,I9dc228ab into main
+e7c805cf picture_csp_enc.c: remove SafeInitSharpYuv
+6af8845a sharpyuv: prefer webp/types.h
+639619ce cmake: fix dll exports
+782ed48c sharpyuv,SharpYuvInit: add mutex protection when available
+cad0d5ad sharyuv_{neon,sse2}.c: merge WEBP_USE_* sections
+ef70ee06 add a few missing <stddef.h> includes for NULL
+f0f9eda4 sharpyuv.h: remove <inttypes.h>
+9b902cba Merge "picture_csp_enc.c,CheckNonOpaque: rm unneeded local" into main
+9c1d457c cmake/cpu.cmake: remove unused variable
+9ac25bcb CMakeLists.txt,win32: match naming convention used by nmake
+76c353ba picture_csp_enc.c,CheckNonOpaque: rm unneeded local
+5000de54 Merge "cwebp: fix WebPPictureHasTransparency call" into main
+e1729309 Merge "WebPPictureHasTransparency: add missing pointer check" into main
+00ff988a vp8l_enc,AddSingleSubGreen: clear int sanitizer warnings
+e2fecc22 dsp/lossless_enc.c: clear int sanitizer warnings
+129cf9e9 dsp/lossless.c: clear int sanitizer warnings
+ad7d1753 dsp/lossless_enc.c: clear int sanitizer warnings
+5037220e VP8LSubtractGreenFromBlueAndRed_C: clear int sanitizer warnings
+2ee786c7 upsampling_sse2.c: clear int sanitizer warnings
+4cc157d4 ParseOptionalChunks: clear int sanitizer warning
+892cf033 BuildHuffmanTable: clear int sanitizer warning
+3a9a4d45 VP8GetSigned: clear int sanitizer warnings
+704a3d0a dsp/lossless.c: quiet int sanitizer warnings
+1a6c109c WebPPictureHasTransparency: add missing pointer check
+c626e7d5 cwebp: fix WebPPictureHasTransparency call
+866e349c Merge tag 'v1.2.4'
+c170df38 Merge "Create libsharpyuv.a in makefile.unix." into main
+9d7ff74a Create libsharpyuv.a in makefile.unix.
+0d1f1254 update ChangeLog (tag: v1.2.4, origin/1.2.4)
+fcbc2d78 Merge "doc/*.txt: restrict code to 69 columns" into main
+4ad0e189 Merge "webp-container-spec.txt: normalize fourcc spelling" into main
+980d2488 update NEWS
+9fde8127 bump version to 1.2.4
+7a0a9935 doc/*.txt: restrict code to 69 columns
+c040a615 webp-container-spec.txt: normalize fourcc spelling
+aff1c546 dsp,x86: normalize types w/_mm_cvtsi128_si32 calls
+ab540ae0 dsp,x86: normalize types w/_mm_cvtsi32_si128 calls
+8980362e dsp,x86: normalize types w/_mm_set* calls (2)
+e626925c lossless: fix crunch mode w/WEBP_REDUCE_SIZE
+83539239 dsp,x86: normalize types w/_mm_set* calls
+8a4576ce webp-container-spec.txt: replace &amp; with &
+db870881 Merge "webp-container-spec.txt: make reserved 0 values a MUST" into main
+01d7d378 webp-lossless-bitstream-spec: number all sections
+337cf69f webp-lossless-bitstream-spec: mv Nomenclature after Intro
+79be856e Merge changes I7111d1f7,I872cd62c into main
+5b87983a webp-container-spec.txt: make reserved 0 values a MUST
+bd939123 Merge changes I7a25b1a6,I51b2c2a0,I87d0cbcf,I6ec60af6,I0a3fe9dc into main
+04764b56 libwebp.pc: add libsharpyuv to requires
+7deee810 libsharpyuv: add pkg-config file
+1a64a7e6 webp-container-spec.txt: clarify some SHOULDs
+bec2c88a webp-container-spec.txt: move ChunkHeader to terminology
+c9359332 webp-container-spec.txt: clarify 'VP8 '/'XMP ' fourccs
+70fe3063 webp-container-spec.txt: rightsize table entries
+ddbf3f3f webp-container-spec.txt: update 'key words' text
+c151e95b utils.h,WEBP_ALIGN: make bitmask unsigned
+748e92bb add WebPInt32ToMem
+3fe15b67 Merge "Build libsharpyuv as a full installable library." into main
+4f402f34 add WebPMemToInt32
+a3b68c19 Build libsharpyuv as a full installable library.
+b4994eaa CMake: set rpath for shared objects
+94cd7117 Merge "CMake: fix dylib versioning" into main
+e91451b6 Fix the lossless specs a bit more.
+231bdfb7 CMake: fix dylib versioning
+bfad7ab5 CMakeLists.txt: correct libwebpmux name in WebPConfig.cmake
+c2e3fd30 Revert "cmake: fix webpmux lib name for cmake linking"
+7366f7f3 Merge "lossless: fix crunch mode w/WEBP_REDUCE_SIZE" into main
+84163d9d lossless: fix crunch mode w/WEBP_REDUCE_SIZE
+d01c1eb3 webp-lossless-bitstream-spec,cosmetics: normalize capitalization
+8813ca8e Merge tag 'v1.2.3'
+3c4a0fbf update ChangeLog (tag: v1.2.3)
+56a480e8 dsp/cpu.h: add missing extern "C"
+62b45bdd update ChangeLog (tag: v1.2.3-rc1)
+8764ec7a Merge changes Idb037953,Id582e395 into 1.2.3
+bcb872c3 vwebp: fix file name display in windows unicode build
+67c44ac5 webpmux: fix -frame option in windows unicode build
+8278825a makefile.unix: add sharpyuv objects to clean target
+14a49e01 update NEWS
+34b1dc33 bump version to 1.2.3
+0b397fda update AUTHORS
+c16488ac update .mailmap
+5a2d929c Merge "unicode.h: set console mode before using wprintf" into main
+169f867f unicode.h: set console mode before using wprintf
+a94b855c Merge "libsharpyuv: add version defines" into main
+f83bdb52 libsharpyuv: add version defines
+bef0d797 unicode_gif.h: fix -Wdeclaration-after-statement
+404c1622 Rename Huffman coding to prefix coding in the bitstream spec
+8895f8a3 Merge "run_static_analysis.sh: fix scan-build archive path" into main
+92a673d2 Merge "Add -fvisibility=hidden flag in CMakeLists." into main
+67c1d722 Merge "add WEBP_MSAN" into main
+1124ff66 Add -fvisibility=hidden flag in CMakeLists.
+e15b3560 add WEBP_MSAN
+ec9e782a sharpyuv: remove minimum image size from sharpyuv library
+7bd07f3b run_static_analysis.sh: fix scan-build archive path
+5ecee06f Merge "sharpyuv: increase precision of gamma<->linear conversion" into main
+f81dd7d6 Merge changes I3d17d529,I53026880,I1bd61639,I6bd4b25d,Icfec8fba into main
+2d607ee6 sharpyuv: increase precision of gamma<->linear conversion
+266cbbc5 sharpyuv: add 32bit version of SharpYuvFilterRow.
+9fc12274 CMake: add src to webpinfo includes
+7d18f40a CMake: add WEBP_BUILD_WEBPINFO to list of checks for exampleutil
+11309aa5 CMake: add WEBP_BUILD_WEBPMUX to list of checks for exampleutil
+4bc762f7 CMake: link imageioutil to exampleutil after defined
+0d1b9bc4 WEBP_DEP_LIBRARIES: use Threads::Threads
+20ef48f0 Merge "sharpyuv: add support for 10/12/16 bit rgb and 10/12 bit yuv." into main
+93c54371 sharpyuv: add support for 10/12/16 bit rgb and 10/12 bit yuv.
+53cf2b49 normalize WebPValidatePicture declaration w/definition
+d3006f4b sharpyuv: slightly improve precision
+ea967098 Merge changes Ia01bd397,Ibf3771af into main
+11bc8410 Merge changes I2d317c4b,I9e77f6db into main
+30453ea4 Add an internal WebPValidatePicture.
+6c43219a Some renamings for consistency.
+4f59fa73 update .mailmap
+e74f8a62 webp-lossless-bitstream-spec,cosmetics: normalize range syntax
+5a709ec0 webp-lossless-bitstream-spec,cosmetics: fix code typo
+a2093acc webp-lossless-bitstream-spec: add amendment note
+86c66930 webp-lossless-bitstream-spec: fix BNF
+232f22da webp-lossless-bitstream-spec: fix 'simple code' snippet
+44dd765d webp-lossless-bitstream-spec: fix ColorTransform impl
+7a7e33e9 webp-lossless-bitstream-spec: fix TR-pixel right border note
+86f94ee0 Update lossless spec with Huffman codes.
+a3927cc8 sharpyuv.c,cosmetics: fix indent
+6c45cef7 Make sure the stride has a minimum value in the importer.
+0c8b0e67 sharpyuv: cleanup/cosmetic changes
+dc3841e0 {histogram,predictor}_enc: quiet int -> float warnings
+a19a25bb Replace doubles by floats in lossless misc cost estimations.
+42888f6c Add an option to enable static builds.
+7efcf3cc Merge "Fix typo in color constants: Marix -> Matrix" into main
+8f4b5c62 Fix typo in color constants: Marix -> Matrix
+90084d84 Merge "demux,IsValidExtendedFormat: remove unused variable" into main
+ed643f61 Merge changes I452d2485,Ic6d75475 into main
+8fa053d1 Rename SharpYUV to SharpYuv for consistency.
+99a87562 SharpYuvComputeConversionMatrix: quiet int->float warnings
+deb426be Makefile.vc: add sharpyuv_csp.obj to SHARPYUV_OBJS
+779597d4 demux,IsValidExtendedFormat: remove unused variable
+40e8aa57 Merge "libsharpyuv: add colorspace utilities" into main
+01a05de1 libsharpyuv: add colorspace utilities
+2de4b05a Merge changes Id9890a60,I376d81e6,I1c958838 into main
+b8bca81f Merge "configure.ac: use LT_INIT if available" into main
+e8e77b9c Merge changes I479bc487,I39864691,I5d486c2c,I186d13be into main
+7e7d5d50 Merge ".gitignore: add Android Studio & VS code dirs" into main
+10c50848 normalize label indent
+89f774e6 mux{edit,internal}: fix leaks on error
+2d3293ad ExUtilInitCommandLineArguments: fix leak on error
+ec34fd70 anim_util: fix leaks on error
+e4717287 gif2webp: fix segfault on OOM
+e3cfafaf GetBackwardReferences: fail on alloc error
+a828a59b BackwardReferencesHashChainDistanceOnly: fix segfault on OOM
+fe153fae VP8LEncodeStream: fix segfault on OOM
+919acc0e .gitignore: add Android Studio & VS code dirs
+efa0731b configure.ac: use LT_INIT if available
+0957fd69 tiffdec: add grayscale support
+e685feef Merge "Make libsharpyuv self-contained by removing dependency on cpu.c" into main
+841960b6 Make libsharpyuv self-contained by removing dependency on cpu.c
+617cf036 image_dec: add WebPGetEnabledInputFileFormats()
+7a68afaa Let SharpArgbToYuv caller pass in an RGB>YUV conversion matrix.
+34bb332c man/cwebp.1: add note about crop/resize order
+f0e9351c webp-lossless-bitstream-spec,cosmetics: fix some typos
+5ccbd6ed vp8l_dec.c,cosmetics: fix a few typos
+c3d0c2d7 fix ios build scripts after sharpyuv dep added
+d0d2292e Merge "Make libwebp depend on libsharpyuv." into main
+03d12190 alpha_processing_neon.c: fix 0x01... typo
+d55d447c Make libwebp depend on libsharpyuv.
+e4cbcdd2 Fix lossless encoding for MIPS.
+924e7ca6 alpha_processing_neon.c: fix Dispatch/ExtractAlpha_NEON
+0fa0ea54 Makefile.vc: use /MANIFEST:EMBED
+29cc95ce Basic version of libsharpyuv in libwebp, in C.
+a30f2190 examples/webpmux.c: fix a couple of typos
+66b3ce23 Fix bad overflow check in ReadTIFF()
+54e61a38 Markdownify libwebp docs and reorganize them.
+b4533deb CMakeLists.txt,cosmetics: break long line
+b9d2f9cd quant_enc.c: use WEBP_RESTRICT qualifier
+ec178f2c Add progress hook granularity in lossless
+26139c73 Rename MAX_COST to MAX_BIT_COST in histogram_enc.c
+13b82816 cmake: fix webpmux lib name for cmake linking
+88b6a396 webp-container-spec.txt,cosmetics: normalize formatting
+6f496540 Merge tag 'v1.2.2'
+4074acf8 dsp.h: bump msvc arm64 version requirement to 16.6
+b0a86089 update ChangeLog (tag: v1.2.2)
+6db8248c libwebp: Fix VP8EncTokenLoop() progress
+827a307f BMP enc: fix the transparency case
+db25f1b4 libwebp: Fix VP8EncTokenLoop() progress
+286e7fce libwebp: do not destroy jpeg codec twice on error
+6e8a4126 libwebp: do not destroy jpeg codec twice on error
+faf21968 Merge "BMP enc: fix the transparency case" into main
+480cd51d BMP enc: fix the transparency case
+9195ea05 update ChangeLog (tag: v1.2.2-rc2)
+4acae017 update NEWS
+883f0633 man/img2webp.1: update date
+567e1f44 Reword img2webp synopsis command line
+1b0c15db man/img2webp.1: update date
+17bade38 Merge "Reword img2webp synopsis command line" into main
+a80954a1 Reword img2webp synopsis command line
+f084244d anim_decode: fix alpha blending with big-endian
+b217b4ff webpinfo: fix fourcc comparison w/big-endian
+ec497b75 Merge "anim_decode: fix alpha blending with big-endian" into main
+e4886716 anim_decode: fix alpha blending with big-endian
+e3cb052c webpinfo: fix fourcc comparison w/big-endian
+a510fedb patch-check: detect duplicated files
+f035d2e4 update ChangeLog (tag: v1.2.2-rc1)
+7031946a update NEWS
+973390b6 bump version to 1.2.2
+abd6664f update AUTHORS
+5b7e7930 Merge "add missing USE_{MSA,NEON} checks in headers" into main
+02ca04c3 add missing USE_{MSA,NEON} checks in headers
+e94716e2 xcframeworkbuild.sh: place headers in a subdir
+c846efd8 patch-check: commit subject length check
+b6f756e8 update http links
+8f5cb4c1 update rfc links
+8ea81561 change VP8LPredictorFunc signature to avoid reading 'left'
+6b1d18c3 webpmux: fix the -bgcolor description
+3368d876 Merge "webpmux: add "-set bgcolor A,R,G,B"" into main
+f213abf6 webpinfo: print the number of warnings
+50c97c30 webpmux: add "-set bgcolor A,R,G,B"
+2c206aaf Remove CMakeLists.txt check in compile.sh
+96e3dfef Merge "infra/common.sh: add shard_should_run()" into main
+0e0f74b7 infra/common.sh: add shard_should_run()
+35b7436a Jenkins scripts port: update shell function comments
+21d24b4c webp-container-spec.txt: remove 'experimental' markers
+cdcf8902 Merge "Port Jenkins script: compile" into main
+dc683cde Jenkins scripts port: static analysis
+0858494e Port Jenkins script: compile
+c2cf6a93 Jenkins scripts port: android compilation
+df0e808f presubmit: Add pylint-2.7 and .pylintrc
+676c57db patch-check: shfmt
+7bb7f747 patch-check: Add shellcheck
+abcd1797 Reformat docstrings and imports
+edaf0895 Port Jenkins scripts: compile js
+b9622063 Set CheckPatchFormatted flags to fail on diffs
+e23cd548 dsp.h: enable NEON w/VS2019+ ARM64 targets
+3875c7de CMakeLists.txt: set minimum version to 3.7
+1a8f0d45 Have a hard-coded value for memset in TrellisQuantizeBlock.
+93480160 Speed up TrellisQuantizeBlock
+45eaacc9 Convert deprecated uint32 to uint32_t.
+42592af8 webp,cmake: Remove unnecessary include dirs
+e298e05f Add patch-check steps in PRESUBMIT.py
+29148919 Merge tag 'v1.2.1'
+9ce5843d update ChangeLog (tag: v1.2.1)
+d9191588 fuzzer/*: normalize src/ includes
+c5bc3624 fuzzer/*: normalize src/ includes
+53b6f762 fix indent
+d2caaba4 fix indent
+731246ba update ChangeLog (tag: v1.2.1-rc2)
+d250f01d dsp/*: use WEBP_HAVE_* to determine Init availability
+1fe31625 dsp/*: use WEBP_HAVE_* to determine Init availability
+3a4d3ecd update NEWS
+b2bc8093 bump version to 1.2.1
+e542fc7a update AUTHORS
+e0241154 Merge "libwebp/CMake: Add <BUILD_INTERFACE> to webp incl" into main
+edea6444 libwebp/CMake: Add <BUILD_INTERFACE> to webp incl
+ece18e55 dsp.h: respect --disable-sse2/sse4.1/neon
+a89a3230 wicdec: support alpha from WebP WIC decoder
+26f4aa01 Merge "alpha_processing: fix visual studio warnings" into main
+8f594663 alpha_processing: fix visual studio warnings
+46d844e6 Merge "cpu.cmake: fix compiler flag detection w/3.17.0+" into main
+298d26ea Merge changes I593adf92,If20675e7,Ifac68eac into main
+a1e5dae0 alpha_processing*: use WEBP_RESTRICT qualifier
+327ef24f cpu.cmake: fix compiler flag detection w/3.17.0+
+f70819de configure: enable libwebpmux by default
+dc7e2b42 configure: add informational notices when disabling binaries
+9df23ddd configure: move lib flag checks before binaries
+a2e18f10 Merge "WebPConfig.config.in: correct WEBP_INCLUDE_DIRS" into main
+e1a8d4f3 Merge "bit_reader_inl_utils: uniformly apply WEBP_RESTRICT" into main
+4de35f43 rescaler.c: fix alignment
+0f13eec7 bit_reader_inl_utils: uniformly apply WEBP_RESTRICT
+277d3074 Fix size_t overflow in  WebPRescalerInit
+97adbba5 WebPConfig.config.in: correct WEBP_INCLUDE_DIRS
+b60d4603 advanced_api_fuzzer: add extreme config value coverage
+72fe52f6 anim_encode.c,cosmetics: normalize indent
+116d235c anim_encode: Fix encoded_frames_[] overflow
+6f445b3e CMake: set CMP0072 to NEW
+b1cf887f define WEBP_RESTRICT for MSVC
+3e265136 Add WEBP_RESTRICT & use it in VP8BitReader
+f6d29247 vp8l_dec::ProcessRows: fix int overflow in multiply
+de3b4ba8 CMake: add WEBP_BUILD_LIBWEBPMUX
+7f09d3d1 CMakeLists.txt: rm libwebpmux dep from anim_{diff,dump}
+4edea4a6 Init{RGB,YUV}Rescaler: fix a few more int overflows
+c9e26bdb rescaler_utils: set max valid scaled w/h to INT_MAX/2
+28d488e6 utils.h: add SizeOverflow()
+695bdaa2 Export/EmitRescaledRowsRGBA: fix pointer offset int overflow
+685d073e Init{RGB,YUV}Rescaler: fix int overflows in multiplication
+d38bd0dd WebPFlipBuffer: fix integer overflow
+109ff0f1 utils: allow MALLOC_LIMIT to indicate a max
+a2fce867 WebPRescalerImportRowExpand_C: promote some vals before multiply
+776983d4 AllocateBuffer: fix int multiplication overflow check
+315abbd6 Merge "Revert "Do not use a palette for one color images.""
+eae815d0 Merge changes Ica3bbf75,I82f82954
+afbca5a1 Require Emscripten 2.0.18
+3320416b CMakeLists,emscripten: use EXPORTED_RUNTIME_METHODS
+29145ed6 Update README instructions for using Emscripten
+1f579139 cosmetics: remove use of 'sanity' / 'master'
+29b6129c WebPAnimEncoderNewInternal: remove some unnecessary inits
+b60869a1 Revert "Do not use a palette for one color images."
+6fb4cddc demux: move padded size calc post unpadded validation
+05b72d42 vp8l_enc.c: normalize index types
+b6513fba Do not use a palette for one color images.
+98bbe35b Fix multi-threading with palettes.
+b1674240 Add modified Zeng's method to palette sorting.
+88c90c45 add CONTRIBUTING.md
+6a9916d7 WebPRescalerInit: add missing int64_t promotion
+b6cf52d5 WebPIoInitFromOptions: treat use_scaling as a bool
+3b12b7f4 WebPIoInitFromOptions: treat use_cropping as a bool
+595fa13f add WebPCheckCropDimensions()
+8fdaecb0 Disable cross-color when palette is used.
+8933bac2 WebPIoInitFromOptions: respect incoming bypass_filtering val
+7d416ff0 webpdec,cosmetics: match error text to function call
+ec6cfeb5 Fix typo on WebPPictureAlloc() in README
+7e58a1a2 *.cmake: add license header
+5651a6b2 cmake: fix .so versioning
+25ae67b3 xcframeworkbuild.sh: add arm64 simulator target
+5d4ee4c3 cosmetics: remove use of the term 'dummy'
+01b38ee1 faster CollectColorXXXTransforms_SSE41
+652aa344 Merge "Use BitCtz for FastSLog2Slow_C"
+0320e1e3 add the missing default BitsCtz() code
+8886f620 Use BitCtz for FastSLog2Slow_C
+fae41617 faster CombinedShannonEntropy_SSE2
+5bd2704e Introduce the BitCtz() function.
+fee64287 Merge "wicdec,icc: treat unsupported op as non-fatal"
+33ddb894 lossless_sse{2,41}: remove some unneeded includes
+b27ea852 wicdec,icc: treat unsupported op as non-fatal
+b78494a9 Merge "Fix undefined signed shift."
+e79974cd Fix undefined signed shift.
+a8853394 SSE4.1 versions of BGRA to RGB/BGR color-space conversions
+a09a6472 SSE4.1 version of TransformColorInverse
+401da22b Merge "pngdec: check version before using png_get_chunk_malloc_max"
+26907822 pngdec: check version before using png_get_chunk_malloc_max
+06c1e72e Code cleanup
+8f0d41aa Merge changes Id135bbf4,I99e59797
+373eb170 gif2webp: don't store loop-count if there's only 1 frame
+759b9d5a cmake: add WEBP_USE_THREAD option
+926ce921 cmake: don't install binaries from extras/
+9c367bc6 WebPAnimDecoderNewInternal: validate bitstream before alloc
+47f64f6e filters_sse2: import Chromium change
+cc3577e9 fuzzer/*: use src/ based include paths
+004d77ff Merge tag 'v1.2.0'
+fedac6cc update ChangeLog (tag: v1.2.0-rc3, tag: v1.2.0)
+170a8712 Fix check_c_source_compiles with pthread.
+ceddb5fc Fix check_c_source_compiles with pthread.
+85995719 disable CombinedShannonEntropy_SSE2 on x86
+289757fe TiffDec: enforce stricter mem/dimension limit on tiles
+8af7436f Merge "{ios,xcframework}build.sh: make min version(s) more visible" into 1.2.0
+e56c3c5b pngdec: raise memory limit if needed
+8696147d pngdec: raise memory limit if needed
+13b8e9fe {ios,xcframework}build.sh: make min version(s) more visible
+a9225410 animdecoder_fuzzer: fix memory leak
+d6c2285d update gradle to 6.1.1
+8df77fb1 animdecoder_fuzzer: fix memory leak
+52ce6333 update NEWS
+28c49820 bump version to 1.2.0
+7363dff2 webp/encode.h: restore WEBP_ENCODER_ABI_VERSION to v1.1.0
+826aafa5 update AUTHORS
+63258823 animdecoder_fuzzer: validate canvas size
+9eb26381 CMake: remove duplicate "include(GNUInstallDirs)"
+2e7bed79 WebPPicture: clarify the ownership of user-owned data.
+cccf5e33 webpmux: add an '-set loop <value>' option
+c9a3f6a1 Merge changes Ie29f9867,I289c54c4
+319f56f1 iosbuild.sh: sync some aspects of xcframeworkbuild.sh
+e8e8db98 add xcframeworkbuild.sh
+ae545534 dsp.h: allow config.h to override MSVC SIMD autodetection
+fef789f3 Merge "cmake: fix per-file assembly flags"
+fc14fc03 Have C encoding predictors use decoding predictors.
+7656f0b3 README,cosmetics: fix a couple typos
+d2e245ea cmake: disable webp.js if WEBP_ENABLE_SIMD=1
+96099a79 cmake: fix per-file assembly flags
+5abb5582 Merge "cmake: fix compilation w/Xcode generator"
+8484a120 cmake: fix compilation w/Xcode generator
+d7bf01c9 Merge changes Ifcae0f38,Iee2d7401
+36c81ff6 WASM-SIMD: port 2 patches from rreverser@'s tree
+988b02ab Merge "Couple of fixes to allow SIMD on Emscripten"
+26faf770 wicdec: fail with animated images
+ab2d08a8 [cd]webp: document lack of animated webp support
+52273943 Couple of fixes to allow SIMD on Emscripten
+8870ba7f Fix skia bug #10952
+4b3c6953 Detect if StoreFrame read more than anmf_payload_size bytes
+17fd4ba8 webp/decode.h,cosmetics: normalize 'flip' comment
+411d3677 remove some unreachable break statements
+3700ffd7 WebPPictureHasTransparency: remove unreachable return
+83604bf3 {animencoder,enc_dec}_fuzzer: convert some abort()s to returns
+eb44119c Merge changes I8ae09473,I678c8b1e
+9f6055fc fuzz_utils.h: rename max() to Max()
+695788e7 fuzz_utils.h: make functions WEBP_INLINE
+906c1fcd make ImgIoUtilReadFile use WebPMalloc instead of malloc
+8cb7e536 rename demux_api_fuzzer.c -> mux_demux_api_fuzzer.c
+443db47d add animdecoder_fuzzer.cc
+36a6eea3 Merge "import fuzzers from oss-fuzz/chromium"
+ec5f12c1 Makefile.vc: remove deprecated /Gm option
+64425a08 picture_tools_enc: fix windows build warning
+bd94090a import fuzzers from oss-fuzz/chromium
+cf847cba use WEBP_DSP_INIT_FUNC for Init{GammaTables*,GetCoeffs}
+55a080e5 Add WebPReplaceTransparentPixels() in dsp
+84739717 GetBackgroundColorGIF: promote to uint32_t before << 24
+def64e92 cwebp: Fix -print_psnr for near_lossless
+cf2f88b3 Add palette and spatial for q >= 75 and -m 5
+f0110bae Add no-color cache configuration to the cruncher
+749a8b99 Better estimate of the cache cost.
+4f9f00cc Use spatial predictors on top of palette no matter what.
+7658c686 Add spatial prediction on top of palette in cruncher.
+133ff0e3 webp_js: force WASM=0 option explicitly
+e3c259a2 Fix integer overflow in EmitFancyRGB.
+b3ff0bde man/{gif2,img2}webp,webpmux: normalize some wording
+f9b30586 fix ABI breakage introduced by 6a0ff358
+1d58dcfc README.webp_js: update note about emscripten version
+44070266 README.webp_js: s/fastcomp/upstream/
+2565fa8f README.webp_js: update cmake command
+47309ef5 webp: WEBP_OFFSET_PTR()
+687ab00e DC{4,8,16}_NEON: replace vmovl w/vaddl
+1b92fe75 DC16_NEON,aarch64: use vaddlv
+53f3d8cf dec_neon,DC8_NEON: use vaddlv instead of movl+vaddv
+27d08240 Fix integer overflow in WebPAnimDecoderGetNext()
+69776e38 Merge "remove call to MBAnalyzeBestIntra4Mode for method >= 5"
+a99078c1 remove call to MBAnalyzeBestIntra4Mode for method >= 5
+22e404cc CMakeLists.txt: fix set(CACHE) argument order
+71690b52 fix MSVC warning
+6a0ff358 Enc: add a qmin / qmax range for quality factor
+0fa56f30 Merge tag 'v1.1.0'
+6cf504d0 PNM decoding: handle max_value != 255
+d7844e97 update ChangeLog (tag: v1.1.0-rc2, tag: v1.1.0)
+7f006436 Makefile.vc: fix webp_quality.exe link
+cf047e83 Makefile.vc: fix webp_quality.exe link
+c074c653 update NEWS
+30f09551 bump version to 1.1.0
+a76694a1 update AUTHORS
+6e3ef7b3 extras: fix WEBP_SWAP_16BIT_CSP check
+47178dbd extras: add WebPUnmultiplyARGB() convenience function
+22cbae33 idec_dec: fix 0 offset of NULL pointer
+290dd0b4 muxread: fix 0 offset of NULL pointer
+0df474ac Merge "lossless_(enc_|)sse2: avoid offsetting a NULL pointer"
+c6b75a19 lossless_(enc_|)sse2: avoid offsetting a NULL pointer
+295e5e38 fix UBSAN warning
+e2575e05 DC8_NEON,aarch64: use vaddv
+b0e09e34 dec_neon: Fix build failure under some toolchains
+cf0e903c dsp/lossless: Fix non gcc ARM builds
+bb7bc40b Remove ubsan errors.
+78881b76 CMake: fix GLUT library link
+9f750f7a cmake: fix BUILD_SHARED_LIBS build on mac
+17850e74 libwebp: Remove char-subscripts warning in pnmdec.c
+2fa2552d Merge "Expose WebPMalloc() in addition to WebPFree()"
+a4df4aae Expose WebPMalloc() in addition to WebPFree()
+853ea3d8 imageio/tiff: Return error before allocating bad tile size
+af650c0b Fix a Wxor-used-as-pow false positive
+601ef17c libwebp.py: update to swig 3.0.12
+0e48d889 bugfix: last alpha rows were incorrectly decoded
+24d2ccb4 webp: Fix imageio ReadPNM() TUPLTYPE
+fab8f9cf cosmetics: normalize '*' association
+94138e0e update .gitignore
+0fe1a89d update ChangeLog (tag: v1.0.3-rc1, tag: v1.0.3)
+2ad0916d update NEWS
+1287362b bump version to 1.0.3
+7b968cc2 update AUTHORS
+9d6988f4 Fix the oscillating prediction problem at low quality
+312f74d0 makefile.unix: allow *_LIBS to be overridden w/EXTRA_LIBS
+92dbf237 filters_sse2,cosmetics: shorten some long lines
+a277d197 filters_sse2.c: quiet integer sanitizer warnings
+804540f1 Fix cpufeatures in CMake.
+bf00c15b Add CMake option for bittrace.
+a788b498 filters_sse2.c: quiet integer sanitizer warnings
+e6a92c5e filters.c: quiet integer sanitizer warnings
+ec1cc40a lossless.c: remove U32 -> S8 conversion warnings
+1106478f remove conversion U32 -> S8 warnings
+812a6b49 lossless_enc: fix some conversion warning
+4627c1c9 lossless_enc,TransformColorBlue: quiet uint32_t conv warning
+c84673a6 lossless_enc_sse{2,41}: quiet signed conv warnings
+776a7757 dec_sse2: quiet signed conv warnings
+bd39c063 Merge "thread_utils: release mutex before signaling"
+0550576f Merge "(alpha_processing,enc}_sse2: quiet signed conv warnings"
+6682f2c4 thread_utils: release mutex before signaling
+e78dea75 (alpha_processing,enc}_sse2: quiet signed conv warnings
+9acf18ba iosbuild.sh: add WebP{Demux,Mux}.framework
+b9be7e65 vwebp: remove the -fit option (and make it default)
+1394a2bb Merge "README.webp_js: update Emscripten.cmake note"
+dd3e7f8a README.webp_js: update Emscripten.cmake note
+32cf8801 predictor_enc,GetBestGreenRedToBlue: quiet implicit conv warnings
+e1c8acb5 Merge "vwebp: add a -fit option"
+cbd23dd5 vwebp: add a -fit option
+2e672351 bit_writer_utils,Flush: quiet implicit conversion warnings
+1326988d swig: update libwebp_python_wrap.c
+0e7f8548 update generated swig files
+17ed1438 Merge "PutLE{16,24}: quiet implicit conversion warnings"
+24686538 PutLE{16,24}: quiet implicit conversion warnings
+153bb3a0 fix some clang-7 warnings:
+ab2dc893 Rescaler: fix rounding error
+aa65f89a HistogramCombineStochastic: fix free of uninit value
+af0bac64 Merge "encode.h: mention 'exact' default in WebPEncodeLossless*"
+6d2e11ec encode.h: mention 'exact' default in WebPEncodeLossless*
+8c3f04fe AndroidCPUInfo: reorder terms in conditional
+fcfd9c71 BitTrace: if BITTRACE is > 0, record and print syntax bits used
+067031ea Speedups for unused Huffman groups.
+01ac46ba libwebp: Display "libjpeg error:" in imageio/jpegdec
+d9a662e1 WebPRescalerGetScaledDimensions: round scaled dimension up
+62eb3f08 libwebp: Fix missing '{' in README
+e05f785a Merge "unicode,INIT_WARGV: add missing cast"
+63c9a69f tag the VP8LHashPix() function for potential uint roll-over
+2b7214ab unicode,INIT_WARGV: add missing cast
+bf424b46 tag the GetPixPairHash64() function for potential uint roll-over
+7d05d6ca Have the color cache computation be u32-bit only.
+6bcf8769 Remove BINARYEN_METHOD in wasm settings.
+2b98df90 update ChangeLog (tag: v1.0.2-rc1, tag: v1.0.2)
+61e372b7 update NEWS
+7ae658a0 bump version to 1.0.2
+51c4907d update AUTHORS
+666bd6c6 man/cwebp.1: refine near-lossless text
+561cdce5 Clarify the doc about GetFeatures.
+aec2cf02 near_lossless: fix fuzzing-detected integer overflow
+928a75de webp: Fix VP8LBitWriterClone() bug
+5173d4ee neon IsFlat
+5b081219 IsFlat: inline when possible
+381b7b54 IsFlat: use int for thresh
+6ed15ea1 fix unprobable leak in webp_sdl.c
+22bbb24e Merge "IsFlat: return int"
+8b3fb238 Merge tag 'v1.0.1'
+f435de95 IsFlat: return int
+41521aed utils.h: only define WEBP_NEED_LOG_TABLE_8BIT when needed
+9f4d4a3f neon: GetResidualCost
+0fd7514b neon: SetResidualCoeffs
+f95a996c Simpler histogram clustering.
+e85d3313 update ChangeLog (tag: v1.0.1-rc2, tag: v1.0.1)
+fa8210e4 Fix pair update in stochastic entropy merging.
+fd198f73 add codereview.settings
+825389ac README.mux: add a reference to the AnimDecoder API
+3be698c3 CMake: fix webp_js compilation
+485ff86f Fix pair update in stochastic entropy merging.
+4cd0582d CMake: fix webp_js compilation
+4cbb4caf update NEWS
+f5a5918d bump version to 1.0.1
+d61385db Speed-up: Make sure we only initialize histograms when needed.
+6752904b Speed-up: Make sure we only initialize histograms when needed.
+0c570316 update AUTHORS
+301a2dda img2webp: add help note about arguments from a file
+f0abab92 Speedups for empty histograms.
+f2dfd925 Split HistogramAdd to only have the high level logic in C.
+06b7bc7d Fix compilation on windows and clang-cl+ninja.
+b6284d82 img2webp: add help note about arguments from a file
+decf6f6b Speedups for empty histograms.
+dea3e899 Split HistogramAdd to only have the high level logic in C.
+632798ae Merge "Fix compilation on windows and clang-cl+ninja."
+dc1a9518 Merge "libwebp: Unicode command tools on Windows"
+9cf9841b libwebp: Unicode command tools on Windows
+98179495 remove some minor TODOs
+a376e7b9 Fix compilation on windows and clang-cl+ninja.
+cbf82cc0 Remove AVX2 files.
+5030e902 Merge "TIFF decoder: remove unused KINV definition"
+ac543311 Remove a few more useless #defines
+123d3306 TIFF decoder: remove unused KINV definition
+ef1094b0 Merge "- install pkg-config files during the CMake build"
+b911fbc9 libwebp: Remove duplicate GIFDisplayError in anim_util
+eee00b66 - install pkg-config files during the CMake build
+ac3ec8c9 Merge "Clean-up the common sources in dsp."
+3e13da7b Clean-up the common sources in dsp.
+5c395f1d libwebp: cmake-format all
+e7a69729 libwebp: Add extras targets in CMakeLists.txt
+e52485d6 libwebp: Rename macros in webpmux.c
+92dc0f09 clean-up MakeInputImageCopy()
+39952de2 VP8IteratorImport: add missing 'const'
+382af7a2 clean-up WebPBlendAlpha
+14d020f6 libwebp: Use ExUtilGet*() in anim_diff
+0d92ff25 libwebp: remove useless variable in gif2webp
+556cb1b4 Merge "CMake: Set WEBP_BUILD_GIF2WEBP to off"
+da26ee49 CMake: Set WEBP_BUILD_GIF2WEBP to off
+b2a867c0 cwebp: Don't premultiply during -resize if -exact
+637141bc pngdec: fix build w/libpng < 1.4.x
+bc5092b1 pngdec: set memory functions
+50d8345a Fix CMake math library.
+6aa3e8aa Fix math library on Visual Studio.
+d71df4e2 Fix math library finding in CMake.
+de08d727 cosmetics: normalize include guard comment
+009562b4 vwebp: Fix bug when Dispose then NoBlend frames
+423f2579 Fix up CMake to create targets.
+907208f9 Wait for all threads to be done in DecodeRemaining.
+4649b3c4 vwebp: Add background color display option
+78ad57a3 Fix bad glClearColor parameters
+da96d8d9 Allow for a non-initialized alpha decompressor in DoRemap.
+2563db47 fix rescaling rounding inaccuracy
+211f37ee fix endian problems in pattern copy
+5f0f5c07 Make sure partition #0 is read before VP8 data in IDecode.
+de98732b fix GetColorf() bug
+4338cd36 misc fixes in libwebpmux
+e00af13e fix signatures after a9ceda7ff1
+a9ceda7f Speed-up chunk list operations.
+2281bbf6 Merge "Better handling of bogus Huffman codes."
+39cb9aad Better handling of bogus Huffman codes.
+89cc9d37 Merge "fix read-overflow while parsing VP8X chunk"
+95fd6507 fix read-overflow while parsing VP8X chunk
+9e729fe1 Fix VP8IoTeardownHook being called twice on worker sync failure
+29fb8562 Merge "muxread,anmf: fail on multiple image chunks"
+eb82ce76 muxread,anmf: fail on multiple image chunks
+1344a2e9 fix alpha-filtering crash when image width is larger than radius
+be738c6d muxread,ChunkVerifyAndAssign: validate chunk_size
+2c70ad76 muxread,CreateInternal: fix riff size checks
+569001f1 Fix for thread race heap-use-after-free
+c56a02d9 Android.mk: use LOCAL_EXPORT_C_INCLUDES w/public libs
+15795596 CMakeLists.txt,cosmetics: normalize if() formatting
+1a44c233 Merge "cmake: add support for webpmux"
+e9569ad7 Merge "configure,*am,cosmetics: s/WANT_/BUILD_/"
+35c7de6f cmake: add support for webpmux
+0f25e61c WebpToSDL(): fix the return value in case of error
+5d8985de configure,*am,cosmetics: s/WANT_/BUILD_/
+895fd28f Merge "man/Makefile.am: add img2webp.1"
+5cf3e2af man/Makefile.am: add img2webp.1
+2a9de5b9 Add build rules for anim_diff & anim_dump utils.
+71ed73cf fix invalid check for buffer size
+af0e4fbb gif2webp: fix transcode of loop count=65535
+dce5d764 Limit memory allocation when reading invalid Huffman codes.
+f9df0081 Merge "cmake: quiet glut deprecation warnings on OS X"
+dc39b16f webpmux.1: correct grammar
+c7aa1264 cwebp.c: fix a missing \n
+53aa51e9 Merge tag 'v1.0.0'
+698b8844 update ChangeLog (tag: v1.0.0)
+8d510751 webp-container-spec: correct frame duration=0 note
+e6b2164e vwebp: Copy Chrome's behavior w/frame duration == 0
+094b3b28 cmake: quiet glut deprecation warnings on OS X
+71c39a06 webp-container-spec: correct frame duration=0 note
+fd3d5756 vwebp: Copy Chrome's behavior w/frame duration == 0
+b0c966fb Build vwebp from CMake.
+d20b7707 update ChangeLog (tag: v1.0.0-rc3)
+0d5fad46 add WEBP_DSP_INIT / WEBP_DSP_INIT_FUNC
+d77bf512 add WEBP_DSP_INIT / WEBP_DSP_INIT_FUNC
+c1cb86af fix 16b overflow in SSE2
+e577feb7 makefile.unix: add DEBUG flag for compiling w/ debug-symbol
+99be34b3 cwebp,get_disto: fix bpp output
+e122e511 cwebp,get_disto: fix bpp output
+f5565ca8 cmake: Make sure we use near-lossless by default.
+d898dc14 fix bug in WebPImport565: alpha value was not set
+1c8f358d Fix CMake with WASM.
+a0215fb7 webp_js: fix webp_js demo html
+882784b0 update ChangeLog (tag: v1.0.0-rc2)
+2f930e08 Revert "Use proper targets for CMake."
+8165e8fb Use proper targets for CMake.
+3f157dd5 Remove some very hard TODOs.
+abb47760 Merge "Use proper targets for CMake."
+cd758a17 {de,}mux/Makefile.am: add missing headers
+e155dda0 Use proper targets for CMake.
+b892b8ba makefile.unix,dist: use ascii for text output
+64a57d05 add -version option to anim_dump,anim_diff and img2webp
+994be82d Merge "Remove some very hard TODOs."
+4033e1d7 Remove some very hard TODOs.
+fc1b8e3a webp_js: fix webp_js demo html
+15aa48d9 update ChangeLog (tag: v1.0.0-rc1)
+e607dabc update AUTHORS
+38410c08 [CFI] Remove function pointer casts
+978eec25 [CFI] Remove function pointer casts
+c57b2736 bump version to 1.0.0
+cba28853 update NEWS
+c909d531 Merge "remove some deprecation warning on MacOSX"
+217443c7 remove some deprecation warning on MacOSX
+b672bdfa configure: quiet glut deprecation warnings on OS X
+daa9fcaf configure: use sdl-config if available
+dd174cae Merge "imagedec: support metadata reading for WebP image decoding"
+641cedcc imagedec: support metadata reading for WebP image decoding
+065b2ce1 anim_diff: add a couple missing newlines in Help()
+c4cc1147 Merge "gif2webp: force low duration frames to 100ms"
+09333097 gif2webp: force low duration frames to 100ms
+e03f0ec3 sharp_yuv: use 14b fixed-point precision for gamma
+b2db361c image_enc,WebPWritePNG: move locals after setjmp
+74e82ec6 Merge "WebPPictureDistortion: fix big-endian results order"
+645d04ca Merge "cwebp,get_disto: report bpp"
+120f58c3 Merge "lossless*sse2: improve non-const 16-bit vector creation"
+a7fe9412 WebPPictureDistortion: fix big-endian results order
+e26fe066 cwebp,get_disto: report bpp
+9df64e28 Merge changes Id5b4a1a4,Ia20ce844
+8043504f lossless*sse2: improve non-const 16-bit vector creation
+1e3dfc48 Import: extract condition from loop
+3b07d327 Import,RGBA: fix for BigEndian import
+551948e4 Remove unused argument in VP8LBitsEntropy.
+3005237a ReadWebP: fix for big-endian
+499c395a Merge "anim_diff: expose the -max_diff option"
+f69dcd69 Merge "remove WEBP_EXPERIMENTAL_FEATURES"
+07d884d5 anim_diff: expose the -max_diff option
+f4dd9256 remove WEBP_EXPERIMENTAL_FEATURES
+94a8377b extract the command-line parsing helpers to example_util
+fc09e6e2 PNM decoder: prevent unsupported depth=2 PAM case.
+6de58603 MIPS64: Fix defined-but-not-used errors with WEBP_REDUCE_CSP
+cbde5728 gif2webp: add support for reading from stdin
+cf1c5054 Add an SSE4 version of some lossless color transforms.
+45a8b5eb Fix lint error with man page.
+cff38e8f Merge "PNG decoder: handle gAMA chunk"
+59cb1a48 Merge "enable dc error-diffusion always"
+78318b30 PNG decoder: handle gAMA chunk
+664c21dd Merge "remove some TODOs"
+815652de enable dc error-diffusion always
+aec45cec remove some TODOs
+5715dfce fix block-count[] increment in case of large image
+c2d04f3e enable DC error-diffusion always for multi-pass
+96bf07c5 use DC error diffusion for U/V at low-quality
+1c59020b fix missing sse41 targets in makefile.unix
+7a8e814b cosmetics: s/color_space/colorspace/
+05f6fe24 upsampling: rm asserts w/REDUCE_CSP+OMIT_C_CODE
+b4cf5597 Merge "Upsampling SSE2/SSE4 speedup."
+ccbeb32c Makefile.vc: add missing sse41 files
+55403a9a Upsampling SSE2/SSE4 speedup.
+807b53c4 Implement the upsampling/yuv functions in SSE41
+84101a81 Fix wasm WebP compilation
+8bebd2a3 fix warning on MSVC
+a7f93fe3 webpmux: allow reading argument from a file
+b69f18a7 gif2webp.1: fix -loop_compatibility layout
+72d530c0 Merge "fix lossless decoding w/WEBP_REDUCE_SIZE"
+296c7dc4 fix lossless decoding w/WEBP_REDUCE_SIZE
+0d5d029c Merge "ImgIoUtilReadFile: fix file leak upon error"
+ae568ce7 ImgIoUtilReadFile: fix file leak upon error
+796b5a8a Merge tag 'v0.6.1'
+6b7a95fd update ChangeLog (tag: v0.6.1)
+f66955de WEBP_REDUCE_CSP: restrict colorspace support
+1af0df76 Merge "WEBP_REDUCE_CSP: restrict colorspace support"
+6de20df0 WEBP_REDUCE_CSP: restrict colorspace support
+a289d8e7 update ChangeLog (tag: v0.6.1-rc2)
+c10a493c vwebp: disable double buffering on windows & mac
+0d4466c2 webp_to_sdl.c: fix file mode
+1b27bf8b WEBP_REDUCE_SIZE: disable all rescaler code
+126be109 webpinfo: add -version option
+0df22b9e WEBP_REDUCE_SIZE: disable all rescaler code
+9add62b5 bump version to 0.6.1
+d3e26144 update NEWS
+2edda639 README: add webpinfo section
+9ca568ef Merge "right-size some tables"
+31f1995c Merge "SSE2 implementation of HasAlphaXXX"
+a80c46bd SSE2 implementation of HasAlphaXXX
+083507f2 right-size some tables
+2e5785b2 anim_utils.c: remove warning when !defined(WEBP_HAVE_GIF)
+b299c47e add WEBP_REDUCE_SIZE
+f593d71a enc: disable pic->stats/extra_info w/WEBP_DISABLE_STATS
+541179a9 Merge "predictor_enc: fix build w/--disable-near-lossless"
+5755a7ec predictor_enc: fix build w/--disable-near-lossless
+eab5bab7 add WEBP_DISABLE_STATS
+8052c585 remove some petty TODOs from vwebp.
+c245343d move LOAD8x4 and STORE8x2 closer to their use location
+b9e734fd dec,cosmetics: normalize function naming style
+c188d546 dec: harmonize function suffixes
+28c5ac81 dec_sse41: harmonize function suffixes
+e65b72a3 Merge "introduce WebPHasAlpha8b and WebPHasAlpha32b"
+b94cee98 dec_sse2: remove HE8uv_SSE2
+44a0ee3f introduce WebPHasAlpha8b and WebPHasAlpha32b
+aebf59ac Merge "WebPPictureAllocARGB: align argb allocation"
+c184665e WebPPictureAllocARGB: align argb allocation
+3daf7509 WebPParseHeaders: remove obsolete animation TODO
+80285d97 cmake: avoid security warnings under msvc
+650eac55 cmake: don't set -Wall with MSVC
+c462cd00 Remove useless code.
+01a98217 Merge "remove WebPWorkerImpl declaration from the header"
+3c49fc47 Merge "thread_utils: fix potentially bad call to Execute"
+fde2782e thread_utils: fix potentially bad call to Execute
+2a270c1d remove WebPWorkerImpl declaration from the header
+f1f437cc remove mention of 'lossy-only parameters' from the doc
+3879074d Merge "WebPMemToUint32: remove ptr cast to int"
+04b029d2 WebPMemToUint32: remove ptr cast to int
+b7971d0e dsp: avoid defining _C functions w/NEON builds
+6ba98764 webpdec: correct alloc size check w/use_argb
+5cfb3b0f normalize include guards
+f433205e Merge changes Ia17c7dfc,I75423abb,Ia2f716b4,I161caa14,I4210081a, ...
+8d033b14 {dec,enc}_neon: harmonize function suffixes x2
+0295e981 upsampling_neon: harmonize function suffixes
+d572c4e5 yuv_neon: harmonize function suffixes
+ab9c2500 rescaler_neon: harmonize function suffixes
+93e0ce27 lossless_neon: harmonize function suffixes
+22fbc50e lossless_enc_neon: harmonize function suffixes
+447875b4 filters_neon,cosmetics: fix indent
+e51bdd43 remove unused VP8TokenToStats() function
+785da7ea enc_neon: harmonize function suffixes
+bc1a251f dec_neon: harmonize function suffixes
+61e535f1 dsp/lossless: workaround gcc-4.8 bug on arm
+68b2eab7 cwebp: fix alpha reporting w/lossless & metadata
+30042faa WebPDemuxGetI: add doc details around WebPFormatFeature
+0a17f471 Merge "WIP: list includes as descendants of the project dir"
+a4399721 WIP: list includes as descendants of the project dir
+08275708 Merge "Make sure we reach the full range for alpha blending."
+d361a6a7 yuv_sse2: harmonize function suffixes
+6921aa6f upsampling_sse2: harmonize function suffixes
+08c67d3e ssim_sse2: harmonize function suffixes
+582a1b57 rescaler_sse2: harmonize function suffixes
+2c1b18ba lossless_sse2: harmonize function suffixes
+0ac46e81 lossless_enc_sse2: harmonize function suffixes
+bc634d57 enc_sse2: harmonize function suffixes
+bcb7347c dec_sse2: harmonize function suffixes
+e14ad93c Make sure we reach the full range for alpha blending.
+7038ca8d demux,StoreFrame: restore hdr size check to min req
+fb3daad6 cpu: fix ssse3 check
+be590e06 Merge "Fix CMake redefinition for HAVE_CPU_FEATURES_H"
+35f736e1 Fix CMake redefinition for HAVE_CPU_FEATURES_H
+a5216efc Fix integer overflow warning.
+a9c8916b decode.h,WebPIDecGetRGB: clarify output ptr validity
+3c74c645 gif2webp: handle 1-frame case properly + fix anim_diff
+c7f295d3 Merge "gif2webp: introduce -loop_compatibility option"
+b4e04677 gif2webp: introduce -loop_compatibility option
+f78da3de add LOCAL_CLANG_PREREQ and avoid WORK_AROUND_GCC w/3.8+
+01c426f1 define WEBP_USE_INTRINSICS w/gcc-4.9+
+8635973d use sdl-config (if available) to determine the link flags
+e9459382 use CPPFLAGS before CFLAGS
+4a9d788e Merge "Android.mk,mips: fix clang build with r15"
+4fbdc9fb Android.mk,mips: fix clang build with r15
+a80fcc4a ifdef code not used by Chrome/Android.
+3993af12 Fix signed integer overflows.
+f66f94ef anim_dump: small tool to dump frames from animated WebP
+6eba857b Merge "rationalize the Makefile.am"
+c5e34fba function definition cleanup
+3822762a rationalize the Makefile.am
+501ef6e4 configure style fix: animdiff -> anim_diff
+f8bdc268 Merge "protect against NULL dump_folder[] value in ReadAnimatedImage()"
+23bfc652 protect against NULL dump_folder[] value in ReadAnimatedImage()
+8dc3d71b cosmetics,ReadAnimatedWebP: correct function comment
+5bd40066 Merge changes I66a64a0a,I4d2e520f
+7945575c cosmetics,webpinfo: remove an else after a return
+8729fa11 cosmetics,cwebp: remove an else after a return
+f324b7f9 cosmetics: normalize fn proto & decl param names
+869eb369 CMake cleanups.
+289e62a3 Remove declaration of unimplemented VP8ApplyNearLosslessPredict
+20a94186 pnmdec,PAM: validate depth before calculating bytes_per_px
+34130afe anim_encode: fix integer overflow
+42c79aa6 Merge "Encoder: harmonize function suffixes"
+b09307dc Encoder: harmonize function suffixes
+bed0456d Merge "SSIM: harmonize the function suffix"
+54f6a3cf lossless_sse2.c: fix some missed suffix changes
+088f1dcc SSIM: harmonize the function suffix
+86fc4dd9 webpdec: use ImgIoUtilCheckSizeArgumentsOverflow
+08ea9ecd imageio: add ability restrict max image size
+6f9daa4a jpegdec,ReadError: fix leaks on error
+a0f72a4f VP8LTransformColorFunc: drop an non-respected 'const' from the signature.
+8c934902 Merge "Lossess dec: harmonize the function suffixes"
+622242aa Lossess dec: harmonize the function suffixes
+1411f027 Lossless Enc: harmonize the function suffixes
+24ad2e3c add const to two variables
+46efe062 Merge "Allow the lossless cruncher to work for alpha."
+8c3f9a47 Speed-up LZ77.
+1aef4c71 Allow the lossless cruncher to work for alpha.
+b8821dbd Improve the box LZ77 speed.
+7beed280 add missing ()s to macro parameters
+6473d20b Merge "fix Android standalone toolchain build"
+dcefed95 Merge "build.gradle: fix arm64 build"
+0c83a8bc Merge "yuv: harmonize suffix naming"
+c6d1db4b fix Android standalone toolchain build
+663a6d9d unify the ALTERNATE_CODE flag usage
+73ea9f27 yuv: harmonize suffix naming
+c71b68ac build.gradle: fix arm64 build
+c4568b47 Rescaler: harmonize the suffix naming
+6cb13b05 Merge "alpha_processing: harmonize the naming suffixes to be _C()"
+83a3e69a Merge "simplify WEBP_EXTERN macro"
+7295fde2 Merge "filters: harmonize the suffixes naming to _SSE2(), _C(), etc."
+8e42ba4c simplify WEBP_EXTERN macro
+331ab34b cost*.c: harmonize the suffix namings
+b161f670 filters: harmonize the suffixes naming to _SSE2(), _C(), etc.
+dec5e4d3 alpha_processing: harmonize the naming suffixes to be _C()
+6878d427 fix memory leak in SDL_Init()
+461ae555 Merge "configure: fix warnings in sdl check"
+62486a22 configure: test for -Wundef
+92982609 dsp.h: fix -Wundef w/__mips_dsp_rev
+0265cede configure: fix warnings in sdl check
+88c73d8a backward_references_enc.h: fix WINDOW_SIZE_BITS check
+4ea49f6b rescaler_sse2.c: fix WEBP_RESCALER_FIX -> _RFIX typo
+1b526638 Clean-up some CMake
+87f57a4b Merge "cmake: fix gif lib detection when cross compiling"
+b34a9db1 cosmetics,dec_sse2: remove some redundant comments
+471c5755 cmake: fix gif lib detection when cross compiling
+c793417a cmake: disable gif2webp if gif lib isn't found
+dcbc1c88 cmake: split gif detection from IMG deps
+66ad84f0 Merge "muxread: remove unreachable code"
+50ec3ab7 muxread: remove unreachable code
+7d67a164 Lossy encoding: smoothen transparent areas to improve compression
+e50650c7 Merge "fix signature for DISABLE_TOKEN_BUFFER compilation"
+671d2567 fix signature for DISABLE_TOKEN_BUFFER compilation
+d6755580 cpu.cmake: use unique flag to test simd disable flags
+28914528 Merge "Remove the argb* files."
+8acb4942 Remove the argb* files.
+3b62347b README: correct cmake invocation note
+7ca0df13 Have the SSE2 version of PackARGB use common code.
+7b250459 Merge "Re-use the transformed image when trying several LZ77 in lossless."
+e132072f Re-use the transformed image when trying several LZ77 in lossless.
+5d7a50ef Get code to compile in C++.
+7b012987 configure: test for -Wparentheses-equality
+f0569adb Fix man pages for multi-threading.
+f1d5a397 multithread cruncher: only copy stats when picture->stats != NULL
+f8c2ac15 Multi-thread the lossless cruncher.
+a88c6522 Merge "Integrate a new LZ77 looking for matches in the neighborhood of a pixel only."
+8f6df1d0 Unroll Predictors 10, 11 and 12.
+355c3d1b Integrate a new LZ77 looking for matches in the neighborhood of a pixel only.
+a1779a01 Refactor LZ77 handling in preparation for a new method.
+67de68b5 Android.mk/build.gradle: fix mips build with clang from r14b
+f209a548 Use the plane code and not the distance when computing statistics.
+b903b80c Split cost-based backward references in its own file.
+498cad34 Cosmetic changes in backward reference.
+e4eb4587 lossless, VP8LTransformColor_C: make sure no overflow happens with colors.
+af6deaff webpinfo: handle alpha flag mismatch
+7caef29b Fix typo that creeped in.
+39e19f92 Merge "near lossless: fix unsigned int overflow warnings."
+9bbc0891 near lossless: fix unsigned int overflow warnings.
+e1118d62 Merge "cosmetics,FindClosestDiscretized: use uint in mask creation"
+186bc9b7 Merge "webpinfo: tolerate ALPH+VP8L"
+b5887297 cosmetics,FindClosestDiscretized: use uint in mask creation
+f1784aee near_lossless,FindClosestDiscretized: use unsigned ops
+0d20abb3 webpinfo: tolerate ALPH+VP8L
+972104b3 webpmux: tolerate false positive Alpha flag
+dd7e83cc tiffdec,ReadTIFF: ensure data_size is < tsize_t max
+d988eb7b tiffdec,MyRead: quiet -Wshorten-64-to-32 warning
+dabda707 webpinfo: add support to parse Alpha bitstream
+4c117643 webpinfo: correct background color output, BGRA->ARGB
+defc98d7 Doc: clarify the role of quality in WebPConfig.
+d78ff780 Merge "Fix code to compile with C++."
+c8f14093 Fix code to compile with C++.
+497dc6a7 pnmdec: sanitize invalid header output
+d78e5867 Merge "configure: test for -Wconstant-conversion"
+481e91eb Merge "pnmdec,PAM: set bytes_per_px based on depth when missing"
+93b12753 configure: test for -Wconstant-conversion
+645f0c53 pnmdec,PAM: set bytes_per_px based on depth when missing
+e9154605 Merge "vwebp: activate GLUT double-buffering"
+818d795b vwebp: activate GLUT double-buffering
+d63e6f4b Add a man page for webpinfo
+4d708435 Merge "NEON: implement ConvertRGB24ToY/BGR24/ARGB/RGBA32ToUV/ARGBToUV"
+faf42213 NEON: implement ConvertRGB24ToY/BGR24/ARGB/RGBA32ToUV/ARGBToUV
+b4d576fa Install man pages with CMake.
+cbc1b921 webpinfo: add features to parse bitstream header
+e644c556 Fix bad bit writer initialization.
+b62cdad2 Merge "Implement a cruncher for lossless at method 6."
+da3e4dfb use the exact constant for the gamma transfer function
+a9c701e0 Merge "tiffdec: fix EXTRASAMPLES check"
+adab8ce0 Implement a cruncher for lossless at method 6.
+1b92b237 Merge "Fix VP8ApplyNearLossless to respect const and stride."
+1923ff02 tiffdec: fix EXTRASAMPLES check
+97cce5ba tiffdec: only request EXTRASAMPLES w/> 3 samples/px
+0dcd85b6 Fix VP8ApplyNearLossless to respect const and stride.
+f7682189 yuv: rationalize the C/SSE2 function naming
+52245424 NEON implementation of some Sharp-YUV420 functions
+690efd82 Avoid several backward reference copies.
+4bb1f607 src/dec/vp8_dec.h, cosmetics: fix comments
+285748be cmake: build/install webpinfo
+78fd199c backward_references_enc.c: clear -Wshadow warnings
+ae836410 WebPLog2FloorC: clear -Wshadow warning
+d0b7404e Merge "WASM support"
+134e314f WASM support
+c08adb6f Merge "VP8LEnc: remove use of BitsLog2Ceiling()"
+28c37ebd VP8LEnc: remove use of BitsLog2Ceiling()
+2cb58ab2 webpinfo: output format as a human readable string
+bb175a93 Merge "rename some symbols clashing with MSVC headers"
+39eda658 Remove a duplicated pixel hash implementation.
+36b8274d rename some symbols clashing with MSVC headers
+274daf54 Add webpinfo tool.
+ec5036e4 add explicit reference to /usr/local/{lib,inc}
+18f0dfac Merge "fix TIFF encoder regarding rgbA/RGBA"
+4e2b0b50 Merge "webpdec.h: fix a doc typo"
+e2eeabff Merge "Install binaries, libraries and headers in CMake."
+836607e6 webpdec.h: fix a doc typo
+9273e441 fix TIFF encoder regarding rgbA/RGBA
+17e3c11f Add limited PAM decoding support
+5f624871 Install binaries, libraries and headers in CMake.
+976adac1 Merge "lossless incremental decoding: fix missing eos_ test"
+f8fad4fa lossless incremental decoding: fix missing eos_ test
+27415d41 Merge "vwebp_sdl: fix the makefile.unix"
+49566182 Merge "ImgIoUtilWriteFile(): use ImgIoUtilSetBinaryMode"
+6f75a51b Analyze the transform entropy on the whole image.
+a5e4e3af Use palette only if we can in entropy analysis.
+75a9c3c4 Improve compression by better entropy analysis.
+39cf6f4f vwebp_sdl: fix the makefile.unix
+699b0416 ImgIoUtilWriteFile(): use ImgIoUtilSetBinaryMode
+7d985bd1 Fix small entropy analysis bug.
+6e7caf06 Optimize the color cache size.
+833c9219 More efficient stochastic histogram merge.
+5183326b Refactor the greedy histogram merge.
+99f6f462 Merge "histogram_enc.c,MyRand: s/ul/u/ for unsigned constants"
+80a22186 ssim.c: remove dead include
+a128dfff histogram_enc.c,MyRand: s/ul/u/ for unsigned constants
+693bf74e move the SSIM calculation code in ssim.c / ssim_sse2.c
+10d791ca Merge "Fix the random generator in HistogramCombineStochastic."
+fa63a966 Fix the random generator in HistogramCombineStochastic.
+16be192f VP8LSetBitPos: remove the eos_ setting
+027151ca don't erase the surface before blitting.
+4105d565 disable WEBP_USE_XXX optimisations when EMSCRIPTEN is defined
+9ee32a75 Merge "WebP-JS: emscripten-based Javascript decoder"
+ca9f7b7d WebP-JS: emscripten-based Javascript decoder
+868aa690 Perform greedy histogram merge in a unified way.
+5b393f2d Merge "fix path typo for vwebp_sdl in Makefile.vc"
+e0012bea CMake: only use libwebpdecoder for building dwebp
+84c2a7b0 fix path typo for vwebp_sdl in Makefile.vc
+1b0e4abf Merge "Add a flag to disable SIMD optimizations."
+32263250 Add a flag to disable SIMD optimizations.
+b494fdec optimize the ARGB->ARGB Import to use memcpy
+f1536039 Merge "ReadWebP: decode directly into a pre-allocated buffer"
+e69ed291 ReadWebP: decode directly into a pre-allocated buffer
+57d8de8a Merge "vwebp_sdl: simple viewer based on SDL"
+5cfd4ebc LZ77 interval speedups. Faster, smaller, simpler.
+1e7ad88b PNM header decoder: add some basic numerical validation
+17c7890c Merge "Add a decoder only library for WebP in CMake."
+be733786 Merge "Add clang build fix for MSA"
+03cda0e4 Add a decoder only library for WebP in CMake.
+aa893914 Add clang build fix for MSA
+31a92e97 Merge "imageio: add limited PNM support for reading"
+dcf9d82a imageio: add limited PNM support for reading
+6524fcd6 vwebp_sdl: simple viewer based on SDL
+6cf24a24 get_disto: fix reference file read
+43d472aa Merge tag 'v0.6.0'
+50d1a848 update ChangeLog (tag: v0.6.0, origin/0.6.0)
+20a7fea0 extras/Makefile.am: fix libwebpextras.la reference
+415f3ffe update ChangeLog (tag: v0.6.0-rc3)
+3c6d1224 update NEWS
+ee4a4141 update AUTHORS
+32ed856f Fix "all|no frames are keyframes" settings.
+1c3190b6 Merge "Fix "all|no frames are keyframes" settings."
+f4dc56fd disable GradientUnfilter_NEON
+4f3e3bbd disable GradientUnfilter_NEON
+2dc0bdca Fix "all|no frames are keyframes" settings.
+0d8e0588 img2webp: treat -loop as a no-op w/single images
+b0450139 ReadImage(): restore size reporting
+0ad3b4ef update ChangeLog (tag: v0.6.0-rc2)
+6451709e img2webp,get_disto: fix image decode w/WIC builds
+92504d21 get_disto: make ReadPicture() return a bool
+c3e4b3a9 update NEWS
+3363eb6d man/img2webp.1: fix formatting warning
+4d1312f2 update NEWS
+36c42ea4 bump version to 0.6.0
+bb498a51 update AUTHORS
+84cef16f Makefile.vc: fix CFG=debug-dynamic build
+919f9e2f Merge "add .rc files for windows dll versioning"
+f1ae8af4 Merge ".gitignore: add img2webp"
+4689ce16 cwebp: add a -sharp_yuv option for 'sharp' RGB->YUV conversion
+79bf46f1 rename the pretentious SmartYUV into SharpYUV
+eb1dc89a silently expose use_delta_palette in the WebPConfig API
+c85b0dde .gitignore: add img2webp
+43d3f01a add .rc files for windows dll versioning
+668e1dd4 src/{dec,enc,utils}: give filenames a unique suffix
+0e6b7f33 Merge "iosbuild.sh: only add required headers to framework"
+29ed6f9a iosbuild.sh: only add required headers to framework
+71c53f1a NEON: speed-up strong filtering
+73f567ea Merge "get_disto: remove redundant reader check"
+9e14276f Merge "makefiles: prune get_disto & webp_quality deps"
+99965bac Merge "Makefile.vc: add get_disto.exe, webp_quality.exe"
+d4912238 get_disto: remove redundant reader check
+ea482409 makefiles: prune get_disto & webp_quality deps
+2ede5a19 Makefile.vc: add get_disto.exe, webp_quality.exe
+a345068a ARM: speed up bitreader by avoiding tables
+1dc82a6b Merge "introduce a generic GetCoeffs() function pointer"
+8074b89e introduce a generic GetCoeffs() function pointer
+749a45a5 Merge "NEON: implement alpha-filters (horizontal/vertical/gradient)"
+74c053b5 Merge "NEON: fix overflow in SSE NxN calculation"
+0a3aeff7 Merge "dsp: WebPExtractGreen function for alpha decompression"
+1de931c6 NEON: implement alpha-filters (horizontal/vertical/gradient)
+9b3aca40 NEON: fix overflow in SSE NxN calculation
+1c07a3c6 dsp: WebPExtractGreen function for alpha decompression
+9ed5e3e5 use pointers for WebPRescaler's in WebPDecParams
+db013a8d Merge "ARM: don't use USE_GENERIC_TREE"
+fcd4784d use a 8b table for C-version for clz()
+fbb5c473 ARM: don't use USE_GENERIC_TREE
+8fda5612 Merge "add a kSlowSSSE3 feature for CPUInfo"
+86bbd245 add a kSlowSSSE3 feature for CPUInfo
+7c2779e9 Get code to fully compile in C++.
+250c3586 Merge "When compiling as C++, avoid narrowing warnings."
+c0648ac2 When compiling as C++, avoid narrowing warnings.
+0d55f60c 40% faster ApplyAlphaMultiply_SSE2
+49d0280d NEON: implement several alpha-processing functions
+48b1e85f SSE2: 15% faster alpha-processing functions
+e3b8abbc fix warning from static analysis.
+28fe054e SSE2: 30% faster ApplyAlphaMultiply()
+f44acd25 Merge "Properly compute the optimal color cache size."
+527844fe Properly compute the optimal color cache size.
+be0ef639 fix a comment typo
+8874b162 Fix a non-deterministic color cache size computation.
+d712e20d Do not allow a color cache size bigger than the number of colors.
+ecff04f6 re-introduce some comments in Huffman Cost.
+259e9828 replace 'ptr + y * stride' by 'ptr += stride'
+00b08c88 Merge "NEON: 5% faster conversion to RGB565 and RGBA4444"
+0e7f4447 Merge "NEON: faster fancy upsampling"
+b016cb91 NEON: faster fancy upsampling
+1cb63801 Call the C function to finish off lossless SSE loops only when necessary.
+875fafc1 Implement BundleColorMap in SSE2.
+3674d49e Merge "remove Clang warnings with unused arch arguments."
+f04eb376 Merge tag 'v0.5.2'
+341d711c NEON: 5% faster conversion to RGB565 and RGBA4444
+abb54827 remove Clang warnings with unused arch arguments.
+ece9684f update ChangeLog (tag: v0.5.2-rc2, tag: v0.5.2, origin/0.5.2)
+aa7744ca anim_util: quiet implicit conv warnings in 32-bit
+d9120271 jpegdec: correct ContextFill signature
+24eb3940 Remove some errors when compiling the code as C++.
+a4a8e5f3 vwebp: clear canvas during resize w/o animation
+67c25ad5 vwebp: clear canvas during resize w/o animation
+a4bbe4b3 fix indentation
+31ca2a80 tiffdec: restore libtiff 3.9.x compatibility
+b2f77b57 update NEWS
+5ab6d9de AnimEncoder: avoid freeing uninitialized memory pointer.
+f29bf582 WebPAnimEncoder: If 'minimize_size' and 'allow_mixed' on, try lossy + lossless.
+3ebe1c00 AnimEncoder: avoid freeing uninitialized memory pointer.
+df780e0e fix a potential overflow with MALLOC_LIMIT
+58fc5078 Merge "PredictorSub: implement fully-SSE2 version"
+9cc42167 PredictorSub: implement fully-SSE2 version
+0aa1f35c remove dependency of imageio/ to stopwatch.h
+cb9ec84b Merge "remove the dependency to stop_watch.[ch] in imageio"
+dc0c01fb Merge "anim_util: quiet implicit conv warnings in 32-bit"
+827d3c50 Merge "fix a potential overflow with MALLOC_LIMIT"
+1e2e25b0 anim_util: quiet implicit conv warnings in 32-bit
+218460cd bump version to 0.5.2
+de7d654d update AUTHORS & .mailmap
+273367c1 Merge "dsp/lossless.c,cosmetics: fix indent"
+76bbcf2e fix a potential overflow with MALLOC_LIMIT
+8ac1abfe Merge "jpegdec: correct ContextFill signature"
+cb215aed remove the dependency to stop_watch.[ch] in imageio
+2423017a dsp/lossless.c,cosmetics: fix indent
+74a12b10 iosbuild.sh: add WebPDecoder.framework + encoder
+a9cc7621 Merge "iosbuild.sh: add WebPDecoder.framework + encoder"
+fbba5bc2 optimize predictor #1 in plain-C For some reason, gcc has hard time inlining this one...
+9ae0b3f6 Merge "SSE2: slightly (~2%) faster Predictor #1"
+c1f97bd7 SSE2: slightly (~2%) faster Predictor #1
+ea664b89 SSE2: 10% faster Predictor #11
+be7dcc08 AnimEncoder: Correctly skip a frame when sub-rectangle is empty.
+40885830 Fix assertions in WebPRescalerExportRow()
+1d5046d1 iosbuild.sh: add WebPDecoder.framework + encoder
+cec72014 jpegdec: correct ContextFill signature
+8f38c72e fix a typo in WebPPictureYUVAToARGB's doc
+33ca93f9 systematically call WebPDemuxReleaseIterator() on dec->prev_iter_
+76e19073 doc: use two's complement explicitly for uint8->int8 conversion
+f91ba963 Anim_encoder: correctly handle enc->prev_candidate_undecided_
+25d74e65 WebPPictureDistortion(): free() -> WebPSafeFree()
+03f1c008 mux/Makefile.am: add missing -lm
+58410cd6 fix bug in RefineUsingDistortion()
+e168af8c fix filtering auto-adjustment
+ed9dec41 fix doc and code snippet for WebPINewDecoder() doc
+3c49178f prevent 32b overflow for very large canvas_width / height
+9595f290 fix anim_util.c compilation when HAVE_GIF is not defined.
+7ec9552c Make gif transparent color to be transparent black
+b3fb8bb6 slightly faster Predictor #11 in NEON
+9871335f Add a CMake option for WEBP_SWAP_16BIT_CSP.
+0ae32226 Fix missing cpu-features for Android.
+ab4c8056 cpu.cmake: improve webp_check_compiler_flag output
+eec5fa3a Provide support for CMake on Android studio 2.2.
+004d5690 Split the main CMake file.
+4fe5d588 Android.mk: use -fvisibility=hidden
+bd63a31a vwebp: ensure setenv() is available in stdlib.h
+363a5681 vwebp: handle window resizing properly
+a0d2753f lower WEBP_MAX_ALLOCABLE_MEMORY default
+31fe11a5  fix infinite loop in case of PARTITION0 overflow
+532215dd Change the rule of picking UV mode in MBAnalyzeBestUVMode()
+9c75dbd3 cwebp.1: improve some grammar
+af2e05cb vwebp: Clear previous frame when a key triggers a redraw
+26ffa296 Add descriptions of default configuration in help info.
+7416280d Fix an unsigned integer overflow error in enc/cost.h
+13cf1d2e Do token recording and counting in a single loop
+eb9a4b97 Reset segment id if we decide not to update segment map
+42ebe3b7 configure: fix NEON flag detection under gcc 6
+76ebbfff NEON: implement predictor #13
+95b12a08 Merge "Revert Average3 and Average4"
+54ab2e75 Revert Average3 and Average4
+fe12330c 3-5% faster Predictor #5, #6, #7 and #10 for NEON
+fbfb3bef ~2% faster predictor #10 for NEON
+d4b7d801 lossless_sse2: use the local functions
+a5e3b225 Lossless decoder SSE2 improvements.
+58a1f124 ~2% faster predictor #12 in NEON.
+906c3b63 Merge "Implement lossless transforms in NEON."
+d23abe4e Implement lossless transforms in NEON.
+2e6cb6f3 Give more flexibility to the predictor generating macro.
+28e0bb70 Merge "Fix race condition in multi-threading initialization."
+64704530 Fix race condition in multi-threading initialization.
+bded7848 img2webp: fix default -lossless value and use pic.argb=1
+0e61a513 Merge "img2webp: convert a sequence of images to an animated webp"
+1cc79e92 AnimEncoder: Correctly skip a frame when sub-rectangle is empty.
+03f40955 img2webp: convert a sequence of images to an animated webp
+ea72cd60 add missing 'extern' keyword for predictor dcl
+67879e6d SSE implementation of decoding predictors.
+34aee990 Merge "vwebp: make 'd' key toggle the debugging of fragments"
+a41296ae Fix potentially uninitialized value.
+c85adb33 vwebp: make 'd' key toggle the debugging of fragments
+4239a148 Make the lossless predictors work on a batch of pixels.
+bc18ebad fix extra 'const's in signatures
+71e2f5ca Remove memcpy in lossless decoding.
+7474d46e Do not use a register array in SSE.
+67748b41 Improve latency of FTransform2.
+16951b19 Merge "Provide an SSE implementation of ConvertBGRAToRGB"
+6540cd0e Provide an SSE implementation of ConvertBGRAToRGB
+de568abf Android.mk: use -fvisibility=hidden
+3c2a61b0 remove some unneeded casts
+9ac063c3 add dsp functions for SmartYUV
+22efabdd Merge "smart_yuv: switch to planar instead of packed r/g/b processing"
+1d6e7bf3 smart_yuv: switch to planar instead of packed r/g/b processing
+0a3838ca fix bug in RefineUsingDistortion()
+c0699515 webpmux -duration: set default 'end' value equal to 'start'
+83cbfa09 Import: use relative pointer offsets
+a1ade40e PreprocessARGB: use relative pointer offsets
+fd4d090f ConvertWRGBToYUV: use relative pointer offsets
+9daad459 ImportYUVAFromRGBA: use relative pointer offsets
+f90c60d1 Merge "add a "-duration duration,start,end" option to webpmux"
+3f182d36 add a "-duration duration,start,end" option to webpmux
+342e15f0 Import: use relative pointer offsets
+1147ab4e PreprocessARGB: use relative pointer offsets
+e4cd4daf fix filtering auto-adjustment
+e7152856 fix doc and code snippet for WebPINewDecoder() doc
+de9fa507 ConvertWRGBToYUV: use relative pointer offsets
+deb1b831 ImportYUVAFromRGBA: use relative pointer offsets
+c284780f imageio_util: add ImgIoUtilCheckSizeArgumentsOverflow
+e375080d gifdec,Remap: avoid out of bounds colormap read
+c222a053 additional fix for stride type as size_t
+bb233617 fix potential overflow when width * height * 4 >= (1<<32)
+883d41fb gif2webp: fix crash with NULL extension data
+cac9a36a gifdec,Remap: avoid out of bounds colormap read
+4595e01f Revert "gifdec,Remap: avoid out of bounds colormap read"
+fb52d443 gifdec: make some constants unsigned
+f048d38d gifdec,Remap: avoid out of bounds colormap read
+31b1e343 fix SSIM metric ... by ignoring too-dark area
+2f51b614 introduce WebPPlaneDistortion to compute plane distortion
+0104d730 configure: fix NEON flag detection under gcc 6
+265abbe9 Merge "additional fix for stride type as size_t"
+f7601aa6 Merge "Introduce a generic WebPGetImageReader(type) function"
+ce873320 Introduce a generic WebPGetImageReader(type) function
+2a2773ea imageio/*dec,Read*: add input parameter checks
+9f5c8eca additional fix for stride type as size_t
+4eb5df28 remove unused stride fields from VP8Iterator
+11bc423a MIN_LENGTH cleanups.
+273d035a Merge "fix a typo in WebPPictureYUVAToARGB's doc"
+4db82a17 Merge "fix potential overflow when width * height * 4 >= (1<<32)"
+e2affacc fix potential overflow when width * height * 4 >= (1<<32)
+dc789ada fix a typo in WebPPictureYUVAToARGB's doc
+539f5a68 Fix non-included header in config.c.
+aaf2a6a6 systematically call WebPDemuxReleaseIterator() on dec->prev_iter_
+20ef9915 Merge "imageio_util: add ImgIoUtilCheckSizeArgumentsOverflow"
+bc86b7a8 imageio_util: add ImgIoUtilCheckSizeArgumentsOverflow
+806f6279 gif2webp: fix crash with NULL extension data
+68ae5b67 Add libwebp/src/mux/animi.h
+28ce3043 Remove some errors when compiling the code as C++.
+b34abcb8 Favor keeping the areas locally similar in spatial prediction mode selection
+ba843a92 fix some SSIM calculations
+51b71fd2 Merge "vwebp: ensure setenv() is available in stdlib.h"
+fb01743a get_disto: fix the r/g/b order for luma calculation
+bfab8947 vwebp: ensure setenv() is available in stdlib.h
+9310d192 vwebp: handle window resizing properly
+f79450ca Speedup ApplyMap.
+cfdda7c6 Merge "prevent 32b overflow for very large canvas_width / height"
+e36396ba Merge "get_disto: new option to compute SSIM map and convert to gray"
+18a9a0ab Add an API to import a color-mapped image.
+30d43706 Speed-up Combined entropy for palettized histograms.
+36aa087b get_disto: new option to compute SSIM map and convert to gray
+86a84b35 2x faster SSE2 implementation of SSIMGet
+b8384b53 lower WEBP_MAX_ALLOCABLE_MEMORY default
+1c364400 prevent 32b overflow for very large canvas_width / height
+eee0cce1 Merge "Small LZ77 speedups."
+5f1caf29 Small LZ77 speedups.
+1effde7b fix anim_util.c compilation when HAVE_GIF is not defined.
+a2fe9bf4 Speedup TrellisQuantizeBlock().
+573cce27 smartYUV improvements
+21e7537a  fix infinite loop in case of PARTITION0 overflow
+053a1565 Merge "Change the rule of picking UV mode in MBAnalyzeBestUVMode()"
+1377ac2e Change the rule of picking UV mode in MBAnalyzeBestUVMode()
+7c1fb7d0 fix uint32_t initialization (0. -> 0)
+bfff0bf3 speed-up SSIM calculation
+64577de8 De-VP8L-ize GetEntropUnrefinedHelper.
+a7be7328 Merge "refactor the PSNR / SSIM calculation code"
+50c3d7da refactor the PSNR / SSIM calculation code
+d6228aed indentation fix after I7055d3ee3bd7ed5e78e94ae82cb858fa7db3ddc0
+dd538b19 Remove unused declaration.
+6cc48b17 Move some lossless logic out of dsp.
+78363e9e Merge "Remove a redundant call to InitLeft() in VP8IteratorReset()"
+ffd01929 Refactor VP8IteratorNext().
+c4f6d9c9 Remove a redundant call to InitLeft() in VP8IteratorReset()
+c27d8210 Merge "smartYUV: simplify main loop"
+07795296 smartYUV: simplify main loop
+c9b45863 Split off common lossless dsp inline functions.
+490ae5b1 smartYUV: improve initial state for faster convergence
+894232be smartYUV: fix and simplify the over-zealous stop criterion
+8de08483 Remove unused code in webpi.h
+41cab7fe imageio/Android.mk: correct imagedec dependencies
+82c91c70 Merge "libimageenc.a: extract image-saving code from dwebp"
+af1ad3e2 libimageenc.a: extract image-saving code from dwebp
+dd7309e3 Merge "doc: use two's complement explicitly for uint8->int8 conversion"
+6105777e Merge "add gif2webp to CMake"
+13ae011e doc: use two's complement explicitly for uint8->int8 conversion
+4bda0cfb add gif2webp to CMake
+6029c7fe Merge "remove mention of fragment, frgm, FRGM, etc."
+545c147f remove mention of fragment, frgm, FRGM, etc.
+5b46f7fc cwebp.1: improve some grammar
+9e478f80 dec/vp8l.c: add assertions in EmitRescaledRowsRGBA/YUVA
+43bd8958 Make gif transparent color to be transparent black
+0887fc2d Merge "get_disto: add a '-o file' option to save a diff map"
+0de48e18 get_disto: add a '-o file' option to save a diff map
+0a57ad0d cosmetics: WebPSafeAlloc -> WebPSafeMalloc
+0a4699bc Merge "WebPPictureDistortion(): free() -> WebPSafeFree()"
+29fedbf5 Anim_encoder: correctly handle enc->prev_candidate_undecided_
+32dead4e WebPPictureDistortion(): free() -> WebPSafeFree()
+85cd5d06 Smarter LZ77 for uniform regions.
+6585075f Change PixelsAreSimilar() to handle black pixels correctly.
+c0a27fd2 vwebp: Clear previous frame when a key triggers a redraw
+57a5e3b6 webp_quality should return '0' in case of success.
+7f1b897b Faster stochastic histogram merging.
+48c810b8 Merge "remove WEBP_FORCE_ALIGNED and use memcpy() instead."
+3884972e remove WEBP_FORCE_ALIGNED and use memcpy() instead.
+485cac1a switch libimagedec.a and libimageio_util.a to avoid undefined symbol
+005e15b1 Merge "{extras,mux}/Makefile.am: add missing -lm"
+6ab496ed fix some 'unsigned integer overflow' warnings in ubsan
+8a4ebc6a Revert "fix 'unsigned integer overflow' warnings in ubsan"
+9d4f209f Merge changes I25711dd5,I43188fab
+e44f5248 fix 'unsigned integer overflow' warnings in ubsan
+27b5d991 Fix assertions in WebPRescalerExportRow()
+74f6f9e7 Add descriptions of default configuration in help info.
+aaf2530c {extras,mux}/Makefile.am: add missing -lm
+1269dc7c Refactor VP8LColorCacheContains()
+40872fb2 dec_neon,NeedsHev: micro optimization
+7b54e26b Add a CMake option for WEBP_SWAP_16BIT_CSP.
+d2223d8d Fix missing cpu-features for Android.
+bf16a4b4 Merge "cpu.cmake: improve webp_check_compiler_flag output"
+ee1057e3 cpu.cmake: improve webp_check_compiler_flag output
+b551e587 cosmetics: add {}s on continued control statements
+d2e4484e dsp/Makefile.am: put msa source in correct lib
+c7f66c82 Merge "utils/thread.c,cosmetics: join a few lines"
+98d8f295 Merge "examples/Makefile.am,cosmetics: sort binary targets"
+39f4ffbc utils/thread.c,cosmetics: join a few lines
+a86ce2b1 Merge "extras/Makefile.am: don't install libwebpextras"
+6fa9fe24 extras/Makefile.am: don't install libwebpextras
+0b2c58a9 Fix an unsigned integer overflow error in enc/cost.h
+d7ce4a2e examples/Makefile.am,cosmetics: sort binary targets
+386e4ba2 Reset segment id if we decide not to update segment map
+7b87e848 Merge "Add MSA optimized YUV to RGB upsampling functions"
+d3ddacb6 Add MSA optimized YUV to RGB upsampling functions
+eb98d8d8 webp_quality: detect lossless format and features
+ebee57f4 move imageio/example_util.[hc] (back to) examples/
+99542bbf webpdec: s/ExUtil//
+da573cf4 imageio_util: s/ExUtil/ImgIoUtil/
+bdda5bd4 split example_util.h
+15ed462b .gitignore: add extras/{get_disto,webp_quality}
+7be57489 Merge "VP8EstimateQuality(): roughty estimate webp bitstream quality factor"
+57020525 Makefile.vc: add missing imageio target
+e8ab6a82 VP8EstimateQuality(): roughty estimate webp bitstream quality factor
+fee7b3d6 Merge "'extras/get_disto' example: compute PSNR between two files"
+1e7d4401 'extras/get_disto' example: compute PSNR between two files
+4cecab63 pngdec.c,jpegdec.[hc]: remove unnecessary includes
+259f0434 makefile.unix: normalize image decode lib name
+ed34c39b fix: examples/libexample_dec.a => imageio/libexample_dec.a
+33d8d0d4 Merge "move examples/{example_util,image_dec} to imageio/"
+c960b82e Merge "extras.h: correct include guard"
+fe3cd28a Merge ".gitignore: add .gradle, /build"
+45fbeba5 Merge "Do token recording and counting in a single loop"
+4f33c820 .gitignore: add .gradle, /build
+c379b55a move examples/{example_util,image_dec} to imageio/
+5108d9aa extras.h: correct include guard
+ad497fbc move src/extras to the top-level
+0c0fb832 Do token recording and counting in a single loop
+9ac74f92 Add MSA optimized rescaling functions
+cb19dbc1 Add MSA optimized color transform functions
+3f4042b5 WebPAnimEncoder: If 'minimize_size' and 'allow_mixed' on, try lossy + lossless.
+5e2eb89e cosmetics,dsp/*msa.c: associate '*' with the type
+5b60db5c FastMBAnalyze() for quick i16/i4 decision
+567e6977 Add MSA optimized CollectHistogram function
+c54ab8dd Add MSA optimized quantization functions
+ec6f68c5 Merge "Remove QuantizeBlockWHT() in enc.c"
+2a5c417c Apply the RLE heuristic to LZ77.
+91b59e88 Remove QuantizeBlockWHT() in enc.c
+fe572737 Add MSA optimized SSE functions
+6b53ca87 cosmetics,(dec|enc)_sse2.c: fix indent
+b15d00d9 Merge "Add MSA optimized encoder IntraChromaPreds function"
+afe3cec8 Add MSA optimized encoder IntraChromaPreds function
+fc8cad9f reduce the number of malloc/free cycles in huffman.c
+7b4b05e0 Add MSA optimized encoder Intra16Preds function
+c18787a0 Add MSA optimized encoder Intra4Preds function
+479d1908 webpmux: Also print compression info per frame.
+a80e8cfd Provide support for CMake on Android studio 2.2.
+6c628410 Split the main CMake file.
+bbb6ecd9 Merge "Add MSA optimized distortion functions"
+7915396f Add MSA optimized distortion functions
+652e944f Merge "build.gradle: remove tab"
+c0991a14 io,EmitRescaledAlphaYUV: factor out a common expr
+48bf5ed1 build.gradle: remove tab
+bfef6c9f Merge tag 'v0.5.1'
+3d97bb75 update ChangeLog (tag: v0.5.1, origin/0.5.1)
+deb54d91 Clarify the expected 'config' lifespan in WebPIDecode()
+435308e0 Add MSA optimized encoder transform functions
+dce64bfa Add MSA optimized alpha filter functions
+429120d0 Add MSA optimized color transform functions
+c7e2d245 update ChangeLog (tag: v0.5.1-rc5)
+55b2fede normalize the macros' "do {...} while (0)" constructs
+701c772e Add MSA optimized colorspace conversion functions
+c7eb06f7 Fix corner case in CostManagerInit.
+f918cb10 fix rescaling bug: alpha plane wasn't filled with 0xff
+ab7937a5 gif2webp: normalize the number of .'s in the help message
+3cdec847 vwebp: normalize the number of .'s in the help message
+bdf6241e cwebp: normalize the number of .'s in the help message
+06a38c7b fix rescaling bug: alpha plane wasn't filled with 0xff
+319e37be Improve lossless compression.
+6a197937 Add MSA optimized intra pred chroma functions
+447adbce 'our bug tracker' -> 'the bug tracker'
+97b9e644 normalize the number of .'s in the help message
+293d786f Added MSA optimized intra prediction 16x16 functions
+0afa0ce2 Added MSA optimized intra prediction 4x4 functions
+a6621bac Added MSA optimized simple edge filtering functions
+bb50bf42 pngdec,ReadFunc: throw an error on invalid read
+38063af1 decode.h,WebPGetInfo: normalize function comment
+1ebf193c Added MSA optimized chroma edge filtering functions
+9ad2352d Merge "Added MSA optimized edge filtering functions"
+60751096 Added MSA optimized edge filtering functions
+9e8e1b7b Inline GetResidual for speed.
+7d58d1b7 Speed-up uniform-region processing.
+8ec7032b simplify HistogramCombineEntropyBin()
+23e29cb1 Merge "Fix a boundary case in BackwardReferencesHashChainDistanceOnly." into 0.5.1
+472a049b remove bin_map[] allocation altogether
+0bb23b2c free -> WebPSafeFree()
+a977b4b5 Merge "rewrite the bin_map clustering to use less memory"
+3591ba66 rewrite the bin_map clustering to use less memory
+e6ac450c utils.[hc]: s/MAX_COLOR_COUNT/MAX_PALETTE_SIZE/
+e7b91772 Merge "DecodeImageData(): change the incorrect assert" into 0.5.1
+2abfa54f DecodeImageData(): change the incorrect assert
+5a48fcd8 Merge "configure: test for -Wfloat-conversion"
+0174d18d Fix a boundary case in BackwardReferencesHashChainDistanceOnly.
+6a9c262a Merge "Added MSA optimized transform functions"
+cfbcc5ec Make sure to consider small distances in LZ77.
+5e60c42a Added MSA optimized transform functions
+3dc28d76 configure: test for -Wfloat-conversion
+f2a0946a add some asserts to delimit the perimeter of CostManager's operation
+9a583c66 fix invalid-write bug for alpha-decoding
+f66512db make gradlew executable
+6fda58f1 backward_references: quiet double->int warning
+a48cc9d2 Merge "Fix a compression regression for images with long uniform regions." into 0.5.1
+cc2720c1 Merge "Revert an LZ77 boundary constant." into 0.5.1
+059aab4f Fix a compression regression for images with long uniform regions.
+b0c7e49e Check more backward matches with higher quality.
+a3611513 Revert an LZ77 boundary constant.
+8190374c README: fix typo
+7551db44 update NEWS
+0fb2269c bump version to 0.5.1
+f4537610 update AUTHORS & .mailmap
+3259571e Refactor GetColorPalette method.
+1df5e260 avoid using tmp histogram in PreparePair()
+7685123a fix comment typos
+a246b921 Speedup backward references.
+76d73f18 Merge "CostManager: introduce a free-list of ~10 intervals"
+eab39d81 CostManager: introduce a free-list of ~10 intervals
+4c59aac0 Merge "mips msa webp configuration"
+043c33f1 Merge "Improve speed and compression in backward reference for lossless."
+71be9b8c Merge "clarify variable names in HistogramRemap()"
+0ba7fd70 Improve speed and compression in backward reference for lossless.
+0481d42a CostManager: cache one interval and re-use it when possible
+41b7e6b5 Merge "histogram: fix bin calculation"
+96c3d624 histogram: fix bin calculation
+fe9e31ef clarify variable names in HistogramRemap()
+ce3c8247 disable near-lossless quantization if palette is used
+e11da081 mips msa webp configuration
+5f8f998d mux: Presence of unknown chunks should trigger VP8X chunk output.
+cadec0b1 Merge "Sync mips32 and dsp_r2 YUV->RGB code with C verison"
+d9637758 Compute the hash chain once and for all for lossless compression.
+50a48665 Sync mips32 and dsp_r2 YUV->RGB code with C verison
+eee788e2 Merge "introduce a common signature for all image reader function"
+d77b877c introduce a common signature for all image reader function
+ca8d9519 remove some obsolete TODOs
+ae2a7222 collect all decoding utilities from examples/ in libexampledec.a
+0b8ae852 Merge "Move DitherCombine8x8 to dsp/dec.c"
+77cad885 Merge "ReadWebP: avoid conversion to ARGB if final format is YUVA"
+ab8d6698 ReadWebP: avoid conversion to ARGB if final format is YUVA
+f8b7ce9e Merge "test pointer to NULL explicitly"
+5df6f214 test pointer to NULL explicitly
+77f21c9c Move DitherCombine8x8 to dsp/dec.c
+c9e6d865 Add gradle support
+c65f41e8 Revert "Add gradle support"
+bf731ede Add gradle support
+08333b85 WebPAnimEncoder: Detect when canvas is modified, restore only when needed.
+0209d7e6 Merge "speed-up MapToPalette() with binary search"
+fdd29a3d speed-up MapToPalette() with binary search
+cf4a651b Revert "Refactor GetColorPalette method."
+0a27aca3 Merge changes Idfa8ce83,I19adc9c4
+f25c4406 WebPAnimEncoder: Restore original canvas between multiple encodes.
+169004b1 Refactor GetColorPalette method.
+576362ab VP8LDoFillBitWindow: support big-endian in fast path
+ac49e4e4 bit_reader.c: s/VP8L_USE_UNALIGNED_LOAD/VP8L_USE_FAST_LOAD/
+d39ceb58 VP8LDoFillBitWindow: remove stale TODO
+2ec2de14 Merge "Speed-up BackwardReferencesHashChainDistanceOnly."
+3e023c17 Speed-up BackwardReferencesHashChainDistanceOnly.
+f2e1efbe Improve near lossless compression when a prediction filter is used.
+e15afbce dsp.h: fix ubsan macro name
+e53c9ccb dsp.h: add WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW
+af81fdb7 utils.h: quiet -fsanitize=undefined warnings
+ea0be354 dsp.h: remove utils.h include
+cd276aec utils/*.c: ../utils/utils.h -> ./utils.h
+c8927131 utils/Makefile.am: add some missing headers
+ea24e026 Merge "dsp.h: add WEBP_UBSAN_IGNORE_UNDEF"
+369e264e dsp.h: add WEBP_UBSAN_IGNORE_UNDEF
+0d020a78 Merge "add runtime NEON detection"
+5ee2136a Merge "add VP8LAddPixels() to lossless.h"
+47435a61 add VP8LAddPixels() to lossless.h
+8fa6ac68 remove two ubsan warnings
+74fb56fb add runtime NEON detection
+4154a839 MIPS update to new Unfilter API
+c80b9fc8 Merge "cherry-pick decoder fix for 64-bit android devices"
+6235147e cherry-pick decoder fix for 64-bit android devices
+d41b8c43 configure: test for -Wformat-* w/-Wformat present
+5f95589f Fix WEBP_ALIGN in case the argument is a pointer to a type larger than a byte.
+2309fd5c replace num_parts_ by num_parts_minus_one_ (unsigned)
+9629f4bc SimplifySegments: quiet -Warray-bounds warning
+de47492e Merge "update the Unfilter API in dsp to process one row independently"
+2102ccd0 update the Unfilter API in dsp to process one row independently
+e3912d56 WebPAnimEncoder: Restore canvas before evaluating blending possibility.
+6e12e1e3 WebPAnimEncoder: Fix for single-frame optimization.
+602f344a Merge changes I1d03acac,Ifcb64219
+95ecccf6 only apply color-mapping for alpha on the cropped area
+47dd0708 anim_diff: Add an experimental option for max inter-frame diff.
+aa809cfe only allocate alpha_plane_ up to crop_bottom row
+31f2b8d8 WebPAnimEncoder: FlattenSimilarPixels(): look for similar
+774dfbdc perform alpha filtering within the decoding loop
+a4cae68d lossless decoding: only process decoded row up to last_row
+238cdcdb Only call WebPDequantizeLevels() on cropped area
+cf6c713a alpha: preparatory cleanup
+b95ac0a2 Merge "VP8GetHeaders(): initialize VP8Io with sane value for crop/scale dimensions"
+89231394 VP8GetHeaders(): initialize VP8Io with sane value for crop/scale dimensions
+5828e199 use_8b_decode -> use_8b_decode_
+8dca0247 fix bug in alpha.c that was triggering a memory error in incremental mode
+9a950c53 WebPAnimEncoder: Disable filtering when blending is used with lossy encoding.
+eb423903 WebPAnimEncoder: choose max diff for framerect based on quality.
+ff0a94be WebPAnimEncoder lossy: ignore small pixel differences for frame rectangles.
+f8040084 gif2webp: Remove the 'prev_to_prev_canvas' buffer.
+6d8c07d3 Merge "WebPDequantizeLevels(): use stride in CountLevels()"
+d96fe5e0 WebPDequantizeLevels(): use stride in CountLevels()
+ec1b2407 WebPPictureImport*: check output pointer
+c0768769 Merge "Revert "Re-enable encoding of alpha plane with color cache for next release.""
+41f14bcb WebPPictureImport*: check src pointer
+64eed387 Pass stride parameter to WebPDequantizeLevels()
+97934e24 Revert "Re-enable encoding of alpha plane with color cache for next release."
+e88c4ca0 fix -m 2 mode-cost evaluation (causing partition0 overflow)
+4562e83d Merge "add extra meaning to WebPDecBuffer::is_external_memory"
+abdb109f add extra meaning to WebPDecBuffer::is_external_memory
+875aec70 enc_neon,cosmetics: break long comment
+71e856cf GetMBSSIM,cosmetics: fix alignment
+a90edffb fix missing 'extern' for SSIM function in dsp/
+423ecaf4 move some SSIM-accumulation function for dsp/
+f08e6624 Merge "Fix FindClosestDiscretized in near lossless:"
+0d40cc5e enc_neon,Disto4x4: remove an unnecessary transpose
+e8feb20e Fix FindClosestDiscretized in near lossless:
+82006430 anim_util: quiet static analysis warning
+a6f23c49 Merge "AnimEncoder: Support progress hook and user data."
+a5193774 Merge "Near lossless feature: fix some comments."
+da98d31c AnimEncoder: Support progress hook and user data.
+33357131 Near lossless feature: fix some comments.
+0beed01a cosmetics: fix indent after 2f5e898
+6753f35c Merge "FTransformWHT optimization."
+6583bb1a Improve SSE4.1 implementation of TTransform.
+7561d0c3 FTransformWHT optimization.
+7ccdb734 fix indentation after patch #328220
+6ec0d2a9 clarify the logic of the error path when decoding fails.
+8aa352b2 Merge "Remove an unnecessary transposition in TTransform."
+db860884 Merge "remove useless #include"
+9960c316 Remove an unnecessary transposition in TTransform.
+6e36b511 Small speedup in FTransform.
+9dbd4aad Merge "fix C and SIMD flags completion."
+e60853ea Add missing common_sse2.h file to makefile.unix
+696eb2b0 fix C and SIMD flags completion.
+2b4fe33e Merge "fix multiple allocation for transform buffer"
+2f5e8986 fix multiple allocation for transform buffer
+bf2b4f11 Regroup common SSE code + optimization.
+4ed650a1 force "-pass 6" if -psnr or -size is used but -pass isn't.
+3ef1ce98 yuv_sse2: fix -Wconstant-conversion warning
+a7a03e9f Merge changes I4852d18f,I51ccb85d
+5e122bd6 gif2webp: set enc_options.verbose = 0 w/-quiet
+ab3c2583 anim_encode,DefaultEncoderOptions: init verbose
+8f0dee77 Merge "configure: fix builtin detection w/-Werror"
+4a7b85a9 cmake: fix builtin detection w/-Werror
+b74657fb configure: fix builtin detection w/-Werror
+3661b980 Add a CMakeLists.txt
+75f4af4d remove useless #include
+6c1d7631 avoid Yoda style for comparison
+8ce975ac SSE optimization for vector mismatch.
+7db53831 Merge tag 'v0.5.0'
+37f04949 update ChangeLog (tag: v0.5.0-rc1, tag: v0.5.0, origin/0.5.0)
+7e7b6ccc faster rgb565/rgb4444/argb output
+4c7f565f update NEWS
+1f62b6b2 update AUTHORS
+e224fdc8 update mailmap
+71100500 bump version to 0.5.0
+230a685e README: update help text, repo link
+d48e427b Merge "demux: accept raw bitstreams"
+99a01f4f Merge "Unify some entropy functions."
+4b025f10 Merge "configure: disable asserts by default"
+92cbddf8 Merge "fix PrintBlockInfo()"
+ca509a33 Unify some entropy functions.
+367bf903 fix PrintBlockInfo()
+b0547ff0 move back common constants for lossless_enc*.c into the .h
+fb4c7832 lossless: simpler alpha cleanup preprocessing
+ba7f4b68 Merge "anim_diff: add brief description of options"
+47ddd5a4 Move some codec logic out of ./dsp .
+b4106c44 anim_diff: add brief description of options
+357f455d yuv_sse2: fix 32-bit visual studio build
+b9d80fa4 configure: disable asserts by default
+7badd3da cosmetic fix: sizeof(type) -> sizeof(*var)
+80ce27d3 Speed up 24-bit packing / unpacking in YUV / RGB conversions.
+68eebcb0 remove a TODO about rotation
+2dee2966 remove few obsolete TODO about aligned loads in SSE2
+e0c0bb34 remove TODO about unused ref_lf_delta[]
+9cf1cc2b remove few TODO:   * 256 -> RD_DISTO_MULT   * don't use TDisto for UV mode picking
+79189645 Merge changes from topic 'demux-fragment-cleanup'
+47399f92 demux: remove GetFragment()
+d3cfb79a demux: remove dead fragment related TODO
+ab714b8a demux, Frame: remove is_fragment_ field
+b105921c yuv_sse2, cosmetics: fix indent
+466c92e8 demux,WebPIterator: remove fragment_num/num_fragments
+11714ff1 demux: remove WebPDemuxSelectFragment
+c0f7cc47 fix for bug #280: UMR in next->bits
+578beeb8 Merge "enc/Makefile.am: add missing headers"
+1a819f00 makefile.unix: make visibility=hidden the default
+d4f9c2ef enc/Makefile.am: add missing headers
+846caff4 configure: check for -fvisibility=hidden
+3f3ea2c5 demux: accept raw bitstreams
+d6dad5d0 man cwebp: add precision about exactness of the 'lossless' mode
+46bb1e34 Merge "gifdec: remove utils.h include"
+2b882e94 Merge "Makefile.vc: define WEBP_HAVE_GIF for gifdec.c"
+892b9238 Merge "man/*, AUTHORS: clarify origin of the tool"
+e5687a18 Merge "fix optimized build with -mcmodel=medium"
+e56e6859 Makefile.vc: define WEBP_HAVE_GIF for gifdec.c
+4077d944 gifdec: remove utils.h include
+b5e30dac man/*, AUTHORS: clarify origin of the tool
+b275e598 fix optimized build with -mcmodel=medium
+64da45a9 cosmetics, cwebp: fix indent
+038a060d Merge "add disto-based refinement for UV mode (if method = 1 or 2)"
+2835089d Provide an SSE2 implementation of CombinedShannonEntropy.
+e6c93519 add disto-based refinement for UV mode (if method = 1 or 2)
+04507dc9 Merge "fix undefined behaviour during shift, using a cast"
+793c5261 Merge "wicdec: add support for reading from stdin"
+d3d16397 Optimize the heap usage in HistogramCombineGreedy.
+202a710b fix undefined behaviour during shift, using a cast
+14d27a46 improve method #2 by merging DistoRefine() and  SimpleQuantize()
+cb1ce996 Merge "10% faster table-less SSE2/NEON version of YUV->RGB conversion"
+ac761a37 10% faster table-less SSE2/NEON version of YUV->RGB conversion
+79fcf29a wicdec: add support for reading from stdin
+015f173f Merge "cwebp: add support for stdin input"
+a9947c32 cwebp: add support for stdin input
+7eb01ff3 Merge "Improved alpha cleanup for the webp encoder when prediction transform is used."
+fb8c9106 Merge "introduce WebPMemToUint32 and WebPUint32ToMem for memory access"
+bd91af20 Merge "bit_reader: remove aarch64 BITS TODO"
+6c702b81 Speed up hash chain initialization using memset.
+4c60f63c make ReadPNG and ReadJPEG take a filename instead of a FILE
+464ed10f bit_reader: remove aarch64 BITS TODO
+d478e589 Merge "configure: update issue tracker"
+69381113 Improved alpha cleanup for the webp encoder when prediction transform is used.
+2c08aac8 introduce WebPMemToUint32 and WebPUint32ToMem for memory access
+010ca3d1 Fix FindMatchLength with non-aligned buffers.
+a90e1e3f README: add prerequisites for an autoconf build
+458f0866 configure: update issue tracker
+33914595 vwebp: work around the transparent background with GLUT bug
+e4a7eed4 cosmetics: fix indent
+08375129 Merge "Make a separate case for low_effort in CopyImageWithPrediction"
+aa2eb2d4 Merge "cosmetics: fix indent"
+b7551e90 cosmetics: fix indent
+5bda52d4 Make a separate case for low_effort in CopyImageWithPrediction
+66fa598a Merge "configure: fix intrinsics build w/older gcc"
+5ae220be backward_references.c: Fixed compiler warning
+1556da09 Merge "configure: restore 2 warnings"
+71a17e58 configure: restore 2 warnings
+9eeabc07 configure: fix intrinsics build w/older gcc
+363babe2 Merge "fix some warning about unaligned 32b reads"
+a1411782 Optimization in hash chain comparison for 64 bit Arrays were compared 32 bits at a time, it is now done 64 bits at a time. Overall encoding speed-up is only of 0.2% on @skal's small PNG corpus. It is of 3% on my initial 1.3 Mp desktop screenshot image.
+829bd141 Combine Huffman cost and bit entropy into one loop
+a7a954c8 Merge "lossless: make prediction in encoder work per scanline"
+61b605b4 Merge "fix of undefined multiply (int32 overflow)"
+239421c5 lossless: make prediction in encoder work per scanline
+f5ca40e0 fix of undefined multiply (int32 overflow)
+5cd2ef4c Merge changes from topic 'win-threading-compat'
+76ce9187 Makefile.vc: enable WEBP_USE_THREAD for windows phone
+d2afe974 thread: use CreateThread for windows phone
+0fd0e12b thread: use WaitForSingleObjectEx if available
+63fadc9f thread: use InitializeCriticalSectionEx if available
+110ad583 thread: use native windows cond var if available
+912c9fdf dec/webp: use GetLE(24|32) from utils
+f1694481 utils/GetLE32: correct uint32 promotion
+158763de Merge "always call WebPInitSamplers(), don't try to be smart"
+3770f3bb Merge "cleanup the YFIX/TFIX difference by removing some code and #define"
+a40f60a9 Merge "3% speed improvement for lossless webp encoder for low effort mode:"
+ed1c2bc6 always call WebPInitSamplers(), don't try to be smart
+b8c44f1a 3% speed improvement for lossless webp encoder for low effort mode:
+997e1038 cleanup the YFIX/TFIX difference by removing some code and #define
+d73d1c8b Merge "Make discarding invisible RGB values (cleanup alpha) the default."
+1f9be97c Make discarding invisible RGB values (cleanup alpha) the default.
+f240117b Make dwebp listen more to the -quiet flag
+b37b0179 fix for issue #275: don't compare to out-of-bound pointers
+21735e06 speed-up trivial one-symbol decoding case for lossless
+397863bd Refactor CopyPlane() and CopyPixels() methods: put them in utils.
+6ecd72f8 Re-enable encoding of alpha plane with color cache for next release.
+1f7148a4 Merge "remove unused fields from WebPDecoderOptions and WebPBitstreamFeatures"
+6ae395fa Merge "use ExReadFile() for ReadYUV()"
+8076a00e gitignore list: add anim_diff.
+1c1702d8 use ExReadFile() for ReadYUV()
+775d3a37 remove unused fields from WebPDecoderOptions and WebPBitstreamFeatures
+c13245c7 AnimEncoder: Add a GetError() method.
+688b265d AnimDecoder API: Add a GetDemuxer() method.
+1aa4e3d6 WebPAnimDecoder: add an option to enable multi-threaded decoding.
+3584abca AnimDecoder: option to decode to common color modes.
+afd5a62c Merge "mux.h does NOT need to include encode.h"
+8550d443 Merge "migrate anim_diff tool from C++ to C89"
+96201e50 migrate anim_diff tool from C++ to C89
+945cfa3b mux.h does NOT need to include encode.h
+8da07e8d Merge "~2x faster SSE2 RGB24toY, BGR24toY, ARGBToY|UV"
+bfd3fc02 ~2x faster SSE2 RGB24toY, BGR24toY, ARGBToY|UV
+02432427 man/cwebp.1, cosmetics: escape '-'s
+96f5b423 man/cwebp: group lossy-only options
+52fdbdfe extract some RGB24 to Luma conversion function from enc/ to dsp/
+ab8c2300 add missing \n
+8304179a sync NEWS with 0.4.4
+5bd04a08 sync versions with 0.4.4
+8f1fcc15 Merge "Move ARGB->YUV functions from dec/vp8l.c to dsp/yuv.c"
+25bf2ce5 fix some warning about unaligned 32b reads
+922268fd s/TIFF/WebP
+fa8927ef Move ARGB->YUV functions from dec/vp8l.c to dsp/yuv.c
+9b373598 Merge "for ReadXXXX() image-readers, use the value of pic->use_argb"
+f7c507a5 Merge "remove unnecessary #include "yuv.h""
+7861578b for ReadXXXX() image-readers, use the value of pic->use_argb
+14e4043b remove unnecessary #include "yuv.h"
+469ba2cd vwebp: fix incorrect clipping w/NO_BLEND
+4b9186b2 update issue tracker url
+d64d376c change WEBP_ALIGN_CST value to 31
+f717b828 vp8l.c, cosmetics: fix indent after 95509f9
+927ccdc4 Merge "fix alignment of allocated memory in AllocateTransformBuffer"
+fea94b2b fix alignment of allocated memory in AllocateTransformBuffer
+5aa8d61f Merge "MIPS: rescaler code synced with C implementation"
+e7fb267d MIPS: rescaler code synced with C implementation
+93c86ed5 Merge "format_constants.h: MKFOURCC, correct cast"
+5d791d26 format_constants.h: MKFOURCC, correct cast
+65726cd3 dsp/lossless: Average2, make a constant unsigned
+d26d9def Use __has_builtin to check clang support
+12ec204e moved ALIGN_CST into util/utils.h and renamed WEBP_ALIGN_xxx
+a2640838 Merge "rescaler: ~20% faster SSE2 implementation for lossless ImportRowExpand"
+3fb600d5 Merge "wicdec: fix alpha detection w/64bpp BGRA/RGBA"
+67c547fd rescaler: ~20% faster SSE2 implementation for lossless ImportRowExpand
+99e3f812 Merge "large re-organization of the delta-palettization code"
+95509f99 large re-organization of the delta-palettization code
+74fb458b fix for weird msvc warning message
+ae49ad86 Merge "SSE2 implementation of ImportRowShrink"
+932fd4df SSE2 implementation of ImportRowShrink
+badfcbaa wicdec: fix alpha detection w/64bpp BGRA/RGBA
+35cafa6c Merge "iosbuild: fix linking with Xcode 7 / iOS SDK 9"
+b0c9d8af label rename: NO_CHANGE -> NoChange
+b4e731cd neon-implementation for rescaler code
+db1321a6 iosbuild: fix linking with Xcode 7 / iOS SDK 9
+6dfa5e3e rescaler: better handling of the fxy_scale=0 special case.
+55c05293 Revert "rescaler: better handling of the fxy_scale=0 special case."
+9f226bf8 rescaler: better handling of the fxy_scale=0 special case.
+f7b8f907 delta_palettization.*: add copyright
+c1e1b710 Changed delta palette to compress better
+0dd28267 Merge "Add delta_palettization feature to WebP"
+48f66b66 Add delta_palettization feature to WebP
+27933e2a anim_encoder: drop a frame if it has same pixels as the prev frame.
+df9f6ec8 Merge "webpmux/DisplayInfo: send non-error output to stdout"
+8af4993b Merge "rescaler_mips_dsp_r2: cosmetics, fix indent"
+2b9d2495 Merge "rescaler: cosmetics, join two lines"
+cc020a8c webpmux/DisplayInfo: send non-error output to stdout
+a288e746 configure: add -Wshorten-64-to-32
+c4c3cf2d pngdec: fix type conversion warnings
+bef8e97d webpmux: fix type conversion warning
+5a84460d rescaler_mips_dsp_r2: cosmetics, fix indent
+acde0aae rescaler: cosmetics, join two lines
+306ce4fd rescaler: move the 1x1 or 2x1 handling one level up
+cced974b remove _mm_set_epi64x(), which is too specific
+56668c9f fix warnings about uint64_t -> uint32_t conversion
+76a7dc39 rescaler: add some SSE2 code
+1df1d0ee rescaler: harmonize function protos
+9ba1894b rescaler: simplify ImportRow logic
+5ff0079e fix rescaler vertical interpolation
+cd82440e VP8LAllocateHistogramSet: align histogram[] entries
+a406b1dd Merge "fix memory over-allocation in lossless rescaler init"
+0fde33e3 add missing const in VP8InitFrame signature
+ac7d5e8d fix memory over-allocation in lossless rescaler init
+017f8ccc Loosen the buffer size checks for Y/U/V/A too.
+15ca5014 loosen the padding check on buffer size
+d623a870 dec_neon: add whitespace around stringizing operator
+29377d55 dsp/mips: cosmetics: add whitespace around XSTR macro
+eebaf97f dsp/mips: add whitespace around stringizing operator
+d39dc8f3 Create a WebPAnimDecoder API.
+03fb7522 gif2webp: print output file size
+14efabbf Android: limit use of cpufeatures
+7b83adbe preparatory cosmetics for Rescaler code fix and clean-up
+77fb41c2 dec/vp8l/DecodeAlphaData: remove redundant cast
+90fcfcd9 Insert less hash chain entries from the beginnings of long copies.
+bd55604d SSE2: add yuv444 converters, re-using yuv_sse2.c
+41a5d99d add a -quiet option to 'dwebp'
+80ab3edb Merge "README: update dwebp help output after 1e595fe"
+32b71b2e README: update dwebp help output after 1e595fe
+3ec11827 use the DispatchAlpha() call from dsp
+c5f00621 incorporate bzero() into WebPRescalerInit() instead of call site
+3ebcdd41 remove duplicate "#include <stdlib.h>"
+1e595fe1 dwebp: add -resize as a synonym for -scale
+24a96932 dec: allow 0 as a scaling dimension
+b9187242 utils/rescaler: add WebPRescalerGetScaledDimensions
+923e8eda Merge "update NEWS"
+020fd099 Merge "WebPPictureDistortion: support ARGB format for 'pic' when computing distortion."
+6a5292f6 update NEWS
+56a2e9f5 WebPPictureDistortion: support ARGB format for 'pic' when computing distortion.
+0ae582e4 configure: test and add -Wunreachable-code
+c2f9dc06 bit_writer: convert VP8L macro values to immediates
+b969f888 Reduce magic in palette reordering
+acb297e9 anim_diff: add a -raw_comparison flag
+155c1b22 Merge changes I76f4d6fe,I45434639
+717e4d5a mips32/mipsDSPr2: function ImportRow rebased
+7df93893 fix rescaling bug (uninitialized read, see bug #254).
+5cdcd561 lossless_enc_neon: add VP8LTransformColor
+a53c3369 lossless_neon: add VP8LTransformColorInverse
+99131e7f Merge changes I9fb25a89,Ibc648e9e
+c4556766 simplify the main loop for downscaling
+2a010f99 lossless_neon: remove predictors 5-13
+ca221bbc ll_enc_neon: enable VP8LSubtractGreenFromBlueAndRed
+585d93db Container spec: clarify ordering of ALPH chunk.
+01d61fd9 lossless: ~20 % speedup
+f722c8f0 lossless: Speed up ComputeCacheEntropy by 40 %
+1ceecdc8 add a VP8LColorCacheSet() method for color cache
+17eb6099 lossless: Allow copying from prev row in rle-mode.
+f3a7a5bf lossless: bit writer optimization
+d97b9ff7 Merge changes from topic 'lossless-enc-improvements'
+0250dfcc msvc: fix pointer type warning in BitsLog2Floor
+52931fd5 lossless: combine the Huffman code with extra bits
+c4855ca2 lossless: Inlining add literal
+8e9c94de lossless: simplify HashChainFindCopy heuristics
+888429f4 lossless: 0.5 % compression density improvement
+7b23b198 lossless: Add zeroes into the predicted histograms.
+85b44d8a lossless: encoding, don't compute unnecessary histo
+d92453f3 lossless: Remove about 25 % of the speed degradation
+2cce0317 Faster alpha coding for webp
+5e75642e lossless: rle mode not to accept lengths smaller than 4.
+84326e4a lossless: Less code for the entropy selection
+16ab951a lossless: 0.37 % compression density improvement
+822f113e add WebPFree() to the API
+0ae2c2e4 SSE2/SSE41: optimize SSE_16xN loops
+39216e59 cosmetics: fix indent after 32462a07
+559e54ca Merge "SSE2: slightly faster FTransformWHT"
+8ef9a63b SSE2: slightly faster FTransformWHT
+f27f7735 lossless_neon: enable VP8LAddGreenToBlueAndRed
+36e9c4bc SSE2: minor cosmetrics on in-loop filter code
+4741fac4 dsp/lossless_*sse2: remove some unnecessary inlines
+1819965e fix warning ("left shift of negative value") using a cast
+70170014 SSE2: speed-up some lossless-encoding functions
+abcb0128 Merge "SSE2: slightly faster (~5%) AddGreenToBlueAndRed()"
+2df5bd30 Merge "Speedup to HuffmanCostCombinedCount"
+9e356d6b SSE2: slightly faster (~5%) AddGreenToBlueAndRed()
+fc6c75a2 SSE2: 53% faster TransformColor[Inverse]
+49073da6 SSE2: 46% speed-up of TransformColor[Inverse]
+32462a07 Speedup to HuffmanCostCombinedCount
+f3d687e3 SSE4.1 implementation of some lossless encoding functions
+bfc300c7 SSE4.1 implementation of some alpha-processing functions
+7f9c98f2 Merge "sse2 in-loop: simplify SignedShift8b() a bit"
+ef314a5d dec_sse2/GetNotHEV: micro optimization
+a729cff9 sse2 in-loop: simplify SignedShift8b() a bit
+422ec9fb simplify Load8x4() a bit
+8df238ec Merge "remove some duplicate FlipSign()"
+751506c4 remove some duplicate FlipSign()
+65ef5afc Merge "lossless: 0.13% compression density gain"
+2beef2f2 lossless: 0.13% compression density gain
+3033f24c lossless: 0.06 % compression density improvement
+64960da9 dec_neon: add VE8uv / VE16
+14dbd87b dec_neon: add HE8uv / HE16
+ac768011 introduce FTransform2 to perform two transforms at a time.
+aa6065ae dec_neon: use vld1_dup(mem) rather than vdup(mem[0])
+8b63ac78 Merge "dec_neon: add TM16"
+f51be09e Merge "dec_neon/TrueMotion: simply left border load"
+dc48196b dec_neon: add TM16
+ea95b305 dec_neon/TrueMotion: simply left border load
+f262d612 speed-up SetResidualSSE2
+bf46d0ac fix mips2 build target
+929a0fdc enc_sse2/TTransform: simplify abs calculation
+17dbd058 enc_sse2/CollectHistogram: simplify abs calculation
+a6c15936 dec_neon: add DC16 intra predictors
+03b4f50d Makefile.vc: add anim_diff build support.
+1b989874 Merge changes I9cd84125,Iee7e387f,I7548be72
+acd7b5af Introduce a test tool anim_diff.
+f274a96c dsp/enc_sse2: add luma4 intra predictors
+040b11bd dsp/enc_sse2: add chroma intra predictors
+aee021bb dsp/enc_sse2: add luma16 intra predictors
+9e00a499 makefile.unix: remove superclean target
+cefc9c09 makefile.unix: clean up after extras target
+4c9af023 dec_neon: add DC8uvNoTopLeft
+dd55b873 Merge "doc/webp-container-spec: update repo browser link"
+f0486968 doc/webp-container-spec: update repo browser link
+9287761d Merge "GetResidualCostSSE2: simplify abs calculation"
+0e009366 dsp/cpu.c(x86): check maximum supported cpuid feature
+b243a4bc GetResidualCostSSE2: simplify abs calculation
+6d4602b8 Merge "fix typo: constitutes -> constitute"
+5fe1fe37 fix typo: constitutes -> constitute
+b83bd7c4 Merge "populate 'libwebpextras' with: import gray, rgb565 and rgb4444 functions"
+b0114a32 Merge "histogram.h: cosmetics: remove unnecessary includes"
+feab45ef gifdec: Move inclusion of webp/config.h to header.
+dbba67d1 histogram.h: cosmetics: remove unnecessary includes
+e978fec6 Merge "VP8LBitReader: fix remaining ubsan error with large shifts"
+d6fe5884 Merge "ReconstructRow: move some one-time inits out of the main loop"
+a21d647c ReconstructRow: move some one-time inits out of the main loop
+7a01c3c3 VP8LBitReader: fix remaining ubsan error with large shifts
+7fa67c9b change GetPixPairHash64() return type to uint32_t
+ec1fb9f8 Merge "dsp/enc.c: cosmetics: move DST() def closer to use"
+7073bfb3 Merge "split 64-mult hashing into two 32-bit multiplies"
+0768b252 dsp/enc.c: cosmetics: move DST() def closer to use
+6a48b8f0 Merge "fix MSVC size_t->int conversion warning"
+1db07cde Merge "anim_encode: cosmetics: fix alignment"
+e28271a3 anim_encode: cosmetics: fix alignment
+7fe357b8 split 64-mult hashing into two 32-bit multiplies
+af74c145 populate 'libwebpextras' with: import gray, rgb565 and rgb4444 functions
+61214134 remove VP8Residual::cost unused field
+e2544823 fix MSVC size_t->int conversion warning
+b69a6c35 vwebp: don't redefine snprintf with VS2015+
+0ac29c51 AnimEncoder API: Consistent use of trailing underscores in struct.
+d4845550 AnimEncoder API: Use timestamp instead of duration as input to Add().
+9904e365 dsp/dec_sse2: DC8uv / DC8uvNoLeft speedup
+7df20497 dsp/dec_sse2: DC16 / DC16NoLeft speedup
+8e515dfe Merge "makefile.unix: add some missing headers"
+db12250f cosmetics: vp8enci.h: break long line
+bf516a87 makefile.unix: add some missing headers
+b44eda3f dsp: add DSP_INIT_STUB
+03e76e96 clarify the comment about double-setting the status in SetError()
+9fecdd71 remove unused EmitRGB()
+43f010dd move ReconstructRow to top
+82d98020 add a dec/common.h header to collect common enc/dec #defines
+5d4744a2 Merge "enc_sse41: add Disto4x4 / Disto16x16"
+e38886a7 mux.h: Bump up ABI version
+46305ca6 configure: add --disable-<avx2|sse4.1|sse2>
+2fc8b658 CPPFLAGS->CFLAGS for detecting sse4.1 in preprocessor
+1a338fb3 enc_sse41: add Disto4x4 / Disto16x16
+94055503 encoding SSE4.1 stub for StoreHistogram + Quantize + SSE_16xN
+c64659e1 remove duplicate variables after the lossless{_enc}.c split
+67ba7c7a enc_sse2: call local FTransform in CollectHistogram
+18249799 dsp: s/VP8LSetHistogramData/VP8SetHistogramData/
+ede5e158 cosmetics: dsp/lossless.h: reorder prototypes
+553051f7 dsp/lossless: split enc/dec functions
+9064adc8 Merge "conditionally add -msse4.1 in Makefile.unix"
+cecf5096 dsp/yuv*.c: rework WEBP_USE_<arch> ifdef
+6584d398 dsp/upsampling*.c: rework WEBP_USE_<arch> ifdef
+80809422 dsp/rescaler*.c: rework WEBP_USE_<arch> ifdef
+1d93ddec dsp/lossless*.c: rework WEBP_USE_<arch> ifdef
+73805ff2 dsp/filters*.c: rework WEBP_USE_<arch> ifdef
+fbdcef24 dsp/enc*.c: rework WEBP_USE_<arch> ifdef
+66de69c1 dsp/dec*.c: rework WEBP_USE_<arch> ifdef
+48e4ffd1 dsp/cost*.c: rework WEBP_USE_<arch> ifdef
+29fd6f90 dsp/argb*.c: rework WEBP_USE_<arch> ifdef
+80ff3813 dsp/alpha*.c: rework WEBP_USE_<arch> ifdef
+bf09cf1e conditionally add -msse4.1 in Makefile.unix
+e9570dd9 stub for SSE4.1 support.
+4a95384b Merge "dsp: add sse4.1 detection"
+cabf4bd2 dsp: add sse4.1 detection
+4ecba1ab thread.h: rename interface param
+b8d706c8 Merge "sync versions with 0.4.3"
+ae64a711 Merge "add shell for libwebpextras"
+92a5da9c sync versions with 0.4.3
+9d4e2d16 Merge "~30% faster smart-yuv (-pre 4) with early-out criterion"
+b1bdbbab ~30% faster smart-yuv (-pre 4) with early-out criterion
+7efb9748 Merge "Disable NEON code on Native Client"
+ac4f5784 Disable NEON code on Native Client
+0873f85b AnimEncoder API: Support input frames in YUV(A) format.
+5c176d2d add shell for libwebpextras
+44bd9561 fix signature for VP8RecordCoeffTokens()
+c9b8ea0e small cosmetics on TokenBuffer.
+76394c09 Merge "MIPS: dspr2: added optimization for TrueMotion"
+0f773693 WebPPictureRescale: add a note about 0 width/height
+241bb5d9 MIPS: dspr2: added optimization for TrueMotion
+6cef0e4f examples/Android.mk: add webpmux_example target
+53c16ff0 Android.mk: add webpmux target
+21852a00 Android.mk: add webpdemux target
+8697a3bc Android.mk: add webpdecoder{,_static} targets
+4a670491 Android.mk: split source lists per-directory
+b5e79422 MIPS: dspr2: Added optimization for some convert functions
+0f595db6 MIPS: dspr2: Added optimization for some convert functions
+8a218b4a MIPS: [mips32|dspr2]: GetResidualCost rebased
+ef987500 Speedup method StoreImageToBitMask by 5%.
+602a00f9 fix iOS arm64 build with Xcode 6.3
+23820507 1-2% faster encoding by removing an indirection in GetResidualCost()
+eddb7e70 MIPS: dspr2: added otpimization for DC8uv, DC8uvNoTop and DC8uvNoLeft
+73ba2915 MIPS: dspr2: added optimization for functions RD4 and LD4
+c7129da5 Merge "4-5% faster encoding using SSE2 for GetResidualCost"
+94380d00 MIPS: dspr2: added optimizaton for functions VE4 and DC4
+2a407092 4-5% faster encoding using SSE2 for GetResidualCost
+17e19862 Merge "MIPS: dspr2: added optimization for simple filtering functions"
+3ec404c4 Merge "dsp: normalize WEBP_TSAN_IGNORE_FUNCTION usage"
+b969f5df dsp: normalize WEBP_TSAN_IGNORE_FUNCTION usage
+d7b8e711 MIPS: dspr2: added optimization for simple filtering functions
+235f774e Merge "MIPS: dspr2: Added optimization for function VP8LTransformColorInverse_C"
+42a8a628 MIPS: dspr2: Added optimization for function VP8LTransformColorInverse_C
+b442bef3 Merge "ApplyFiltersAndEncode: only copy lossless stats"
+b510fbfe doc/webp-container-spec: note MSB order for chunk diagrams
+9bc0f922 ApplyFiltersAndEncode: only copy lossless stats
+3030f115 Merge "dsp/mips: add some missing TSan annotations"
+dfcf4593 Merge "MIPS: dspr2: Added optimization for function VP8LAddGreenToBlueAndRed_C"
+55c75a25 dsp/mips: add some missing TSan annotations
+2cb879f0 MIPS: dspr2: Added optimization for function VP8LAddGreenToBlueAndRed_C
+e1556010 move some cost tables from enc/ to dsp/
+c3a03168 Merge "picture_csp: fix build w/USE_GAMMA_COMPRESSION undefined"
+39537d7c Merge "VP8LDspInitMIPSdspR2: add missing TSan annotation"
+1dd419ce picture_csp: fix build w/USE_GAMMA_COMPRESSION undefined
+43fd3543 VP8LDspInitMIPSdspR2: add missing TSan annotation
+c7233dfc Merge "VP8LDspInit: remove memcpy"
+0ec4da96 picture_csp::InitGammaTables*: add missing TSan annotations
+35579a49 VP8LDspInit: remove memcpy
+97f6aff8 VP8YUVInit: add missing TSan annotation
+f9016d66 dsp/enc::InitTables: add missing TSan annotation
+e3d9771a VP8EncDspCostInit*: add missing TSan annotations
+d97c143d Merge "doc/webp-container-spec: cosmetics"
+309b7908 MIPS: mips32: Added optimization for function SetResidualCoeffs
+a987faed MIPS: dspr2: added optimization for function GetResidualCost
+e7d3df23 doc/webp-container-spec: cosmetics
+be6635e9 Merge "VP8TBufferClear: remove some misleading const's"
+02971e72 Merge "VP8EmitTokens: remove unnecessary param void cast"
+3b77e5a7 VP8TBufferClear: remove some misleading const's
+aa139c8f VP8EmitTokens: remove unnecessary param void cast
+c24d8f14 cosmetics: upsampling_sse2: add const to some casts
+1829c42c cosmetics: lossless_sse2: add const to some casts
+183168f3 cosmetics: enc_sse2: add const to some casts
+860badca cosmetics: dec_sse2: add const to some casts
+0254db97 cosmetics: argb_sse2: add const to some casts
+1aadf856 cosmetics: alpha_processing_sse2: add const to some casts
+1579de3c vwebp: clear canvas at the beginning of each loop
+4b9fa5d0 Merge "webp-container-spec: clarify background clear on loop"
+4c82284d Updated the near-lossless level mapping.
+56039479 webp-container-spec: clarify background clear on loop
+19f0ba0e Implement true-motion prediction in SSE2
+774d4cb7 make VP8PredLuma16[] array non-const
+d7eabb80 Merge "MIPS: dspr2: Added optimization for function CollectHistogram"
+fe42739c Use integers for kmin/kmax for simplicity.
+b9df35f7 AnimEncode API: kmax=0 should imply all keyframes.
+6ce296da MIPS: dspr2: Added optimization for function CollectHistogram
+2c906c40 vwebp: remove unnecessary static Help() prototype
+be0fd1d5 Merge "dec/vp8: clear 'dither_' on skipped blocks"
+e96170fe Merge "vwebp/animation: display last frame on end-of-loop"
+0f017b56 vwebp/animation: display last frame on end-of-loop
+c86b40cc enc/near_lossless.c: fix alignment
+66935fb9 dec/vp8: clear 'dither_' on skipped blocks
+b7de7946 Merge "lossless_neon: enable subtract green for aarch64"
+77724f70 SSE2 version of GradientUnfilter
+416e1cea lossless_neon: enable subtract green for aarch64
+72831f6b Speedup AnalyzeAndInit for low effort compression.
+a6597483 Speedup Analyze methods for lossless compression.
+98c81386 Enable Near-lossless feature.
+c6b24543 AnimEncoder API: Fix for kmax=1 and default kmin case.
+022d2f88 add SSE2 variants for alpha filtering functions
+2db15a95 Temporarily disable encoding of alpha plane with color cache.
+1d575ccd Merge "Lossless decoding: Remove an unnecessary if condition."
+cafa1d88 Merge "Simplify backward refs calculation for low-effort."
+7afdaf84 Alpha coding: reorganize the filter/unfiltering code
+4d6d7285 Simplify backward refs calculation for low-effort.
+ec0d1be5 Cleaup Near-lossless code.
+9814ddb6 Remove the post-transform near-lossless heuristic.
+4509e32e Lossless decoding: Remove an unnecessary if condition.
+f2ebc4a8 Merge "Regression fix for lossless decoding"
+783a8cda Regression fix for lossless decoding
+9a062b8e AnimEncoder: Bugfix for kmin = 1 and kmax = 2.
+0f027a72 simplify smart RGB->YUV conversion code
+0d5b334e BackwardReferencesHashChainFollowChosenPath: remove unused variable
+f480d1a7 Fix to near lossless artefacts on palettized images.
+d4615d08 Merge changes Ia1686828,I399fda40
+cb4a18a7 rename HashChainInit into HashChainReset
+f079e487 use uint16_t for chosen_path[]
+da091212 MIPS: dspr2: Added optimization for function FTransformWHT
+b8c20135 Merge "wicdec: (msvs) quiet some /analyze warnings"
+9b228b54 wicdec: (msvs) quiet some /analyze warnings
+daeb276a Merge "MIPS: dspr2: Added optimization for MultARGBRow function"
+cc087424 Merge "dsp/cpu: (msvs) add include for __cpuidex"
+4a82aab5 Merge changes I87544e92,I0bb6cda5
+7a191398 dwebp/WritePNG: mark png variables volatile
+775dfad2 dwebp: include setjmp.h w/WEBP_HAVE_PNG
+47d26be7 dwebp: correct sign in format strings
+f0e0677b VP8LEncodeStream: add an assert
+c5f7747f VP8LColorCacheCopy: promote an int before shifting
+0de5f33e dsp/cpu: (msvs) add include for __cpuidex
+7d850f7b MIPS: dspr2: Added optimization for MultARGBRow function
+54875293 MIPS: dspr2: added optimization for function QuantizeBlock
+4fbe9cf2 dsp/cpu: (msvs) avoid immintrin.h on _M_ARM
+3fd59039 simplify/reorganize arguments for CollectColorBlueTransforms
+b9e356b9 Disable costly TraceBackwards for method=0.
+a7e7caa4 MIPS: dspr2: added optimization for function TransformColorRed
+2cb39180 Merge "MIPS: dspr2: added optimization for function TransformColorBlue"
+279e6613 Merge "dsp/cpu: add include for _xgetbv() w/MSVS"
+b6c0428e dsp/cpu: add include for _xgetbv() w/MSVS
+d1c4ffae gif2webp: Move GIF decoding related code to a support library.
+07c39559 Merge "AnimEncoder API: Add info in README.mux"
+7b161973 MIPS: dspr2: added optimization for function TransformColorBlue
+d7c4b02a cpu: fix AVX2 detection for gcc/clang targets
+9d299469 AnimEncoder API: Add info in README.mux
+d581ba40 follow-up: clean up WebPRescalerXXX dsp function
+f8740f0d dsp: s/USE_INTRINSICS/WEBP_USE_INTRINSICS/
+ce73abe0 Merge "introduce a separate WebPRescalerDspInit to initialize pointers"
+ab66beca introduce a separate WebPRescalerDspInit to initialize pointers
+205c7f26 fix handling of zero-sized partition #0 corner case
+cbcdd5ff Merge "move rescaler functions to rescaler* files in src/dsp/"
+bf586e88 Merge changes I230b3532,Idf3057a7
+6dc79dc2 Merge "anim_encode: fix type conversion warnings"
+11fce25a Merge "dec_neon: remove returns from void functions"
+c4e63f99 Makefile.vc: add gif2webp target
+4f43d38c enable NEON for Windows ARM builds
+3f6615ac Makefile.vc: add rudimentary Windows ARM support
+e7c5954c dec_neon: remove returns from void functions
+f79c163b anim_encode: fix type conversion warnings
+0f54f1ec Remove gif2webp_util which is no longer needed.
+cbcbedd0 move rescaler functions to rescaler* files in src/dsp/
+ac79ed19 webpmux: remove experimental fragment handling
+e8694d4d mux: remove experimental FRGM parsing
+9e92b6ea AnimEncoder API: Optimize single-frame animated images
+abbae279 Merge "Move over gif2webp to the new AnimEncoder API."
+a28c4b36 MIPS: move WORK_AROUND_GCC define to appropriate place
+012d2c60 MIPS: dspr2: added optimization for functions SSEAxB
+67720c8b Move over gif2webp to the new AnimEncoder API.
+9241ecf4 MIPS: dspr2: added optimization for function Average
+9422211d Merge "Tune BackwardReferencesLz77 for low_effort (m=0)."
+df40057b Merge "Speedup VP8LGetHistoImageSymbols for low effort (m=0) mode."
+ea08466d Tune BackwardReferencesLz77 for low_effort (m=0).
+b0b973c3 Speedup VP8LGetHistoImageSymbols for low effort (m=0) mode.
+c6d32927 argb_sse2: cosmetics
+67f601cd make the 'last_cpuinfo_used' variable names unique
+b9489861 AnimEncoder API: Init method for default options.
+856f8ec1 Merge "AnimEncoder API: Remove AnimEncoderFrameOptions."
+c537514d Merge "AnimEncoder API: GenerateCandidates bugfix."
+dc0ce039 Merge "AnimEncoder API: Compute change rectangle for first frame too."
+f00b639b Merge "AnimEncoder API: In Assemble(), always set animation parameters."
+29ed796c Merge "AnimEncoder lib cleanup: prev to prev canvas not needed."
+9f0dd6e5 Merge "WebPAnimEncoder API: Header and implementation"
+5e56bbe0 AnimEncoder API: Remove AnimEncoderFrameOptions.
+b902c3ea AnimEncoder API: GenerateCandidates bugfix.
+ef3c39bb AnimEncoder API: Compute change rectangle for first frame too.
+eec423ab AnimEncoder API: In Assemble(), always set animation parameters.
+ae1c046e AnimEncoder lib cleanup: prev to prev canvas not needed.
+4b997ae4 WebPAnimEncoder API: Header and implementation
+72208bec move argb_*.o build target to encoder list
+95920538 Merge "multi-thread fix: lock each entry points with a static var"
+4c1b300a Merge "SSE2 implementation of VP8PackARGB"
+fbcc2004 Merge "add -Wformat-nonliteral and -Wformat-security"
+80d950d9 add -Wformat-nonliteral and -Wformat-security
+04c20e75 Merge "MIPS: dspr2: added optimization for function Intra4Preds"
+a437694a multi-thread fix: lock each entry points with a static var
+ca7f60db SSE2 implementation of VP8PackARGB
+72d573f6 simplify the PackARGB signature
+4e2589ff demux: restore strict fragment flag check
+4ba8e074 Merge "webp-container-spec: remove references to fragments"
+e752f0a6 Merge "demux: remove experimental FRGM parsing"
+f8abb112 Merge changes I109ec4d9,I73fe7743
+ae2188a4 MIPS: dspr2: added optimization for function Intra4Preds
+1f4b8642 move VP8EncDspARGBInit() call closer to where it's needed
+14108d78 dec_neon: add DC8uvNoTop / DC8uvNoLeft
+d8340da7 dec_neon: add DC8uv
+a66e66c7 webp-container-spec: remove references to fragments
+7ce8788b MIPS: dspr2: added optimization for function MakeARGB32
+012e623d demux: remove experimental FRGM parsing
+87c3d531 method=0: Don't evaluate any predictor
+6f4fcb98 Merge "MIPS: dspr2: added optimization for function ImportRow"
+24284459 replace unneeded calls to HistogramCopy() by swaps
+bdf7b40c MIPS: dspr2: added optimization for function ImportRow
+e66a9225 Merge "MIPS: dspr2: added optimization for function ExportRowC"
+c279fec1 MIPS: dspr2: added optimization for function ExportRowC
+31a9cf64 Speedup WebP lossless compression for low effort (m=0) mode with following: - Disable Cross-Color transform. - Evaluate predictors #11 (paeth), #12 and #13 only.
+9275d91c MIPS: dspr2: added optimization for function TrueMotion
+26106d66 Merge "enc_neon: fix building with non-Xcode clang (iOS)"
+1c4e3efe unroll the kBands[] indirection to remove a dereference in GetCoeffs()
+a3946b89 enc_neon: fix building with non-Xcode clang (iOS)
+8ed9c00d Merge "simplify the Histogram struct, to only store max_value and last_nz"
+bad77571 simplify the Histogram struct, to only store max_value and last_nz
+3cca0dc7 MIPS: dspr2: Added optimization for DCMode function
+37e395fd MIPS: fix functions to use generic BPS istead of hardcoded value
+9475bef4 PickBestUV: fix VP8Copy16x8 invocation
+441f273f Merge changes I55f8da52,Id73a1e96
+4a279a68 cosmetics: add some missing != NULL comparisons
+66ad3725 factorize BPS definition in dsp.h and add VP8Copy16x8
+432e5b55 make ALIGN_xxx naming consistent
+57606047 encoder: switch BPS to 32 instead of 16
+1b66bbe9 MIPS: dspr2: added optimization for function TransformColor_C
+c6d0f9e7 histogram: cosmetics
+f399d307 Merge changes I6eac17e5,I32d2b514
+9de9074c dec_neon: add TM8uv
+8e517eca bit_reader/kVP8NewRange: range_t -> uint8_t
+e1857139 dsp: initialize VP8PredChroma8 in VP8DspInit()
+e0c809ad Move Entropy methods to lossless.c
+a96ccf8f iosbuild: add x64_64 simulator support
+a0df5510 Remove handling for WEBP_HINT_GRAPH
+413dfc0c Move static method definition before its usage.
+0f235665 Update BackwardRefsWithLocalCache.
+d69e36ec Remove TODOs from lossless encoder code.
+fdaac8e0 Optmize VP8LGetBackwardReferences LZ77 references.
+2f0e2ba8 MIPS: dspr2: added optimization for function Select
+a3e79a46 Merge "WebPEncode: Support encoding same pic twice (even if modified)"
+e4f4dddb WebPEncode: Support encoding same pic twice (even if modified)
+cbc3fbb4 Merge "Updated VP8LGetBackwardReferences and color cache."
+95a9bd85 Updated VP8LGetBackwardReferences and color cache.
+54f2c14c MIPS: dspr2: added optimization for function FTransform
+aa42f423 MIPS: dspr2: Added optimization for function VP8LSubtractGreenFromBlueAndRed
+11a25f75 Merge "FlattenSimilarBlocks should only be tried when blending is possible."
+5cccdadf FlattenSimilarBlocks should only be tried when blending is possible.
+95ca44a7 MIPS: dspr2: added optimization for Disto4x4
+4171b672 backward_references.c: reindent after c8581b0
+c8581b06 Optimize BackwardReferences for RLE encoding.
+5798eee6 MIPS: dspr2: unfilters bugfix (Ie7b7387478a6b5c3f08691628ae00f059cf6d899)
+4167a3f5 Optimize backwardreferences
+d18554c3 Merge "webp/types.h: use inline for clang++/-std=c++11"
+7489b0e7 gif2webp: Add '-min-size' option to get best compression.
+77bdddf0 Speed up BackwardReferences
+6638710b webp/types.h: use inline for clang++/-std=c++11
+abf04205 Enable entropy based merge histo for (q<100)
+572022a3 filters_mips_dsp_r2.c: disable unfilters
+a28e21b1 MIPS: dspr2: Added optimization for function ClampedAddSubtractFull
+18d5a1ef MIPS: dspr2: added optimization for function ClampedAddSubtractHalf
+829a8c19 MIPS: dspr2: added optimization for ITransform
+c94ed49e gif2webp: Use the default hint instead of WEBP_HINT_GRAPH.
+653ace55 Increase the MAX_COLOR_CACHE_BITS from 9 to 10.
+919220c7 Change the logic adjusting the Histogram bits.
+53b096c0 Merge "Fix bug in VP8LCalculateEstimateForCacheSize."
+e912bd55 Fix bug in VP8LCalculateEstimateForCacheSize.
+541d7839 Merge "dec_neon: add RD4 intra predictor"
+f8cd0672 Merge "Makefile.vc: add a 'legacy' RTLIBCFG option"
+22881c99 dec_neon: add RD4 intra predictor
+613d281e update NEWS
+1304eb34 Merge "dec_neon: DC4: use pair-wise adds for top row"
+34c20c06 Makefile.vc: add a 'legacy' RTLIBCFG option
+7083006b Merge "dsp/dec_{neon,sse2}: VE4: normalize variable names"
+0db9031c dsp/dec_{neon,sse2}: VE4: normalize variable names
+b5bc1530 dec_neon: DC4: use pair-wise adds for top row
+5b90d8fe Unify the API between VP8BitWriter and VP8LBitWriter
+f7ada560 Merge changes I2e06907b,Ia9ed4ca6,I782282ff
+5beb6bf0 Merge "dec_neon: add VE4 intra predictor"
+eba6ce06 dec_neon: add DC4 intra predictor
+79abfbd9 dec_neon: add TM4 intra predictor
+fe395f0e dec_neon: add LD4 intra predictor
+32de385e dec_neon: add VE4 intra predictor
+72395ba9 Merge "Modify CostModel to allocate optimal memory."
+65e5eb8a gif2webp: Support GIF_DISPOSE_RESTORE_PREVIOUS
+e4c829ef gif2webp: Handle frames with odd offsets + disposal to background.
+c2b5a039 Modify CostModel to allocate optimal memory.
+b7a33d7e implement VE4/HE4/RD4/... in SSE2
+97c76f1f make VP8PredLuma4[] non-const and initialize array in VP8DspInit()
+0ea8c6c2 Merge "PrintReg: output to stderr"
+d7ff2f97 Merge "stopwatch.h: fix includes"
+f85ec712 PrintReg: output to stderr
+54edbf65 stopwatch.h: fix includes
+139142e4 Optimize BackwardReferenceHashChainFollowPath.
+5f36b68d enc/backward_references.c: fix indent
+e0e9960d Merge "sync version numbers to 0.4.2 release"
+64ac5144 sync version numbers to 0.4.2 release
+c24f8954 Simplify and speedup Backward refs computation.
+d1c359ef fix shared object build with -fvisibility=hidden
+a4c3a31b WEBP_TSAN_IGNORE_FUNCTION: fix gcc compat warning
+f358eeb8 add code for testing random incremental decoding in dwebp
+80247291 mark some init function as being safe for thread_sanitizer.
+79b5bdbf bit_reader.h: cosmetics: fix a typo
+6c673681 Improved near-lossless mode.
+0ce27e71 enc_mips32: workaround gcc-4.9 bug
+aca1b98f enc/vp8l.c: fix indent
+ca005027 Evaluate non-palette compression for palette image
+c8a87bb6 AssignSegments: quiet -Warray-bounds warning
+32f67e30 Merge "enc_neon: initialize vectors w/vdup_n_u32"
+fabc65da 1-3% faster encoding optimizing SSE_NxN functions
+7534d716 enc_neon: initialize vectors w/vdup_n_u32
+5f813912 Merge "Fix return code of EncodeImageInternal()"
+e321abe4 Fix return code of EncodeImageInternal()
+f82cb06a optimize palette ordering
+f545feee don't set the alpha value for histogram index image
+2d9b0a44 add WebPDispatchAlphaToGreen() to dsp
+1bd4c2ad Merge "Change Entropy based Histogram Combine heuristic."
+e295b8f1 Merge "iosbuild: cleanup"
+1be4e760 Merge "iosbuild: output autoconf req. on failure"
+d5e498d4 Change Entropy based Histogram Combine heuristic.
+47a2d8e1 fix MSVC float->int conversion warning
+041956f6 iosbuild: cleanup
+767eb402 iosbuild: output autoconf req. on failure
+35ad48b8 HistoHeapInit: correct positions allocation size
+45d9635f lossless: entropy clustering for high qualities.
+dc37df8c fix type warning for VS9_x64
+9f7d9e6d iosbuild: make iOS 6 the minimum requirement
+fdd6528b Remove unused VP8LDecoder member variable
+ea3bba5a Merge "rewrite Disto4x4 in enc_neon.c with intrinsic"
+f060dfc4 add lossless incremental decoding support
+ab70794d rewrite Disto4x4 in enc_neon.c with intrinsic
+d4471637 MIPS: dspr2: added optimization for function FilterLoop24
+2aef54d4 Merge "prepare VP8LDecodeImage for incremental decode"
+aed0f5a2 Merge "MIPS: dspr2: added optimization for function FilterLoop26"
+28630685 prepare VP8LDecodeImage for incremental decode
+248f3aed remove br->error_ field
+49e15044 MIPS: dspr2: added optimization for function FilterLoop26
+38128cb9 iobuild.sh: only install .h files in Headers
+c792d412 Premultiply with alpha during U/V downsampling
+0cc811d7 gif2webp: Background color correction
+d7167ff7 Amend the lossless spec according to issue #205, #206 and #224
+b901416b Record the lossless size stats.
+cddd3340 Add a WebPExtractAlpha function to dsp
+0716a98e fix indent after I0204949917836f74c0eb4ba5a7f4052a4797833b
+f9ced95a Optimize lossless decoding for trivial(ARB) codes.
+924fcfd9 Merge "webpmux: simplify InitializeConfig()"
+c0a462ca webpmux: simplify InitializeConfig()
+6986bb5e webpmux: fix indent
+f89e1690 webpmux: fix exit status on numeric value parse error
+2172cb62 Merge "webpmux: fix loop_count range check"
+e3b343ec Merge "examples: warn on invalid numeric parameters"
+0e23c487 webpmux: fix loop_count range check
+6208338a Merge "fix loop bug in DispatchAlpha()"
+d51f3e40 gif2webp: Handle frames with missing  graphic control extension
+690b491a fix loop bug in DispatchAlpha()
+96d43a87 examples: warn on invalid numeric parameters
+3101f537 MIPS: dspr2: added optimization for TransformOne
+a6bb9b17 SSE2 for inverse Mult(ARGB)Row and ApplyAlphaMultiply
+d84a8ffd Remove default initialization of decoder status.
+be70b86c configure: simplify libpng-config invocation
+e0a99321 Rectify bug in lossless incremental decoding.
+e2502a97 MIPS: dspr2: added optimization for TransformAC3
+24e1072a MIPS: dspr2: added optimization for TransformDC
+c0e84df8 Merge "Slightly faster lossless decoding (1%)"
+8dd28bb5 Slightly faster lossless decoding (1%)
+f0103595 MIPS: dspr2: added optimization for ColorIndexInverseTransforms
+d3242aee make VP8LSetBitPos() set br->eos_ flag
+a9decb55 Lossless decoding: fix eos_ flag condition
+3fea6a28 fix erroneous dec->status_ setting
+80b8099f MIPS: dspr2: add some specific mips code to commit I2c3f2b12f8df15b785fad5a9c56316e954ae0c53
+e5640625 Merge "further refine the COPY_PATTERN optim for DecodeAlpha"
+854509fe enc/histogram.c: reindent after f4059d0
+34421964 Merge "~3-5% faster encoding optimizing PickBestIntra*()"
+865069c1 further refine the COPY_PATTERN optim for DecodeAlpha
+a5956228 added C-level optimization for DecodeAlphaData function
+187d379d add a fallback to ALPHA_NO_COMPRESSION
+a48a2d76 ~3-5% faster encoding optimizing PickBestIntra*()
+a6140194 ExUtilReadFromStdin: (windows) open stdin in bin mode
+e80eab1f webpmux: (windows) open stdout in binary mode
+e9bfb116 cwebp: (windows) open stdout in binary mode
+5927e15b example_util: add ExUtilSetBinaryMode
+30f3b75b webpmux man page: Clarify some title, descriptions and examples
+77d4c7e3 address cosmetic comments from patch #71380
+f75dfbf2 Speed up Huffman decoding for lossless
+637b3888 dsp/lossless: workaround gcc-4.9 bug on arm
+8323a903 dsp.h: collect gcc/clang version test macros
+e6c4b52f move static initialization of WebPYUV444Converters[] to the Init function.
+49911d4d Merge "fix indentation"
+f4059d0c Code cleanup for HistogramRemap.
+e632b092 fix indentation
+f5c04d64 Merge "add a DispatchAlpha() for SSE2 that handles 8 pixels at a time"
+fc98edd9 add a DispatchAlpha() for SSE2 that handles 8 pixels at a time
+73d361dd introduce VP8EncQuantize2Blocks to quantize two blocks at a time
+0b21c30b MIPS: dspr2: added optimization for EmitAlphaRGB
+953acd56 enc_neon: enable QuantizeBlock for aarch64
+f4ae1437 MIPS: mips32: code rebase
+56977154 MIPS: dspr2: added optimizations for VP8YuvTo*
+2523aa73 SmartRGBYUV: fix odd-width problem with pixel replication
+ee52dc4e fix some MSVC64 warning about float conversion
+3fca851a cpu: check for _MSC_VER before using msvc inline asm
+e2a83d71 faster RGB->YUV conversion function (~7% speedup)
+de2d03e1 Merge "Add smart RGB->YUV conversion option -pre 4"
+3fc4c539 Add smart RGB->YUV conversion option -pre 4
+b4dc4069 MIPS: dspr2: added optimization for (un)filters
+137e6090 Merge "configure: add work around for gcc-4.9 aarch64 bug"
+b61c9cec MIPS: dspr2: Optimization of some simple point-sampling functions
+e2b8cec0 configure: add work around for gcc-4.9 aarch64 bug
+98c54107 MIPS: mips32r2: added optimization for BSwap32
+dab702b3 Update PATENTS to reflect s/VP8/WebM/g
+b564f7c7 Merge "MIPS: detect mips32r6 and disable mips32r1 code"
+b7e5a5c4 MIPS: detect mips32r6 and disable mips32r1 code
+63c2fc02 Correctly use the AC_CANONICAL_* macros
+bb07022b Merge "cosmetics"
+e300c9d8 cosmetics
+0e519eea Merge "cosmetics: remove some extraneous 'extern's"
+3ef0f08a Merge "vp8enci.h: cosmetics: fix '*' placement"
+4c6dde37 bit_writer: cosmetics: rename kFlush() -> Flush()
+f7b4c48b cosmetics: remove some extraneous 'extern's
+b47fb00a vp8enci.h: cosmetics: fix '*' placement
+b5a36cc9 add -near_lossless [0..100] experimental option
+0524d9e5 dsp: detect mips64 & disable mips32 code
+d3485d96 cwebp.1: fix quality description placement
+29a9fe22 Merge tag 'v0.4.1'
+8af27718 update ChangeLog (tag: v0.4.1, origin/0.4.1)
+e09e9ff6 Record & log the image pre-processing time.
+f59c0b4b iosbuild.sh: specify optimization flags
+8d34ea3e update ChangeLog (tag: v0.4.1-rc1)
+dbc3da66 makefile.unix: add vwebp.1 to the dist target
+89a7c83c update ChangeLog
+ffe67ee9 Merge "update NEWS for the next release" into 0.4.1
+2def1fe6 gif2webp: dust up the help message
+fb668d78 remove -noalphadither option from README/vwebp.1
+e49f693b update NEWS for the next release
+cd013580 Merge "update AUTHORS" into 0.4.1
+268d01eb update AUTHORS
+85213b9b bump version to 0.4.1
+695f80ae Merge "restore mux API compatibility" into 0.4.1
+862d296c restore mux API compatibility
+8f6f8c5d remove the !WEBP_REFERENCE_IMPLEMENTATION tweak in Put8x8uv
+d713a696 Merge changes If4debc15,I437a5d5f into 0.4.1
+c2fc52e4 restore encode API compatibility
+793368e8 restore decode API compatibility
+b8984f31 gif2webp: fix compile with giflib 5.1.0
+222f9b1a gif2webp: simplify giflib version checking
+d2cc61b7 Extend MakeARGB32() to accept Alpha channel.
+4595b62b Merge "use explicit size of kErrorMessages[] arrays"
+157de015 Merge "Actuate memory stats for PRINT_MEMORY_INFO"
+fbda2f49 JPEG decoder: delay conversion to YUV to WebPEncode() call
+0b747b1b use explicit size of kErrorMessages[] arrays
+3398d81a Actuate memory stats for PRINT_MEMORY_INFO
+6f3202be Merge "move WebPPictureInit to picture.c"
+6c347bbb move WebPPictureInit to picture.c
+fb3acf19 fix configure message for multi-thread
+40b086f7 configure: check for _beginthreadex
+1549d620 reorder the YUVA->ARGB and ARGB->YUVA functions correctly
+c6461bfd Merge "extract colorspace code from picture.c into picture_csp.c"
+736f2a17 extract colorspace code from picture.c into picture_csp.c
+645daa03 Merge "configure: check for -Wformat-security"
+abafed86 configure: check for -Wformat-security
+fbadb480 split monolithic picture.c into picture_{tools,psnr,rescale}.c
+c76f07ec dec_neon/TransformAC3: initialize vector w/vcreate
+bb4fc051 gif2webp: Allow single-frame animations
+46fd44c1 thread: remove harmless race on status_ in End()
+5a1a7264 Merge "configure: check for __builtin_bswapXX()"
+6781423b configure: check for __builtin_bswapXX()
+6450c48d configure: fix iOS builds
+6422e683 VP8LFillBitWindow: enable fast path for 32-bit builds
+4f7f52b2 VP8LFillBitWindow: respect WEBP_FORCE_ALIGNED
+e458badc endian_inl.h: implement htoleXX with BSwapXX
+f2664d1a endian_inl.h: add BSwap16
+6fbf5345 Merge "configure: add --enable-aligned"
+dc0f479d configure: add --enable-aligned
+9cc69e2b Merge "configure: support WIC + OpenGL under mingw64"
+257adfb0 remove experimental YUV444 YUV422 and YUV400 code
+10f4257c configure: support WIC + OpenGL under mingw64
+380cca4f configure.ac: add AC_C_BIGENDIAN
+ee70a901 endian_inl.h: add BSwap64
+47779d46 endian_inl.h: add BSwap32
+d5104b1f utils: add endian_inl.h
+58ab6224 Merge "make alpha-detection loop in IsKeyFrame() in good x/y order"
+9d562902 make alpha-detection loop in IsKeyFrame() in good x/y order
+516971b1 lossless: Remove unaligned read warning
+b8b596f6 Merge "configure.ac: add an autoconf version prerequisite"
+34b02f8c configure.ac: add an autoconf version prerequisite
+e59f5360 neon: normalize vdup_n_* usage
+6ee7160d Merge changes I0da7b3d3,Idad2f278,I4accc305
+abc02f24 Merge "fix (uncompiled) typo"
+bc03670f neon: add INIT_VECTOR4
+6c1c632b neon: add INIT_VECTOR3
+dc7687e5 neon: add INIT_VECTOR2
+4536e7c4 add WebPMuxSetCanvasSize() to the mux API
+824eab10 fix (uncompiled) typo
+1f3e5f1e remove unused 'shift' argument and QFIX2 define
+8e867051 Merge "VP8LoadNewBytes: use __builtin_bswap32 if available"
+1b6a2635 Merge "Fix handling of weird GIF with canvas dimension 0x0"
+1da3d461 VP8LoadNewBytes: use __builtin_bswap32 if available
+1582e402 Fix handling of weird GIF with canvas dimension 0x0
+b8811dac Merge "rename interface -> winterface"
+db8b8b5f Fix logic in the GIF LOOP-detection parsing
+25aaddc8 rename interface -> winterface
+5584d9d2 make WebPSetWorkerInterface() check its arguments
+a9ef7ef9 Merge "cosmetics: update thread.h comments"
+c6af9991 Merge "dust up the help message"
+0a8b8863 dust up the help message
+a9cf3191 cosmetics: update thread.h comments
+27bfeee4 QuantizeBlock SSE2 Optimization:
+2bc0dc3e Merge "webpmux: warn when odd frame offsets are used"
+3114ebe4 Merge changes Id8edd3c1,Id418eb96,Ide05e3be
+c0726634 webpmux: warn when odd frame offsets are used
+c5c6b408 Merge "add alpha dithering for lossy"
+d5146784 examples/Android.mk: add cwebp
+ca0fa7c7 Android.mk: move dwebp to examples/Android.mk
+73d8fca0 Android.mk: add ENABLE_SHARED flag
+6e93317f muxread: fix out of bounds read
+8b0f6a48 Makefile.vc: fix CFLAGS assignment w/HAVE_AVX2=1
+bbe32df1 add alpha dithering for lossy
+79020767 Merge "make error-code reporting consistent upon malloc failure"
+77bf4410 make error-code reporting consistent upon malloc failure
+7a93c000 **/Makefile.am: remove unused AM_CPPFLAGS
+24e30805 Add an interface abstraction to the WebP worker thread implementation
+d6cd6358 Merge "fix orig_rect==NULL case"
+2bfd1ffa fix orig_rect==NULL case
+059e21c1 Merge "configure: move config.h to src/webp/config.h"
+f05fe006 properly report back encoding error code in WebPFrameCacheAddFrame()
+32b31379 configure: move config.h to src/webp/config.h
+90090d99 Merge changes I7c675e51,I84f7d785
+ae7661b3 makefiles: define WEBP_HAVE_AVX2 when appropriate
+69fce2ea remove the special casing for res->first in VP8SetResidualCoeffs
+6e61a3a9 configure: test for -msse2
+b9d2efc6 rename upsampling_mips32.c to yuv_mips32.c
+bdfeebaa dsp/yuv: move sse2 functions to yuv_sse2.c
+46b32e86 Merge "configure: set WEBP_HAVE_AVX2 when available"
+88305db4 Merge "VP8RandomBits2: prevent signed int overflow"
+73fee88c VP8RandomBits2: prevent signed int overflow
+db4860b3 enc_sse2: prevent signed int overflow
+3fdaf4d2 Merge "real fix for longjmp warning"
+385e3340 real fix for longjmp warning
+230a0555 configure: set WEBP_HAVE_AVX2 when available
+a2ac8a42 restore original value_/range_ field order
+5e2ee56f Merge "remove libwebpdspdecode dep on libwebpdsp_avx2"
+61362db5 remove libwebpdspdecode dep on libwebpdsp_avx2
+42c447ae Merge "lossy bit-reader clean-up:"
+479ffd8b Merge "remove unused #include's"
+9754d39a Merge "strong filtering speed-up (~2-3% x86, ~1-2% for NEON)"
+158aff9b remove unused #include's
+09545eea lossy bit-reader clean-up:
+ea8b0a17 strong filtering speed-up (~2-3% x86, ~1-2% for NEON)
+6679f899 Optimize VP8SetResidualCoeffs.
+ac591cf2 fix for gcc-4.9 warnings about longjmp + local variables
+4dfa86b2 dsp/cpu: NaCl has no support for xgetbv
+4c398699 Merge "cwebp: fallback to native webp decode in WIC builds"
+33aa497e Merge "cwebp: add some missing newlines in longhelp output"
+c9b340a2 fix missing WebPInitAlphaProcessing call for premultiplied colorspace output
+57897bae Merge "lossless_neon: use vcreate_*() where appropriate"
+6aa4777b Merge "(enc|dec)_neon: use vcreate_*() where appropriate"
+0d346e41 Always reinit VP8TransformWHT instead of hard-coding
+7d039fc3 cwebp: fallback to native webp decode in WIC builds
+d471f424 cwebp: add some missing newlines in longhelp output
+bf0e0030 lossless_neon: use vcreate_*() where appropriate
+9251c2f6 (enc|dec)_neon: use vcreate_*() where appropriate
+399b916d lossy decoding: correct alpha-rescaling for YUVA format
+78c12ed8 Merge "Makefile.vc: add rudimentary avx2 support"
+dc5b122f try to remove the spurious warning for static analysis
+ddfefd62 Makefile.vc: add rudimentary avx2 support
+a8911643 Merge "simplify VP8LInitBitReader()"
+fdbcd44d simplify VP8LInitBitReader()
+7c004287 makefile.unix: add rudimentary avx2 support
+515e35cf Merge "add stub dsp/enc_avx2.c"
+a05dc140 SSE2: yuv->rgb speed-up for point-sampling
+178e9a69 add stub dsp/enc_avx2.c
+1b99c09c Merge "configure: add a test for -mavx2"
+fe728071 configure: add a test for -mavx2
+e46a247c cpu: fix check for __cpuidex availability
+176fda26 fix the bit-writer for lossless in 32bit mode
+541784c7 dsp.h: add a check for AVX2 / define WEBP_USE_AVX2
+bdb151ee dsp/cpu: add AVX2 detection
+ab9f2f86 Merge "revamp the point-sampling functions by processing a full plane"
+a2f8b289 revamp the point-sampling functions by processing a full plane
+ef076026 use decoder's DSP functions for autofilter
+2b5cb326 Merge "dsp/cpu: add AVX detection"
+df08e67e dsp/cpu: add AVX detection
+e2f405c9 Merge "clean-up and slight speed-up in-loop filtering SSE2"
+f60957bf clean-up and slight speed-up in-loop filtering SSE2
+9fc3ae46 .gitattributes: treat .ppm as binary
+3da924b5 Merge "dsp/WEBP_USE_NEON: test for __aarch64__"
+c7164490 Android.mk: always include *_neon.c in the build
+a577b23a dsp/WEBP_USE_NEON: test for __aarch64__
+54bfffca move RemapBitReader() from idec.c to bit_reader code
+34168ecb Merge "remove all unused layer code"
+f1e77173 remove all unused layer code
+b0757db7 Code cleanup for VP8LGetHistoImageSymbols.
+5fe628d3 make the token page size be variable instead of fixed 8192
+f948d08c memory debug: allow setting pre-defined malloc failure points
+ca3d746e use block-based allocation for backward refs storage, and free-lists
+1ba61b09 enable NEON intrinsics in aarch64 builds
+b9d2bb67 dsp/neon.h: coalesce intrinsics-related defines
+b5c75258 iosbuild: add support for iOSv7/aarch64
+9383afd5 Reduce number of memory allocations while decoding lossless.
+888e63ed Merge "dsp/lossless: prevent signed int overflow in left shift ops"
+8137f3ed Merge "instrument memory allocation routines for debugging"
+2aa18736 instrument memory allocation routines for debugging
+d3bcf72b Don't allocate VP8LHashChain, but treat like automatic object
+bd6b8619 dsp/lossless: prevent signed int overflow in left shift ops
+b7f19b83 Merge "dec/vp8l: prevent signed int overflow in left shift ops"
+29059d51 Merge "remove some uint64_t casts and use."
+e69a1df4 dec/vp8l: prevent signed int overflow in left shift ops
+cf5eb8ad remove some uint64_t casts and use.
+38e2db3e MIPS: MIPS32r1: Added optimization for HistogramAdd.
+e0609ade dwebp: fix exit code on webp load failure
+bbd358a8 Merge "example_util.h: avoid forward declaring enums"
+8955da21 example_util.h: avoid forward declaring enums
+6d6865f0 Added SSE2 variants for Average2/3/4
+b3a616b3 make HistogramAdd() a pointer in dsp
+c8bbb636 dec_neon: relocate some inline-asm defines
+4e393bb9 dec_neon: enable intrinsics-only functions
+ba99a922 dec_neon: use positive tests for USE_INTRINSICS
+69058ff8 Merge "example_util: add ExUtilDecodeWebPIncremental"
+a7828e8b dec_neon: make WORK_AROUND_GCC conditional on version
+3f3d717a Merge "enc_neon: enable intrinsics-only functions"
+de3cb6c8 Merge "move LOCAL_GCC_VERSION def to dsp.h"
+1b2fe14d example_util: add ExUtilDecodeWebPIncremental
+ca49e7ad Merge "enc_neon: move Transpose4x4 to dsp/neon.h"
+ad900abd Merge "fix warning about size_t -> int conversion"
+4825b436 fix warning about size_t -> int conversion
+42b35e08 enc_neon: enable intrinsics-only functions
+f937e012 move LOCAL_GCC_VERSION def to dsp.h
+5e1a17ef enc_neon: move Transpose4x4 to dsp/neon.h
+c7b92a5a dec_neon: (WORK_AROUND_GCC) delete unused Load4x8
+8e5f90b0 Merge "make ExUtilLoadWebP() accept NULL bitstream param."
+05d4c1b7 Merge "cwebp: add webpdec"
+ddeb6ac8 cwebp: add webpdec
+35d7d095 Merge "Reduce memory footprint for encoding WebP lossless."
+0b896101 Reduce memory footprint for encoding WebP lossless.
+f0b65c9a make ExUtilLoadWebP() accept NULL bitstream param.
+9c0a60cc Merge "dwebp: move webp decoding to example_util"
+1d62acf6 MIPS: MIPS32r1: Added optimization for HuffmanCost functions.
+4a0e7390 dwebp: move webp decoding to example_util
+c0220460 Merge "Bugfix: Incremental decode of lossy-alpha"
+8c7cd722 Bugfix: Incremental decode of lossy-alpha
+7955152d MIPS: fix error with number of registers.
+b1dabe37 Merge "Move the HuffmanCost() function to dsp lib"
+75b12006 Move the HuffmanCost() function to dsp lib
+2772b8bd MIPS: fix assembler error revealed by clang's debug build
+6653b601 enc_mips32: fix unused symbol warning in debug
+8dec1209 enc_mips32: disable ITransform(One) in debug builds
+98519dd5 enc_neon: convert Disto4x4 to intrinsics
+fe9317c9 cosmetics:
+953b0746 enc_neon: cosmetics
+a9fc697c Merge "WIP: extract the float-calculation of HuffmanCost from loop"
+3f84b521 Merge "replace some mult-long (vmull_u8) with mult-long-accumulate (vmlal_u8)"
+4ae0533f MIPS: MIPS32r1: Added optimizations for ExtraCost functions.
+b30a04cf WIP: extract the float-calculation of HuffmanCost from loop
+a8fe8ce2 Merge "NEON intrinsics version of CollectHistogram"
+95203d2d NEON intrinsics version of CollectHistogram
+7ca2e74b replace some mult-long (vmull_u8) with mult-long-accumulate (vmlal_u8)
+41c6efbd fix lossless_neon.c
+8ff96a02 NEON intrinsics version of FTransform
+0214f4a9 Merge "MIPS: MIPS32r1: Added optimizations for FastLog2"
+baabf1ea MIPS: MIPS32r1: Added optimizations for FastLog2
+3d49871d NEON functions for lossless coding
+3fe02915 MIPS: MIPS32r1: Added optimizations for SSE functions.
+c503b485 Merge "fix the gcc-4.6.0 bug by implementing alternative method"
+abe6f487 fix the gcc-4.6.0 bug by implementing alternative method
+5598bdec enc_mips32.c: fix file mode
+2b1b4d5a MIPS: MIPS32r1: Add optimization for GetResidualCost
+f0a1f3cd Merge "MIPS: MIPS32r1: Added optimization for FTransform"
+7231f610 MIPS: MIPS32r1: Added optimization for FTransform
+869eaf6c  ~30% encoding speedup: use NEON for QuantizeBlock()
+f758af6b enc_neon: convert FTransformWHT to intrinsics
+7dad095b MIPS: MIPS32r1: Added optimization for Disto4x4 (TTransform)
+2298d5f3 MIPS: MIPS32r1: Added optimization for QuantizeBlock
+e88150c9 Merge "MIPS: MIPS32r1: Add optimization for ITransform"
+de693f25 lossless_neon: disable VP8LConvert* functions
+4143332b NEON intrinsics for encoding
+0ca2914b MIPS: MIPS32r1: Add optimization for ITransform
+71bca5ec dec_neon: use vst_lane instead of vget_lane
+bf061052 Intrinsics NEON version of TransformOne
+19c6f1ba Merge "dec_neon: use vld?_lane instead of vset?_lane"
+7a94c0cf upsampling_neon: drop NEON suffix from local functions
+d14669c8 upsampling_sse2: drop SSE2 suffix from local functions
+2ca42a4f enc_sse2: drop SSE2 suffix from local functions
+d038e619 dec_sse2: drop SSE2 suffix from local functions
+fa52d752 dec_neon: use vld?_lane instead of vset?_lane
+c520e77d cosmetic: fix long line
+4b0f2dae Merge "add intrinsics NEON code for chroma strong-filtering"
+e351ec07 add intrinsics NEON code for chroma strong-filtering
+aaf734b8 Merge "Add SSE2 version of forward cross-color transform"
+c90a902e Add SSE2 version of forward cross-color transform
+bc374ff3 Use histogram_bits to initalize transform_bits.
+2132992d Merge "Add strong filtering intrinsics (inner and outer edges)"
+5fbff3a6 Add strong filtering intrinsics (inner and outer edges)
+d4813f0c Add SSE2 function for Inverse Cross-color Transform
+26029568 dec_neon: add strong loopfilter intrinsics
+cca7d7ef Merge "add intrinsics version of SimpleHFilter16NEON()"
+1a05dfa7 windows: fix dll builds
+d6c50d8a Merge "add some colorspace conversion functions in NEON"
+4fd7c82e SSE2 variants of Subtract-Green: Rectify loop condition
+97e5fac3 add some colorspace conversion functions in NEON
+b9a7a45f add intrinsics version of SimpleHFilter16NEON()
+daccbf40 add light filtering NEON intrinsics
+af444608 fix typo in STORE_WHT
+6af6b8e1 Tune HistogramCombineBin for large images.
+af93bdd6 use WebPSafe[CM]alloc/WebPSafeFree instead of [cm]alloc/free
+51f406a5 lossless_sse2: relocate VP8LDspInitSSE2 proto
+0f4f721b separate SSE2 lossless functions into its own file
+514fc251 VP8LConvertFromBGRA: use conversion function pointers
+6d2f3527 dsp/dec: TransformDCUV: use VP8TransformDC
+defc8e1b Merge "fix out-of-bound read during alpha-plane decoding"
+fbed3643 Merge "dsp: reuse wht transform from dec in encoder"
+d8467084 Merge "Add SSE2 version of ARGB -> BGR/RGB/... conversion functions"
+207d03b4 fix out-of-bound read during alpha-plane decoding
+d1b33ad5 2-5% faster trellis with clang/MacOS (and ~2-3% on ARM)
+369c26dd Add SSE2 version of ARGB -> BGR/RGB/... conversion functions
+df230f27 dsp: reuse wht transform from dec in encoder
+80e218d4 Android.mk: fix build with APP_ABI=armeabi-v7a-hard
+59daf083 Merge "cosmetics:"
+53622008 cosmetics:
+3e7f34a3 AssignSegments: quiet array-bounds warning
+3c2ebf58 Merge "UpdateHistogramCost: avoid implicit double->float"
+cf821c82 UpdateHistogramCost: avoid implicit double->float
+312e638f Extend the search space for GetBestGreenRedToBlue
+1c58526f Fix few nits
+fef22704 Optimize and re-structure VP8LGetHistoImageSymbols
+068b14ac Optimize lossless decoding.
+5f0cfa80 Do a binary search to get the optimum cache bits.
+24ca3678 Merge "allow 'cwebp -o -' to emit output to stdout"
+e12f874e allow 'cwebp -o -' to emit output to stdout
+2bcad89b allow some more stdin/stout I/O
+84ed4b3a fix cwebp.1 typos after patch #69199
+65b99f1c add a -z option to cwebp, and WebPConfigLosslessPreset() function
+30176619 4-5% faster trellis by removing some unneeded calculations.
+687a58ec histogram.c: reindent after b33e8a0
+06d456f6 Merge "~3-4% faster lossless encoding"
+c60de260 ~3-4% faster lossless encoding
+42eb06fc Merge "few cosmetics after patch #69079"
+82af8264 few cosmetics after patch #69079
+b33e8a05 Refactor code for HistogramCombine.
+ca1bfff5 Merge "5-10% encoding speedup with faster trellis (-m 6)"
+5aeeb087 5-10% encoding speedup with faster trellis (-m 6)
+82ae1bf2 cosmetics: normalize VP8GetCPUInfo checks
+e3dd9243 Merge "Refactor GetBestPredictorForTile for future tuning."
+206cc1be Refactor GetBestPredictorForTile for future tuning.
+3cb84062 Merge "speed-up trellis quant (~5-10% overall speed-up)"
+b66f2227 Merge "lossy encoding: ~3% speed-up"
+4287d0d4 speed-up trellis quant (~5-10% overall speed-up)
+390c8b31 lossy encoding: ~3% speed-up
+9a463c4a Merge "dec_neon: convert TransformWHT to intrinsics"
+e8605e96 Merge "dec_neon: add ConvertU8ToS16"
+4aa3e412 MIPS: MIPS32r1: rescaler bugfix
+c16cd99a Speed up lossless encoder.
+9d6b5ff1 dec_neon: convert TransformWHT to intrinsics
+2ff0aae2 dec_neon: add ConvertU8ToS16
+77a8f919 fix compilation with USE_YUVj flag
+4acbec1b Merge changes I3b240ffb,Ia9370283,Ia2d28728
+2719bb7e dec_neon: TransformAC3: work on packed vectors
+b7b60ca1 dec_neon: add SaturateAndStore4x4
+b7685d73 Rescale: let ImportRow / ExportRow be pointer-to-function
+e02f16ef dec_neon.c: convert TransformDC to intrinsics
+9cba963f add missing file
+8992ddb7 use static clipping tables
+0235d5e4 1-2% faster quantization in SSE2
+b2fbc36c fix VC12-x64 warning
+6e37cb94 Merge "cosmetics: backward_references.c: reindent after a7d2ee3"
+a42ea974 cosmetics: backward_references.c: reindent after a7d2ee3
+6c327442 Merge "fix missing __BIG_ENDIAN__ definition on some platform"
+a8b6aad1 fix missing __BIG_ENDIAN__ definition on some platform
+fde2904b Increase initial buffer size for VP8L Bit Writer.
+a7d2ee39 Optimize cache estimate logic.
+7fb6095b Merge "dec_neon.c: add TransformAC3"
+bf182e83 VP8LBitWriter: use a bit-accumulator
+3f40b4a5 Merge "MIPS: MIPS32r1: clang macro warning resolved"
+1684f4ee WebP Decoder: Mark some truncated bitstreams as invalid
+acbedac4 MIPS: MIPS32r1: clang macro warning resolved
+228e4877 dec_neon.c: add TransformAC3
+393f89b7 Android.mk: avoid gcc-specific flags with clang
+32aeaf11 revamp VP8LColorSpaceTransform() a bit
+0c7cc4ca Merge "Don't dereference NULL, ensure HashChain fully initialized"
+391316fe Don't dereference NULL, ensure HashChain fully initialized
+926ff402 WEBP_SWAP_16BIT_CSP: remove code dup
+1d1cd3bb Fix decode bug for rgbA_4444/RGBA_4444 color-modes.
+939e70e7 update AUTHORS file
+8934a622 cosmetics: *_mips32.c
+dd438c9a MIPS: MIPS32r1: Optimization of some simple point-sampling functions. PATCH [6/6]
+53520911 Added support for calling sampling functions via pointers.
+d16c6974 MIPS: MIPS32r1: Optimization of filter functions. PATCH [5/6]
+04336fc7 MIPS: MIPS32r1: Optimization of function TransformOne. PATCH [4/6]
+92d8fc7d MIPS: MIPS32r1: Optimization of function WebPRescalerImportRow. PATCH [3/6]
+bbc23ff3 parse one row of intra modes altogether
+a2f608f9 Merge "MIPS: MIPS32r1: Optimization of function WebPRescalerExportRow. [2/6]"
+88230854 MIPS: MIPS32r1: Optimization of function WebPRescalerExportRow. [2/6]
+c5a5b028 decode mt+incremental: fix segfault in debug builds
+9882b2f9 always use fast-analysis for all methods.
+000adac0 Merge "autoconf: update ax_pthread.m4"
+2d2fc37d update .gitignore
+5bf4255a Merge "Make it possible to avoid automagic dependencies"
+c1cb1933 disable NEON for arm64 platform
+73a304e9 Make it possible to avoid automagic dependencies
+4d493f8d MIPS: MIPS32r1: Decoder bit reader function optimized. PATCH [1/6]
+c741183c make WebPCleanupTransparentArea work with argb picture
+5da18552 add a decoding option to flip image vertically
+00c3c4e1 Merge "add man/vwebp.1"
+2c6bb428 add man/vwebp.1
+ea59a8e9 Merge "Merge tag 'v0.4.0'"
+7574bed4 fix comments related to array sizes
+0b5a90fd dwebp.1: fix option formatting
+effcb0fd Merge tag 'v0.4.0'
+7c76255d autoconf: update ax_pthread.m4
+fff2a11b make -short work with -print_ssim, -print_psnr, etc.
+68e7901d update ChangeLog (tag: v0.4.0-rc1, tag: v0.4.0, origin/0.4.0)
+256e4333 update NEWS description with new general features
+29625340 Merge "gif2webp: don't use C99 %zu" into 0.4.0
+3b9f9dd0 gif2webp: don't use C99 %zu
+b5b2e3c7 cwebp: fix metadata output w/lossy+alpha
+ad26df1a makefile.unix: clean up libgif2webp_util.a
+c3b45570 update Changelog
+ca841121 Merge "bump version to 0.4.0" into 0.4.0
+8c524db8 bump version to 0.4.0
+eec2398c update AUTHORS & .mailmap
+b9bbf6a1 update NEWS for 0.4.0
+c72e0811 Merge "dec/webp.c: don't wait for data before reporting w/h"
+5ad65314 dec/frame.c: fix formatting
+f7fc4bc8 dec/webp.c: don't wait for data before reporting w/h
+66a32af5 Merge "NEON speed up"
+26d842eb NEON speed up
+f307f98b Merge "webpmux: let -- stop parameter parsing"
+fe051da7 Merge "README: add a section on gif2webp"
+6fd2bd62 Merge "manpage pedantry"
+4af19007 README: add a section on gif2webp
+6f36ade9 manpage pedantry
+f9016cb9 README: update dwebp options
+b4fa0a47 webpmux: let -- stop parameter parsing
+a9a20acf gif2webp: Add a multi-threaded encode option
+495bef41 fix bug in TrellisQuantize
+605a7127 simplify __cplusplus ifdef
+33109f99 Merge "drop: ifdef __cplusplus checks from C files"
+7f9de0b9 Merge changes I994a5587,I8467bb71,I13b50688,I1e2c9c7b
+5459030b gif2webp: let -- stop parameter parsing
+a4b0aa06 vwebp: let -- stop parameter parsing
+98af68fe cwebp: let -- stop parameter parsing
+a33831e2 dwebp: let -- stop parameter parsing
+36301249 add some checks on error paths
+ce4c7139 Merge "autoconf: add --disable-wic"
+5227d991 drop: ifdef __cplusplus checks from C files
+f6453559 dwebp.1: fix typo
+f91034f2 Merge "cwebp: print metadata stats when no output file is given"
+d4934553 gif2webp: Backward compatibility for giflib version <= 4.1.3
+4c617d32 gif2webp: Disable output of ICC profile by default
+73b731fb introduce a special quantization function for WHT
+41c0cc4b Make Forward WHT transform use 32bit fixed-point calculation
+a3359f5d Only compute quantization params once
+70490437 cwebp: print metadata stats when no output file is given
+d513bb62 * fix off-by-one zthresh calculation * remove the sharpening for non luma-AC coeffs * adjust the bias a little bit to compensate for this
+ad9dec0c Merge "cosmetics: dwebp: fix local function name format"
+f737f037 Merge "dwebp: remove a dead store"
+3c3a70da Merge "makefile.unix: install binaries in $(DESTDIR)/bin/"
+150b655f Merge "Android.mk: add some release compile flags"
+dbebd33b cosmetics: dwebp: fix local function name format
+27749951 dwebp: remove a dead store
+a01e04fe autoconf: add --disable-wic
+5009b227 makefile.unix: install binaries in $(DESTDIR)/bin/
+bab30fca Merge "fix -print_psnr / ssim options"
+ebef7fb3 fix -print_psnr / ssim options
+cb637855 Merge "fix bug due to overzealous check in WebPPictureYUVAToARGB()"
+8189885b Merge "EstimateBestFilter: use an int to iterate WEBP_FILTER_TYPE"
+4ad7d335 Android.mk: add some release compile flags
+c12e2369 cosmetics: fix a few typos
+6f104034 fix bug due to overzealous check in WebPPictureYUVAToARGB()
+3f6c35c6 EstimateBestFilter: use an int to iterate WEBP_FILTER_TYPE
+cc55790e Merge changes I8bb7a4dc,I2c180051,I021a014f,I8a224a62
+c536afb5 Merge "cosmetics: fix some typos"
+cbdd3e6e add a -dither dithering option to the decoder
+e8124012 Updated iosbuild.sh for XCode 5.x
+4931c329 cosmetics: fix some typos
+05aacf77 mux: add some missing casts
+617d9348 enc/vp8l: add a missing cast
+46db2865 idec: add some missing casts
+b524e336 ErrorStatusLossless: correct return type
+cb261f79 fix a descaling bug for vertical/horizontal U/V interpolation
+bcb3955c Merge changes I48968468,I181bc736
+73f52133 gif2webp: Add a mixed compression mode
+6198715e demux: split chunk parsing from ParseVP8X
+d2e3f4e6 demux: add a tail pointer for chunks
+87cffcc3 demux: cosmetics: s/has_frames/is_animation/
+e18e6677 demux: strictly enforce the animation flag
+c4f39f4a demux: cosmetics: remove a useless break
+61cb884d demux: (non-exp) fail if the fragmented flag is set
+ff379db3 few % speedup of lossless encoding
+df3649a2 remove all disabled code related to P-frames
+6d0cb3de Merge "gif2webp: kmin = 0 should suppress key-frame addition."
+36555983 gif2webp: kmin = 0 should suppress key-frame addition.
+7708e609 Merge "detect flatness in blocks and favor DC prediction"
+06b1503e Merge "add comment about the kLevelsFromDelta[][] LUT generation"
+5935259c add comment about the kLevelsFromDelta[][] LUT generation
+e3312ea6 detect flatness in blocks and favor DC prediction
+ebc9b1ee Merge "VPLBitReader bugfix: Catch error if bit_pos > LBITS too."
+96ad0e0a VPLBitReader bugfix: Catch error if bit_pos > LBITS too.
+a014e9c9 tune quantization biases toward higher precision
+1e898619 add helpful PrintBlockInfo() function
+596a6d73 make use of 'extern' consistent in function declarations
+c8d48c6e Merge "extract random utils to their own file util/random.[ch]"
+98aa33cf extract random utils to their own file util/random.[ch]
+432a723e Merge "swig: add basic go bindings"
+fab618b5 Merge "rename libwebp.i -> libwebp.swig"
+e4e7fcd6 swig: add basic go bindings
+d3408720 Merge "fast auto-determined filtering strength"
+f8bfd5cd fast auto-determined filtering strength
+ac0bf951 small clean-up in ExpandMatrix()
+1939607e rename libwebp.i -> libwebp.swig
+43148b6c filtering: precompute ilimit and hev_threshold
+18f992ec simplify f_inner calculation a little
+241d11f1 add missing const
+86c0031e add a 'format' field to WebPBitstreamFeatures
+dde91fde Demux: Correct the extended format validation
+5d6c5bd2 add entry for '-resize' option in cwebp's man
+7c098d18 Use some gamma-curve range compression when computing U/V average
+0b2b0504 Use deterministic random-dithering during RGB->YUV conversion
+8a2fa099 Add a second multi-thread method
+7d6f2da0 Merge "up to 20% faster multi-threaded decoding"
+266f63ea Merge "libwebp.jar: build w/Java 1.6 for Android compat"
+0532149c up to 20% faster multi-threaded decoding
+38efdc2e Simplify the gif2webp tool: move the optimization details to util
+de899516 libwebp.jar: build w/Java 1.6 for Android compat
+cb221552 Decode a full row of bitstream before reconstructing
+dca8a4d3 Merge "NEON/simple loopfilter: avoid q4-q7 registers"
+9e84d901 Merge "NEON/TransformWHT: avoid q4-q7 registers"
+fc10249b NEON/simple loopfilter: avoid q4-q7 registers
+2f09d63e NEON/TransformWHT: avoid q4-q7 registers
+77585a2b Merge "use a macrofunc for setting NzCoeffs bits"
+d155507c Merge "use HINT_GRAPH as image_hint for gif source"
+9c561646 Merge "only print GIF_DISPOSE_WARNING once"
+05879865 use HINT_GRAPH as image_hint for gif source
+0b28d7ab use a macrofunc for setting NzCoeffs bits
+f9bbc2a0 Special-case sparse transform
+00125196 gif2webp: detect and flatten uniformly similar blocks
+0deaf0fa only print GIF_DISPOSE_WARNING once
+6a8c0eb7 Merge "small optimization in segment-smoothing loop"
+f7146bc1 small optimization in segment-smoothing loop
+5a7533ce small gif2webp fix
+4df0c89e Merge changes Ic697660c,I27285521
+5b2e6bd3 Android.mk: add a dwebp target
+f910a84e Android.mk: update build flags
+63f9aba4 special-case WHT transform when there's only DC
+80911aef Merge "7-8% faster decoding by rewriting GetCoeffs()"
+606c4304 gif2webp: Improved compression for lossy animated WebP
+fb887f7f gif2webp: Different kmin/kmax defaults for lossy and lossless
+2a981366 7-8% faster decoding by rewriting GetCoeffs()
+92d47e4c improve VP8L signature detection by checking the version bits too
+5cd43e43 Add -incremental option to dwebp
+54b8e3f6 webpmux: DisplayInfo(): remove unnecessary error checks.
+40ae3520 fix memleak in WebPIDelete()
+d9662658 mux.h doc: WebPMuxGetFrame() can return WEBP_MUX_MEMORY_ERROR too.
+0e6747f8 webpmux -info: display dimensions and has_alpha per frame
+d78a82c4 Sanity check for underflow
+8498f4bf Merge "remove -Wshadow warnings"
+e89c6fc8 Avoid a potential memleak
+3ebe1757 Merge "break down the proba 4D-array into some handy structs"
+6a44550a break down the proba 4D-array into some handy structs
+2f5e8934 remove -Wshadow warnings
+bf3a29b3 Merge "add proper WEBP_HAVE_GIF and WEBP_HAVE_GL flags"
+2b0a7593 Merge "fix some warnings from static analysis"
+22dd07ce mux.h: Some doc corrections
+79ff0346 add proper WEBP_HAVE_GIF and WEBP_HAVE_GL flags
+d51f45f0 fix some warnings from static analysis
+d134307b fix conversion warning on MSVC
+d538cea8 gif2webp: Support a 'min' and 'max'  key frame interval
+80b54e1c allow search with token buffer loop and fix PARTITION0 problem
+b7d4e042 add VP8EstimateTokenSize()
+10fddf53 enc/quant.c: silence a warning
+399cd456 Merge "fix compile error on ARM/gcc"
+9f24519e encoder: misc rate-related fixes
+c663bb21 Merge "simplify VP8IteratorSaveBoundary() arg passing"
+fa46b312 Demux.h: Correct a method name reference
+f8398c9d fix compile error on ARM/gcc
+f691f0e4 simplify VP8IteratorSaveBoundary() arg passing
+42542be8 up to 6% faster encoding with clang compiler
+93402f02 multi-threaded segment analysis
+7e2d6595 Merge "remove the PACK() bit-packing tricks"
+c13fecf9 remove the PACK() bit-packing tricks
+2fd091c9 Merge "use NULL for lf_stats_ testing, not bool"
+b11c9d62 dwebp: use default dct_method
+4bb8465f Merge "(de)mux.h: wrap pseudo-code in /* */"
+cfb56b17 make -pass option work with token buffers
+5416aab4 (de)mux.h: wrap pseudo-code in /* */
+35dba337 use NULL for lf_stats_ testing, not bool
+733a7faa enc->Iterator memory cleanup
+e81fac86 Add support for "no blend" in webpmux binary
+3b80bc48 gif2webp: Separate out each step into a method
+bef7e9cc Add doc precision about demux object keeping pointers to data.
+61405a14 dwebp: enable stdout output with WIC
+6eabb886 Merge "Animated WebP: add "do no blend" option to spec"
+be20decb fix compilation for BITS 24
+e58cc137 Merge "dwebp: s/unsigned char/uint8_t/"
+72501d43 dwebp: s/unsigned char/uint8_t/
+2c9633e8 Merge "gif2webp: Insert independent frames at regular intervals."
+f0d6a14b gif2webp: Insert independent frames at regular intervals.
+b25a6fbf yuv.h: fix indent
+ede3602e Merge "cosmetics: fix indent"
+3a65122a dwebp: fix stdout related output
+388a7249 cosmetics: fix indent
+4c7322c8 Merge "dsp: msvc compatibility"
+d50c7e32 Merge "5-7% faster SSE2 versions of YUV->RGB conversion functions"
+b8ab7847 Merge "simplify upsampler calls: only allow 'bottom' to be NULL"
+df6cebfa 5-7% faster SSE2 versions of YUV->RGB conversion functions
+ad6ac32d simplify upsampler calls: only allow 'bottom' to be NULL
+a5e8afaf output to stdout if file name is "-"
+f358450f dsp: msvc compatibility
+43a7c8eb Merge "cosmetics"
+4c5f19c1 Merge "bit_reader.h: cosmetics"
+f72fab70 cosmetics
+14dd5e78 fix const-ness
+b20aec49 Merge "Support for 'do not blend' option in vwebp"
+dcf65222 Support for 'do not blend' option in vwebp
+d5bad033 Animated WebP: add "do no blend" option to spec
+a2f5f73d Merge "Support for "Do not blend" in mux and demux libraries"
+e081f2f3 Pack code & extra_bits to Struct (VP8LPrefixCode).
+6284854b Support for "Do not blend" in mux and demux libraries
+f486aaa9 Merge "slightly faster ParseIntraMode"
+d1718632 slightly faster ParseIntraMode
+3ceca8ad bit_reader.h: cosmetics
+69257f70 Create LUT for PrefixEncode.
+988b7084 add WebPWorkerExecute() for convenient bypass
+06e24987 Merge "VP8EncIterator clean-up"
+de4d4ad5 VP8EncIterator clean-up
+7bbe9529 Merge "cosmetics: thread.c: drop a redundant comment"
+da411485 cosmetics: thread.c: drop a redundant comment
+feb4b6e6 thread.h: #ifdef when checking WEBP_USE_THREAD
+8924a3a7 thread.c: drop WebPWorker prefix from static funcs
+1aed8f2a Merge "fix indent"
+4038ed15 fix indent
+1693fd9b Demux: A new state WEBP_DEMUX_PARSE_ERROR
+8dcae8b3 fix rescaling-with-alpha inaccuracy
+11249abf Merge changes I9b4dc36c,I4e0eef4d
+52508a1f Mux: support parsing unknown chunks within a frame/fragment.
+05db0572 WebPMuxSetChunk: remove unused variable
+8ba1bf61 Stricter check for presence of alpha when writing lossless images
+a03c3516 Demux: WebPIterator now also denotes if the frame has alpha.
+6df743a3 Decoder: handle fragments case correctly too.
+faa4b07e Support for unknown chunks in mux library
+7d60bbc6 Speed up HashChainFindCopy function.
+66740140 Speedup Alpha plane encoding.
+b7346a1e 0.1 % speedup to decoding
+c606182e webp-container-spec: Tighten language added by last
+a34a5029 pngdec: output error messages from libpng
+e84c625d Merge "Detect canvas and image size mismatch in decoder."
+f626fe2e Detect canvas and image size mismatch in decoder.
+f5fbdee0 demux: stricter image bounds check
+30c8158a add extra assert in Huffman decode code
+8967b9f3 SSE2 for lossless decoding (critical) functions.
+699d80ea Jump-lookup for Huffman coding
+c34307ab fix some VS9 warnings about type conversion
+eeada35c pngdec: add missing include
+54b65108 gif2webp: If aligning to even offsets, extra pixels should be transparent
+0bcf5ce3 Merge "remove a malloc() in case we're using only FILTER_NONE for alpha"
+2c07143b remove a malloc() in case we're using only FILTER_NONE for alpha
+a4d5f59d Faster lossless decoding
+fd53bb75 Merge "alternate LUT-base reverse-bits code"
+d1c166ef Merge "Container spec: a clarification on background color."
+fdb91779 Rename a method
+5e967532 Container spec: a clarification on background color.
+30e77d0f Merge branch '0.3.0'
+1b631e29 alternate LUT-base reverse-bits code
+24cc307a ~20% faster lossless decoding
+313d853f Speedup for decoding lossless WebP photographs:
+24ee098a change the bytes_per_pixels_ field into more evocative use_8b_decode
+2a04b034 update ChangeLog (tag: v0.3.1-rc2, tag: v0.3.1)
+7288950b Regression fix for alpha channels using color cache:
+2e377b53 wicdec: silence a format warning
+ad9e42a6 muxedit: silence some uninitialized warnings
+3307c163 Don't set alpha-channel to 0xff for alpha->green uplift
+5130770c Merge "wicdec: silence a format warning"
+a37eff47 Regression fix for alpha channels using color cache:
+241cf99b Merge "muxedit: silence some uninitialized warnings"
+c8f9c84d Regression fix for alpha unfiltering:
+14cd5c6c muxedit: silence some uninitialized warnings
+a368db81 dec/vp8l: quiet vs9 x64 type conversion warning
+ffae9f31 wicdec: silence a format warning
+8cf0701e Alpha encoding: never filter in case of NO_COMPRESSION
+825e73b1 update ChangeLog (tag: v0.3.1-rc1)
+abf6f691 update NEWS
+5a92c1a5 bump version to 0.3.1
+86daf77c store top Y/U/V samples in packed fashion
+67bc353e Revert "add WebPBlendAlpha() function to blend colors against background"
+068db59e Intertwined decoding of alpha and RGB
+38cc0114 Simplify forward-WHT + SSE2 version
+3fa595a5 Support decoding upto given row in DECODE_DATA_FUNC
+520f005f DequantizeLevels(): Add 'row' and 'num_rows' args
+47374b82 Alpha unfilter for given set of rows
+f32097e0 probe input file and quick-check for WebP format.
+a2aed1d0 configure: improve gl/glut library test
+c7e89cbb update copyright text
+a00380d2 configure: remove use of AS_VAR_APPEND
+a94a88dd fix EXIF parsing in PNG
+a71e5d84 add doc precision for WebPPictureCopy() and WebPPictureView()
+8287012e remove datatype qualifier for vmnv
+e1908430 fix a memory leak in gif2webp
+0b18b9ee fix two minor memory leaks in webpmux
+db5095d5 remove some cruft from swig/libwebp.jar
+850e956f README: update swig notes
+bddd9b0a swig/python: add minimal documentation
+d573a8d5 swig: add python encode support
+6b931875 swig/java: reduce wrapper function code duplication
+6fe536f4 swig/java: rework uint8_t typemap
+a2ea4643 Fix the bug in ApplyPalette.
+7bb28d2a webp/lossless: fix big endian BGRA output
+f036d4bf Speed up ApplyPalette for ARGB pixels.
+8112c8cf remove some warnings:
+cc128e0b Further reduce memory to decode lossy+alpha images
+07db70d2 fix for big-endian
+eda8a7de gif2webp: Fix signed/unsigned comparison mismatch
+31f346fe Makefile.vc: fix libwebpdemux dll variable typo
+6c76d28e swig: add python (decode) support
+b4f5bb6c swig: cosmetics
+498d4dd6 WebP-Lossless encoding improvements.
+26e72442 swig: ifdef some Java specific code
+8ecec686 configure: add warning related flags
+e676b043 configure: add GLUT detection; build vwebp
+b0ffc437 Alpha decoding: significantly reduce memory usage
+20aa7a8d configure: add --enable-everything
+b8307cc0 configure.ac: add some helper macros
+980e7ae9 Remove the gcc compilation comments
+7f25ff99 gif2webp: Fix ICC and XMP support
+d8e53211 Add missing name to AUTHORS
+11edf5e2 Demux: Fix a potential memleak
+c7b92184 don't forward declare enums
+7a650c6a prevent signed int overflow in left shift ops
+31bea324 add precision about dynamic output reallocation with IDecoder
+c22877f7 Add incremental support for extended format files
+5051245f Makefile.vc: have 'all' target build everything
+8191deca Makefile.vc: flags cleanup
+b9d74735 Makefile.vc: drop /FD flag
+5568dbcf update gitignore
+f4c7b654 WebPEncode: An additional check. Start VP8EncLoop/VP8EncTokenLoop only if VP8EncStartAlpha succeeded.
+1fb04bec pngdec: Avoid a double-free.
+dcbb1ca5 add WebPBlendAlpha() function to blend colors against background
+bc9f5fbe configure.ac: add AM_PROG_AR for automake >= 1.12
+bf867bf2 Tuned cross_color parameter (step) for lower qual
+90e2ec5a Merge "probe input file and quick-check for WebP format."
+7180d7ff Merge "update copyright text"
+830f72b7 probe input file and quick-check for WebP format.
+2ccf58d6 configure: improve gl/glut library test
+d640614d update copyright text
+c2113ad4 Merge "configure: remove use of AS_VAR_APPEND"
+9326a56f configure: remove use of AS_VAR_APPEND
+ea63d619 fix a type warning on VS9 x86
+bec11092 fix EXIF parsing in PNG
+b6e65f3d Merge "fix warnings for vs9 x64"
+438946dc fix warnings for vs9 x64
+f4710e3b collect macroblock reconstruction data in VP8MBData struct
+23d28e21 add doc precision for WebPPictureCopy() and WebPPictureView()
+518f2cd7 cosmetics: gif2webp: fix indent
+af358e68 Merge "remove datatype qualifier for vmnv"
+3fe91635 remove datatype qualifier for vmnv
+764fdffa fix a memory leak in gif2webp
+3e59a74d fix two minor memory leaks in webpmux
+47b9862f Merge "README: update swig notes"
+325d15ff remove some cruft from swig/libwebp.jar
+4a7627c2 README: update swig notes
+5da81e33 Merge "swig/python: add minimal documentation"
+f39e08f2 Merge "swig: add python encode support"
+6ca4a3e3 Merge "swig/java: reduce wrapper function code duplication"
+8f8702b0 Merge "swig/java: rework uint8_t typemap"
+91413be2 reduce memory for VP8MB and remove bitfields use
+7413394e Fix the memory leak in ApplyFilters.
+2053c2cf simplify the alpha-filter testing loop
+825b64db swig/python: add minimal documentation
+14677e11 swig: add python encode support
+a5c297c8 swig/java: reduce wrapper function code duplication
+ad4a367d swig/java: rework uint8_t typemap
+0d25876b use uint8_t for inv_palette[]
+afa3450c Fix the bug in ApplyPalette.
+2d6ac422 Merge "webp/lossless: fix big endian BGRA output"
+2ca83968 webp/lossless: fix big endian BGRA output
+742110cc Speed up ApplyPalette for ARGB pixels.
+2451e47d misc code cleanup
+83db4043 Merge "swig: add python (decode) support"
+eeeea8b5 Merge "swig: cosmetics"
+d5f9b8f3 Merge "libwebp: fix vp8 encoder mem alloc offsetting"
+d8edd835 libwebp: fix vp8 encoder mem alloc offsetting
+8983b83e remove use of bit-fields in VP8FInfo
+87a4fca2 remove some warnings:
+ba8f74e2 Merge "fix for big-endian"
+a65067fa Merge "Further reduce memory to decode lossy+alpha images"
+64c84486 Further reduce memory to decode lossy+alpha images
+332130b9 Mux: make a few methods static
+44370617 fix for big-endian
+5199eab5 Merge "add uncompressed TIFF output support"
+a3aede97 add uncompressed TIFF output support
+f975b67f Merge "gif2webp: Fix signed/unsigned comparison mismatch"
+5fbc734b Merge "GetFeatures: Detect invalid VP8X/VP8/VP8L data"
+d5060c87 Merge "mux.h: A comment fix + some consistency fixes"
+352d0dee GetFeatures: Detect invalid VP8X/VP8/VP8L data
+3ef79fef Cosmetic: "width * height"
+043e1ae4 gif2webp: Fix signed/unsigned comparison mismatch
+5818cff7 mux.h: A comment fix + some consistency fixes
+1153f888 Merge "swig: ifdef some Java specific code"
+3eeedae1 Makefile.vc: fix libwebpdemux dll variable typo
+f980faf4 swig: add python (decode) support
+7f5f42bb swig: cosmetics
+8eae188a WebP-Lossless encoding improvements.
+c7247c4c swig: ifdef some Java specific code
+4cb234d5 Merge "Mux: make ValidateForSingleImage() method static"
+ed6f5308 Merge "Add GetCanvasSize() method to mux"
+1d530c9a Mux: make ValidateForSingleImage() method static
+bba4c2b2 configure: add warning related flags
+fffefd18 Add GetCanvasSize() method to mux
+732da8d0 Merge "configure: add GLUT detection; build vwebp"
+0e513f7a configure: add GLUT detection; build vwebp
+55d1c150 Merge "Alpha decoding: significantly reduce memory usage"
+13d99fb5 Merge "configure: add --enable-everything"
+2bf698fe Merge "configure.ac: add some helper macros"
+edccd194 Alpha decoding: significantly reduce memory usage
+3cafcc9a configure: add --enable-everything
+4ef14477 configure.ac: add some helper macros
+a4e1cdbb Remove the gcc compilation comments
+6393fe4b Cosmetic fixes
+9c4ce971 Simplify forward-WHT + SSE2 version
+878b9da5 fix missed optim
+00046171 VP8GetInfo(): Check for zero width or height.
+9bf31293 align VP8Encoder::nz_ allocation
+5da165cf fix CheckMode() signature
+0ece07dc Merge "explicitly pad bitfields to 32-bits"
+9dbc9d19 explicitly pad bitfields to 32-bits
+5369a80f Merge "prevent signed int overflow in left shift ops"
+70e39712 Merge "cosmetics: remove unnecessary ';'s"
+d3136ce2 Merge "don't forward declare enums"
+b26e5ad5 gif2webp: Fix ICC and XMP support
+46089b20 Add missing name to AUTHORS
+94328d64 Demux: Fix a potential memleak
+96e948d7 don't forward declare enums
+f4f90880 prevent signed int overflow in left shift ops
+0261545e cosmetics: remove unnecessary ';'s
+7ebdf110 Merge "Fix few missing comparisons to NULL"
+1579989e Fix few missing comparisons to NULL
+ea1b21cf Cleaned up VP8GetHeaders() so that it parses only frame header
+b66caee4 dwebp: add support for BMP output
+ff885bfe add precision about dynamic output reallocation with IDecoder
+79241d5a Merge "Makefile.vc: have 'all' target build everything"
+ac1c729b Merge "Makefile.vc: flags cleanup"
+118a055c Merge "Makefile.vc: drop /FD flag"
+ecad0109 Merge "update gitignore"
+a681b4f4 Rename PRE_VP8 state to WEBP_HEADER
+ead4d478 Add incremental support for extended format files
+69d0f926 Makefile.vc: have 'all' target build everything
+52967498 Makefile.vc: flags cleanup
+c61baf0c Makefile.vc: drop /FD flag
+3a15125d update gitignore
+5167ca47 Merge "WebPEncode: An additional check. Start VP8EncLoop/VP8EncTokenLoop only if VP8EncStartAlpha succeeded."
+67708d67 WebPEncode: An additional check. Start VP8EncLoop/VP8EncTokenLoop only if VP8EncStartAlpha succeeded.
+b68912af pngdec: Avoid a double-free.
+82abbe12 Merge "configure.ac: add AM_PROG_AR for automake >= 1.12"
+e7d9548c add WebPBlendAlpha() function to blend colors against background
+ed4dc717 configure.ac: add AM_PROG_AR for automake >= 1.12
+df4a406d Merge branch '0.3.0'
+1e0d4b8c Update ChangeLog (tag: v0.3.0-rc7, tag: v0.3.0)
+d52b405d Cosmetic fixes
+6cb4a618 misc style fix
+68111ab0 add missing YUVA->ARGB automatic conversion in WebPEncode()
+e9a7990b Cosmetic fixes
+403bfe82 Container spec: Clarify frame disposal
+2aaa423b Merge "add missing YUVA->ARGB automatic conversion in WebPEncode()"
+07d87bda add missing YUVA->ARGB automatic conversion in WebPEncode()
+142c4629 misc style fix
+3e7a13a0 Merge "Container spec: clarify the background color field" into 0.3.0
+14af7745 container doc: add a note about the 'ANMF' payload
+cc635efa Container spec: clarify the background color field
+e3e33949 container doc: move RIFF description to own section
+4299f398 libwebp/mux: fix double free
+33f9a692 Merge "demux: keep a frame tail pointer; used in AddFrame" into 0.3.0
+a2a7b959 use WebPDataCopy() instead of re-coding it.
+6f18f12f demux: keep a frame tail pointer; used in AddFrame
+e5af49e9 add doc precision about WebPParseHeaders() return codes
+db46daab Merge "Makefile.vc: fix dynamic builds" into 0.3.0
+53c77afc Merge "gif2webp: Bgcolor fix for a special case" into 0.3.0
+a5ebd143 gif2webp: Bgcolor fix for a special case
+6378f238 Merge "vwebp/animation: fix background dispose" into 0.3.0
+3c8eb9a8 fix bad saturation order in QuantizeBlock
+04c7a2ec vwebp/animation: fix background dispose
+81a50695 Makefile.vc: fix dynamic builds
+5f25c396 update ChangeLog (tag: v0.3.0-rc6)
+14d42af2 examples: don't use C99 %zu
+5ccf1fe5 update ChangeLog
+2560c243 update NEWS
+f43bafc3 Merge changes Iecccb09c,If5ee9fd2,I3e181ce4 into 0.3.0
+a788644f dwebp: warn when decoding animated webp's
+302efcdb Decode: return more meaningful error for animation
+ad452735 WebPBitstreamFeatures: add has_animation field
+783dfa49 disable FRGM decoding for good in libwebpmux
+4b956be0 Update ChangeLog
+ad8b86d7 update NEWS
+3e084f63 Merge "demux cosmetics: comments/rename internal function" into 0.3.0
+d3f8c621 Merge "move WebPFeatureFlags declaration" into 0.3.0
+7386fe50 Merge "libwebp{demux,mux}: install mux_types.h" into 0.3.0
+d6cd4e90 Merge "bump decode abi" into 0.3.0
+17f8da5c bump decode abi
+97684ae2 Merge "add doc precision about WebPDemuxPartial()" into 0.3.0
+f933fd2a move WebPFeatureFlags declaration
+289bc47b libwebp{demux,mux}: install mux_types.h
+224e8d46 add doc precision about WebPDemuxPartial()
+4c18e80c demux cosmetics: comments/rename internal function
+7cfd1bf1 update AUTHORS
+401f7b85 Merge "speed-up lossless (~3%) with ad-hoc histogram cost evaluation" into 0.3.0
+1fc8ffca Merge "makefile.unix: dist related changes" into 0.3.0
+8a89c6ed Merge changes I466c377f,Ib761ebd3,I694857fc into 0.3.0
+f4ffb2d5 speed-up lossless (~3%) with ad-hoc histogram cost evaluation
+723847d5 gif2webp: only write error messages to stderr
+701b9e2a makefile.unix: dist related changes
+bb85b437 Merge "update NEWS" into 0.3.0
+59423a24 gif2webp: fix crash on open failure with libgif5
+9acb17de gif2webp: silence a unused param warning
+7d9fdc23 Merge "README updates" into 0.3.0
+5621934e Merge "build: fix install race on shared headers" into 0.3.0
+70809d89 Merge "bump version to 0.3.0" into 0.3.0
+d851cd1d demux: make the parse a bit more strict
+28bb4107 update NEWS
+cef93882 bump version to 0.3.0
+9048494d build: fix install race on shared headers
+1e67e8ef README updates
+42b611a4 Merge "configure: drop experimental from mux/demux" into 0.3.0
+096a8e32 Merge "vwebp: add color profile support" into 0.3.0
+ddfee5dc vwebp: add color profile support
+0d6927d3 Merge "Mark fragment options as experimental in webpmux" into 0.3.0
+5dbd4030 Mark fragment options as experimental in webpmux
+a0a6648c configure: drop experimental from mux/demux
+ee65bad8 Merge "add support for BITS > 32" into 0.3.0
+744930db add support for BITS > 32
+7dd288f0 cwebp: fix build
+19a8dd01 Merge "Makefile.vc: add vwebp.exe target" into 0.3.0
+50eeddad Merge "examples: normalize icc related program arguments" into 0.3.0
+757f637f Merge "Makefile.vc: add libwebpdecoder target" into 0.3.0
+b65c4b7c Makefile.vc: add libwebpdecoder target
+f8db7b4a Merge "vwebp: replace doubles w/floats where appropriate" into 0.3.0
+d99aa56f Makefile.vc: add vwebp.exe target
+013023e7 vwebp: replace doubles w/floats where appropriate
+9b3db894 README.mux: add version reference
+7b6a26cf Merge "cwebp: output metadata statistics" into 0.3.0
+d8dc72a0 examples: normalize icc related program arguments
+7bfc9056 Merge "make alpha unfilter work in-place" into 0.3.0
+0037b2d2 Merge "add LUT-free reference code for YUV->RGB conversion." into 0.3.0
+166bf744 Merge "demux: disable fragment parsing" into 0.3.0
+126974b4 add LUT-free reference code for YUV->RGB conversion.
+0aef3ebd make alpha unfilter work in-place
+14ef5005 Merge "Remove 'status: experimental' from container spec" into 0.3.0
+d40c98e1 Merge "webpmux binary: tiny style fix" into 0.3.0
+0bc42689 cwebp: output metadata statistics
+bc039803 Merge "autoconf: normalize experimental define" into 0.3.0
+d1e21b13 Remove 'status: experimental' from container spec
+7681bb96 webpmux binary: tiny style fix
+a3dd3d0f avoid installing example_util.h
+252320e2 demux: disable fragment parsing
+537bde05 autoconf: normalize experimental define
+5e338e0b Merge changes I33e8a613,I8e8a7b44 into 0.3.0
+d9d0ea1b Merge changes If21e3ec7,I991fc30b into 0.3.0
+627f5ca6 automake: add reference to libwebp for mux/demux
+eef73d07 don't consolidate proba stats too often
+05ec4cc2 libwebp{,decoder}.pc: add pthread flags
+1bfcf5bf add libwebpmux.pc
+26ca843d add libwebpdemux.pc
+69e25906 Merge "Tune Lossless compression for lower qualities."
+0478b5d2 Tune Lossless compression for lower qualities.
+39f7586f add a mention of parallel alpha encoding in the NEWS
+5a21d967 Merge "1.5x-2x faster encoding for method 3 and up"
+9bfbdd14 1.5x-2x faster encoding for method 3 and up
+27dc741b Correct frame options order in README.mux
+be2fd173 Mux: fix a scenario with bad ANMF/FRGM size
+19eb012c Merge "Demux: Add option to get frame count using GetI()"
+7368b8cb Merge "WebPGetFeatures() out of if condition for clarity."
+f604c9a4 Merge "fix windows build"
+153f94e8 fix windows build
+847b4924 Merge "vwebp: use magenta for 'i'nfo display"
+25ea46bd Merge "vwebp: add keyboard shortcuts to help output"
+bea7ccaf vwebp: use magenta for 'i'nfo display
+8fab161a webpmux: correct -frame param order in help output
+03cc23d6 vwebp: add keyboard shortcuts to help output
+068eba8d Demux: Add option to get frame count using GetI()
+988b8f56 WebPGetFeatures() out of if condition for clarity.
+6933d910 Merge "gif2webp: Be lenient about background color index."
+4d0f7c55 Merge "WebPGetFeatures() behavior change:"
+fdeeb01d gif2webp: Be lenient about background color index.
+ad250320 Merge "multi-threaded alpha encoding for lossy"
+4e32d3e1 Merge "fix compilation of token.c"
+f817930a multi-threaded alpha encoding for lossy
+88050351 fix compilation of token.c
+fc816219 code using the actual values for num_parts_, not the ones from config
+72655350 Merge "move the config check from .c to .h"
+dd9e76f7 move the config check from .c to .h
+956b217a WebPGetFeatures() behavior change:
+df02e4ce WebPDemuxGetI behavior change:
+633c004d Merge "rebalance method tools (-m) for methods [0..4]"
+58ca6f65 rebalance method tools (-m) for methods [0..4]
+7648c3cc Merge "describe rd-opt levels introduce VP8RDLevel enum"
+67fb1003 Merge "autoconf: enable silent-rules by default"
+a5042a32 GetVersion() methods for mux and demux
+5189957e describe rd-opt levels introduce VP8RDLevel enum
+4e094ace autoconf: enable silent-rules by default
+b7eaa85d inline VP8LFastLog2() and VP8LFastSLog2 for small values
+5cf7792e split quant_levels.c into decoder and encoder version
+e5d3ffe2 Merge "Update code example in README.mux"
+ac5a9156 Update code example in README.mux
+38a91e99 Add example code snippet for demux API
+5f557f3c README.mux: add info about Demux API and vwebp
+c0ba0903 backward_references: avoid signed integer overflow
+943386db disable SSE2 for now
+9479fb7d lossless encoding speedup
+ec2030a8 merge two lines together
+b67956c0 Merge "Remove ReadOneBit() and ReadSymbolUnsafe()"
+1667bded Remove ReadOneBit() and ReadSymbolUnsafe()
+3151669b wicdec + dwebp cosmetics: normalize formatting
+92668da6 change default filtering parameters:   * type is now 'strong'   * strength is now '60'
+b7490f85 introduce WEBP_REFERENCE_IMPLEMENTATION compile option
+33838857 faster decoding (3%-6%)
+5c3e381b Merge "add a -jpeg_like option"
+c2311046 remove unused declaration of VP8Zigzag
+36152957 Merge "wicdec: add alpha support for paletted formats"
+c9f16490 wicdec: add alpha support for paletted formats
+1262f81e Merge "wicdec: silence some warnings"
+e7ea61eb wicdec: silence some warnings
+23c0f354 fix missing intptr_t->int cast for MSVC
+e895059a add a -jpeg_like option
+1f803f64 Merge "Tune alpha quality mapping to more reasonable values."
+1267d498 Tune alpha quality mapping to more reasonable values.
+043076e2 Merge "speed-up lossless in BackwardTrace"
+f3a44dcd remove one malloc from TraceBackwards()
+0fc1a3a0 speed-up lossless in BackwardTrace
+7c732e59 cwebp: centralize WebPCleanupTransparentArea()
+7381254e Merge "wicdec: add ICC profile extraction"
+e83ff7de wicdec: add ICC profile extraction
+146c6e3b Merge "cosmetics: pngdec: normalize default label location"
+a8f549d7 Merge "manpages: italicize option parameters"
+e118db83 Merge "encode.h: note the need to free() WebPMemoryWriter"
+1dfee6db cosmetics: pngdec: normalize default label location
+14c38200 manpages: italicize option parameters
+7defbfad encode.h: note the need to free() WebPMemoryWriter
+88d382a0 cwebp: cleanup after memory_writer
+12d6cecf fix extra space in dwebp.1 man
+b01681a9 Fix for demuxer frame iteration:
+56c12aa6 Demuxer creation fix:
+66c810bc add a -yuv option to dwebp (very similar to -pgm)
+841a3ba5 Merge "Remove -Wshadow warnings."
+8fd02527 Merge "upsampling_neon.c: fix build"
+6efed268 Remove -Wshadow warnings.
+60904aa6 Merge "allow WebPINewRGB/YUVA to be passed a NULL output buffer."
+b7adf376 allow WebPINewRGB/YUVA to be passed a NULL output buffer.
+27f8f742 upsampling_neon.c: fix build
+06b9cdf1 gitignore: add IOS related directories
+f112221e Merge "Fix more comments for iobuild.sh"
+fe4d25dd Fix more comments for iobuild.sh
+1de3e252 Merge "NEON optimised yuv to rgb conversion"
+090b708a NEON optimised yuv to rgb conversion
+daa06476 Merge "Add ios build script for building iOS library."
+79fe39e2 Add ios build script for building iOS library.
+126c035f remove some more -Wshadow warnings
+522e9d61 Merge "cwebp: enable '-metadata'"
+76ec5fa1 cwebp: enable '-metadata'
+aeb91a9d Merge "cosmetics: break a few long lines"
+be7c96b0 cosmetics: break a few long lines
+cff8ddb6 Merge "add libwebpdecoder.pc"
+93148ab8 Merge "libwebp.pc.in: detab"
+6477f955 Merge "Makefile.vc: normalize path separator"
+bed1ed7c add libwebpdecoder.pc
+46168b2d libwebp.pc.in: detab
+a941a346 Fixed few nits in the build files.
+dd7a49b2 Makefile.vc: normalize path separator
+9161be86 Merge "cwebp: extract WIC decoding to its own module"
+08e7c58e Merge "Provide an option to build decoder library."
+0aeba528 Provide an option to build decoder library.
+757ebcb1 catch malloc(0)/calloc(0) with an assert
+152ec3d2 Merge "handle malloc(0) and calloc(0) uniformly on all platforms"
+a452a555 cwebp: extract WIC decoding to its own module
+2b252a53 Merge "Provide option to swap bytes for 16 bit colormodes"
+94a48b4b Provide option to swap bytes for 16 bit colormodes
+42f8f934 handle malloc(0) and calloc(0) uniformly on all platforms
+8b2152c5 Merge "add an extra assert to check memory bounds"
+0d19fbff remove some -Wshadow warnings
+cd22f655 add an extra assert to check memory bounds
+8189feda Merge "Add details and reference about the YUV->RGB conversion"
+1d2702b1 Merge "Formatting fixes in lossless bitstream spec"
+8425aaee Formatting fixes in lossless bitstream spec
+a556cb1a Add details and reference about the YUV->RGB conversion
+d8f21e0b add link to SSIM description on Wikipedia
+18e9167e Merge "WebP-lossless spec clarifications:"
+98e25b9b Merge "cwebp: add -metadata option"
+f01c2a53 WebP-lossless spec clarifications:
+f4a97970 Merge "Disto4x4 and Disto16x16 in NEON"
+47b7b0ba Disto4x4 and Disto16x16 in NEON
+7eaee9f1 cwebp: add -metadata option
+36c52c2c tiffdec: use toff_t for exif ifd offset
+7c8111e4 Merge "cwebp/tiffdec: add TIFF metadata extraction"
+e6409adc Remove redundant include from dsp/lossless code.
+1ab5b3a7 Merge "configure: fix --with-gifincludedir"
+03c749eb configure: fix --with-gifincludedir
+8b650635 multiple libgif versions support for gif2webp
+476e293f gif2webp: Use DGifOpenFileName()
+b50f277b tiffdec: correct format string
+2b9048e3 Merge "tiffdec: check error returns for width/height"
+a1b5a9a3 Merge "cwebp/tiff: use the first image directory"
+079423f5 tiffdec: check error returns for width/height
+d62824af Merge "cwebp/jpegdec: add JPEG metadata extraction"
+03afaca4 Merge "cwebp: add PNG metadata extraction"
+2c724968 cwebp/jpegdec: add JPEG metadata extraction
+dba64d91 cwebp: add PNG metadata extraction
+1f075f89 Lossless spec corrections/rewording/clarifications
+2914ecfd cwebp/tiffdec: add TIFF metadata extraction
+d82a3e33 More corrections/clarifications in lossless spec:
+bd002557 cwebp/tiff: use the first image directory
+df7aa076 Merge "Cleanup around jpegdec"
+0f57dcc3 decoding speed-up (~1%)
+bcec339b Lossless bitstream clarification:
+6bf20874 add examples/metadata.c
+207f89c0 Merge "configure: add libwebpdemux status to summary"
+1bd287a6 Cleanup around jpegdec
+91455679 Merge "cosmetics: use '== 0' in size checks"
+d6b88b76 cosmetics: use '== 0' in size checks
+d3dace2f cosmetics: jpegdec
+2f69af73 configure: add libwebpdemux status to summary
+1c1c5646 cwebp: extract tiff decoding to its own module
+6a871d66 cwebp: extract jpeg decoding to its own module
+2ee228f9 cwebp: extract png decoding to its own module
+4679db00 Merge "cwebp: add metadata framework"
+63aba3ae cwebp: add metadata framework
+931bd516 lossless bitstream: block size bits correction
+e4fc4c1c lossless bitstream: block size bits correction
+d65ec678 fix build, move token.c to src/enc/
+657f5c91 move token buffer to its own file (token.c)
+c34a3758 introduce GetLargeValue() to slim-fast GetCoeffs().
+d5838cd5 faster non-transposing SSE2 4x4 FTransform
+f76191f9 speed up GetResidualCost()
+ba2aa0fd Add support for BITS=24 case
+2e7f6e8e makefile.unix: Dependency on libraries
+dca84219 Merge "Separate out mux and demux code and libraries:"
+23782f95 Separate out mux and demux code and libraries:
+bd56a01f configure: add summary output
+90e5e319 dwebp manual: point to webpmux, gif2webp.
+540790ca gif2webp.c: add a note about prerequisites
+d1edf697 cwebp man page: meaning of '-q' for lossy/lossless
+79efa1d0 Add man page for gif2webp utility
+2243e40c Merge "gif2webp build support with autoconf tools"
+c40efca1 gif2webp build support with autoconf tools
+6523e2d4 WebP Container:
+4da788da Merge "simplify the fwd transform"
+42c3b550 simplify the fwd transform
+41a6ced9 user GLfloat instead of float
+b5426119 fix indentation
+68f282f7 * handle offset in anim viewer 'vwebp' * fix gif2webp to handle disposal method and odd offset correctly
+118cb312 Merge "add SSE2 version of Sum of Square error for 16x16, 16x8 and 8x8 case"
+8a7c3cc8 Merge "Change the order of -frame argument to be more natural"
+99e0a707 Merge "Simplify the texture evaluation Disto4x4()"
+0f923c3f make the bundling work in a tmp buffer
+e5c3b3f5 Simplify the texture evaluation Disto4x4()
+48600084 Change the order of -frame argument to be more natural
+35bfd4c0 add SSE2 version of Sum of Square error for 16x16, 16x8 and 8x8 case
+a7305c2e Clarification for unknown chunks
+4c4398e2 Refine WebP Container Spec wrt unknown chunks.
+2ca642e0 Rectify WebPMuxGetFeatures:
+7caab1d8 Some cosmetic/comment fixes.
+60b2651a Merge "Write a GIF to WebP converter based on libgif."
+c7127a4d Merge "Add NEON version of FTransformWHT"
+11b27212 Write a GIF to WebP converter based on libgif.
+e9a15a37 ExUtilWriteFile() to write memory segment to file
+74356eb5 Add a simple cleanup step in mux assembly:
+51bb1e5d mux.h: correct WebPDemuxSelectFragment() prototype
+22a0fd9d Add NEON version of FTransformWHT
+fa30c863 Update mux code to match the spec wrt animation
+d9c5fbef by-pass Analysis pass in case segments=1
+d2ad4450 Merge changes Ibeccffc3,Id1585b16
+5c8be251 Merge "Chunk fourCCs for XMP/EXIF"
+a00a3daf Use 'frgm' instead of 'tile' in webpmux parameters
+81b8a741 Design change in ANMF and FRGM chunks:
+f903cbab Chunk fourCCs for XMP/EXIF
+812933d6 Tune performance of HistogramCombine
+52ad1979 Animation specification in container spec
+001b9302 Image fragment specification in container spec
+391f9db9 Ordering of description of bits in container spec
+d5735776 Metadata specification in container spec
+1c4609b1 Merge commit 'v0.2.1'
+0ca584cb Merge "Color profile specification in container spec"
+e8b41ad1 add NEON asm version for WHT inverse transform
+af6f0db2 Color profile specification in container spec
+a61a824b Merge "Add NULL check in chunk APIs"
+0e8b7eed fix WebPPictureView() unassigned strides
+75e5f17e ARM/NEON: 30% encoding speed-up
+02b43568 Add NULL check in chunk APIs
+a0770727 mux struct naming
+6c66dde8 Merge "Tune Lossless encoder"
+ab5ea217 Tune Lossless encoder
+74fefc8c Update ChangeLog (tag: v0.2.1, origin/0.2.0)
+92f8059c Rename some chunks:
+3bb4bbeb Merge "Mux API change:"
+d0c79f05 Mux API change:
+abc06044 Merge "update NEWS" into 0.2.0
+57cf313b update NEWS
+25f585c4 bump version to 0.2.1
+fed7c048 libwebp: validate chunk size in ParseOptionalChunks
+552cd9bc cwebp (windows): fix alpha image import on XP
+b14fea99 autoconf/libwebp: enable dll builds for mingw
+4a8fb272 [cd]webp: always output windows errors
+d6621580 fix double to float conversion warning
+72b96a69 cwebp: fix jpg encodes on XP
+734f762a VP8LAllocateHistogramSet: fix overflow in size calculation
+f9cb58fb GetHistoBits: fix integer overflow
+b30add20 EncodeImageInternal: fix uninitialized free
+3de58d77 fix the -g/O3 discrepancy for 32bit compile
+77aa7d50 fix the BITS=8 case
+e5970bda Make *InitSSE2() functions be empty on non-SSE2 platform
+ef5cc47e make *InitSSE2() functions be empty on non-SSE2 platform
+c4ea259d make VP8DspInitNEON() public
+8344eadf Merge "libwebp: validate chunk size in ParseOptionalChunks"
+4828bb93 Merge "cwebp (windows): fix alpha image import on XP"
+30763333 libwebp: validate chunk size in ParseOptionalChunks
+70481898 AccumulateLSIM: fix double -> float warnings
+eda8ee4b cwebp (windows): fix alpha image import on XP
+c6e98658 Merge "add EXPERIMENTAL code for YUV-JPEG colorspace"
+f0360b4f add EXPERIMENTAL code for YUV-JPEG colorspace
+f86e6abe add LSIM metric to WebPPictureDistortion()
+c3aa215a Speed up HistogramCombine for lower qualities.
+1765cb1c Merge "autoconf/libwebp: enable dll builds for mingw"
+a13562e8 autoconf/libwebp: enable dll builds for mingw
+9f469b57 typo: no_fancy -> no_fancy_upsampling
+1a27f2f8 Merge "fix double to float conversion warning"
+cf1e90de Merge "cwebp: fix jpg encodes on XP"
+f2b5d19b [cd]webp: always output windows errors
+e855208c fix double to float conversion warning
+ecd66f77 cwebp: fix jpg encodes on XP
+7b3eb372 Tune lossless compression to get better gains.
+ce8bff45 Merge "VP8LAllocateHistogramSet: fix overflow in size calculation"
+ab5b67a1 Merge "EncodeImageInternal: fix uninitialized free"
+7fee5d12 Merge "GetHistoBits: fix integer overflow"
+a6ae04d4 VP8LAllocateHistogramSet: fix overflow in size calculation
+80237c43 GetHistoBits: fix integer overflow
+8a997235 EncodeImageInternal: fix uninitialized free
+0b9e6829 minor cosmetics
+a792b913 fix the -g/O3 discrepancy for 32bit compile
+73ba4357 Merge "detect and merge similar segments"
+fee66275 detect and merge similar segments
+0c44f415 src/webp/*.h: don't forward declare enums in C++
+d7a5ac86 vwebp: use demux interface
+931e0ea1 Merge "replace 'typedef struct {} X;" by "typedef struct X X; struct X {};""
+8f216f7e remove cases of equal comparison for qsort()
+28d25c82 replace 'typedef struct {} X;" by "typedef struct X X; struct X {};"
+2afee60a speed up for ARM using 8bit for boolean decoder
+5725caba new segmentation algorithm
+2cf1f815 Merge "fix the BITS=8 case"
+12f78aec fix the BITS=8 case
+6920c71f fix MSVC warnings regarding implicit uint64 to uint32 conversions
+f6c096aa webpmux binary: Rename 'xmp' option to 'meta'
+ddfe871a webpmux help correction
+b7c55442 Merge "Make *InitSSE2() functions be empty on non-SSE2 platform"
+1c04a0d4 Common APIs for chunks metadata and color profile.
+2a3117a1 Merge "Create WebPMuxFrameInfo struct for Mux APIs"
+5c3a7231 Make *InitSSE2() functions be empty on non-SSE2 platform
+7c6e60f4 make *InitSSE2() functions be empty on non-SSE2 platform
+c7eb4576 make VP8DspInitNEON() public
+ab3234ae Create WebPMuxFrameInfo struct for Mux APIs
+e3990fd8 Alignment fixes
+e55fbd6d Merge branch '0.2.0'
+4238bc0a Update ChangeLog (tag: v0.2.0)
+c655380c dec/io.c: cosmetics
+fe1958f1 RGBA4444: harmonize lossless/lossy alpha values
+681cb30a fix RGBA4444 output w/fancy upsampling
+f06c1d8f Merge "Alignment fix" into 0.2.0
+f56e98fd Alignment fix
+6fe843ba avoid rgb-premultiply if there's only trivial alpha values
+528a11af fix the ARGB4444 premultiply arithmetic
+a0a48855 Lossless decoder fix for a special transform order
+62dd9bb2 Update encoding heuristic w.r.t palette colors.
+6f4272b0 remove unused ApplyInverseTransform()
+93bf0faa Update ChangeLog (tag: v0.2.0-rc1)
+5934fc59 update AUTHORS
+014a711d update NEWS
+43b0d610 add support for ARGB -> YUVA conversion for lossless decoder
+33705ca0 bump version to 0.2.0
+c40d7ef1 fix alpha-plane check + add extra checks
+a06f8023 MODE_YUVA: set alpha to opaque if the image has none
+52a87dd7 Merge "silence one more warning" into 0.2.0
+3b023093 silence one more warning
+f94b04f0 move some RGB->YUV functions to yuv.h
+4b71ba03 README: sync [cd]webp help output
+c9ae57f5 man/dwebp.1: add links to output file format details
+292ec5cc quiet a few 'uninitialized' warnings
+4af3f6c4 fix indentation
+9b261bf5 remove the last NOT_HAVE_LOG2 instances
+323dc4d9 remove use of log2(). Use VP8LFastLog2() instead.
+8c515d54 Merge "harness some malloc/calloc to use WebPSafeMalloc and WebPSafeCalloc" into 0.2.0
+d4b4bb02 Merge changes I46090628,I1a41b2ce into 0.2.0
+bff34ac1 harness some malloc/calloc to use WebPSafeMalloc and WebPSafeCalloc
+a3c063c7 Merge "extra size check for security" into 0.2.0
+5e796300 Merge "WebPEncode: clear stats at the start of encode" into 0.2.0
+f1edf62f Merge "rationalize use of color-cache" into 0.2.0
+c1933317 extra size check for security
+906be657 rationalize use of color-cache
+dd1c3873 Add image-hint for low-color images.
+4eb7aa64 Merge "WebPCheckMalloc() and WebPCheckCalloc():" into 0.2.0
+80cc7303 WebPCheckMalloc() and WebPCheckCalloc():
+183cba83 check VP8LBitWriterInit return
+cbfa9eec lossless: fix crash on user abort
+256afefa cwebp: exit immediately on version mismatch
+475d87d7 WebPEncode: clear stats at the start of encode
+a7cc7291 fix type and conversion warnings
+7d853d79 add stats for lossless
+d39177b7 make QuantizeLevels() store the sum of squared error
+5955cf5e replace x*155/100 by x*101581>>16
+7d732f90 make QuantizeLevels() store the sum of squared error
+e45a446a replace x*155/100 by x*101581>>16
+159b75d3 cwebp output size consistency:
+cbee59eb Merge commit 'v0.1.99'
+1889e9b6 dwebp: report -alpha option
+3bc3f7c0 Merge "dwebp: add PAM output support" into 0.2.0
+d919ed06 dwebp: add PAM output support
+85e215d3 README/manpages/configure: update website link
+c3a207b9 Update ChangeLog (tag: v0.1.99)
+d1fd7826 Merge "add extra precision about default values and behaviour" into 0.2.0
+efc826e0 add extra precision about default values and behaviour
+9f29635d header/doc clean up
+ff9fd1ba Makefile.vc: fix webpmux.exe *-dynamic builds
+8aacc7b0 remove INAM, ICOP, ... chunks from the test webp file.
+2fc13015 harmonize authors as "Name (mail@address)"
+4a9f37b7 Merge "update NEWS" into 0.2.0
+7415ae13 makefile.unix: provide examples/webpmux target
+ce82cedc update NEWS
+641e28e8 Merge "man/cwebp.1: wording, change the date" into 0.2.0
+c37c23e5 README: cosmetics
+3976dcd5 man/cwebp.1: wording, change the date
+3e5bbe1c Merge "rename 'use_argb_input' to 'use_argb'" into 0.2.0
+ce90847a Merge "add some padding bytes areas for later use" into 0.2.0
+2390dabc Merge "fixing the findings by Frederic Kayser to the bitstream spec" into 0.2.0
+02751591 add a very crude progress report for lossless
+a4b9b1c6 Remove some unused enum values.
+dd108176 rename 'use_argb_input' to 'use_argb'
+90516ae8 add some padding bytes areas for later use
+d03b2503 fixing the findings by Frederic Kayser to the bitstream spec
+ce156afc add missing ABI compatibility checks
+9d45416a Merge "Doc: container spec text tweaks" into 0.2.0
+4e2e0a8c Doc: container spec text tweaks
+f7f16a29 add ABI compatibility check
+2a775570 Merge "swig: add WebPEncodeLossless* wrappers" into 0.2.0
+a3ec6225 mux.h: remove '* const' from function parameters
+31426eba encode.h: remove '* const' from function parameters
+9838e5d5 decode.h: remove '* const' from function parameters
+4972302d swig: add WebPEncodeLossless* wrappers
+9ff00cae bump encoder/decoder versions
+c2416c9b add lossless quick encoding functions to the public API
+4c1f5d64 Merge "NEWS: mention decode_vp8.h is no longer installed" into 0.2.0
+6cb2277d NEWS: mention decode_vp8.h is no longer installed
+d5e5ad63 move decode_vp8.h from webp/ to dec/
+8d3b04a2 Merge "header clean-up" into 0.2.0
+02201c35 Merge "remove one malloc() by making color_cache non dynamic" into 0.2.0
+d708ec14 Merge "move MIN/MAX_HISTO_BITS to format_constants.h" into 0.2.0
+ab2da3e9 Merge "add a malloc() check" into 0.2.0
+2d571bd8 add a malloc() check
+7f0c178e remove one malloc() by making color_cache non dynamic
+6569cd7c Merge "VP8LFillBitWindow: use 64-bit path for msvc x64 builds" into 0.2.0
+23d34f31 header clean-up
+2a3ab6f9 move MIN/MAX_HISTO_BITS to format_constants.h
+985d3da6 Merge "shuffle variables in HashChainFindCopy" into 0.2.0
+cdf885c6 shuffle variables in HashChainFindCopy
+c3b014db Android.mk: add missing lossless files
+8c1cc6b5 makefile.unix dist: explicitly name installed includes
+7f4647ee Merge "clarify the colorspace naming and byte ordering of decoded samples" into 0.2.0
+cbf69724 clarify the colorspace naming and byte ordering of decoded samples
+857650c8 Mux: Add WebPDataInit() and remove WebPImageInfo
+ff771e77 don't install webp/decode_vp8.h
+596dff78 VP8LFillBitWindow: use 64-bit path for msvc x64 builds
+3ca7ce98 Merge "doc: remove non-finalized chunk references" into 0.2.0
+1efaa5a3 Merge "bump versions" into 0.2.0
+51fa13e1 Merge "README: update cwebp help output" into 0.2.0
+12f9aede README: update cwebp help output
+f0b5defb bump versions
+4c42a61b update AUTHORS
+6431a1ce doc: remove non-finalized chunk references
+8130c4cc Merge "build: remove libwebpmux from default targets/config"
+23b44438 Merge "configure: broaden test for libpng-config"
+85bff2cd Merge "doc: correct lossless prefix coding table & code"
+05108f6e Merge "More spec/code matching in mux:"
+6808e69d More spec/code matching in mux:
+bd2b46f5 Merge "doc/webp-container-spec: light cosmetics"
+20ead329 doc/webp-container-spec: light cosmetics
+1d40a8bc configure: add pthread detection
+b5e9067a fix some int <-> size_t mix for buffer sizes
+e41a7596 build: remove libwebpmux from default targets/config
+0fc2baae configure: broaden test for libpng-config
+45b8272c Merge "restore authorship to lossless bitstream doc"
+06ba0590 restore authorship to lossless bitstream doc
+44a09a3c add missing description of the alpha filtering methods
+63db87dd Merge "vwebp: add checkboard background for alpha display"
+a73b8978 vwebp: add checkboard background for alpha display
+939158ce Merge "vwebp: fix info display"
+b35c07d9 vwebp: fix info display
+48b39eb1 fix underflow for very short bitstreams
+7e622984 cosmetics: param alignment, manpage wording
+1bd7dd50 Merge changes I7b0afb0d,I7ecc9708
+ac69e63e Merge "Updated cwebp man's help for Alpha & Lossless."
+c0e8859d Get rid of image_info_ from WebPChunk struct.
+135ca69e WebP Container Spec:
+eb6f9b8a Updated cwebp man's help for Alpha & Lossless.
+0fa844fb cosmetic fixes on assert and 'const' where applicable
+7f22bd25 check limit of width * height is 32 bits
+16c46e83 autoconf/make: cosmetics: break long lines
+ab22a07a configure: add helper macro to define --with-*
+c17699b3 configure: add libtiff test
+0e09732c Merge "cwebp: fix crash with yuv input + lossless"
+88a510ff Merge "fix big-endian VP8LWriteBits"
+da99e3bf Merge "Makefile.vc: split mux into separate lib"
+7bda392b cwebp: fix crash with yuv input + lossless
+f56a369a fix big-endian VP8LWriteBits
+54169d6c Merge "cwebp: name InputFileFormat members consistently"
+e2feefa9 Makefile.vc: split mux into separate lib
+27caa5aa Merge "cwebp: add basic TIFF support"
+d8921dd4 cwebp: name InputFileFormat members consistently
+6f76d246 cwebp: add basic TIFF support
+4691407b Merge changes If39ab7f5,I3658b5ae
+cca7c7b8 Fixed nit: 10 -> 10.f
+5d09a244 WebPMuxCreate() error handling:
+777341c3 Fix a memleak in WebPMuxCreate()
+61c9d161 doc: correct lossless prefix coding table & code
+4c397579 Merge "mark VP8{,L}{GetInfo,CheckSignature} as WEBP_EXTERN"
+e4e36cc6 Merge "Mux: Allow only some frames/tiles to have alpha."
+ad2aad3c Merge "WebP Decoding error handling:"
+97649c8f Mux: Allow only some frames/tiles to have alpha.
+f864be3b Lower the quality settings for Alpha encoding.
+3ba81bbe WebP Decoding error handling:
+fcc69923 add automatic YUVA/ARGB conversion during WebPEncode()
+802e012a fix compilation in non-FANCY_UPSAMPLING mode
+e012dfd9 make width/height coding match the spec
+228d96a5 mark VP8{,L}{GetInfo,CheckSignature} as WEBP_EXTERN
+637a314f remove the now unused *KeepA variants
+d11f6fcc webpmux returns error strings rather than numbers
+fcec0593 makefile.unix: cwebp: fix OSX link
+6b811f1b Merge "doc: remove lossless pdf"
+c9634821 doc: remove lossless pdf
+b9ae4f0d cosmetics after mux changes b74ed6e, b494ad5
+b494ad50 Mux: only allow adding frame/tiles at the end.
+2c341b0e Merge "Added image characteristic hint for the codec."
+d373076a Added image characteristic hint for the codec.
+2ed2adb5 Merge "msvc: add intrinsic based BitsLog2Floor"
+e595e7c5 Merge "add demux.c to the makefiles"
+da47b5bd Merge "demux: add {Next,Prev}Chunk"
+e5f46742 add demux.c to the makefiles
+4708393c demux: add {Next,Prev}Chunk
+e8a0a821 demux: quiet msvc warnings
+7f8472a6 Update the WebP Container Spec.
+31b68fe6 cleanup WebPPicture struct and API
+9144a186 add overflow check before calling malloc()
+81720c91 consistency cosmetics
+2ebe8394 Merge "Add kramdown version information to README"
+71443084 enc/vp8l.c: fix build
+b7ac19fe Add kramdown version information to README
+efdcb667 Merge "Edit for consistency, usage and grammar."
+08220102 Enable alpha in vvwebp
+8de9a084 Merge "Mux API change:"
+b74ed6e7 Mux API change:
+233a589e take picture->argb_stride into account for lossless coding
+04e33f17 Edit for consistency, usage and grammar.
+a575b4bc Merge "cosmetics: add missing const"
+8d99b0f4 Merge "cosmetics: remove unimplemented function proto"
+69d02217 cosmetics: add missing const
+5b08318b cosmetics: remove unimplemented function proto
+b7fb0ed5 Log warning for unsupported options for lossless.
+e1f769fe msvc: add intrinsic based BitsLog2Floor
+8a69c7d8 Bug-fix: Clamp backward dist to 1.
+b5b6ac97 Merge "Bring the special writer 'WebPMemoryWriter' to public API"
+a6a1909f Merge "Fix floating point exception with cwebp -progress"
+f2cee067 Fix floating point exception with cwebp -progress
+91b7a8c7 Bring the special writer 'WebPMemoryWriter' to public API
+310e2972 support resize and crop for RGBA input
+a89835d3 Merge changes Ice662960,Ie8d7aa90,I2d996d5e,I01c04772
+ce614c0c Merge "dec/vp8: avoid setting decoder status twice"
+900285da dec/vp8: avoid setting decoder status twice
+8227adc8 Merge changes I6f02b0d0,I5cbc9c0a,I9dd9d4ed,Id684d2a1
+dcda59c1 Merge "demux: rename SetTile to SelectTile"
+622ef12e demux: rename SetTile to SelectTile
+81ebd375 Merge "demux: add {Next,Prev}Frame"
+02dd37a2 demux: add {Next,Prev}Frame
+4b79fa59 Merge "Limit the maximum size of huffman Image to 16MB."
+9aa34b34 Manually number "chapters," as chapter numbers are used in the narrative.
+2a4c6c29 Re-wrap at <= 72 columns
+a45adc19 Apply inline emphasis and monospacing, per gdoc / PDF
+91011206 Incorporate gdoc changes through 2012-06-08
+7a182487 Removed CodeRay syntax declarations ...
+b3ec18c5 Provide for code-block syntax highlighting.
+709d7702 Replace high ASCII artifacts (curly quotes, etc.).
+930e8abb Lossless WebP doc largely ported to markdown text.
+18cae37b msvc: silence some build warnings
+b3923084 Limit the maximum size of huffman Image to 16MB.
+f180df2a Merge "libwebp/demux: add Frame/Chunk iteration"
+2bbe1c9a Merge "Enable lossless encoder code"
+d0601b01 Merge changes I1d97a633,I81c59093
+78f3e345 Enable lossless encoder code
+d974a9cc Merge "libwebp/demux: add simple format parsing"
+26bf2232 Merge "libwebp: add WebPDemux stub functions"
+2f666688 Merge "modify WebPParseHeaders to allow reuse by GetFeatures"
+b402b1fb libwebp/demux: add Frame/Chunk iteration
+ad9ada3b libwebp/demux: add WebPDemuxGetI
+2f2d4d58 libwebp/demux: add extended format parsing
+962dcef6 libwebp/demux: add simple format parsing
+f8f94081 libwebp: add WebPDemux stub functions
+fb47bb5c Merge "NumNamedElements() should take an enum param."
+7c689805 Fix asserts in Palette and BackwardReference code.
+fbdcb7ea NumNamedElements() should take an enum param.
+fb4943bd modify WebPParseHeaders to allow reuse by GetFeatures
+3697b5ce write an ad-hoc EncodeImageInternal variant
+eaee9e79 Bug-Fix: Decode small (less than 32 bytes) images.
+0bceae48 Merge "cwebp: fix alpha reporting in stats output"
+0424b1ef Rebase default encoding settings.
+c71ff9e3 cwebp: fix alpha reporting in stats output
+e2ffe446 Merge "Stop indefinite recursion for Huffman Image."
+70eb2bd6 Stop indefinite recursion for Huffman Image.
+f3bab8eb Update vwebp
+6d5c797c Remove support for partial files in Mux.
+f1df5587 WebPMuxAssemble() returns WebPData*.
+814a0639 Rename 'Add' APIs to 'Set'.
+bbb0218f Update Mux psuedo-code examples.
+4fc4a47f Use WebPData in MUX set APIs
+c67bc979 Merge "add WebPPictureImportRGBX() and WebPPictureImportBGRX()"
+27519bc2 add WebPPictureImportRGBX() and WebPPictureImportBGRX()
+f80cd27e factorize code in Import()
+9b715026 histogram: add log2 wrapper
+8c34378f Merge "fix some implicit type conversion warnings"
+42f6df9d fix some implicit type conversion warnings
+250c16e3 Merge "doc: update lossless pdf"
+9d9daba4 Merge "add a PDF of the lossless spec"
+8fbb9188 prefer webp/types.h over stdint.h
+0ca170c2 doc: update lossless pdf
+0862ac6e add a PDF of the lossless spec
+437999fb introduce a generic WebPPictureHasTransparency() function
+d2b6c6c0 cosmetic fixes after Idaba281a
+b4e6645c Merge "add colorspace for premultiplied alpha"
+48f82757 add colorspace for premultiplied alpha
+069f903a Change in lossless bit-stream.
+5f7bb3f5 Merge "WebPReportProgress: use non-encoder specific params"
+f18281ff WebPReportProgress: use non-encoder specific params
+9ef32283 Add support for raw lossless bitstream in decoder.
+7cbee29a Fix bug: InitIo reseting fancy_upsampling flag.
+880fd98c vwebp: fix exit w/freeglut
+1875d926 trap two unchecked error conditions
+87b4a908 no need to have mux.h as noinst clause in enc/
+88f41ec6 doc: fix bit alignment in VP8X chunk
+52f5a4ef Merge "fix bug with lossy-alpha output stride"
+3bde22d7 fix bug with lossy-alpha output stride
+42d61b6d update the spec for the lossy-alpha compression methods.
+e75dc805 Move some more defines to format_constants.h
+c13f6632 Move consts to internal header format_constants.h
+7f2dfc92 use a bit-set transforms_seen_ instead of looping
+18da1f53 modulate alpha-compression effort according to config.method
+f5f2fff6 Merge "Alpha flag fix for lossless."
+c975c44e Alpha flag fix for lossless.
+4f067fb2 Merge "Android: only build dec_neon with NEON support"
+255c66b4 Android: only build dec_neon with NEON support
+8f9117a9 cosmetics: signature fixes
+39bf5d64 use header-less lossless bitstream for alpha channel
+75d7f3b2 Merge "make input data be 'const' for VP8LInverseTransform()"
+9a721c6d make input data be 'const' for VP8LInverseTransform()
+9fc64edc Disallow re-use of same transformation.
+98ec717f  use a function pointer for ProcessRows()
+f7ae5e37 cosmetics: join line
+140b89a3 factor out buffer alloc in AllocateARGBBuffers()
+a107dfa8 Rectify WebPParseOptionalChunks().
+237eab67 Add two more color-spaces for lossless decoding.
+27f417ab fix orthographic typo
+489ec335 add VP8LEncodeStream() to compress lossless image stream
+fa8bc3db make WebPEncodingSetError() take a const picture
+638528cd bitstream update for lossy alpha compression
+d73e63a7 add DequantizeLevels() placeholder
+ec122e09 remove arch-dependent rand()
+d40e7653 fix alignment
+1dd6a8b6 Merge "remove tcoder, switch alpha-plane compression to lossless"
+3e863dda remove tcoder, switch alpha-plane compression to lossless
+8d77dc29 Add support for lossless in mux:
+831bd131 Make tile size a function of encoding method.
+778c5228 Merge "remove some variable shadowing"
+817c9dce Few more HuffmanTreeToken conversions.
+37a77a6b remove some variable shadowing
+89c07c96 Merge "normalize example header includes"
+4aff411f Merge "add example_util.[hc]"
+00b29e28 normalize example header includes
+061263a7 add example_util.[hc]
+c6882c49 merge all tree processing into a single VP8LProcessTree()
+9c7a3cf5 fix VP8LHistogramNumCodes to handle the case palette_code_bits == 0
+b5551d2e Merge "Added HuffmanTreeCode Struct for tree codes."
+8b85d01c Added HuffmanTreeCode Struct for tree codes.
+093f76d8 Merge "Allocate single memory in GetHuffBitLengthsAndCodes."
+41d80494 Allocate single memory in GetHuffBitLengthsAndCodes.
+1b04f6d2 Correct size in VP8L header.
+2924a5ae Makefile.vc: split object lists based on directory
+c8f24165 Merge "add assert(tokens)"
+43239947 add assert(tokens)
+9f547450 Catch an error in DecodeImageData().
+ac8e5e42 minor typo and style fix
+9f566d1d clean-up around Huffman-encode
+c579a710 Introduce CHUNK_SIZE_BYTES in muxi.h.
+14757f8a Make sure huffman trees always have valid symbols
+41050618 makefile.unix: add support for building vwebp
+48b37721 Merge "fixed signed/unsigned comparison warning"
+57f696da Merge "EncodeImageInternal: fix potential leak"
+d972cdf2 EncodeImageInternal: fix potential leak
+5cd12c3d fixed signed/unsigned comparison warning
+cdca30d0 Merge "cosmetics: shorten long line"
+e025fb55 cosmetics: shorten long line
+22671ed6 Merge "enc/vp8l: fix double free on error"
+e1b9b052 Merge "cosmetics: VP8LCreateHuffmanTree: fix indent"
+a8e725f8 enc/vp8l: fix double free on error
+27541fbd cosmetics: VP8LCreateHuffmanTree: fix indent
+1d38b258 cwebp/windows: use MAKE_REFGUID where appropriate
+817ef6e9 Merge "cwebp: fix WIC/Microsoft SDK compatibility issue"
+902d3e3b cwebp: fix WIC/Microsoft SDK compatibility issue
+89d803c4 Merge "Fix a crash due to wrong pointer-integer arithmetic."
+cb1bd741 Merge "Fix a crash in lossless decoder."
+de2fe202 Merge "Some cleanup in VP8LCreateHuffmanTree() (and related functions CompareHuffmanTrees() and SetBitDepths()): - Move 'tree_size' initialization and malloc for 'tree + tree_pool'   outside the loop. - Some renames/tweaks for readability."
+ce69177a Fix a crash due to wrong pointer-integer arithmetic.
+e40a3684 Fix a crash in lossless decoder.
+3927ff3a remove unneeded error condition for WebPMuxNumNamedElements()
+2c140e11 Some cleanup in VP8LCreateHuffmanTree() (and related functions CompareHuffmanTrees() and SetBitDepths()): - Move 'tree_size' initialization and malloc for 'tree + tree_pool'   outside the loop. - Some renames/tweaks for readability.
+861a5b7b add support for animation
+eb5c16cc Merge "Set correct encode size in encoder's stats."
+4abe04a2 fix the return value and handle missing input file case.
+2fafb855 Set correct encode size in encoder's stats.
+e7167a2b Provide one entry point for backward references.
+c4ccab64 Print relevant lossless encoding stats in cwebp.
+e3302cfd GetHuffBitLengthsAndCodes: reduce level of indirection
+b5f2a9ed enc/vp8l: fix uninitialized variable warning
+7885f8b2 makefile.unix: add lossless encoder files
+1261a4c8 Merge "cosmetics"
+3926b5be Merge "dsp/cpu.c: Android: fix crash on non-neon arm builds"
+834f937f dsp/cpu.c: Android: fix crash on non-neon arm builds
+126e1606 cosmetics
+e38602d2 Merge branch 'lossless_encoder'
+e8d3d6a0 split StoreHuffmanCode() into smaller functions
+d0d88990 more consolidation: introduce VP8LHistogramSet
+1a210ef1 big code clean-up and refactoring and optimization
+41b5c8ff Some cosmetics in histogram.c
+ada6ff77 Approximate FastLog between value range [256, 8192]
+ec123ca3 Forgot to update out_bit_costs to symbol_bit_costs at one instance.
+cf33ccd1 Evaluate output cluster's bit_costs once in HistogramRefine.
+781c01f4 Simple Huffman code changes.
+a2849bc5 Lossless decoder: remove an unneeded param in ReadHuffmanCodeLengths().
+b39e7487 Reducing emerging palette size from 11 to 9 bits.
+bfc73db4 Move GetHistImageSymbols to histogram.c
+889a5786 Improve predict vs no-predict heuristic.
+01f50663 code-moving and clean-up
+31035f3b reduce memory usage by allocating only one histo
+fbb501b8 Restrict histo_bits to ensure histo_image size is under 32MB
+8415ddf3 further simplification for the meta-Huffman coding
+e4917299 A quick pass of cleanup in backward reference code
+83332b3c Make transform bits a function of encode method (-m).
+72920caa introduce -lossless option, protected by USE_LOSSLESS_ENCODER
+c6ac4dfb Run TraceBackwards for higher qualities.
+412222c8 Make histo_bits and transform_bits function of quality.
+149b5098 Update lossless encoder strategy:
+0e6fa065 cache_bits passed to EncodeImageInternal()
+e38b40a9 Factorize code for clearing HtreeGroup.
+6f4a16ea Removing the indirection of meta-huffman tables.
+3d33ecd1 Some renaming/comments related to palette in lossless encoder.
+4d02d586 Lossless encoder: correction in Palette storage
+4a636235 fix a memleak in EncodeImageInternal()
+0993a611 Full and final fix for prediction transform
+afd2102f Fix cross-color transform in lossless encoder
+b96d8740 Need to write a '0' bit at the end of transforms.
+54dad7e5 Color cache size should be counted as 0 when cache bits = 0
+4f0c5caf Fix prediction transform in lossless encoder.
+36dabdad Fix memory leak in method EncodeImageInternal for histogram_image.
+352a4f49 Get rid of PackLiteralBitLengths()
+d673b6b9 Change the predictor function to pass left pixel
+b2f99465 Fix CopyTileWithPrediction()
+84547f54 Add EncodeImageInternal() method.
+6b38378a Guard the lossless encoder (in flux) under a flag
+09f7532c Fix few nits (const qualifiers)
+648be393 Added implementation for various lossless functions
+32714ce3 Add VP8L prefix to backward ref & histogram methods.
+fcba7be2 Fixed header file tag (WEBP_UTILS_HUFFMAN_ENCODE_H_)
+bc703746 Add backward_ref, histogram & huffman encode modules from lossless.
+fdccaadd Fixing nits
+227110c4 libwebp interface changes for lossless encoding.
+50679acf minor style fixes
+b38dfccf remove unneeded reference to NUM_LITERAL_CODES
+8979675b harmonize header description
+c04eb7be tcoder.c: define NOT_HAVE_LOG2 for MSVC builds
+9a214fa1 Merge "VP8[L]GetInfo: check input pointers"
+5c5be8ba VP8[L]GetInfo: check input pointers
+0c188fec Merge changes I431acdfe,I713659b7
+b3515c62 mux: drop 'chunk' from ChunkInfo member names
+aea7923c muxi.h: remove some unused defines
+01422492 update NEWS file for next release
+29e3f7ec Merge "dec: remove deprecated WebPINew()"
+4718e449 Merge "muxedit: a few more size_t changes"
+82654f96 Merge "muxedit: remove a few redundant NULL checks"
+02f27fbd dec: remove deprecated WebPINew()
+ccddb3fc muxedit: remove a few redundant NULL checks
+a6cdf710 muxedit: a few more size_t changes
+a3846892 Merge "mux: remove unused LIST_ID"
+11ae46ae alpha.c: quiet some size_t -> int conversion warnings
+dee46692 mux: remove unused LIST_ID
+03f1f493 mux: add version checked entry points
+6a0abdaa Merge "doc: tile/alpha corrections"
+c8139fbe Merge "few cosmetics"
+68338737 Merge "lossless: remove some size_t -> int conversions"
+5249e94a doc: tile/alpha corrections
+d96e722b huffman: quiet int64 -> int conversion warning
+532020f2 lossless: remove some size_t -> int conversions
+23be6edf few cosmetics
+1349edad Merge "configure: AC_ARG_* use AS_HELP_STRING"
+bfbcc60a configure: AC_ARG_* use AS_HELP_STRING
+1427ca8e Merge "Makefile.am: header file maintenance"
+087332e3 Merge "remove unused parameter 'round' from CalcProba()"
+9630e168 remove unused parameter 'round' from CalcProba()
+92092eaa Merge "bit_reader.h: correct include"
+a87fc3f6 Merge "mux: ensure # images = # tiles"
+53af99b1 Merge "mux: use size_t consistently"
+39a57dae Makefile.am: header file maintenance
+1bd0bd0d bit_reader.h: correct include
+326a3c6b mux: ensure # images = # tiles
+95667b8d mux: use size_t consistently
+231ec1fb Removing the indirection of meta-huffman tables.
+15ebcbaa check return pointer from MuxImageGetListFromId
+b0d6c4a7 Merge "configure: remove test for zlib.h"
+8cccac50 Merge "dsp/lossless: silence some build warnings"
+b08819a6 dsp/lossless: silence some build warnings
+7ae22521 Android.mk: SSE2 & NEON updates
+0a49e3f3 Merge "makefile.unix add missing header files"
+2e75a9a1 Merge "decode.h: use size_t consistently"
+fa13035e configure: remove test for zlib.h
+d3adc81d makefile.unix add missing header files
+262fe01b Merge "makefile.unix & Android.mk: cosmetics"
+4cce137e Merge "enc_sse2 add missing stdlib.h include"
+80256b85 enc_sse2 add missing stdlib.h include
+9b3d1f3a decode.h: use size_t consistently
+64083d3c Merge "Makefile.am: cosmetics"
+dceb8b4d Merge changes If1331d3c,I86fe3847
+0e33d7bf Merge "webp/decode.h: fix prototypes"
+fac0f12e rename BitReader to VP8LBitReader
+fbd82b5a types.h: centralize use of stddef.h
+2154835f Makefile.am: cosmetics
+1c92bd37 vp8io: use size_t for buffer size
+90ead710 fix some more uint32_t -> size_t typing
+cbe705c7 webp/decode.h: fix prototypes
+3f8ec1c2 makefile.unix & Android.mk: cosmetics
+217ec7f4 Remove tabs in configure.ac
+b3d35fc1 Merge "Android.mk & Makefile.vc: add new files"
+0df04b9e Android.mk & Makefile.vc: add new files
+e4f20c5b Merge "automake: replace 'silent-rules' w/AM_SILENT_RULES"
+8d254a09 cosmetics
+6860c2ea fix some uint32_t -> size_t typing
+4af1858a Fix a crash due to max symbol in a tree >= alphabet size
+6f01b830 split the VP8 and VP8L decoding properly
+f2623dbe enable lossless decoder
+b96efd7d add dec/vp8i.h changes from experimental
+19f6398e add dec/vp8l{i.h,.c} from experimental
+c4ae53c8 add utils/bit_reader.[hc] changes from experimental
+514d0089 add dsp/lossless.[hc] from experimental
+9c67291d add utils/huffman.[hc] from experimental
+337914a0 add utils/color_cache.[hc] from experimental
+b3bf8fe7 the read-overflow code-path wasn't reporting as an error
+1db888ba take colorspace into account when cropping
+61c2d51f move the rescaling code into its own file and make enc/ and dec/ use it.
+efc2016a Make rescaler methods generic
+3eacee81 Move rescaler methods out of io.c.
+a69b893d automake: replace 'silent-rules' w/AM_SILENT_RULES
+6f7bf645 issue 111: fix little-endian problem in bit-reader
+ed278e22 Removed unnecessary lookup
+cd8c3ba7 fix some warnings: down-cast and possibly-uninitialized variable
+0a7102ba ~1% improvement of alpha compression
+3bc1b141 Merge "Reformat container doc"
+dc17abdc mux: cosmetics
+cb5810df Merge "WebPMuxGetImage: allow image param to be NULL"
+506a4af2 mux: cosmetics
+135e8b19 WebPMuxGetImage: allow image param to be NULL
+de556b68 Merge "README.mux: reword some descriptions"
+0ee2aeb9 Makefile.vc: use batch mode rules
+d9acddc0 msvc: move {i,p}db creation to object directory
+237c9aa7 Merge "expose WebPFree function for DLL builds"
+b3e4054f silence msvc debug build warning
+45feb55d expose WebPFree function for DLL builds
+11316d84 README.mux: reword some descriptions
+4be52f4a factorize WebPMuxValidate
+14f6b9f6 mux: light cleanup
+5e96a5db add more param checks to WebPPictureDistortion()
+8abaf820 Merge "silence some type size related warnings"
+1601a39b silence some type size related warnings
+f3abe520 Merge "idec: simplify buffer size calculation"
+a9c5cd4c idec: simplify buffer size calculation
+7b06bd7f Merge "configure/automake: add silent-rules option"
+e9a7d145 Reformat container doc
+d4e5c7f3 configure/automake: add silent-rules option
+5081db78 configure/automake: no -version-info for convenience libs
+85b6ff68 Merge "idec: fix WebPIUpdate failure"
+7bb6a9cc idec: fix internal state corruption
+89cd1bb8 idec: fix WebPIUpdate failure
+01b63806 4-5% faster decoding, optimized byte loads in arithmetic decoder.
+631117ea Merge "cosmetics & warnings"
+a0b2736d cosmetics & warnings
+f73947f4 use 32bit for storing dequant coeffs, instead of 16b.
+b9600308 Merge "store prediction mode array as uint8_t[16], not int[16]."
+7b67881a store prediction mode array as uint8_t[16], not int[16].
+cab8d4dc Merge "NEON TransformOne"
+ba503fda NEON TransformOne
+9f740e3b Merge "gcc warning fix: remove the 'const' qualifier."
+f76d3587 gcc warning fix: remove the 'const' qualifier.
+e78478d6 Merge "webpmux: make more use of WebPData"
+f85bba3d Merge "manpages: add BUGS section"
+48a43bbf Merge "makefile.unix: variable cosmetics"
+c274dc96 makefile.unix: variable cosmetics
+1f7b8595 re-organize the error-handling in the main loop a bit
+1336fa71 Only recompute level_cost_[] when needed
+771ee449 manpages: add BUGS section
+0f7820e6 webpmux: make more use of WebPData
+974aaff3 examples: logging updates
+6c14aadd Merge "better token buffer code"
+f4054250 better token buffer code
+18d959fa Merge "mux: add WebPData type"
+eec4b877 mux: add WebPData type
+0de3096b use 16bit counters for recording proba counts
+7f23678d fix for LevelCost + little speed-up
+7107d544 further speed-up/cleanup of RecordCoeffs() and GetResidualCost()
+fd221040 Introduce Token buffer (unused for now)
+5fa148f4 Merge "speed-up GetResidualCost()"
+28a9d9b4 speed-up GetResidualCost()
+11e7dadd Merge "misc cosmetics"
+378086bd misc cosmetics
+d61479f9 add -print_psnr and -print_ssim options to cwebp.
+2e3e8b2e add a WebPCleanupTransparentArea() method
+552c1217 Merge "mux: plug some memory leaks on error"
+a2a81f7d Merge "fix Mach-O shared library build"
+b3482c43 Merge "fix gcc-4.0 apple 32-bit build"
+e4e3ec19 fix gcc-4.0 apple 32-bit build
+b0d2fecf mux: plug some memory leaks on error
+f0d2c7a7 pass of cosmetics
+b309a6f9 fix Mach-O shared library build
+241ddd38 doc: delete mux container pdf
+8b1ba272 doc: update VP8 decode guide link
+7e4371c5 WebPMuxCreate: fix unchecked malloc
+eb425586 Merge "have makefile.unix clean up src/webp/*~ too"
+a85c3631 Merge "correct EncodeAlpha documentation"
+a33842fd Merge "Update webp container spec with alpha filter options."
+8d6490da Incremental support for some of the mux APIs.
+b8375abd have makefile.unix clean up src/webp/*~ too
+b5855fc7 correct EncodeAlpha documentation
+dba37fea Update webp container spec with alpha filter options.
+2e74ec8b fix compile under MINGW
+716d1d7f fix suboptimal MAX_LEN cut-off limit
+57cab7b8 Harmonize the alpha-filter predictions at boundary
+3a989534 Merge "Fix bug for Alpha in RGBA_4444 color-mode."
+8ca2076d Introduce a 'fast' alpha mode
+221a06bb Fix bug for Alpha in RGBA_4444 color-mode.
+ad1e163a cosmetics: normalize copyright headers
+c77424d7 cosmetics: light include cleanup
+9d0e17c9 fix msvc build breakage after 252028a
+7c4c177c Some readability fixes for mux library
+d8a47e66 Merge "Add predictive filtering option for Alpha."
+252028aa Add predictive filtering option for Alpha.
+9b69be1c Merge "Simplify mux library code"
+a056170e Simplify mux library code
+992187a3 improve log2 test
+e852f832 update Android.mk file list
+a90cb2be reduce number of copies and mallocs in alpha plane enc/dec
+b1662b05 fix some more type conversion warnings w/MSVC
+223d8c60 fix some uint64_t -> int conversion warnings with MSC
+c1a0437b Merge "simplify checks for enabling SSE2 code"
+f06817aa simplify checks for enabling SSE2 code
+948d4fe9 silence a msvc build warning
+91179549 vwebp: msvc build tweaks
+7937b409 simple WebP viewer, based on OpenGL
+6aac1df1 add a bunch of missing 'extern "C"'
+421eb99d Merge "Remove assigned-but-not-used variable "br""
+91e27f45 better fitting names for upsampling functions
+a5d7ed5c Remove assigned-but-not-used variable "br"
+f62d2c94 remove unused 'has_alpha' from VP8GetInfo() signature
+08e86582 trap alpha-decoding error
+b361eca1 add cut-off to arith coder probability update.
+8666a93a Some bug-fixes for images with alpha.
+273a12a0 fix off-by-1 diff in case cropping and simple filtering
+2f741d1e webpmux: ReadImage: fix ptr free in error case
+721f3f48 fix alpha decode
+60942c8c fix the has_alpha_ order
+30971c9e Implement progress report (and user abort)
+eda520a9 cosmetics after 9523f2a
+38bd5bb5 Merge "Better alpha support in webpmux binary"
+ccbaebfe Merge "Updated the includes to relative paths."
+d71fbdcc fix small typo in error message array
+cdf97aa2 Better alpha support in webpmux binary
+885f25bc Updated the includes to relative paths.
+a0ec9aac Update WebP encoder (cwebp) to support Alpha.
+667b769a Fixed the include for types.h within mux.h
+9523f2a5 Add Alpha Encode support from WebPEncode.
+16612ddd Merge "Add Alpha Decode support from WebPDecode."
+d117a940 Add Alpha Decode support from WebPDecode.
+67228734 cosmetics after e1947a9
+e1947a92 Add Alpha encode/decode code.
+afc4c5d6 simplify code by introducing a CopyPlane() helper func
+113b3128 Merge "MUX API Updates"
+c398f595 MUX API Updates
+5acf04ef remove orphan source file
+059f03ef Merge "dec: validate colorspace before using as array index"
+70a03989 Merge "factorize some code"
+9b243b3d factorize some code
+372e2b46 Correct a bug in ReadPNG() with GRAY_ALPHA images
+469d6eb9 Merge "Makefile.am: remove redundant noinst_HEADERS"
+9fe3372f dec: validate colorspace before using as array index
+8962030f remove orphan source file
+ced3e3f4 Makefile.am: remove redundant noinst_HEADERS
+964387ed use WEBP_INLINE for inline function declarations
+90880a11 Merge "manpages: break long lines"
+b5910895 Merge "manpages: minor formatting updates"
+4c451e4a Merge "Rectify the Chunk parsing logic."
+04e84cf1 examples: slight cleanup
+099717ce manpages: break long lines
+1daf39bb manpages: minor formatting updates
+abd030b5 fix missing "(void)" in function signature
+f6a7d758 remove useless test
+f07b2138 Rectify the Chunk parsing logic.
+b8634f7d webpmux: fix lib link order
+42c2e682 Fix missing coma (on uncompiled code)
+d8329d41 Android.mk: add missing source files
+13a54df5 Merge "More aggressive copy-edit; add TODO; validate HTML5"
+868b96ae More aggressive copy-edit; add TODO; validate HTML5
+767afea2 configure: check for a symbol contained in libpng
+408b8918 Merge "Linewrap at 72 cols. Casual copy-edit."
+3ae318c7 Merge "Restore (most) emphasis; add emphasis to normative RFC 2119 terms (MUST, etc.)"
+918eb2d8 Merge "Basic container doc source clean-up; fix lists and pseudocode blocks."
+03bec9e0 Linewrap at 72 cols. Casual copy-edit.
+2678d819 Restore (most) emphasis; add emphasis to normative RFC 2119 terms (MUST, etc.)
+428674da Basic container doc source clean-up; fix lists and pseudocode blocks.
+6a77d928 Merge "Makefile.vc: cosmetics"
+28c38e8c Merge "Makefile.vc: condense directory creation rules"
+55be2cf8 Initial import of container spec document, from pdftotext transform.
+a82a788b Makefile.vc: cosmetics
+c8f41ce5 Makefile.vc: condense directory creation rules
+2b877cd0 Some fixes to Makefile.vc to support the src\mux directory.
+3eb969b3 Merge "Add Makefile.vc for Mux library & binary."
+e78e971e Add Makefile.vc for Mux library & binary.
+6aedde58 Add manual for WebPMux tool.
+8a360d0a Merge "Added WebPMux Binary."
+a4f32cae Added WebPMux Binary.
+f3bf4c76 Added Mux Container Spec & README for MUX-API.
+9f761cfa Changed function signature for WebPMuxCreate
+5f31b5ec Merge "Add Mux library for manipulating WebP container."
+2315785f Add Mux library for manipulating WebP container.
+7e198abb update ChangeLog (tag: v0.1.3)
+dfc9c1ea Harmonize the dates
+28ad70c5 Fix PNG decoding bug
+846e93c5 Update AUTHORS & add .mailmap
+563e52d6 cosmetics after '76036f5 Refactor decoder library'
+76036f54 Refactor decoder library
+377ef43c configure.ac: update AC_INIT params
+7a8d8762 use a user-visible MACRO for max width/height.
+d4e9f559 NEON decode support in WebP
+0ee683b5 update libtool version-info
+fdbe02c5 windows: match _cond_destroy logic w/return variable name
+206b686b README: correct advanced decode api pseudo-code
+6a32a0f5 make VP8BitReader a typedef, for better re-use
+b112e836 create a libwebputils under src/utils
+ee697d9f harmonize the include guards and #endif comments
+a1ec07a6 Fixing compiler error in non x86 arch.
+dcfa509a Fixed recursive inclusion of bit_writer.h and vp8enci.h.
+e06ac088 create a separate libwebpdsp under src/dsp
+ebeb412a use unsigned int for bitfields
+341cc56a make kNewRange a static array
+227a91e5 README: minor wording update
+05bd8e6a add man pages to dist
+812dfa1a bump up versions in preparations for 0.1.3
+a5b78c81 wrap alpha-related options under WEBP_EXPERIMENTAL_FEATURES flag
+34dc7907 regen ChangeLog for 0.1.3-rc2
+7c436630 Silence some (more) Visual Studio warnings.
+60306e8c add top-level gitattributes
+2aa6b80e Slience some Visual Studio warnings.
+4cbbb290 Merge "bump up version for next freeze"
+a3291674 bump up version for next freeze
+c7e86aba cosmetics: fix comment line lengths
+c9e037ab makefile.unix: add simple dist target
+87d58ce9 makefile.unix: rule maintenance
+d477de77 mend
+fac15ec7 Update NEWS & README for next release V0.1.3
+6215595c Merge "add a -partition_limit option to limit the number of bits used by intra4x4"
+3814b76c Merge "reorganize chunk-parsing code"
+900286e0 add a -partition_limit option to limit the number of bits used by intra4x4
+cd12b4b0 add the missing cost for I4/I16 mode selection
+dfcc2136 reorganize chunk-parsing code
+3cf20306 initialize pointers to function within VP8DspInit()
+d21b4795 Merge "windows: add decode threading support"
+473ae953 fix hang on thread creation failure
+fccca420 windows: add decode threading support
+a31f843a Use the exact PNG_INCLUDES/PNG_LIBS when testing for -lpng
+ad9b45f1 Merge "Makefile.vc: rule maintenance"
+565a2cab Makefile.vc: rule maintenance
+2d0da681 makefile.unix: disable Wvla by default
+fc7815d6 multi-thread decoding: ~25-30% faster
+acd8ba42 io->teardown() was not always called upon error
+c85527b1 Merge "Makefile.vc: add DLL configs"
+e1e9be35 cosmetics: spelling/grammar in README and lib headers
+b4d0ef8f Makefile.vc: add DLL configs
+998754a7 remove unused nb_i4_ and nb_i16_ fields.
+9f01ce3a rename WebPDecBuffer::memory -> private_memory
+fb5d659b fix an overflow bug in LUT calculation
+d646d5c7 swig: add WebPDecodeARGB
+78aeed40 add missing WebPDecodeARGBInto() and switch ARGB4444 to RGBA4444 as was intended
+cd7c5292 explicitly mark library functions as extern
+19db59f8 add support for RGB565, ARGB4444 and ARGB colorspace (decoder)
+c915fb2a encoder speed-up: hardcode special level values
+c558bdad Rename and improve the API to retrieve decoded area
+bf599d74 Merge "makefile.unix: disable -Wvla by default"
+c9ea03d7 SSE2 version of strong filtering
+993af3e2 makefile.unix: disable -Wvla by default
+3827e1bc Merge "examples: (windows/WIC) add alpha support"
+e291fae0 SSE2 functions for the fancy upsampler.
+a06bbe2e add WebPISetIOHooks() to set some custom hooks on the incremental decoder object.
+7643a6f2 Merge "makefile.unix: use uname to detect OSX environment"
+5142a0be export alpha channel (if present) when dumping to PGM format
+14d5731c makefile.unix: use uname to detect OSX environment
+08057062 examples: quiet warnings
+3cfe0888 examples: (windows/WIC) add alpha support
+13ed94b8 add compile warning for variable-length-array
+5a18eb1a Merge "add Advanced Decoding Interface"
+5c4f27f9 add missing \n
+f4c4e416 80 cols fix
+d2603105 add Advanced Decoding Interface
+bd2f65f6 sse2 version of the complex filter
+96ed9ce0 perform two idct transforms at a time when possible
+01af7b69 use aligned stored
+0e1d1fdf Merge "Makefile.vc: add experimental target"
+2a1292a6 Makefile.vc: add experimental target
+23bf351e Enable decode SSE2 for Visual Studio
+131a4b7b dec/dsp_sse2: fix visual studio compile
+00d9d680 swig: file reorganization
+7fc7e0d9 Merge "swig/java: basic encode support"
+3be57b16 fix MSVC compile for WEBP_EXPERIMENTAL_FEATURES
+40a7e347 dec/dsp: disable sse2 for Visual Studio builds
+e4d540c8 add SSE2 code for transform
+54f2170a swig/java: basic encode support
+c5d4584b call function pointers instead of C-version
+ea43f045 Merge "configure: mingw32 targets: test for WIC support"
+a11009d7 SSE2 version of simple in-loop filtering
+42548da9 shave one unneeded filter-cache line
+31f9dc6f configure: mingw32 targets: test for WIC support
+19559699 Merge "split expression in two."
+415dbe46 split expression in two.
+e29072a8 configure: test for zlib only w/--enable-experimental
+b2b0090b Simplify Visual Studio ifdefs
+ca7a2fd6 Add error reporting from encoding failures.
+6c9405db Merge "Makefile.vc: require CFG with clean target"
+0424ecd9 Makefile.vc: require CFG with clean target
+003417c7 Enable SSE2 for Visual Studio builds
+af10db4a little speed up for VP8BitUpdate()
+e71418f8 more MSVC files to ignore
+46d90363 cosmetics
+edf59ab3 typo fix
+72229f5f Add support for x64 and SSE2 builds under Windows.
+92e5c6e1 VP8GetInfo() + WebPResetDecParams()
+416b7a6b raise the fixed-point precision for the rescaler
+aa87e4e0 fix alignment
+eb66670c disable WEBP_EXPERIMENTAL_FEATURES
+c5ae7f65 typo fix: USE_ => WEBP_
+d041efae swig: add libwebp.jar/libwebp_java_wrap.c
+f6fb3877 add swig interface
+e9273902 align buffer for double too
+842c009b fix -strong option
+d0a70387 Merge "cosmetics"
+fc0a02e5 fix the dichotomy loop
+38369c03 cosmetics
+8dfc4c6f factorize and unify GetAlpha() between the C and SSE2 version
+6d0e66c2 prepare experimentation with yuv444 / 422
+79cc49f5 add a --enable-experimental option to './configure'
+d7575238 sse2 version of CollectHistogram()
+c1c728d6 add an extra #ifdef WEBP_EXPERIMENTAL_FEATURES to avoid 'unused variable' warning
+60c61d2d always call VP*EncDeleteAlpha() unconditionnally, for simplicity
+0f8c6384 simply don't call WriteExtensions() if WEBP_EXPERIMENTAL_FEATURES is not defined
+47c661d5 rename swap -> swap_rb
+10d55bbb move chunk[] declaration out of the for() loop
+517cec21 fix indentation
+f7d9e261 fix merge problems
+8fd42b3a add a stride 'a_stride' for the alpha plane
+b8dcbf2f fix alpha-plane copy and crop methods
+cdef89de fix some 'unused variable' warning
+fb29c262 SSE2 version of the fwd transform and the squared sum metric
+2ab4b72f EXPERIMENTAL: add support for alpha channel
+cfbf88a6 add SSE2 functions. ~2x faster encoding on average.
+e7ff3f9a merge two ITransforms together when applicable and change the TTransform to return the sum directly.
+ca554137 fix WebPIDecGetRGB() to accept any RGB(A) mode, not just MODE_RGB
+8aa50efd fix some 'man' typos
+d3f3bdda update ChangeLog (tag: v0.1.2)
+d7e9a69c update contributor list
+261abb8e add a 'superclean' section
+276ae825 Remove files not mean to be in git, and update .gitignore
+24868455 build: prepare libwebp.pc
+14ceb6e8 add "-version" description to man pages
+b247a3b2 Create the m4 directory, and also place .gitignore in there for libtool.
+cdd734c9 Resolve automake warnings
+c5fa726e build: add pkgconfig files
+b20aaca2 build: just use autoreconf, avoid calling tools manually
+4b0b0d66 cwebp: use modern functions
+efbc6c41 update Android.mk
+7777570b better version of ChangeLog
+fa70d2b7 update version number in the DOC
+f8db5d5d more C89-fixes
+0de013b3 fix typos
+650ffa3b add version getters for decoder and encoder
+be4867d2 doc for incremental decoding
+56732a1b add idec.obj in MSVC makefile
+208afb5e add c++ guards
+8bf76fe0 add incremental decoding
+1f288328 'inline' isn't defined in strict ansi c89
+8b77c632 move the quantization function to dsp.c
+b2c3575c add a 'last_y' field to WebPDecParams
+2654c3da correctly pass along the exact same status returned from ParsePartitions
+4704146a add missing precision in the man
+6d978a6c add error messages
+6463e6ab add some install instructions, and fix intel-mac flags
+05fb7bfc Merge ".gitignore: initial version"
+c33f0195 .gitignore: initial version
+e532b9ab Makefile: allow out of tree builds
+4c0da7aa enable sparse dc/ac transforms
+07dbb8d5 clarify the return logic
+5c69e1bb fix bigger-by-1 array
+7c5267e3 fix a (harmless) typo: non_zero_ -> non_zero_ac_
+bc752135 fix missing free()
+af3e2aaa remove trailing spaces
+13e50da6 make the bitreader preload at least 8bits, instead of post-load them (this makes initialization easier and will be helpful for incremental decoding). Modify ParsePartitions() to accommodate for truncated input.
+f4888f77 emit 9 - nb_bits trailing zeros instead of 8
+3db65255 separate block-parsing into a visible VP8DecodeMB()
+a871de02 add missing extern "C"
+b3ce8c52 remove a gcc warning about type pun by using a proper union'd type
+e1863715 update after addition of webpi.h
+3e856e2d Extract some useful functions around decoding buffer WebPDecParams.
+d5bc05a4 make the filtering process match libvpx and ffvp8
+dd60138d add man pages for cwebp(1) and dwebp(1)
+c4fa3644 fix header
+5b70b378 * add an option to bypass_filtering in VP8Io.
+b97a4003 simplify QuantizeBlock code a bit
+84b58ebb add more checks around picture allocation
+b65a3e10     remove absolute_delta_ field and syntax code
+0744e842 Dont' open output file until we're sure the input file is valid
+d5bd54c7 fix typo and buggy line
+f7a9549d Add a simple top-level makefile.unix for quick & easy build.
+5f36b944 update the doc for the -f option
+f61d14aa a WebP encoder converts PNG & JPEG to WebP
+81c96621 oops: forgotten call to Initialize() + move the error message to a more useful place
+87ffa005 typo: fix a missing 'R', was confusing.
+b04b857a * add decoding measurement using stopwatch.h (use -v option) * support PNG output through WIC on Win32
+746a4820 * make (*put)() hook return a bool for abort request. * add an enum for VP8Status() to make things clearer
+73c973e6 * strengthen riff/chunk size checks * don't consider odd-sized chunks being an error
+1dc4611a add support for PNG output (default) regularize include guards
+860641df fix a typo: sizeof(kYModeProbaInter0) => sizeof(kUVModeProbaInter0)
+3254fc52 fix some petty constness fix the ./configure file too
+504d3393 fix eof_ mis-initialization
+2bc0778f leftover Makefile.* from previous commit
+d2cf04e4 move Makefile.am one level below, to src/dec fix typos here and there dwebp is now an installed program
+ade92de8 typo: vp8.h -> decode_vp8.h
+d7241241 forgot to declare types.h to be installed
+6421a7a4 move the decoder sourcetree to a sub-location src/dec to make room for future libs sources
+a9b3eab6 correct layout name is IMC4.
+2330522c handle corner case of zero-dimensions
+280c3658 make VP8Init() handle short buffers (< 2 bytes) correctly
+b1c9e8b4 handle error cases more robustly
+0e94935c Merge "table-less version of clip_8b()"
+1e0a2d25 table-less version of clip_8b()
+e12109ee dwebp: change -yuv option to -raw change the layout to IMC2
+d72180a4 speed-up fancy upscaler
+9145f3bc reset eof_ at construction time
+a7ee0559 simplify the logic of GetCoeffs()
+f67b5939 lot of cosmetics
+ea27d7c6 fix endian problem on PowerPC
+beb0a1ba fix signature of VP8StoreBlock
+b128c5e2 Merge "fancy chroma upscaling"
+6a37a2aa fancy chroma upscaling
+ff565edc fix two numeric typos
+5a936a0a use uintptr_t for casting pointers to ints
+e14a0301 for cross_compiling=yes to prevent executing any binary
+83b545ee add vc9+ makefile
+296f6914 fix output loop for small height
+cbfbb5c3 convert to plain-C
+f09f96ee Fix declaration after statement warning
+5981ee55 Fix UV plane ac/dc quantizer transposition
+c8d15efa convert to ANSI-C
+c3f41cb4 Initial commit
diff --git a/METADATA b/METADATA
index d97975c..def00a9 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,15 @@
+name: "webp"
+description: "Android fork of the libwebp library."
 third_party {
+  url {
+    type: GIT
+    value: "https://chromium.googlesource.com/webm/libwebp"
+  }
+  version: "v1.3.0"
   license_type: NOTICE
+  last_upgrade_date {
+    year: 2023
+    month: 01
+    day: 13
+  }
 }
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..e1c1dd4
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,9 @@
+ACLOCAL_AMFLAGS = -I m4
+SUBDIRS = sharpyuv src imageio man
+EXTRA_DIST = COPYING autogen.sh
+
+if BUILD_EXTRAS
+  SUBDIRS += extras
+endif
+
+SUBDIRS += examples
diff --git a/Makefile.vc b/Makefile.vc
new file mode 100644
index 0000000..00d899e
--- /dev/null
+++ b/Makefile.vc
@@ -0,0 +1,527 @@
+#
+# Stem for static libs and DLLs
+#
+LIBWEBPDECODER_BASENAME = libwebpdecoder
+LIBWEBP_BASENAME = libwebp
+LIBWEBPMUX_BASENAME = libwebpmux
+LIBWEBPDEMUX_BASENAME = libwebpdemux
+LIBSHARPYUV_BASENAME = libsharpyuv
+
+!IFNDEF ARCH
+!IF ! [ cl 2>&1 | find "x86" > NUL ]
+ARCH = x86
+!ELSE IF ! [ cl 2>&1 | find "x64" > NUL ]
+ARCH = x64
+!ELSE IF ! [ cl 2>&1 | find "ARM" > NUL ]
+ARCH = ARM
+!ELSE
+!ERROR Unable to auto-detect toolchain architecture! \
+If cl.exe is in your PATH rerun nmake with ARCH=<arch>.
+!ENDIF
+!ENDIF
+
+!IF "$(ARCH)" == "x86"
+PLATFORM_LDFLAGS = /SAFESEH
+!ENDIF
+
+#############################################################
+## Nothing more to do below this line!
+
+NOLOGO     = /nologo
+CCNODBG    = cl.exe $(NOLOGO) /O2 /DNDEBUG
+CCDEBUG    = cl.exe $(NOLOGO) /Od /Zi /D_DEBUG /RTC1
+CFLAGS     = /I. /Isrc $(NOLOGO) /W3 /EHsc /c
+CFLAGS     = $(CFLAGS) /DWIN32 /D_CRT_SECURE_NO_WARNINGS /DWIN32_LEAN_AND_MEAN
+LDFLAGS    = /LARGEADDRESSAWARE /MANIFEST:EMBED /NXCOMPAT /DYNAMICBASE
+LDFLAGS    = $(LDFLAGS) $(PLATFORM_LDFLAGS)
+LNKDLL     = link.exe /DLL $(NOLOGO)
+LNKEXE     = link.exe $(NOLOGO)
+LNKLIB     = lib.exe $(NOLOGO)
+RCNODBG    = rc.exe $(NOLOGO) /l"0x0409"  # 0x409 = U.S. English
+RCDEBUG    = $(RCNODBG) /D_DEBUG
+
+!IF "$(ARCH)" == "ARM"
+CFLAGS = $(CFLAGS) /DWINAPI_FAMILY=WINAPI_FAMILY_PHONE_APP /DWEBP_USE_THREAD
+!ELSE
+CFLAGS = $(CFLAGS) /DHAVE_WINCODEC_H /DWEBP_USE_THREAD
+!ENDIF
+
+CFGSET     = FALSE
+!IF "$(OBJDIR)" == ""
+OUTDIR = ..\obj\
+!ELSE
+OUTDIR = $(OBJDIR)
+!ENDIF
+
+##############################################################
+# Runtime library configuration
+!IF "$(RTLIBCFG)" == "static"
+RTLIB  = /MT
+RTLIBD = /MTd
+!ELSE IF "$(RTLIBCFG)" == "legacy"
+RTLIBCFG = static
+RTLIB  = /MT
+RTLIBD = /MTd
+CFLAGS = $(CFLAGS) /GS- /arch:IA32
+!ELSE
+RTLIB   = /MD
+RTLIBD  = /MDd
+!ENDIF
+DIRBASE = $(OUTDIR)\$(CFG)\$(ARCH)
+DIROBJ = $(DIRBASE)\obj
+DIRLIB = $(DIRBASE)\lib
+DIRINC = $(DIRBASE)\include
+DIRBIN = $(DIRBASE)\bin
+LIBWEBP_PDBNAME = $(DIROBJ)\$(LIBWEBP_BASENAME).pdb
+OUTPUT_DIRS = $(DIRBIN) $(DIRINC) $(DIRLIB) \
+              $(DIROBJ)\dec \
+              $(DIROBJ)\demux \
+              $(DIROBJ)\dsp \
+              $(DIROBJ)\enc \
+              $(DIROBJ)\examples \
+              $(DIROBJ)\extras \
+              $(DIROBJ)\imageio \
+              $(DIROBJ)\mux \
+              $(DIROBJ)\sharpyuv \
+              $(DIROBJ)\utils \
+
+# Target configuration
+!IF "$(CFG)" == "release-static"
+CC             = $(CCNODBG)
+STATICLIBBUILD = TRUE
+!ELSE IF "$(CFG)" == "debug-static"
+CC             = $(CCDEBUG)
+RTLIB          = $(RTLIBD)
+STATICLIBBUILD = TRUE
+LIBWEBPDECODER_BASENAME = $(LIBWEBPDECODER_BASENAME)_debug
+LIBWEBP_BASENAME = $(LIBWEBP_BASENAME)_debug
+LIBWEBPMUX_BASENAME = $(LIBWEBPMUX_BASENAME)_debug
+LIBWEBPDEMUX_BASENAME = $(LIBWEBPDEMUX_BASENAME)_debug
+LIBSHARPYUV_BASENAME = $(LIBSHARPYUV_BASENAME)_debug
+!ELSE IF "$(CFG)" == "release-dynamic"
+CC        = $(CCNODBG)
+RC        = $(RCNODBG)
+DLLBUILD  = TRUE
+!ELSE IF "$(CFG)" == "debug-dynamic"
+CC        = $(CCDEBUG)
+RC        = $(RCDEBUG)
+RTLIB     = $(RTLIBD)
+DLLBUILD  = TRUE
+LIBWEBPDECODER_BASENAME = $(LIBWEBPDECODER_BASENAME)_debug
+LIBWEBP_BASENAME = $(LIBWEBP_BASENAME)_debug
+LIBWEBPMUX_BASENAME = $(LIBWEBPMUX_BASENAME)_debug
+LIBWEBPDEMUX_BASENAME = $(LIBWEBPDEMUX_BASENAME)_debug
+LIBSHARPYUV_BASENAME = $(LIBSHARPYUV_BASENAME)_debug
+!ENDIF
+
+!IF "$(STATICLIBBUILD)" == "TRUE"
+CC     = $(CC) $(RTLIB)
+CFGSET = TRUE
+LIBWEBPDECODER = $(DIRLIB)\$(LIBWEBPDECODER_BASENAME).lib
+LIBWEBP = $(DIRLIB)\$(LIBWEBP_BASENAME).lib
+LIBWEBPMUX = $(DIRLIB)\$(LIBWEBPMUX_BASENAME).lib
+LIBWEBPDEMUX = $(DIRLIB)\$(LIBWEBPDEMUX_BASENAME).lib
+LIBSHARPYUV = $(DIRLIB)\$(LIBSHARPYUV_BASENAME).lib
+!ELSE IF "$(DLLBUILD)" == "TRUE"
+CC     = $(CC) /I$(DIROBJ) $(RTLIB) /DWEBP_DLL
+LIBWEBPDECODER = $(DIRLIB)\$(LIBWEBPDECODER_BASENAME)_dll.lib
+LIBWEBP = $(DIRLIB)\$(LIBWEBP_BASENAME)_dll.lib
+LIBWEBPMUX = $(DIRLIB)\$(LIBWEBPMUX_BASENAME)_dll.lib
+LIBWEBPDEMUX = $(DIRLIB)\$(LIBWEBPDEMUX_BASENAME)_dll.lib
+LIBSHARPYUV = $(DIRLIB)\$(LIBSHARPYUV_BASENAME)_dll.lib
+LIBWEBP_PDBNAME = $(DIROBJ)\$(LIBWEBP_BASENAME)_dll.pdb
+CFGSET = TRUE
+!ENDIF
+
+!IF "$(UNICODE)" == "1"
+CFLAGS = $(CFLAGS) /D_UNICODE /DUNICODE
+!ENDIF
+
+#######################
+# Usage
+#
+!IF "$(CFGSET)" == "FALSE"
+!MESSAGE Usage: nmake /f Makefile.vc [CFG=<config>]
+!MESSAGE .          [OBJDIR=<path>] [RTLIBCFG=<rtlib>] [UNICODE=1] [<target>]
+!MESSAGE
+!MESSAGE where <config> is one of:
+!MESSAGE -  release-static                - release static library
+!MESSAGE -  debug-static                  - debug static library
+!MESSAGE -  release-dynamic               - release dynamic link library (DLL)
+!MESSAGE -  debug-dynamic                 - debug dynamic link library (DLL)
+!MESSAGE
+!MESSAGE <target> may be:
+!MESSAGE -  clean                         - perform a clean for CFG
+!MESSAGE -  experimental                  - build CFG with experimental
+!MESSAGE .                                  features enabled.
+!MESSAGE - (empty)                        - build libwebp-based targets for CFG
+!MESSAGE - all                            - build (de)mux-based targets for CFG
+!MESSAGE - gif2webp                       - requires libgif & >= VS2013
+!MESSAGE - anim_diff                      - requires libgif & >= VS2013
+!MESSAGE - anim_dump
+!MESSAGE
+!MESSAGE RTLIBCFG controls the runtime library linkage - 'static' or 'dynamic'.
+!MESSAGE   'legacy' will produce a Windows 2000 compatible library.
+!MESSAGE OBJDIR is the path where you like to build (obj, bins, etc.),
+!MESSAGE   defaults to ..\obj
+
+!IF "$(CFG)" != ""
+!MESSAGE
+!ERROR please choose a valid configuration instead of "$(CFG)"
+!ENDIF
+!ENDIF
+
+#######################
+# Rules
+#
+!IF "$(CFGSET)" == "TRUE"
+# A config was provided, so the library can be built.
+#
+
+SHARPYUV_OBJS = \
+    $(DIROBJ)\sharpyuv\sharpyuv.obj \
+    $(DIROBJ)\sharpyuv\sharpyuv_cpu.obj \
+    $(DIROBJ)\sharpyuv\sharpyuv_csp.obj \
+    $(DIROBJ)\sharpyuv\sharpyuv_dsp.obj \
+    $(DIROBJ)\sharpyuv\sharpyuv_gamma.obj \
+    $(DIROBJ)\sharpyuv\sharpyuv_neon.obj \
+    $(DIROBJ)\sharpyuv\sharpyuv_sse2.obj \
+
+DEC_OBJS = \
+    $(DIROBJ)\dec\alpha_dec.obj \
+    $(DIROBJ)\dec\buffer_dec.obj \
+    $(DIROBJ)\dec\frame_dec.obj \
+    $(DIROBJ)\dec\idec_dec.obj \
+    $(DIROBJ)\dec\io_dec.obj \
+    $(DIROBJ)\dec\quant_dec.obj \
+    $(DIROBJ)\dec\tree_dec.obj \
+    $(DIROBJ)\dec\vp8_dec.obj \
+    $(DIROBJ)\dec\vp8l_dec.obj \
+    $(DIROBJ)\dec\webp_dec.obj \
+
+DEMUX_OBJS = \
+    $(DIROBJ)\demux\anim_decode.obj \
+    $(DIROBJ)\demux\demux.obj \
+
+DSP_DEC_OBJS = \
+    $(DIROBJ)\dsp\alpha_processing.obj \
+    $(DIROBJ)\dsp\alpha_processing_mips_dsp_r2.obj \
+    $(DIROBJ)\dsp\alpha_processing_neon.obj \
+    $(DIROBJ)\dsp\alpha_processing_sse2.obj \
+    $(DIROBJ)\dsp\alpha_processing_sse41.obj \
+    $(DIROBJ)\dsp\cpu.obj \
+    $(DIROBJ)\dsp\dec.obj \
+    $(DIROBJ)\dsp\dec_clip_tables.obj \
+    $(DIROBJ)\dsp\dec_mips32.obj \
+    $(DIROBJ)\dsp\dec_mips_dsp_r2.obj \
+    $(DIROBJ)\dsp\dec_msa.obj \
+    $(DIROBJ)\dsp\dec_neon.obj \
+    $(DIROBJ)\dsp\dec_sse2.obj \
+    $(DIROBJ)\dsp\dec_sse41.obj \
+    $(DIROBJ)\dsp\filters.obj \
+    $(DIROBJ)\dsp\filters_mips_dsp_r2.obj \
+    $(DIROBJ)\dsp\filters_msa.obj \
+    $(DIROBJ)\dsp\filters_neon.obj \
+    $(DIROBJ)\dsp\filters_sse2.obj \
+    $(DIROBJ)\dsp\lossless.obj \
+    $(DIROBJ)\dsp\lossless_mips_dsp_r2.obj \
+    $(DIROBJ)\dsp\lossless_msa.obj \
+    $(DIROBJ)\dsp\lossless_neon.obj \
+    $(DIROBJ)\dsp\lossless_sse2.obj \
+    $(DIROBJ)\dsp\lossless_sse41.obj \
+    $(DIROBJ)\dsp\rescaler.obj \
+    $(DIROBJ)\dsp\rescaler_mips32.obj \
+    $(DIROBJ)\dsp\rescaler_mips_dsp_r2.obj \
+    $(DIROBJ)\dsp\rescaler_msa.obj \
+    $(DIROBJ)\dsp\rescaler_neon.obj \
+    $(DIROBJ)\dsp\rescaler_sse2.obj \
+    $(DIROBJ)\dsp\upsampling.obj \
+    $(DIROBJ)\dsp\upsampling_mips_dsp_r2.obj \
+    $(DIROBJ)\dsp\upsampling_msa.obj \
+    $(DIROBJ)\dsp\upsampling_neon.obj \
+    $(DIROBJ)\dsp\upsampling_sse2.obj \
+    $(DIROBJ)\dsp\upsampling_sse41.obj \
+    $(DIROBJ)\dsp\yuv.obj \
+    $(DIROBJ)\dsp\yuv_mips32.obj \
+    $(DIROBJ)\dsp\yuv_mips_dsp_r2.obj \
+    $(DIROBJ)\dsp\yuv_neon.obj \
+    $(DIROBJ)\dsp\yuv_sse2.obj \
+    $(DIROBJ)\dsp\yuv_sse41.obj \
+
+DSP_ENC_OBJS = \
+    $(DIROBJ)\dsp\cost.obj \
+    $(DIROBJ)\dsp\cost_mips32.obj \
+    $(DIROBJ)\dsp\cost_mips_dsp_r2.obj \
+    $(DIROBJ)\dsp\cost_neon.obj \
+    $(DIROBJ)\dsp\cost_sse2.obj \
+    $(DIROBJ)\dsp\enc.obj \
+    $(DIROBJ)\dsp\enc_mips32.obj \
+    $(DIROBJ)\dsp\enc_mips_dsp_r2.obj \
+    $(DIROBJ)\dsp\enc_msa.obj \
+    $(DIROBJ)\dsp\enc_neon.obj \
+    $(DIROBJ)\dsp\enc_sse2.obj \
+    $(DIROBJ)\dsp\enc_sse41.obj \
+    $(DIROBJ)\dsp\lossless_enc.obj \
+    $(DIROBJ)\dsp\lossless_enc_mips32.obj \
+    $(DIROBJ)\dsp\lossless_enc_mips_dsp_r2.obj \
+    $(DIROBJ)\dsp\lossless_enc_msa.obj \
+    $(DIROBJ)\dsp\lossless_enc_neon.obj \
+    $(DIROBJ)\dsp\lossless_enc_sse2.obj \
+    $(DIROBJ)\dsp\lossless_enc_sse41.obj \
+    $(DIROBJ)\dsp\ssim.obj \
+    $(DIROBJ)\dsp\ssim_sse2.obj \
+
+EX_ANIM_UTIL_OBJS = \
+    $(DIROBJ)\examples\anim_util.obj \
+
+IMAGEIO_DEC_OBJS = \
+    $(DIROBJ)\imageio\image_dec.obj \
+    $(DIROBJ)\imageio\jpegdec.obj \
+    $(DIROBJ)\imageio\metadata.obj \
+    $(DIROBJ)\imageio\pngdec.obj \
+    $(DIROBJ)\imageio\pnmdec.obj \
+    $(DIROBJ)\imageio\tiffdec.obj \
+    $(DIROBJ)\imageio\webpdec.obj \
+    $(DIROBJ)\imageio\wicdec.obj \
+
+IMAGEIO_ENC_OBJS = \
+    $(DIROBJ)\imageio\image_enc.obj \
+
+EX_GIF_DEC_OBJS = \
+    $(DIROBJ)\examples\gifdec.obj \
+
+EX_UTIL_OBJS = \
+    $(DIROBJ)\examples\example_util.obj \
+
+ENC_OBJS = \
+    $(DIROBJ)\enc\alpha_enc.obj \
+    $(DIROBJ)\enc\analysis_enc.obj \
+    $(DIROBJ)\enc\backward_references_cost_enc.obj \
+    $(DIROBJ)\enc\backward_references_enc.obj \
+    $(DIROBJ)\enc\config_enc.obj \
+    $(DIROBJ)\enc\cost_enc.obj \
+    $(DIROBJ)\enc\filter_enc.obj \
+    $(DIROBJ)\enc\frame_enc.obj \
+    $(DIROBJ)\enc\histogram_enc.obj \
+    $(DIROBJ)\enc\iterator_enc.obj \
+    $(DIROBJ)\enc\near_lossless_enc.obj \
+    $(DIROBJ)\enc\picture_enc.obj \
+    $(DIROBJ)\enc\picture_csp_enc.obj \
+    $(DIROBJ)\enc\picture_psnr_enc.obj \
+    $(DIROBJ)\enc\picture_rescale_enc.obj \
+    $(DIROBJ)\enc\picture_tools_enc.obj \
+    $(DIROBJ)\enc\predictor_enc.obj \
+    $(DIROBJ)\enc\quant_enc.obj \
+    $(DIROBJ)\enc\syntax_enc.obj \
+    $(DIROBJ)\enc\token_enc.obj \
+    $(DIROBJ)\enc\tree_enc.obj \
+    $(DIROBJ)\enc\vp8l_enc.obj \
+    $(DIROBJ)\enc\webp_enc.obj \
+
+EXTRAS_OBJS = \
+    $(DIROBJ)\extras\extras.obj \
+    $(DIROBJ)\extras\quality_estimate.obj \
+
+IMAGEIO_UTIL_OBJS = \
+    $(DIROBJ)\imageio\imageio_util.obj \
+
+MUX_OBJS = \
+    $(DIROBJ)\mux\anim_encode.obj \
+    $(DIROBJ)\mux\muxedit.obj \
+    $(DIROBJ)\mux\muxinternal.obj \
+    $(DIROBJ)\mux\muxread.obj \
+
+UTILS_DEC_OBJS = \
+    $(DIROBJ)\utils\bit_reader_utils.obj \
+    $(DIROBJ)\utils\color_cache_utils.obj \
+    $(DIROBJ)\utils\filters_utils.obj \
+    $(DIROBJ)\utils\huffman_utils.obj \
+    $(DIROBJ)\utils\quant_levels_dec_utils.obj \
+    $(DIROBJ)\utils\rescaler_utils.obj \
+    $(DIROBJ)\utils\random_utils.obj \
+    $(DIROBJ)\utils\thread_utils.obj \
+    $(DIROBJ)\utils\utils.obj \
+
+UTILS_ENC_OBJS = \
+    $(DIROBJ)\utils\bit_writer_utils.obj \
+    $(DIROBJ)\utils\huffman_encode_utils.obj \
+    $(DIROBJ)\utils\quant_levels_utils.obj \
+
+LIBWEBPDECODER_OBJS = $(DEC_OBJS) $(DSP_DEC_OBJS) $(UTILS_DEC_OBJS)
+LIBWEBP_OBJS = $(LIBWEBPDECODER_OBJS) $(ENC_OBJS) \
+               $(DSP_ENC_OBJS) $(UTILS_ENC_OBJS) $(DLL_OBJS)
+LIBWEBPMUX_OBJS = $(MUX_OBJS) $(LIBWEBPMUX_OBJS)
+LIBWEBPDEMUX_OBJS = $(DEMUX_OBJS) $(LIBWEBPDEMUX_OBJS)
+LIBSHARPYUV_OBJS = $(SHARPYUV_OBJS)
+
+OUT_LIBS = $(LIBWEBPDECODER) $(LIBWEBP) $(LIBSHARPYUV)
+!IF "$(ARCH)" == "ARM"
+ex: $(OUT_LIBS)
+all: ex
+!ELSE
+OUT_EXAMPLES = $(DIRBIN)\cwebp.exe $(DIRBIN)\dwebp.exe
+EXTRA_EXAMPLES = $(DIRBIN)\vwebp.exe $(DIRBIN)\webpmux.exe \
+                 $(DIRBIN)\img2webp.exe $(DIRBIN)\get_disto.exe \
+                 $(DIRBIN)\webp_quality.exe $(DIRBIN)\vwebp_sdl.exe \
+                 $(DIRBIN)\webpinfo.exe
+
+ex: $(OUT_LIBS) $(OUT_EXAMPLES)
+all: ex $(EXTRA_EXAMPLES)
+# NB: gif2webp.exe and anim_diff.exe are excluded from 'all' as libgif requires
+# C99 support which is only available from VS2013 onward.
+gif2webp: $(DIRBIN)\gif2webp.exe
+anim_diff: $(DIRBIN)\anim_diff.exe
+anim_dump: $(DIRBIN)\anim_dump.exe
+
+$(DIRBIN)\anim_diff.exe: $(DIROBJ)\examples\anim_diff.obj $(EX_ANIM_UTIL_OBJS)
+$(DIRBIN)\anim_diff.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS)
+$(DIRBIN)\anim_diff.exe: $(EX_GIF_DEC_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP)
+$(DIRBIN)\anim_dump.exe: $(DIROBJ)\examples\anim_dump.obj $(EX_ANIM_UTIL_OBJS)
+$(DIRBIN)\anim_dump.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS)
+$(DIRBIN)\anim_dump.exe: $(EX_GIF_DEC_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP)
+$(DIRBIN)\anim_dump.exe: $(IMAGEIO_ENC_OBJS)
+$(DIRBIN)\cwebp.exe: $(DIROBJ)\examples\cwebp.obj $(IMAGEIO_DEC_OBJS)
+$(DIRBIN)\cwebp.exe: $(IMAGEIO_UTIL_OBJS)
+$(DIRBIN)\cwebp.exe: $(LIBWEBPDEMUX) $(LIBSHARPYUV)
+$(DIRBIN)\dwebp.exe: $(DIROBJ)\examples\dwebp.obj $(IMAGEIO_DEC_OBJS)
+$(DIRBIN)\dwebp.exe: $(IMAGEIO_ENC_OBJS)
+$(DIRBIN)\dwebp.exe: $(IMAGEIO_UTIL_OBJS)
+$(DIRBIN)\dwebp.exe: $(LIBWEBPDEMUX)
+$(DIRBIN)\gif2webp.exe: $(DIROBJ)\examples\gif2webp.obj $(EX_GIF_DEC_OBJS)
+$(DIRBIN)\gif2webp.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) $(LIBWEBPMUX)
+$(DIRBIN)\gif2webp.exe: $(LIBWEBP)
+$(DIRBIN)\vwebp.exe: $(DIROBJ)\examples\vwebp.obj $(EX_UTIL_OBJS)
+$(DIRBIN)\vwebp.exe: $(IMAGEIO_UTIL_OBJS) $(LIBWEBPDEMUX) $(LIBWEBP)
+$(DIRBIN)\vwebp_sdl.exe: $(DIROBJ)\extras\vwebp_sdl.obj
+$(DIRBIN)\vwebp_sdl.exe: $(DIROBJ)\extras\webp_to_sdl.obj
+$(DIRBIN)\vwebp_sdl.exe: $(IMAGEIO_UTIL_OBJS) $(LIBWEBP)
+$(DIRBIN)\webpmux.exe: $(DIROBJ)\examples\webpmux.obj $(LIBWEBPMUX)
+$(DIRBIN)\webpmux.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS) $(LIBWEBP)
+$(DIRBIN)\img2webp.exe: $(DIROBJ)\examples\img2webp.obj $(LIBWEBPMUX)
+$(DIRBIN)\img2webp.exe: $(IMAGEIO_DEC_OBJS)
+$(DIRBIN)\img2webp.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS)
+$(DIRBIN)\img2webp.exe: $(LIBWEBPDEMUX) $(LIBWEBP)
+$(DIRBIN)\get_disto.exe: $(DIROBJ)\extras\get_disto.obj
+$(DIRBIN)\get_disto.exe: $(IMAGEIO_DEC_OBJS) $(IMAGEIO_UTIL_OBJS)
+$(DIRBIN)\get_disto.exe: $(LIBWEBPDEMUX) $(LIBWEBP)
+$(DIRBIN)\webp_quality.exe: $(DIROBJ)\extras\webp_quality.obj
+$(DIRBIN)\webp_quality.exe: $(IMAGEIO_UTIL_OBJS)
+$(DIRBIN)\webp_quality.exe: $(EXTRAS_OBJS)
+# EXTRA_OBJS requires private symbols from dsp. Explicitly add those when
+# building libwebp as a dll.
+!IF "$(DLLBUILD)" == "TRUE"
+$(DIRBIN)\webp_quality.exe: $(DSP_DEC_OBJS)
+!ENDIF
+$(DIRBIN)\webp_quality.exe: $(LIBWEBP)
+$(DIRBIN)\webpinfo.exe: $(DIROBJ)\examples\webpinfo.obj
+$(DIRBIN)\webpinfo.exe: $(IMAGEIO_DEC_OBJS)
+$(DIRBIN)\webpinfo.exe: $(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS)
+$(DIRBIN)\webpinfo.exe: $(LIBWEBPDEMUX) $(LIBWEBP)
+
+$(OUT_EXAMPLES): $(EX_UTIL_OBJS) $(LIBWEBP)
+$(EX_UTIL_OBJS) $(IMAGEIO_UTIL_OBJS): $(OUTPUT_DIRS)
+$(IMAGEIO_DEC_OBJS) $(IMAGEIO_ENC_OBJS) $(EXTRAS_OBJS): $(OUTPUT_DIRS)
+!ENDIF  # ARCH == ARM
+
+$(LIBSHARPYUV): $(LIBSHARPYUV_OBJS)
+$(LIBWEBPDECODER): $(LIBWEBPDECODER_OBJS)
+$(LIBWEBP): $(LIBWEBP_OBJS) $(LIBSHARPYUV)
+$(LIBWEBPMUX): $(LIBWEBPMUX_OBJS)
+$(LIBWEBPDEMUX): $(LIBWEBPDEMUX_OBJS)
+
+$(LIBWEBP_OBJS) $(LIBWEBPMUX_OBJS) $(LIBWEBPDEMUX_OBJS) $(LIBSHARPYUV_OBJS): \
+    $(OUTPUT_DIRS)
+
+!IF "$(DLLBUILD)" == "TRUE"
+{$(DIROBJ)}.c{$(DIROBJ)}.obj:
+	$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$@  $<
+
+{src}.rc{$(DIROBJ)}.res:
+	$(RC) /fo$@ $<
+{src\demux}.rc{$(DIROBJ)\demux}.res:
+	$(RC) /fo$@ $<
+{src\mux}.rc{$(DIROBJ)\mux}.res:
+	$(RC) /fo$@ $<
+{sharpyuv}.rc{$(DIROBJ)\sharpyuv}.res:
+	$(RC) /fo$@ $<
+
+$(LIBSHARPYUV): $(DIROBJ)\sharpyuv\$(LIBSHARPYUV_BASENAME:_debug=).res
+$(LIBWEBP): $(LIBSHARPYUV) $(DIROBJ)\$(LIBWEBP_BASENAME:_debug=).res
+$(LIBWEBPDECODER): $(DIROBJ)\$(LIBWEBPDECODER_BASENAME:_debug=).res
+$(LIBWEBPMUX): $(LIBWEBP) $(DIROBJ)\mux\$(LIBWEBPMUX_BASENAME:_debug=).res
+$(LIBWEBPDEMUX): $(LIBWEBP) $(DIROBJ)\demux\$(LIBWEBPDEMUX_BASENAME:_debug=).res
+
+$(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX) $(LIBSHARPYUV):
+	$(LNKDLL) /out:$(DIRBIN)\$(@B:_dll=.dll) /implib:$@ $(LFLAGS) $**
+	-xcopy $(DIROBJ)\*.pdb $(DIRLIB) /y
+!ELSE
+$(LIBWEBPDECODER) $(LIBWEBP) $(LIBWEBPMUX) $(LIBWEBPDEMUX) $(LIBSHARPYUV):
+	$(LNKLIB) /out:$@ $**
+	-xcopy $(DIROBJ)\*.pdb $(DIRLIB) /y
+!ENDIF
+
+$(OUTPUT_DIRS):
+	@if not exist "$(@)" mkdir "$(@)"
+
+.SUFFIXES: .c .obj .res .exe
+# File-specific flag builds. Note batch rules take precedence over wildcards,
+# so for now name each file individually.
+$(DIROBJ)\examples\anim_diff.obj: examples\anim_diff.c
+	$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \
+	  /Fo$(DIROBJ)\examples\ examples\$(@B).c
+$(DIROBJ)\examples\anim_dump.obj: examples\anim_dump.c
+	$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \
+	  /Fo$(DIROBJ)\examples\ examples\$(@B).c
+$(DIROBJ)\examples\anim_util.obj: examples\anim_util.c
+	$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \
+	  /Fo$(DIROBJ)\examples\ examples\$(@B).c
+$(DIROBJ)\examples\gif2webp.obj: examples\gif2webp.c
+	$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \
+	  /Fo$(DIROBJ)\examples\ examples\$(@B).c
+$(DIROBJ)\examples\gifdec.obj: examples\gifdec.c
+	$(CC) $(CFLAGS) /DWEBP_HAVE_GIF /Fd$(LIBWEBP_PDBNAME) \
+	  /Fo$(DIROBJ)\examples\ examples\$(@B).c
+# Batch rules
+{examples}.c{$(DIROBJ)\examples}.obj::
+	$(CC) $(CFLAGS) /Fd$(DIROBJ)\examples\ /Fo$(DIROBJ)\examples\ $<
+{extras}.c{$(DIROBJ)\extras}.obj::
+	$(CC) $(CFLAGS) /Fd$(DIROBJ)\extras\ /Fo$(DIROBJ)\extras\ $<
+{imageio}.c{$(DIROBJ)\imageio}.obj::
+	$(CC) $(CFLAGS) /Fd$(DIROBJ)\imageio\ /Fo$(DIROBJ)\imageio\ $<
+{sharpyuv}.c{$(DIROBJ)\sharpyuv}.obj::
+	$(CC) $(CFLAGS) /Fd$(DIROBJ)\sharpyuv\ /Fo$(DIROBJ)\sharpyuv\ $<
+{src\dec}.c{$(DIROBJ)\dec}.obj::
+	$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dec\ $<
+{src\demux}.c{$(DIROBJ)\demux}.obj::
+	$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\demux\ $<
+{src\dsp}.c{$(DIROBJ)\dsp}.obj::
+	$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\dsp\ $<
+{src\enc}.c{$(DIROBJ)\enc}.obj::
+	$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\enc\ $<
+{src\mux}.c{$(DIROBJ)\mux}.obj::
+	$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\mux\ $<
+{src\utils}.c{$(DIROBJ)\utils}.obj::
+	$(CC) $(CFLAGS) /Fd$(LIBWEBP_PDBNAME) /Fo$(DIROBJ)\utils\ $<
+
+LNKLIBS     = ole32.lib windowscodecs.lib shlwapi.lib
+!IF "$(UNICODE)" == "1"
+LNKLIBS     = $(LNKLIBS) Shell32.lib
+!ENDIF
+
+{$(DIROBJ)\examples}.obj{$(DIRBIN)}.exe:
+	$(LNKEXE) $(LDFLAGS) /OUT:$@ $** $(LNKLIBS)
+
+{$(DIROBJ)\extras}.obj{$(DIRBIN)}.exe:
+	$(LNKEXE) $(LDFLAGS) /OUT:$@ $** $(LNKLIBS)
+
+clean::
+	@-erase /s $(DIROBJ)\*.dll 2> NUL
+	@-erase /s $(DIROBJ)\*.exp 2> NUL
+	@-erase /s $(DIROBJ)\*.idb 2> NUL
+	@-erase /s $(DIROBJ)\*.lib 2> NUL
+	@-erase /s $(DIROBJ)\*.obj 2> NUL
+	@-erase /s $(DIROBJ)\*.pch 2> NUL
+	@-erase /s $(DIROBJ)\*.pdb 2> NUL
+	@-erase /s $(DIROBJ)\*.res 2> NUL
+
+!ENDIF  # End of case where a config was provided.
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..c4f8ef7
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,296 @@
+- 12/16/2022: version 1.3.0
+  This is a binary compatible release.
+  * add libsharpyuv, which exposes -sharp_yuv/config.use_sharp_yuv
+    functionality to other libraries; libwebp now depends on this library
+  * major updates to the container and lossless bitstream docs (#448, #546,
+    #551)
+  * miscellaneous warning, bug & build fixes (#576, #583, #584)
+
+- 8/4/2022: version 1.2.4
+  This is a binary compatible release.
+  * restore CMake libwebpmux target name for compatibility with 1.2.2 (#575)
+  * fix lossless crunch mode encoding with WEBP_REDUCE_SIZE
+    (chromium: #1345547, #1345595, #1345772, #1345804)
+
+- 6/30/2022: version 1.2.3
+  This is a binary compatible release.
+  * security fix for lossless encoder (#565, chromium:1313709)
+  * improved progress granularity in WebPReportProgress() when using lossless
+  * improved precision in Sharp YUV (-sharp_yuv) conversion
+  * many corrections to webp-lossless-bitstream-spec.txt (#551)
+  * crash/leak fixes on error/OOM and other bug fixes (#558, #563, #569, #573)
+
+- 1/11/2022: version 1.2.2
+  This is a binary compatible release.
+  * webpmux: add "-set bgcolor A,R,G,B"
+  * add ARM64 NEON support for MSVC builds (#539)
+  * fix duplicate include error in Xcode when using multiple XCFrameworks in a
+    project (#542)
+  * doc updates and bug fixes (#538, #544, #548, #550)
+
+- 7/20/2021: version 1.2.1
+  This is a binary compatible release.
+  * minor lossless encoder improvements and x86 color conversion speed up
+  * add ARM64 simulator support to xcframeworkbuild.sh (#510)
+  * further security related hardening in libwebp & examples
+    (issues: #497, #508, #518)
+    (chromium: #1196480, #1196773, #1196775, #1196777, #1196778, #1196850)
+    (oss-fuzz: #28658, #28978)
+  * toolchain updates and bug fixes (#498, #501, #502, #504, #505, #506, #509,
+                                     #533)
+  * use more inclusive language within the source (#507)
+
+- 12/23/2020: version 1.2.0
+  * API changes:
+    - libwebp:
+      encode.h: add a qmin / qmax range for quality factor (cwebp adds -qrange)
+  * lossless encoder improvements
+  * SIMD support for Wasm builds
+  * add xcframeworkbuild.sh, supports Mac Catalyst builds
+  * import fuzzers from oss-fuzz & chromium (#409)
+  * webpmux: add an '-set loop <value>' option (#494)
+  * toolchain updates and bug fixes (#449, #463, #470, #475, #477, #478, #479,
+    #488, #491)
+
+- 12/18/2019: version 1.1.0
+  * API changes:
+    - libwebp:
+      WebPMalloc (issue #442)
+    - extras:
+      WebPUnmultiplyARGB
+  * alpha decode fix (issue #439)
+  * toolchain updates and bug fixes
+    (chromium: #1026858, #1027136, #1027409, #1028620, #1028716, #995200)
+    (oss-fuzz: #19430, #19447)
+
+- 7/4/2019: version 1.0.3
+  This is a binary compatible release.
+  * resize fixes for Nx1 sizes and the addition of non-opaque alpha values for
+    odd sizes (issues #418, #434)
+  * lossless encode/decode performance improvements
+  * lossy compression performance improvement at low quality levels with flat
+    content (issue #432)
+  * python swig files updated to support python 3
+  Tool updates:
+    vwebp will now preserve the aspect ratio of images that exceed monitor
+    resolution by scaling the image to fit (issue #433)
+
+- 1/14/2019: version 1.0.2
+  This is a binary compatible release.
+  * (Windows) unicode file support in the tools (linux and mac already had
+    support, issue #398)
+  * lossless encoder speedups
+  * lossy encoder speedup on ARM
+  * lossless multi-threaded security fix (chromium:917029)
+
+- 11/2/2018: version 1.0.1
+  This is a binary compatible release.
+  * lossless encoder speedups
+  * big-endian fix for alpha decoding (issue #393)
+  * gif2webp fix for loop count=65535 transcode (issue #382)
+  * further security related hardening in libwebp & libwebpmux
+    (issues #383, #385, #386, #387, #388, #391)
+    (oss-fuzz #9099, #9100, #9105, #9106, #9111, #9112, #9119, #9123, #9170,
+              #9178, #9179, #9183, #9186, #9191, #9364, #9417, #9496, #10349,
+              #10423, #10634, #10700, #10838, #10922, #11021, #11088, #11152)
+  * miscellaneous bug & build fixes (issues #381, #394, #396, #397, #400)
+
+- 4/2/2018: version 1.0.0
+  This is a binary compatible release.
+  * lossy encoder improvements to avoid chroma shifts in various circumstances
+    (issues #308, #340)
+  * big-endian fixes for decode, RGBA import and WebPPictureDistortion
+  Tool updates:
+    gifwebp, anim_diff - default duration behavior (<= 10ms) changed to match
+                         web browsers, transcoding tools (issue #379)
+    img2webp, webpmux - allow options to be passed in via a file (issue #355)
+
+- 11/24/2017: version 0.6.1
+  This is a binary compatible release.
+  * lossless performance and compression improvements + a new 'cruncher' mode
+    (-m 6 -q 100)
+  * ARM performance improvements with clang (15-20% w/ndk r15c, issue #339)
+  * webp-js: emscripten/webassembly based javascript decoder
+  * miscellaneous bug & build fixes (issue #329, #332, #343, #353, #360, #361,
+    #363)
+  Tool updates / additions:
+    added webpinfo - prints file format information (issue #330)
+    gif2webp - loop behavior modified to match Chrome M63+ (crbug.com/649264);
+               '-loop_compatibility' can be used for the old behavior
+
+- 1/26/2017: version 0.6.0
+  * lossless performance and compression improvements
+  * miscellaneous performance improvements (SSE2, NEON, MSA)
+  * webpmux gained a -duration option allowing for frame timing modification
+  * new img2webp utility allowing a sequence of images to be converted to
+    animated webp
+  * API changes:
+    - libwebp:
+      WebPPictureSharpARGBToYUVA
+      WebPPlaneDistortion
+    - libwebpmux / gif2webp:
+      WebPAnimEncoderOptions: kmax <= 0 now disables keyframes, kmax == 1
+                              forces all keyframes. See mux.h and the gif2webp
+                              manpage for details.
+
+- 12/13/2016: version 0.5.2
+  This is a binary compatible release.
+  This release covers CVE-2016-8888 and CVE-2016-9085.
+  * further security related hardening in the tools; fixes to
+    gif2webp/AnimEncoder (issues #310, #314, #316, #322), cwebp/libwebp (issue
+    #312)
+  * full libwebp (encoder & decoder) iOS framework; libwebpdecoder
+    WebP.framework renamed to WebPDecoder.framework (issue #307)
+  * CMake support for Android Studio (2.2)
+  * miscellaneous build related fixes (issue #306, #313)
+  * miscellaneous documentation improvements (issue #225)
+  * minor lossy encoder fixes and improvements
+
+- 6/14/2016: version 0.5.1
+  This is a binary compatible release.
+  * miscellaneous bug fixes (issues #280, #289)
+  * reverted alpha plane encoding with color cache for compatibility with
+    libwebp 0.4.0->0.4.3 (issues #291, #298)
+  * lossless encoding performance improvements
+  * memory reduction in both lossless encoding and decoding
+  * force mux output to be in the extended format (VP8X) when undefined chunks
+    are present (issue #294)
+  * gradle, cmake build support
+  * workaround for compiler bug causing 64-bit decode failures on android
+    devices using clang-3.8 in the r11c NDK
+  * various WebPAnimEncoder improvements
+
+- 12/17/2015: version 0.5.0
+  * miscellaneous bug & build fixes (issues #234, #258, #274, #275, #278)
+  * encoder & decoder speed-ups on x86/ARM/MIPS for lossy & lossless
+    - note! YUV->RGB conversion was sped-up, but the results will be slightly
+      different from previous releases
+  * various lossless encoder improvements
+  * gif2webp improvements, -min_size option added
+  * tools fully support input from stdin and output to stdout (issue #168)
+  * New WebPAnimEncoder API for creating animations
+  * New WebPAnimDecoder API for decoding animations
+  * other API changes:
+    - libwebp:
+      WebPPictureSmartARGBToYUVA() (-pre 4 in cwebp)
+      WebPConfig::exact (-exact in cwebp; -alpha_cleanup is now the default)
+      WebPConfig::near_lossless (-near_lossless in cwebp)
+      WebPFree() (free'ing webp allocated memory in other languages)
+      WebPConfigLosslessPreset()
+      WebPMemoryWriterClear()
+    - libwebpdemux: removed experimental fragment related fields and functions
+    - libwebpmux: WebPMuxSetCanvasSize()
+  * new libwebpextras library with some uncommon import functions:
+    WebPImportGray/WebPImportRGB565/WebPImportRGB4444
+
+- 10/15/15: version 0.4.4
+  This is a binary compatible release.
+  * rescaling out-of-bounds read fix (issue #254)
+  * various build fixes and improvements (issues #253, #259, #262, #267, #268)
+  * container documentation update
+  * gif2webp transparency fix (issue #245)
+
+- 3/3/15: version 0.4.3
+  This is a binary compatible release.
+  * Android / gcc / iOS / MSVS build fixes and improvements
+  * lossless decode fix (issue #239 -- since 0.4.0)
+  * documentation / vwebp updates for animation
+  * multi-threading fix (issue #234)
+
+- 10/13/14: version 0.4.2
+  This is a binary compatible release.
+  * Android / gcc build fixes
+  * (Windows) fix reading from stdin and writing to stdout
+  * gif2webp: miscellaneous fixes
+  * fix 'alpha-leak' with lossy compression (issue #220)
+  * the lossless bitstream spec has been amended to reflect the current code
+
+- 7/24/14: version 0.4.1
+  This is a binary compatible release.
+  * AArch64 (arm64) & MIPS support/optimizations
+  * NEON assembly additions:
+    - ~25% faster lossy decode / encode (-m 4)
+    - ~10% faster lossless decode
+    - ~5-10% faster lossless encode (-m 3/4)
+  * dwebp/vwebp can read from stdin
+  * cwebp/gif2webp can write to stdout
+  * cwebp can read webp files; useful if storing sources as webp lossless
+
+- 12/19/13: version 0.4.0
+  * improved gif2webp tool
+  * numerous fixes, compression improvement and speed-up
+  * dither option added to decoder (dwebp -dither 50 ...)
+  * improved multi-threaded modes (-mt option)
+  * improved filtering strength determination
+  * New function: WebPMuxGetCanvasSize
+  * BMP and TIFF format output added to 'dwebp'
+  * Significant memory reduction for decoding lossy images with alpha.
+  * Intertwined decoding of RGB and alpha for a shorter
+    time-to-first-decoded-pixel.
+  * WebPIterator has a new member 'has_alpha' denoting whether the frame
+    contains transparency.
+  * Container spec amended with new 'blending method' for animation.
+
+- 6/13/13: version 0.3.1
+  This is a binary compatible release.
+  * Add incremental decoding support for images containing ALPH and ICCP chunks.
+  * Python bindings via swig for the simple encode/decode interfaces similar to
+    Java.
+
+- 3/20/13: version 0.3.0
+  This is a binary compatible release.
+  * WebPINewRGB/WebPINewYUVA accept being passed a NULL output buffer
+    and will perform auto-allocation.
+  * default filter option is now '-strong -f 60'
+  * encoding speed-up for lossy methods 3 to 6
+  * alpha encoding can be done in parallel to lossy using 'cwebp -mt ...'
+  * color profile, metadata (XMP/EXIF) and animation support finalized in the
+    container.
+  * various NEON assembly additions
+  Tool updates / additions:
+    * gif2webp added
+    * vwebp given color profile & animation support
+    * cwebp can preserve color profile / metadata with '-metadata'
+
+- 10/30/12: version 0.2.1
+  * Various security related fixes
+  * cwebp.exe: fix import errors on Windows XP
+  * enable DLL builds for mingw targets
+
+- 8/3/12: version 0.2.0
+  * Add support for ARGB -> YUVA conversion for lossless decoder
+    New functions: WebPINewYUVA, WebPIDecGetYUVA
+  * Add stats for lossless and alpha encoding
+  * Security related hardening: allocation and size checks
+  * Add PAM output support to dwebp
+
+- 7/19/12: version 0.1.99
+  * This is a pre-release of 0.2.0, not an rc to allow for further
+    incompatible changes based on user feedback.
+  * Alpha channel encode/decode support.
+  * Lossless encoder/decoder.
+  * Add TIFF input support to cwebp.
+  Incompatible changes:
+    * The encode ABI has been modified to support alpha encoding.
+    * Deprecated function WebPINew() has been removed.
+    * Decode function signatures have changed to consistently use size_t over
+      int/uint32_t.
+    * decode_vp8.h is no longer installed system-wide.
+    * cwebp will encode the alpha channel if present.
+
+- 9/19/11: version 0.1.3
+  * Advanced decoding APIs.
+  * On-the-fly cropping and rescaling of images.
+  * SSE2 instructions for decoding performance optimizations on x86 based
+    platforms.
+  * Support Multi-threaded decoding.
+  * 40% improvement in Decoding performance.
+  * Add support for RGB565, RGBA4444 & ARGB image colorspace.
+  * Better handling of large picture encoding.
+
+- 3/25/11: version 0.1.2
+  * Incremental decoding: picture can be decoded byte-by-byte if needs be.
+  * lot of bug-fixes, consolidation and stabilization
+
+- 2/23/11: initial release of version 0.1, with the new encoder
+- 9/30/10: initial release version with only the lightweight decoder
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
new file mode 100644
index 0000000..91ad12e
--- /dev/null
+++ b/PRESUBMIT.py
@@ -0,0 +1,245 @@
+# Copyright (c) 2021, Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#   * Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#
+#   * Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in
+#     the documentation and/or other materials provided with the
+#     distribution.
+#
+#   * Neither the name of Google nor the names of its contributors may
+#     be used to endorse or promote products derived from this software
+#     without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Top-level presubmit script for libwebp.
+
+See https://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for
+details on the presubmit API built into depot_tools.
+"""
+
+import re
+import subprocess2
+
+USE_PYTHON3 = True
+_BASH_INDENTATION = "2"
+_GIT_COMMIT_SUBJECT_LENGTH = 65
+_INCLUDE_BASH_FILES_ONLY = [r".*\.sh$"]
+_INCLUDE_MAN_FILES_ONLY = [r"man/.+\.1$"]
+_INCLUDE_SOURCE_FILES_ONLY = [r".*\.[ch]$"]
+_LIBWEBP_MAX_LINE_LENGTH = 80
+
+
+def _CheckCommitSubjectLength(input_api, output_api):
+  """Ensures commit's subject length is no longer than 65 chars."""
+  name = "git-commit subject"
+  cmd = ["git", "log", "-1", "--pretty=%s"]
+  start = input_api.time.time()
+  proc = subprocess2.Popen(
+      cmd,
+      stderr=subprocess2.PIPE,
+      stdout=subprocess2.PIPE,
+      universal_newlines=True)
+
+  stdout, _ = proc.communicate()
+  duration = input_api.time.time() - start
+
+  if not re.match(r"^Revert",
+                  stdout) and (len(stdout) - 1) > _GIT_COMMIT_SUBJECT_LENGTH:
+    failure_msg = (
+        "The commit subject: %s is too long (%d chars)\n"
+        "Try to keep this to 50 or less (up to 65 is permitted for "
+        "non-reverts).\n"
+        "https://www.git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-"
+        "Project#_commit_guidelines") % (stdout, len(stdout) - 1)
+    return output_api.PresubmitError("%s\n (%4.2fs) failed\n%s" %
+                                     (name, duration, failure_msg))
+
+  return output_api.PresubmitResult("%s\n (%4.2fs) success" % (name, duration))
+
+
+def _CheckDuplicateFiles(input_api, output_api):
+  """Ensures there are not repeated filenames."""
+  all_files = []
+  for f in input_api.change.AllFiles():
+    for include_file in _INCLUDE_SOURCE_FILES_ONLY:
+      if re.match(include_file, f):
+        all_files.append(f)
+        break
+
+  basename_to_path = {}
+  for f in all_files:
+    basename_file = input_api.basename(f)
+    if basename_file in basename_to_path:
+      basename_to_path[basename_file].append(f)
+    else:
+      basename_to_path[basename_file] = [f]
+
+  dupes = []
+  for files in basename_to_path.values():
+    if len(files) > 1:
+      dupes.extend(files)
+
+  if dupes:
+    return output_api.PresubmitError(
+        "Duplicate source files, rebase or rename some to make them unique:\n%s"
+        % dupes)
+  return output_api.PresubmitResult("No duplicates, success\n")
+
+
+def _GetFilesToSkip(input_api):
+  return list(input_api.DEFAULT_FILES_TO_SKIP) + [
+      r"swig/.*\.py$",
+      r"\.pylintrc$",
+  ]
+
+
+def _RunManCmd(input_api, output_api, man_file):
+  """man command wrapper."""
+  cmd = ["man", "--warnings", "-EUTF-8", "-l", "-Tutf8", "-Z", man_file]
+  name = "Check %s file." % man_file
+  start = input_api.time.time()
+  output, _ = subprocess2.communicate(
+      cmd, stdout=None, stderr=subprocess2.PIPE, universal_newlines=True)
+  duration = input_api.time.time() - start
+  if output[1]:
+    return output_api.PresubmitError("%s\n%s (%4.2fs) failed\n%s" %
+                                     (name, " ".join(cmd), duration, output[1]))
+  return output_api.PresubmitResult("%s\n%s (%4.2fs)\n" %
+                                    (name, " ".join(cmd), duration))
+
+
+def _RunShellCheckCmd(input_api, output_api, bash_file):
+  """shellcheck command wrapper."""
+  cmd = ["shellcheck", "-x", "-oall", "-sbash", bash_file]
+  name = "Check %s file." % bash_file
+  start = input_api.time.time()
+  output, rc = subprocess2.communicate(
+      cmd, stdout=None, stderr=subprocess2.PIPE, universal_newlines=True)
+  duration = input_api.time.time() - start
+  if rc == 0:
+    return output_api.PresubmitResult("%s\n%s (%4.2fs)\n" %
+                                      (name, " ".join(cmd), duration))
+  return output_api.PresubmitError("%s\n%s (%4.2fs) failed\n%s" %
+                                   (name, " ".join(cmd), duration, output[1]))
+
+
+def _RunShfmtCheckCmd(input_api, output_api, bash_file):
+  """shfmt command wrapper."""
+  cmd = [
+      "shfmt", "-i", _BASH_INDENTATION, "-bn", "-ci", "-sr", "-kp", "-d",
+      bash_file
+  ]
+  name = "Check %s file." % bash_file
+  start = input_api.time.time()
+  output, rc = subprocess2.communicate(
+      cmd, stdout=None, stderr=subprocess2.PIPE, universal_newlines=True)
+  duration = input_api.time.time() - start
+  if rc == 0:
+    return output_api.PresubmitResult("%s\n%s (%4.2fs)\n" %
+                                      (name, " ".join(cmd), duration))
+  return output_api.PresubmitError("%s\n%s (%4.2fs) failed\n%s" %
+                                   (name, " ".join(cmd), duration, output[1]))
+
+
+def _RunCmdOnCheckedFiles(input_api, output_api, run_cmd, files_to_check):
+  """Ensure that libwebp/ files are clean."""
+  file_filter = lambda x: input_api.FilterSourceFile(
+      x, files_to_check=files_to_check, files_to_skip=None)
+
+  affected_files = input_api.change.AffectedFiles(file_filter=file_filter)
+  results = [
+      run_cmd(input_api, output_api, f.AbsoluteLocalPath())
+      for f in affected_files
+  ]
+  return results
+
+
+def _CommonChecks(input_api, output_api):
+  """Ensures this patch does not have trailing spaces, extra EOLs,
+     or long lines.
+  """
+  results = []
+  results.extend(
+      input_api.canned_checks.CheckChangeHasNoCrAndHasOnlyOneEol(
+          input_api, output_api))
+  results.extend(
+      input_api.canned_checks.CheckChangeHasNoTabs(input_api, output_api))
+  results.extend(
+      input_api.canned_checks.CheckChangeHasNoStrayWhitespace(
+          input_api, output_api))
+  results.append(_CheckCommitSubjectLength(input_api, output_api))
+  results.append(_CheckDuplicateFiles(input_api, output_api))
+
+  source_file_filter = lambda x: input_api.FilterSourceFile(
+      x, files_to_skip=_GetFilesToSkip(input_api))
+  results.extend(
+      input_api.canned_checks.CheckLongLines(
+          input_api,
+          output_api,
+          maxlen=_LIBWEBP_MAX_LINE_LENGTH,
+          source_file_filter=source_file_filter))
+
+  results.extend(
+      input_api.canned_checks.CheckPatchFormatted(
+          input_api,
+          output_api,
+          check_clang_format=False,
+          check_python=True,
+          result_factory=output_api.PresubmitError))
+  results.extend(
+      _RunCmdOnCheckedFiles(input_api, output_api, _RunManCmd,
+                            _INCLUDE_MAN_FILES_ONLY))
+  # Run pylint.
+  results.extend(
+      input_api.canned_checks.RunPylint(
+          input_api,
+          output_api,
+          files_to_skip=_GetFilesToSkip(input_api),
+          pylintrc=".pylintrc",
+          version="2.7"))
+
+  # Binaries shellcheck and shfmt are not installed in depot_tools.
+  # Installation is needed
+  try:
+    subprocess2.communicate(["shellcheck", "--version"])
+    results.extend(
+        _RunCmdOnCheckedFiles(input_api, output_api, _RunShellCheckCmd,
+                              _INCLUDE_BASH_FILES_ONLY))
+    print("shfmt")
+    subprocess2.communicate(["shfmt", "-version"])
+    results.extend(
+        _RunCmdOnCheckedFiles(input_api, output_api, _RunShfmtCheckCmd,
+                              _INCLUDE_BASH_FILES_ONLY))
+  except OSError as os_error:
+    results.append(
+        output_api.PresubmitPromptWarning(
+            "%s\nPlease install missing binaries locally." % os_error.args[0]))
+  return results
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  results = []
+  results.extend(_CommonChecks(input_api, output_api))
+  return results
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  results = []
+  results.extend(_CommonChecks(input_api, output_api))
+  return results
diff --git a/README b/README
deleted file mode 100644
index f6eaf2c..0000000
--- a/README
+++ /dev/null
@@ -1,795 +0,0 @@
-          __   __  ____  ____  ____
-         /  \\/  \/  _ \/  _ )/  _ \
-         \       /   __/  _  \   __/
-          \__\__/\____/\_____/__/ ____  ___
-                / _/ /    \    \ /  _ \/ _/
-               /  \_/   / /   \ \   __/  \__
-               \____/____/\_____/_____/____/v1.2.2
-
-Description:
-============
-
-WebP codec: library to encode and decode images in WebP format. This package
-contains the library that can be used in other programs to add WebP support,
-as well as the command line tools 'cwebp' and 'dwebp'.
-
-See https://developers.google.com/speed/webp
-
-The latest source tree is available at
-https://chromium.googlesource.com/webm/libwebp
-
-It is released under the same license as the WebM project.
-See https://www.webmproject.org/license/software/ or the
-"COPYING" file for details. An additional intellectual
-property rights grant can be found in the file PATENTS.
-
-Building:
-=========
-
-Windows build:
---------------
-
-By running:
-
-  nmake /f Makefile.vc CFG=release-static RTLIBCFG=static OBJDIR=output
-
-the directory output\release-static\(x64|x86)\bin will contain the tools
-cwebp.exe and dwebp.exe. The directory output\release-static\(x64|x86)\lib will
-contain the libwebp static library.
-The target architecture (x86/x64) is detected by Makefile.vc from the Visual
-Studio compiler (cl.exe) available in the system path.
-
-Unix build using makefile.unix:
--------------------------------
-
-On platforms with GNU tools installed (gcc and make), running
-
-  make -f makefile.unix
-
-will build the binaries examples/cwebp and examples/dwebp, along
-with the static library src/libwebp.a. No system-wide installation
-is supplied, as this is a simple alternative to the full installation
-system based on the autoconf tools (see below).
-Please refer to makefile.unix for additional details and customizations.
-
-Using autoconf tools:
----------------------
-Prerequisites:
-A compiler (e.g., gcc), make, autoconf, automake, libtool.
-On a Debian-like system the following should install everything you need for a
-minimal build:
-$ sudo apt-get install gcc make autoconf automake libtool
-
-When building from git sources, you will need to run autogen.sh to generate the
-configure script.
-
-./configure
-make
-make install
-
-should be all you need to have the following files
-
-/usr/local/include/webp/decode.h
-/usr/local/include/webp/encode.h
-/usr/local/include/webp/types.h
-/usr/local/lib/libwebp.*
-/usr/local/bin/cwebp
-/usr/local/bin/dwebp
-
-installed.
-
-Note: A decode-only library, libwebpdecoder, is available using the
-'--enable-libwebpdecoder' flag. The encode library is built separately and can
-be installed independently using a minor modification in the corresponding
-Makefile.am configure files (see comments there). See './configure --help' for
-more options.
-
-Building for MIPS Linux:
-------------------------
-MIPS Linux toolchain stable available releases can be found at:
-https://community.imgtec.com/developers/mips/tools/codescape-mips-sdk/available-releases/
-
-# Add toolchain to PATH
-export PATH=$PATH:/path/to/toolchain/bin
-
-# 32-bit build for mips32r5 (p5600)
-HOST=mips-mti-linux-gnu
-MIPS_CFLAGS="-O3 -mips32r5 -mabi=32 -mtune=p5600 -mmsa -mfp64 \
-  -msched-weight -mload-store-pairs -fPIE"
-MIPS_LDFLAGS="-mips32r5 -mabi=32 -mmsa -mfp64 -pie"
-
-# 64-bit build for mips64r6 (i6400)
-HOST=mips-img-linux-gnu
-MIPS_CFLAGS="-O3 -mips64r6 -mabi=64 -mtune=i6400 -mmsa -mfp64 \
-  -msched-weight -mload-store-pairs -fPIE"
-MIPS_LDFLAGS="-mips64r6 -mabi=64 -mmsa -mfp64 -pie"
-
-./configure --host=${HOST} --build=`config.guess` \
-  CC="${HOST}-gcc -EL" \
-  CFLAGS="$MIPS_CFLAGS" \
-  LDFLAGS="$MIPS_LDFLAGS"
-make
-make install
-
-CMake:
-------
-With CMake, you can compile libwebp, cwebp, dwebp, gif2webp, img2webp, webpinfo
-and the JS bindings.
-
-Prerequisites:
-A compiler (e.g., gcc with autotools) and CMake.
-On a Debian-like system the following should install everything you need for a
-minimal build:
-$ sudo apt-get install build-essential cmake
-
-When building from git sources, you will need to run cmake to generate the
-makefiles.
-
-mkdir build && cd build && cmake ../
-make
-make install
-
-If you also want any of the executables, you will need to enable them through
-CMake, e.g.:
-
-cmake -DWEBP_BUILD_CWEBP=ON -DWEBP_BUILD_DWEBP=ON ../
-
-or through your favorite interface (like ccmake or cmake-qt-gui).
-
-Use option -DWEBP_UNICODE=ON for Unicode support on Windows (with chcp 65001).
-
-Finally, once installed, you can also use WebP in your CMake project by doing:
-
-find_package(WebP)
-
-which will define the CMake variables WebP_INCLUDE_DIRS and WebP_LIBRARIES.
-
-Gradle:
--------
-The support for Gradle is minimal: it only helps you compile libwebp, cwebp and
-dwebp and webpmux_example.
-
-Prerequisites:
-A compiler (e.g., gcc with autotools) and gradle.
-On a Debian-like system the following should install everything you need for a
-minimal build:
-$ sudo apt-get install build-essential gradle
-
-When building from git sources, you will need to run the Gradle wrapper with the
-appropriate target, e.g. :
-
-./gradlew buildAllExecutables
-
-SWIG bindings:
---------------
-
-To generate language bindings from swig/libwebp.swig at least swig-1.3
-(http://www.swig.org) is required.
-
-Currently the following functions are mapped:
-Decode:
-  WebPGetDecoderVersion
-  WebPGetInfo
-  WebPDecodeRGBA
-  WebPDecodeARGB
-  WebPDecodeBGRA
-  WebPDecodeBGR
-  WebPDecodeRGB
-
-Encode:
-  WebPGetEncoderVersion
-  WebPEncodeRGBA
-  WebPEncodeBGRA
-  WebPEncodeRGB
-  WebPEncodeBGR
-  WebPEncodeLosslessRGBA
-  WebPEncodeLosslessBGRA
-  WebPEncodeLosslessRGB
-  WebPEncodeLosslessBGR
-
-See swig/README for more detailed build instructions.
-
-Java bindings:
-
-To build the swig-generated JNI wrapper code at least JDK-1.5 (or equivalent)
-is necessary for enum support. The output is intended to be a shared object /
-DLL that can be loaded via System.loadLibrary("webp_jni").
-
-Python bindings:
-
-To build the swig-generated Python extension code at least Python 2.6 is
-required. Python < 2.6 may build with some minor changes to libwebp.swig or the
-generated code, but is untested.
-
-Encoding tool:
-==============
-
-The examples/ directory contains tools for encoding (cwebp) and
-decoding (dwebp) images.
-
-The easiest use should look like:
-  cwebp input.png -q 80 -o output.webp
-which will convert the input file to a WebP file using a quality factor of 80
-on a 0->100 scale (0 being the lowest quality, 100 being the best. Default
-value is 75).
-You might want to try the -lossless flag too, which will compress the source
-(in RGBA format) without any loss. The -q quality parameter will in this case
-control the amount of processing time spent trying to make the output file as
-small as possible.
-
-A longer list of options is available using the -longhelp command line flag:
-
-> cwebp -longhelp
-Usage:
- cwebp [-preset <...>] [options] in_file [-o out_file]
-
-If input size (-s) for an image is not specified, it is
-assumed to be a PNG, JPEG, TIFF or WebP file.
-Note: Animated PNG and WebP files are not supported.
-
-Options:
-  -h / -help ............. short help
-  -H / -longhelp ......... long help
-  -q <float> ............. quality factor (0:small..100:big), default=75
-  -alpha_q <int> ......... transparency-compression quality (0..100),
-                           default=100
-  -preset <string> ....... preset setting, one of:
-                            default, photo, picture,
-                            drawing, icon, text
-     -preset must come first, as it overwrites other parameters
-  -z <int> ............... activates lossless preset with given
-                           level in [0:fast, ..., 9:slowest]
-
-  -m <int> ............... compression method (0=fast, 6=slowest), default=4
-  -segments <int> ........ number of segments to use (1..4), default=4
-  -size <int> ............ target size (in bytes)
-  -psnr <float> .......... target PSNR (in dB. typically: 42)
-
-  -s <int> <int> ......... input size (width x height) for YUV
-  -sns <int> ............. spatial noise shaping (0:off, 100:max), default=50
-  -f <int> ............... filter strength (0=off..100), default=60
-  -sharpness <int> ....... filter sharpness (0:most .. 7:least sharp), default=0
-  -strong ................ use strong filter instead of simple (default)
-  -nostrong .............. use simple filter instead of strong
-  -sharp_yuv ............. use sharper (and slower) RGB->YUV conversion
-  -partition_limit <int> . limit quality to fit the 512k limit on
-                           the first partition (0=no degradation ... 100=full)
-  -pass <int> ............ analysis pass number (1..10)
-  -qrange <min> <max> .... specifies the permissible quality range
-                           (default: 0 100)
-  -crop <x> <y> <w> <h> .. crop picture with the given rectangle
-  -resize <w> <h> ........ resize picture (after any cropping)
-  -mt .................... use multi-threading if available
-  -low_memory ............ reduce memory usage (slower encoding)
-  -map <int> ............. print map of extra info
-  -print_psnr ............ prints averaged PSNR distortion
-  -print_ssim ............ prints averaged SSIM distortion
-  -print_lsim ............ prints local-similarity distortion
-  -d <file.pgm> .......... dump the compressed output (PGM file)
-  -alpha_method <int> .... transparency-compression method (0..1), default=1
-  -alpha_filter <string> . predictive filtering for alpha plane,
-                           one of: none, fast (default) or best
-  -exact ................. preserve RGB values in transparent area, default=off
-  -blend_alpha <hex> ..... blend colors against background color
-                           expressed as RGB values written in
-                           hexadecimal, e.g. 0xc0e0d0 for red=0xc0
-                           green=0xe0 and blue=0xd0
-  -noalpha ............... discard any transparency information
-  -lossless .............. encode image losslessly, default=off
-  -near_lossless <int> ... use near-lossless image
-                           preprocessing (0..100=off), default=100
-  -hint <string> ......... specify image characteristics hint,
-                           one of: photo, picture or graph
-
-  -metadata <string> ..... comma separated list of metadata to
-                           copy from the input to the output if present.
-                           Valid values: all, none (default), exif, icc, xmp
-
-  -short ................. condense printed message
-  -quiet ................. don't print anything
-  -version ............... print version number and exit
-  -noasm ................. disable all assembly optimizations
-  -v ..................... verbose, e.g. print encoding/decoding times
-  -progress .............. report encoding progress
-
-Experimental Options:
-  -jpeg_like ............. roughly match expected JPEG size
-  -af .................... auto-adjust filter strength
-  -pre <int> ............. pre-processing filter
-
-
-The main options you might want to try in order to further tune the
-visual quality are:
- -preset
- -sns
- -f
- -m
-
-Namely:
-  * 'preset' will set up a default encoding configuration targeting a
-     particular type of input. It should appear first in the list of options,
-     so that subsequent options can take effect on top of this preset.
-     Default value is 'default'.
-  * 'sns' will progressively turn on (when going from 0 to 100) some additional
-     visual optimizations (like: segmentation map re-enforcement). This option
-     will balance the bit allocation differently. It tries to take bits from the
-     "easy" parts of the picture and use them in the "difficult" ones instead.
-     Usually, raising the sns value (at fixed -q value) leads to larger files,
-     but with better quality.
-     Typical value is around '75'.
-  * 'f' option directly links to the filtering strength used by the codec's
-     in-loop processing. The higher the value, the smoother the
-     highly-compressed area will look. This is particularly useful when aiming
-     at very small files. Typical values are around 20-30. Note that using the
-     option -strong/-nostrong will change the type of filtering. Use "-f 0" to
-     turn filtering off.
-  * 'm' controls the trade-off between encoding speed and quality. Default is 4.
-     You can try -m 5 or -m 6 to explore more (time-consuming) encoding
-     possibilities. A lower value will result in faster encoding at the expense
-     of quality.
-
-Decoding tool:
-==============
-
-There is a decoding sample in examples/dwebp.c which will take
-a .webp file and decode it to a PNG image file (amongst other formats).
-This is simply to demonstrate the use of the API. You can verify the
-file test.webp decodes to exactly the same as test_ref.ppm by using:
-
- cd examples
- ./dwebp test.webp -ppm -o test.ppm
- diff test.ppm test_ref.ppm
-
-The full list of options is available using -h:
-
-> dwebp -h
-Usage: dwebp in_file [options] [-o out_file]
-
-Decodes the WebP image file to PNG format [Default].
-Note: Animated WebP files are not supported.
-
-Use following options to convert into alternate image formats:
-  -pam ......... save the raw RGBA samples as a color PAM
-  -ppm ......... save the raw RGB samples as a color PPM
-  -bmp ......... save as uncompressed BMP format
-  -tiff ........ save as uncompressed TIFF format
-  -pgm ......... save the raw YUV samples as a grayscale PGM
-                 file with IMC4 layout
-  -yuv ......... save the raw YUV samples in flat layout
-
- Other options are:
-  -version ..... print version number and exit
-  -nofancy ..... don't use the fancy YUV420 upscaler
-  -nofilter .... disable in-loop filtering
-  -nodither .... disable dithering
-  -dither <d> .. dithering strength (in 0..100)
-  -alpha_dither  use alpha-plane dithering if needed
-  -mt .......... use multi-threading
-  -crop <x> <y> <w> <h> ... crop output with the given rectangle
-  -resize <w> <h> ......... scale the output (*after* any cropping)
-  -flip ........ flip the output vertically
-  -alpha ....... only save the alpha plane
-  -incremental . use incremental decoding (useful for tests)
-  -h ........... this help message
-  -v ........... verbose (e.g. print encoding/decoding times)
-  -quiet ....... quiet mode, don't print anything
-  -noasm ....... disable all assembly optimizations
-
-WebP file analysis tool:
-========================
-
-'webpinfo' can be used to print out the chunk level structure and bitstream
-header information of WebP files. It can also check if the files are of valid
-WebP format.
-
-Usage: webpinfo [options] in_files
-Note: there could be multiple input files;
-      options must come before input files.
-Options:
-  -version ........... Print version number and exit.
-  -quiet ............. Do not show chunk parsing information.
-  -diag .............. Show parsing error diagnosis.
-  -summary ........... Show chunk stats summary.
-  -bitstream_info .... Parse bitstream header.
-
-Visualization tool:
-===================
-
-There's a little self-serve visualization tool called 'vwebp' under the
-examples/ directory. It uses OpenGL to open a simple drawing window and show
-a decoded WebP file. It's not yet integrated in the automake build system, but
-you can try to manually compile it using the recommendations below.
-
-Usage: vwebp in_file [options]
-
-Decodes the WebP image file and visualize it using OpenGL
-Options are:
-  -version ..... print version number and exit
-  -noicc ....... don't use the icc profile if present
-  -nofancy ..... don't use the fancy YUV420 upscaler
-  -nofilter .... disable in-loop filtering
-  -dither <int>  dithering strength (0..100), default=50
-  -noalphadither disable alpha plane dithering
-  -usebgcolor .. display background color
-  -mt .......... use multi-threading
-  -info ........ print info
-  -h ........... this help message
-
-Keyboard shortcuts:
-  'c' ................ toggle use of color profile
-  'b' ................ toggle background color display
-  'i' ................ overlay file information
-  'd' ................ disable blending & disposal (debug)
-  'q' / 'Q' / ESC .... quit
-
-Building:
----------
-
-Prerequisites:
-1) OpenGL & OpenGL Utility Toolkit (GLUT)
-  Linux:
-    $ sudo apt-get install freeglut3-dev mesa-common-dev
-  Mac + Xcode:
-    - These libraries should be available in the OpenGL / GLUT frameworks.
-  Windows:
-    http://freeglut.sourceforge.net/index.php#download
-
-2) (Optional) qcms (Quick Color Management System)
-  i. Download qcms from Mozilla / Chromium:
-    https://hg.mozilla.org/mozilla-central/file/0e7639e3bdfb/gfx/qcms
-    https://source.chromium.org/chromium/chromium/src/+/main:third_party/qcms/;drc=d4a2f8e1ed461d8fc05ed88d1ae2dc94c9773825
-  ii. Build and archive the source files as libqcms.a / qcms.lib
-  iii. Update makefile.unix / Makefile.vc
-    a) Define WEBP_HAVE_QCMS
-    b) Update include / library paths to reference the qcms directory.
-
-Build using makefile.unix / Makefile.vc:
-$ make -f makefile.unix examples/vwebp
-> nmake /f Makefile.vc CFG=release-static \
-    ../obj/x64/release-static/bin/vwebp.exe
-
-Animation creation tool:
-========================
-The utility 'img2webp' can turn a sequence of input images (PNG, JPEG, ...)
-into an animated WebP file. It offers fine control over duration, encoding
-modes, etc.
-
-Usage:
-
-  img2webp [file_options] [[frame_options] frame_file]...
-
-File-level options (only used at the start of compression):
- -min_size ............ minimize size
- -loop <int> .......... loop count (default: 0, = infinite loop)
- -kmax <int> .......... maximum number of frame between key-frames
-                        (0=only keyframes)
- -kmin <int> .......... minimum number of frame between key-frames
-                        (0=disable key-frames altogether)
- -mixed ............... use mixed lossy/lossless automatic mode
- -v ................... verbose mode
- -h ................... this help
- -version ............. print version number and exit
-
-Per-frame options (only used for subsequent images input):
- -d <int> ............. frame duration in ms (default: 100)
- -lossless  ........... use lossless mode (default)
- -lossy ... ........... use lossy mode
- -q <float> ........... quality
- -m <int> ............. method to use
-
-example: img2webp -loop 2 in0.png -lossy in1.jpg
-                  -d 80 in2.tiff -o out.webp
-
-Note: if a single file name is passed as the argument, the arguments will be
-tokenized from this file. The file name must not start with the character '-'.
-
-Animated GIF conversion:
-========================
-Animated GIF files can be converted to WebP files with animation using the
-gif2webp utility available under examples/. The files can then be viewed using
-vwebp.
-
-Usage:
- gif2webp [options] gif_file -o webp_file
-Options:
-  -h / -help ............. this help
-  -lossy ................. encode image using lossy compression
-  -mixed ................. for each frame in the image, pick lossy
-                           or lossless compression heuristically
-  -q <float> ............. quality factor (0:small..100:big)
-  -m <int> ............... compression method (0=fast, 6=slowest)
-  -min_size .............. minimize output size (default:off)
-                           lossless compression by default; can be
-                           combined with -q, -m, -lossy or -mixed
-                           options
-  -kmin <int> ............ min distance between key frames
-  -kmax <int> ............ max distance between key frames
-  -f <int> ............... filter strength (0=off..100)
-  -metadata <string> ..... comma separated list of metadata to
-                           copy from the input to the output if present
-                           Valid values: all, none, icc, xmp (default)
-  -loop_compatibility .... use compatibility mode for Chrome
-                           version prior to M62 (inclusive)
-  -mt .................... use multi-threading if available
-
-  -version ............... print version number and exit
-  -v ..................... verbose
-  -quiet ................. don't print anything
-
-Building:
----------
-With the libgif development files installed, gif2webp can be built using
-makefile.unix:
-$ make -f makefile.unix examples/gif2webp
-
-or using autoconf:
-$ ./configure --enable-everything
-$ make
-
-Comparison of animated images:
-==============================
-Test utility anim_diff under examples/ can be used to compare two animated
-images (each can be GIF or WebP).
-
-Usage: anim_diff <image1> <image2> [options]
-
-Options:
-  -dump_frames <folder> dump decoded frames in PAM format
-  -min_psnr <float> ... minimum per-frame PSNR
-  -raw_comparison ..... if this flag is not used, RGB is
-                        premultiplied before comparison
-  -max_diff <int> ..... maximum allowed difference per channel
-                        between corresponding pixels in subsequent
-                        frames
-  -h .................. this help
-  -version ............ print version number and exit
-
-Building:
----------
-With the libgif development files and a C++ compiler installed, anim_diff can
-be built using makefile.unix:
-$ make -f makefile.unix examples/anim_diff
-
-or using autoconf:
-$ ./configure --enable-everything
-$ make
-
-Encoding API:
-=============
-
-The main encoding functions are available in the header src/webp/encode.h
-The ready-to-use ones are:
-size_t WebPEncodeRGB(const uint8_t* rgb, int width, int height, int stride,
-                     float quality_factor, uint8_t** output);
-size_t WebPEncodeBGR(const uint8_t* bgr, int width, int height, int stride,
-                     float quality_factor, uint8_t** output);
-size_t WebPEncodeRGBA(const uint8_t* rgba, int width, int height, int stride,
-                      float quality_factor, uint8_t** output);
-size_t WebPEncodeBGRA(const uint8_t* bgra, int width, int height, int stride,
-                      float quality_factor, uint8_t** output);
-
-They will convert raw RGB samples to a WebP data. The only control supplied
-is the quality factor.
-
-There are some variants for using the lossless format:
-
-size_t WebPEncodeLosslessRGB(const uint8_t* rgb, int width, int height,
-                             int stride, uint8_t** output);
-size_t WebPEncodeLosslessBGR(const uint8_t* bgr, int width, int height,
-                             int stride, uint8_t** output);
-size_t WebPEncodeLosslessRGBA(const uint8_t* rgba, int width, int height,
-                              int stride, uint8_t** output);
-size_t WebPEncodeLosslessBGRA(const uint8_t* bgra, int width, int height,
-                              int stride, uint8_t** output);
-
-Of course in this case, no quality factor is needed since the compression
-occurs without loss of the input values, at the expense of larger output sizes.
-
-Advanced encoding API:
-----------------------
-
-A more advanced API is based on the WebPConfig and WebPPicture structures.
-
-WebPConfig contains the encoding settings and is not tied to a particular
-picture.
-WebPPicture contains input data, on which some WebPConfig will be used for
-compression.
-The encoding flow looks like:
-
--------------------------------------- BEGIN PSEUDO EXAMPLE
-
-#include <webp/encode.h>
-
-  // Setup a config, starting form a preset and tuning some additional
-  // parameters
-  WebPConfig config;
-  if (!WebPConfigPreset(&config, WEBP_PRESET_PHOTO, quality_factor)) {
-    return 0;   // version error
-  }
-  // ... additional tuning
-  config.sns_strength = 90;
-  config.filter_sharpness = 6;
-  config_error = WebPValidateConfig(&config);  // not mandatory, but useful
-
-  // Setup the input data
-  WebPPicture pic;
-  if (!WebPPictureInit(&pic)) {
-    return 0;  // version error
-  }
-  pic.width = width;
-  pic.height = height;
-  // allocated picture of dimension width x height
-  if (!WebPPictureAlloc(&pic)) {
-    return 0;   // memory error
-  }
-  // at this point, 'pic' has been initialized as a container,
-  // and can receive the Y/U/V samples.
-  // Alternatively, one could use ready-made import functions like
-  // WebPPictureImportRGB(), which will take care of memory allocation.
-  // In any case, past this point, one will have to call
-  // WebPPictureFree(&pic) to reclaim memory.
-
-  // Set up a byte-output write method. WebPMemoryWriter, for instance.
-  WebPMemoryWriter wrt;
-  WebPMemoryWriterInit(&wrt);     // initialize 'wrt'
-
-  pic.writer = MyFileWriter;
-  pic.custom_ptr = my_opaque_structure_to_make_MyFileWriter_work;
-
-  // Compress!
-  int ok = WebPEncode(&config, &pic);   // ok = 0 => error occurred!
-  WebPPictureFree(&pic);  // must be called independently of the 'ok' result.
-
-  // output data should have been handled by the writer at that point.
-  // -> compressed data is the memory buffer described by wrt.mem / wrt.size
-
-  // deallocate the memory used by compressed data
-  WebPMemoryWriterClear(&wrt);
-
--------------------------------------- END PSEUDO EXAMPLE
-
-Decoding API:
-=============
-
-This is mainly just one function to call:
-
-#include "webp/decode.h"
-uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
-                       int* width, int* height);
-
-Please have a look at the file src/webp/decode.h for the details.
-There are variants for decoding in BGR/RGBA/ARGB/BGRA order, along with
-decoding to raw Y'CbCr samples. One can also decode the image directly into a
-pre-allocated buffer.
-
-To detect a WebP file and gather the picture's dimensions, the function:
-  int WebPGetInfo(const uint8_t* data, size_t data_size,
-                  int* width, int* height);
-is supplied. No decoding is involved when using it.
-
-Incremental decoding API:
-=========================
-
-In the case when data is being progressively transmitted, pictures can still
-be incrementally decoded using a slightly more complicated API. Decoder state
-is stored into an instance of the WebPIDecoder object. This object can be
-created with the purpose of decoding either RGB or Y'CbCr samples.
-For instance:
-
-  WebPDecBuffer buffer;
-  WebPInitDecBuffer(&buffer);
-  buffer.colorspace = MODE_BGR;
-  ...
-  WebPIDecoder* idec = WebPINewDecoder(&buffer);
-
-As data is made progressively available, this incremental-decoder object
-can be used to decode the picture further. There are two (mutually exclusive)
-ways to pass freshly arrived data:
-
-either by appending the fresh bytes:
-
-  WebPIAppend(idec, fresh_data, size_of_fresh_data);
-
-or by just mentioning the new size of the transmitted data:
-
-  WebPIUpdate(idec, buffer, size_of_transmitted_buffer);
-
-Note that 'buffer' can be modified between each call to WebPIUpdate, in
-particular when the buffer is resized to accommodate larger data.
-
-These functions will return the decoding status: either VP8_STATUS_SUSPENDED if
-decoding is not finished yet or VP8_STATUS_OK when decoding is done. Any other
-status is an error condition.
-
-The 'idec' object must always be released (even upon an error condition) by
-calling: WebPDelete(idec).
-
-To retrieve partially decoded picture samples, one must use the corresponding
-method: WebPIDecGetRGB or WebPIDecGetYUVA.
-It will return the last displayable pixel row.
-
-Lastly, note that decoding can also be performed into a pre-allocated pixel
-buffer. This buffer must be passed when creating a WebPIDecoder, calling
-WebPINewRGB() or WebPINewYUVA().
-
-Please have a look at the src/webp/decode.h header for further details.
-
-Advanced Decoding API:
-======================
-
-WebP decoding supports an advanced API which provides on-the-fly cropping and
-rescaling, something of great usefulness on memory-constrained environments like
-mobile phones. Basically, the memory usage will scale with the output's size,
-not the input's, when one only needs a quick preview or a zoomed in portion of
-an otherwise too-large picture. Some CPU can be saved too, incidentally.
-
--------------------------------------- BEGIN PSEUDO EXAMPLE
-     // A) Init a configuration object
-     WebPDecoderConfig config;
-     CHECK(WebPInitDecoderConfig(&config));
-
-     // B) optional: retrieve the bitstream's features.
-     CHECK(WebPGetFeatures(data, data_size, &config.input) == VP8_STATUS_OK);
-
-     // C) Adjust 'config' options, if needed
-     config.options.no_fancy_upsampling = 1;
-     config.options.use_scaling = 1;
-     config.options.scaled_width = scaledWidth();
-     config.options.scaled_height = scaledHeight();
-     // etc.
-
-     // D) Specify 'config' output options for specifying output colorspace.
-     // Optionally the external image decode buffer can also be specified.
-     config.output.colorspace = MODE_BGRA;
-     // Optionally, the config.output can be pointed to an external buffer as
-     // well for decoding the image. This externally supplied memory buffer
-     // should be big enough to store the decoded picture.
-     config.output.u.RGBA.rgba = (uint8_t*) memory_buffer;
-     config.output.u.RGBA.stride = scanline_stride;
-     config.output.u.RGBA.size = total_size_of_the_memory_buffer;
-     config.output.is_external_memory = 1;
-
-     // E) Decode the WebP image. There are two variants w.r.t decoding image.
-     // The first one (E.1) decodes the full image and the second one (E.2) is
-     // used to incrementally decode the image using small input buffers.
-     // Any one of these steps can be used to decode the WebP image.
-
-     // E.1) Decode full image.
-     CHECK(WebPDecode(data, data_size, &config) == VP8_STATUS_OK);
-
-     // E.2) Decode image incrementally.
-     WebPIDecoder* const idec = WebPIDecode(NULL, NULL, &config);
-     CHECK(idec != NULL);
-     while (bytes_remaining > 0) {
-       VP8StatusCode status = WebPIAppend(idec, input, bytes_read);
-       if (status == VP8_STATUS_OK || status == VP8_STATUS_SUSPENDED) {
-         bytes_remaining -= bytes_read;
-       } else {
-         break;
-       }
-     }
-     WebPIDelete(idec);
-
-     // F) Decoded image is now in config.output (and config.output.u.RGBA).
-     // It can be saved, displayed or otherwise processed.
-
-     // G) Reclaim memory allocated in config's object. It's safe to call
-     // this function even if the memory is external and wasn't allocated
-     // by WebPDecode().
-     WebPFreeDecBuffer(&config.output);
-
--------------------------------------- END PSEUDO EXAMPLE
-
-Bugs:
-=====
-
-Please report all bugs to the issue tracker:
-    https://bugs.chromium.org/p/webp
-Patches welcome! See this page to get started:
-    https://www.webmproject.org/code/contribute/submitting-patches/
-
-Discuss:
-========
-
-Email: webp-discuss@webmproject.org
-Web: https://groups.google.com/a/webmproject.org/group/webp-discuss
diff --git a/README.android b/README.android
index 4f445bb..8e5fca2 100644
--- a/README.android
+++ b/README.android
@@ -1,13 +1,9 @@
 URL: https://chromium.googlesource.com/webm/libwebp
-Version: 1.2.2
+Version: 1.3.0
 License: Google BSD like
 
 Local modifications:
-- Copy public headers from src/webp to include/webp, so path to headers
-  may be appended into CFLAGS without risk for other private headers
-  (e.g. bits.h) to leak into
-- Removed build files necessary for building via autoconf/automake tools
-  These files are not required to build via Android.bp
+- LOCAL_LICENSE_KINDS and related variables added to **/Android.mk
 
 The Android.bp file creates WebP decoder and encoder static libraries which
 can be added to any application by adding libwebp-decode and libwebp-encode to
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..4e201d3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,53 @@
+# WebP Codec
+
+```
+      __   __  ____  ____  ____
+     /  \\/  \/  _ \/  _ )/  _ \
+     \       /   __/  _  \   __/
+      \__\__/\____/\_____/__/ ____  ___
+            / _/ /    \    \ /  _ \/ _/
+           /  \_/   / /   \ \   __/  \__
+           \____/____/\_____/_____/____/v1.3.0
+```
+
+WebP codec is a library to encode and decode images in WebP format. This package
+contains the library that can be used in other programs to add WebP support, as
+well as the command line tools 'cwebp' and 'dwebp' to compress and decompress
+images respectively.
+
+See https://developers.google.com/speed/webp for details on the image format.
+
+The latest source tree is available at
+https://chromium.googlesource.com/webm/libwebp
+
+It is released under the same license as the WebM project. See
+https://www.webmproject.org/license/software/ or the "COPYING" file for details.
+An additional intellectual property rights grant can be found in the file
+PATENTS.
+
+## Building
+
+See the [building documentation](doc/building.md).
+
+## Encoding and Decoding Tools
+
+The examples/ directory contains tools to encode and decode images and
+animations, view information about WebP images, and more. See the
+[tools documentation](doc/tools.md).
+
+## APIs
+
+See the [APIs documentation](doc/api.md), and API usage examples in the
+`examples/` directory.
+
+## Bugs
+
+Please report all bugs to the issue tracker: https://bugs.chromium.org/p/webp
+
+Patches welcome! See [how to contribute](CONTRIBUTING.md).
+
+## Discuss
+
+Email: webp-discuss@webmproject.org
+
+Web: https://groups.google.com/a/webmproject.org/group/webp-discuss
diff --git a/README.version b/README.version
index 873040b..9189f43 100644
--- a/README.version
+++ b/README.version
@@ -1,3 +1,3 @@
-URL: https://chromium.googlesource.com/webm/libwebp/+archive/20ef03ee351d4ff03fc5ff3ec4804a879d1b9d5c.tar.gz
-Version: 1.2.2
+URL: https://chromium.googlesource.com/webm/libwebp
+Version: v1.3.0
 BugComponent: 20174
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..8ef9bab
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,2 @@
+#! /bin/sh -e
+exec autoreconf -fi
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..c25314c
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,443 @@
+// Define dependencies.
+buildscript {
+  repositories {
+    maven {
+      url "https://jcenter.bintray.com"
+    }
+  }
+  dependencies {
+    classpath "com.android.tools.build:gradle:${ANDROID_GRADLE_PLUGIN_VERSION}"
+  }
+}
+
+// Define versions in the project.
+project.ext {
+  buildToolsVersion = "${BUILD_TOOLS_VERSION}"
+  compileSdkVersion = COMPILE_SDK_VERSION.toInteger()
+}
+
+// Core libraries and executables.
+apply plugin: "c"
+def NEON
+model {
+  buildTypes {
+    debug
+    release
+  }
+  platforms {
+    arm {
+      architecture "arm"
+    }
+    arm64 {
+      architecture "arm64"
+    }
+    x86 {
+      architecture "x86"
+    }
+    x64 {
+      architecture "x86_64"
+    }
+    mips32r2
+    mips32r5
+    mips64r6
+  }
+  toolChains {
+    gcc(Gcc) {
+      target("mips32r2") {
+        cCompiler.args "-mips32r2"
+      }
+      target("mips32r5") {
+        cCompiler.args "-mips32r5"
+      }
+      target("mips64r6") {
+        cCompiler.args "-mips64r6"
+      }
+    }
+  }
+  binaries {
+    all {
+      if (toolChain in Gcc) {
+        cCompiler.args "-fPIC"
+        cCompiler.args "-Wall"
+        cCompiler.define "ANDROID"
+        cCompiler.define "HAVE_MALLOC_H"
+      }
+      // Optimizations.
+      if (buildType == buildTypes.release) {
+        if (toolChain in Gcc) {
+          cCompiler.args "-finline-functions"
+          cCompiler.args "-ffast-math"
+          cCompiler.args "-ffunction-sections"
+          cCompiler.args "-fdata-sections"
+        }
+        if (toolChain in Clang) {
+          cCompiler.args "-frename-registers -s"
+        }
+      }
+      // mips32 fails to build with clang from r14b
+      // https://bugs.chromium.org/p/webp/issues/detail?id=343
+      if (toolChain in Clang) {
+        if (getTargetPlatform() == "mips") {
+          cCompiler.args "-no-integrated-as"
+        }
+      }
+      // Check for NEON usage.
+      if (getTargetPlatform() == "arm") {
+        NEON = "c.neon"
+        cCompiler.define "HAVE_CPU_FEATURES_H"
+      } else {
+        NEON = "c"
+      }
+
+      cCompiler.args "-I" + file(".").absolutePath
+    }
+    // Link to pthread for shared libraries.
+    withType(SharedLibraryBinarySpec) {
+      if (toolChain in Gcc) {
+        cCompiler.define "HAVE_PTHREAD"
+        cCompiler.define "WEBP_USE_THREAD"
+        linker.args "-pthread"
+      }
+    }
+  }
+  components {
+    webp(NativeLibrarySpec) {
+      sources {
+        c {
+          source {
+            srcDir "sharpyuv"
+            include "sharpyuv.c"
+            include "sharpyuv_cpu.c"
+            include "sharpyuv_csp.c"
+            include "sharpyuv_dsp.c"
+            include "sharpyuv_gamma.c"
+            include "sharpyuv_neon.c"
+            include "sharpyuv_sse2.c"
+            srcDir "src/dec"
+            include "alpha_dec.c"
+            include "buffer_dec.c"
+            include "frame_dec.c"
+            include "idec_dec.c"
+            include "io_dec.c"
+            include "quant_dec.c"
+            include "tree_dec.c"
+            include "vp8_dec.c"
+            include "vp8l_dec.c"
+            include "webp_dec.c"
+            srcDir "src/dsp"
+            include "alpha_processing.c"
+            include "alpha_processing_mips_dsp_r2.c"
+            include "alpha_processing_neon.$NEON"
+            include "alpha_processing_sse2.c"
+            include "alpha_processing_sse41.c"
+            include "cpu.c"
+            include "dec.c"
+            include "dec_clip_tables.c"
+            include "dec_mips32.c"
+            include "dec_mips_dsp_r2.c"
+            include "dec_msa.c"
+            include "dec_neon.$NEON"
+            include "dec_sse2.c"
+            include "dec_sse41.c"
+            include "filters.c"
+            include "filters_mips_dsp_r2.c"
+            include "filters_msa.c"
+            include "filters_neon.$NEON"
+            include "filters_sse2.c"
+            include "lossless.c"
+            include "lossless_mips_dsp_r2.c"
+            include "lossless_msa.c"
+            include "lossless_neon.$NEON"
+            include "lossless_sse2.c"
+            include "lossless_sse41.c"
+            include "rescaler.c"
+            include "rescaler_mips32.c"
+            include "rescaler_mips_dsp_r2.c"
+            include "rescaler_msa.c"
+            include "rescaler_neon.$NEON"
+            include "rescaler_sse2.c"
+            include "upsampling.c"
+            include "upsampling_mips_dsp_r2.c"
+            include "upsampling_msa.c"
+            include "upsampling_neon.$NEON"
+            include "upsampling_sse2.c"
+            include "upsampling_sse41.c"
+            include "yuv.c"
+            include "yuv_mips32.c"
+            include "yuv_mips_dsp_r2.c"
+            include "yuv_neon.$NEON"
+            include "yuv_sse2.c"
+            include "yuv_sse41.c"
+            srcDir "src/utils"
+            include "bit_reader_utils.c"
+            include "color_cache_utils.c"
+            include "filters_utils.c"
+            include "huffman_utils.c"
+            include "quant_levels_dec_utils.c"
+            include "random_utils.c"
+            include "rescaler_utils.c"
+            include "thread_utils.c"
+            include "utils.c"
+            srcDir "src/dsp"
+            include "cost.c"
+            include "cost_mips32.c"
+            include "cost_mips_dsp_r2.c"
+            include "cost_neon.$NEON"
+            include "cost_sse2.c"
+            include "enc.c"
+            include "enc_mips32.c"
+            include "enc_mips_dsp_r2.c"
+            include "enc_msa.c"
+            include "enc_neon.$NEON"
+            include "enc_sse2.c"
+            include "enc_sse41.c"
+            include "lossless_enc.c"
+            include "lossless_enc_mips32.c"
+            include "lossless_enc_mips_dsp_r2.c"
+            include "lossless_enc_msa.c"
+            include "lossless_enc_neon.$NEON"
+            include "lossless_enc_sse2.c"
+            include "lossless_enc_sse41.c"
+            include "ssim.c"
+            include "ssim_sse2.c"
+            srcDir "src/enc"
+            include "alpha_enc.c"
+            include "analysis_enc.c"
+            include "backward_references_cost_enc.c"
+            include "backward_references_enc.c"
+            include "config_enc.c"
+            include "cost_enc.c"
+            include "filter_enc.c"
+            include "frame_enc.c"
+            include "histogram_enc.c"
+            include "iterator_enc.c"
+            include "near_lossless_enc.c"
+            include "picture_enc.c"
+            include "picture_csp_enc.c"
+            include "picture_psnr_enc.c"
+            include "picture_rescale_enc.c"
+            include "picture_tools_enc.c"
+            include "predictor_enc.c"
+            include "quant_enc.c"
+            include "syntax_enc.c"
+            include "token_enc.c"
+            include "tree_enc.c"
+            include "vp8l_enc.c"
+            include "webp_enc.c"
+            srcDir "src/utils"
+            include "bit_writer_utils.c"
+            include "huffman_encode_utils.c"
+            include "quant_levels_utils.c"
+          }
+          exportedHeaders {
+            srcDir "src"
+          }
+        }
+      }
+    }
+
+    webpdemux(NativeLibrarySpec) {
+      sources {
+        c {
+          source {
+            srcDir "src/demux"
+            include "anim_decode.c"
+            include "demux.c"
+          }
+        }
+      }
+    }
+
+    webpmux(NativeLibrarySpec) {
+      sources {
+        c {
+          source {
+            srcDir "src/mux/"
+            include "anim_encode.c"
+            include "muxedit.c"
+            include "muxinternal.c"
+            include "muxread.c"
+          }
+        }
+      }
+    }
+
+    // Executables from examples.
+    example_util(NativeLibrarySpec) {
+      binaries {
+        all {
+          lib library: "webp", linkage: "static"
+        }
+      }
+      sources {
+        c {
+          source {
+            srcDir "./examples"
+            include "example_util.c"
+          }
+        }
+      }
+    }
+
+    imageio_util(NativeLibrarySpec) {
+      binaries {
+        all {
+          lib library: "webp", linkage: "static"
+        }
+      }
+      sources {
+        c {
+          source {
+            srcDir "./imageio"
+            include "imageio_util.c"
+          }
+        }
+      }
+    }
+
+    imagedec(NativeLibrarySpec) {
+      binaries {
+        all {
+          lib library: "webpdemux", linkage: "static"
+          lib library: "webp", linkage: "static"
+        }
+      }
+      sources {
+        c {
+          source {
+            srcDir "./imageio"
+            include "image_dec.c"
+            include "jpegdec.c"
+            include "metadata.c"
+            include "pngdec.c"
+            include "pnmdec.c"
+            include "tiffdec.c"
+            include "webpdec.c"
+          }
+        }
+      }
+    }
+
+    imageenc(NativeLibrarySpec) {
+      binaries {
+        all {
+          lib library: "webp", linkage: "static"
+          lib library: "imageio_util", linkage: "static"
+        }
+      }
+      sources {
+        c {
+          source {
+            srcDir "./imageio"
+            include "image_enc.c"
+          }
+        }
+      }
+    }
+
+    cwebp(NativeExecutableSpec) {
+      binaries {
+        all {
+          lib library: "example_util", linkage: "static"
+          lib library: "imagedec", linkage: "static"
+          lib library: "imageio_util", linkage: "static"
+          lib library: "webpdemux", linkage: "static"
+          lib library: "webp", linkage: "static"
+        }
+      }
+      sources {
+        c {
+          source {
+            srcDir "./examples"
+            include "cwebp.c"
+          }
+        }
+      }
+    }
+
+    dwebp(NativeExecutableSpec) {
+      binaries {
+        all {
+          lib library: "example_util", linkage: "static"
+          lib library: "imagedec", linkage: "static"
+          lib library: "imageenc", linkage: "static"
+          lib library: "imageio_util", linkage: "static"
+          lib library: "webpdemux", linkage: "static"
+          lib library: "webp"
+        }
+      }
+      sources {
+        c {
+          source {
+              srcDir "./examples"
+              include "dwebp.c"
+          }
+        }
+      }
+    }
+
+    webpmux_example(NativeExecutableSpec) {
+      binaries {
+        all {
+          lib library: "example_util", linkage: "static"
+          lib library: "imageio_util", linkage: "static"
+          lib library: "webpmux", linkage: "static"
+          lib library: "webp"
+        }
+      }
+      sources {
+        c {
+          source {
+            srcDir "./examples"
+            include "webpmux.c"
+          }
+        }
+      }
+    }
+
+    img2webp_example(NativeExecutableSpec) {
+      binaries {
+        all {
+          lib library: "example_util", linkage: "static"
+          lib library: "imagedec", linkage: "static"
+          lib library: "imageio_util", linkage: "static"
+          lib library: "webpmux", linkage: "static"
+          lib library: "webpdemux", linkage: "static"
+          lib library: "webp"
+        }
+      }
+      sources {
+        c {
+          source {
+            srcDir "./examples"
+            include "img2webp.c"
+          }
+        }
+      }
+    }
+
+    webpinfo_example(NativeExecutableSpec) {
+      binaries {
+        all {
+          lib library: "example_util", linkage: "static"
+          lib library: "imageio_util", linkage: "static"
+          lib library: "webp"
+        }
+      }
+      sources {
+        c {
+          source {
+            srcDir "./examples"
+            include "webpinfo.c"
+          }
+        }
+      }
+    }
+  }
+  tasks {
+    // Task to test all possible configurations.
+    buildAllExecutables(Task) {
+      dependsOn $.binaries.findAll { it.buildable }
+    }
+  }
+}
diff --git a/cmake/WebPConfig.cmake.in b/cmake/WebPConfig.cmake.in
new file mode 100644
index 0000000..f334739
--- /dev/null
+++ b/cmake/WebPConfig.cmake.in
@@ -0,0 +1,16 @@
+set(WebP_VERSION @PROJECT_VERSION@)
+set(WEBP_VERSION ${WebP_VERSION})
+
+@PACKAGE_INIT@
+
+if(@WEBP_USE_THREAD@)
+  include(CMakeFindDependencyMacro)
+  find_dependency(Threads REQUIRED)
+endif()
+
+include ("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
+
+set(WebP_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@")
+set(WEBP_INCLUDE_DIRS ${WebP_INCLUDE_DIRS})
+set(WebP_LIBRARIES "@INSTALLED_LIBRARIES@")
+set(WEBP_LIBRARIES "${WebP_LIBRARIES}")
diff --git a/cmake/config.h.in b/cmake/config.h.in
new file mode 100644
index 0000000..3770935
--- /dev/null
+++ b/cmake/config.h.in
@@ -0,0 +1,149 @@
+/* Adapted from the autotools src/webp/config.h.in.  */
+
+/* Define if building universal (internal helper macro) */
+/* TODO: handle properly in CMake */
+#cmakedefine AC_APPLE_UNIVERSAL_BUILD 1
+
+/* Set to 1 if __builtin_bswap16 is available */
+#cmakedefine HAVE_BUILTIN_BSWAP16 1
+
+/* Set to 1 if __builtin_bswap32 is available */
+#cmakedefine HAVE_BUILTIN_BSWAP32 1
+
+/* Set to 1 if __builtin_bswap64 is available */
+#cmakedefine HAVE_BUILTIN_BSWAP64 1
+
+/* Define to 1 if you have the <cpu-features.h> header file. */
+#cmakedefine HAVE_CPU_FEATURES_H 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#cmakedefine HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <GLUT/glut.h> header file. */
+#cmakedefine HAVE_GLUT_GLUT_H 1
+
+/* Define to 1 if you have the <GL/glut.h> header file. */
+#cmakedefine HAVE_GL_GLUT_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#cmakedefine HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#cmakedefine HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <OpenGL/glut.h> header file. */
+#cmakedefine HAVE_OPENGL_GLUT_H 1
+
+/* Have PTHREAD_PRIO_INHERIT. */
+#cmakedefine HAVE_PTHREAD_PRIO_INHERIT @HAVE_PTHREAD_PRIO_INHERIT@
+
+/* Define to 1 if you have the <shlwapi.h> header file. */
+#cmakedefine HAVE_SHLWAPI_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#cmakedefine HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#cmakedefine HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#cmakedefine HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#cmakedefine HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#cmakedefine HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#cmakedefine HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#cmakedefine HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the <wincodec.h> header file. */
+#cmakedefine HAVE_WINCODEC_H 1
+
+/* Define to 1 if you have the <windows.h> header file. */
+#cmakedefine HAVE_WINDOWS_H 1
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+/* TODO: handle properly in CMake */
+#cmakedefine LT_OBJDIR "@LT_OBJDIR@"
+
+/* Name of package */
+#cmakedefine PACKAGE "@PROJECT_NAME@"
+
+/* Define to the address where bug reports for this package should be sent. */
+#cmakedefine PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@"
+
+/* Define to the full name of this package. */
+#cmakedefine PACKAGE_NAME "@PACKAGE_NAME@"
+
+/* Define to the full name and version of this package. */
+#cmakedefine PACKAGE_STRING "@PACKAGE_STRING@"
+
+/* Define to the one symbol short name of this package. */
+#cmakedefine PACKAGE_TARNAME "@PACKAGE_TARNAME@"
+
+/* Define to the home page for this package. */
+#cmakedefine PACKAGE_URL "@PACKAGE_URL@"
+
+/* Define to the version of this package. */
+#cmakedefine PACKAGE_VERSION "@PACKAGE_VERSION@"
+
+/* Define to 1 if you have the ANSI C header files. */
+#cmakedefine STDC_HEADERS 1
+
+/* Version number of package */
+#cmakedefine VERSION "@VERSION@"
+
+/* Set to 1 if GIF library is installed */
+#cmakedefine WEBP_HAVE_GIF 1
+
+/* Set to 1 if OpenGL is supported */
+#cmakedefine WEBP_HAVE_GL 1
+
+/* Set to 1 if JPEG library is installed */
+#cmakedefine WEBP_HAVE_JPEG 1
+
+/* Set to 1 if NEON is supported */
+#cmakedefine WEBP_HAVE_NEON
+
+/* Set to 1 if runtime detection of NEON is enabled */
+/* TODO: handle properly in CMake */
+#cmakedefine WEBP_HAVE_NEON_RTCD
+
+/* Set to 1 if PNG library is installed */
+#cmakedefine WEBP_HAVE_PNG 1
+
+/* Set to 1 if SDL library is installed */
+#cmakedefine WEBP_HAVE_SDL 1
+
+/* Set to 1 if SSE2 is supported */
+#cmakedefine WEBP_HAVE_SSE2 1
+
+/* Set to 1 if SSE4.1 is supported */
+#cmakedefine WEBP_HAVE_SSE41 1
+
+/* Set to 1 if TIFF library is installed */
+#cmakedefine WEBP_HAVE_TIFF 1
+
+/* Enable near lossless encoding */
+#cmakedefine WEBP_NEAR_LOSSLESS 1
+
+/* Undefine this to disable thread support. */
+#cmakedefine WEBP_USE_THREAD 1
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+   significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+#  define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+#  undef WORDS_BIGENDIAN
+# endif
+#endif
diff --git a/cmake/cpu.cmake b/cmake/cpu.cmake
new file mode 100644
index 0000000..7513ca8
--- /dev/null
+++ b/cmake/cpu.cmake
@@ -0,0 +1,159 @@
+#  Copyright (c) 2021 Google LLC.
+#
+#  Use of this source code is governed by a BSD-style license
+#  that can be found in the LICENSE file in the root of the source
+#  tree. An additional intellectual property rights grant can be found
+#  in the file PATENTS.  All contributing project authors may
+#  be found in the AUTHORS file in the root of the source tree.
+
+# Check for SIMD extensions.
+include(CMakePushCheckState)
+
+function(webp_check_compiler_flag WEBP_SIMD_FLAG ENABLE_SIMD)
+  if(NOT ENABLE_SIMD)
+    message(STATUS "Disabling ${WEBP_SIMD_FLAG} optimization.")
+    set(WEBP_HAVE_${WEBP_SIMD_FLAG} 0 PARENT_SCOPE)
+    return()
+  endif()
+  unset(WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG} CACHE)
+  cmake_push_check_state()
+  set(CMAKE_REQUIRED_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR})
+  check_c_source_compiles(
+    "
+      #include \"${CMAKE_CURRENT_LIST_DIR}/../src/dsp/dsp.h\"
+      int main(void) {
+        #if !defined(WEBP_USE_${WEBP_SIMD_FLAG})
+        this is not valid code
+        #endif
+        return 0;
+      }
+    "
+    WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG})
+  cmake_pop_check_state()
+  if(WEBP_HAVE_FLAG_${WEBP_SIMD_FLAG})
+    set(WEBP_HAVE_${WEBP_SIMD_FLAG} 1 PARENT_SCOPE)
+  else()
+    set(WEBP_HAVE_${WEBP_SIMD_FLAG} 0 PARENT_SCOPE)
+  endif()
+endfunction()
+
+# those are included in the names of WEBP_USE_* in c++ code.
+set(WEBP_SIMD_FLAGS "SSE41;SSE2;MIPS32;MIPS_DSP_R2;NEON;MSA")
+set(WEBP_SIMD_FILE_EXTENSIONS
+    "_sse41.c;_sse2.c;_mips32.c;_mips_dsp_r2.c;_neon.c;_msa.c")
+if(MSVC AND CMAKE_C_COMPILER_ID STREQUAL "MSVC")
+  # With at least Visual Studio 12 (2013)+ /arch is not necessary to build SSE2
+  # or SSE4 code unless a lesser /arch is forced. MSVC does not have a SSE4
+  # flag, but an AVX one. Using that with SSE4 code risks generating illegal
+  # instructions when used on machines with SSE4 only. The flags are left for
+  # older (untested) versions to avoid any potential compatibility issues.
+  if(MSVC_VERSION GREATER_EQUAL 1800 AND NOT CMAKE_C_FLAGS MATCHES "/arch:")
+    set(SIMD_ENABLE_FLAGS)
+  else()
+    set(SIMD_ENABLE_FLAGS "/arch:AVX;/arch:SSE2;;;;")
+  endif()
+  set(SIMD_DISABLE_FLAGS)
+else()
+  set(SIMD_ENABLE_FLAGS "-msse4.1;-msse2;-mips32;-mdspr2;-mfpu=neon;-mmsa")
+  set(SIMD_DISABLE_FLAGS "-mno-sse4.1;-mno-sse2;;-mno-dspr2;;-mno-msa")
+endif()
+
+set(WEBP_SIMD_FILES_TO_INCLUDE)
+set(WEBP_SIMD_FLAGS_TO_INCLUDE)
+
+if(${ANDROID})
+  if(${ANDROID_ABI} STREQUAL "armeabi-v7a")
+    # This is because Android studio uses the configuration "-march=armv7-a
+    # -mfloat-abi=softfp -mfpu=vfpv3-d16" that does not trigger neon
+    # optimizations but should (as this configuration does not exist anymore).
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=neon ")
+  endif()
+endif()
+
+list(LENGTH WEBP_SIMD_FLAGS WEBP_SIMD_FLAGS_LENGTH)
+math(EXPR WEBP_SIMD_FLAGS_RANGE "${WEBP_SIMD_FLAGS_LENGTH} - 1")
+
+foreach(I_SIMD RANGE ${WEBP_SIMD_FLAGS_RANGE})
+  # With Emscripten 2.0.9 -msimd128 -mfpu=neon will enable NEON, but the source
+  # will fail to compile.
+  if(EMSCRIPTEN AND ${I_SIMD} GREATER_EQUAL 2)
+    break()
+  endif()
+
+  list(GET WEBP_SIMD_FLAGS ${I_SIMD} WEBP_SIMD_FLAG)
+
+  # First try with no extra flag added as the compiler might have default flags
+  # (especially on Android).
+  unset(WEBP_HAVE_${WEBP_SIMD_FLAG} CACHE)
+  cmake_push_check_state()
+  set(CMAKE_REQUIRED_FLAGS)
+  webp_check_compiler_flag(${WEBP_SIMD_FLAG} ${WEBP_ENABLE_SIMD})
+  if(NOT WEBP_HAVE_${WEBP_SIMD_FLAG})
+    list(GET SIMD_ENABLE_FLAGS ${I_SIMD} SIMD_COMPILE_FLAG)
+    if(EMSCRIPTEN)
+      set(SIMD_COMPILE_FLAG "-msimd128 ${SIMD_COMPILE_FLAG}")
+    endif()
+    set(CMAKE_REQUIRED_FLAGS ${SIMD_COMPILE_FLAG})
+    webp_check_compiler_flag(${WEBP_SIMD_FLAG} ${WEBP_ENABLE_SIMD})
+  else()
+    if(MSVC AND SIMD_ENABLE_FLAGS)
+      # The detection for SSE2/SSE4 support under MSVC is based on the compiler
+      # version so e.g., clang-cl will require flags to enable the assembly.
+      list(GET SIMD_ENABLE_FLAGS ${I_SIMD} SIMD_COMPILE_FLAG)
+    else()
+      set(SIMD_COMPILE_FLAG " ")
+    endif()
+  endif()
+  # Check which files we should include or not.
+  list(GET WEBP_SIMD_FILE_EXTENSIONS ${I_SIMD} WEBP_SIMD_FILE_EXTENSION)
+  file(GLOB SIMD_FILES "${CMAKE_CURRENT_LIST_DIR}/../"
+       "src/dsp/*${WEBP_SIMD_FILE_EXTENSION}")
+  if(WEBP_HAVE_${WEBP_SIMD_FLAG})
+    # Memorize the file and flags.
+    foreach(FILE ${SIMD_FILES})
+      list(APPEND WEBP_SIMD_FILES_TO_INCLUDE ${FILE})
+      list(APPEND WEBP_SIMD_FLAGS_TO_INCLUDE ${SIMD_COMPILE_FLAG})
+    endforeach()
+  else()
+    # Remove the file from the list.
+    foreach(FILE ${SIMD_FILES})
+      list(APPEND WEBP_SIMD_FILES_NOT_TO_INCLUDE ${FILE})
+    endforeach()
+    # Explicitly disable SIMD.
+    if(SIMD_DISABLE_FLAGS)
+      list(GET SIMD_DISABLE_FLAGS ${I_SIMD} SIMD_COMPILE_FLAG)
+      include(CheckCCompilerFlag)
+      if(SIMD_COMPILE_FLAG)
+        # Between 3.17.0 and 3.18.2 check_cxx_compiler_flag() sets a normal
+        # variable at parent scope while check_cxx_source_compiles() continues
+        # to set an internal cache variable, so we unset both to avoid the
+        # failure / success state persisting between checks. See
+        # https://gitlab.kitware.com/cmake/cmake/-/issues/21207.
+        unset(HAS_COMPILE_FLAG)
+        unset(HAS_COMPILE_FLAG CACHE)
+        check_c_compiler_flag(${SIMD_COMPILE_FLAG} HAS_COMPILE_FLAG)
+        if(HAS_COMPILE_FLAG)
+          # Do one more check for Clang to circumvent CMake issue 13194.
+          if(COMMAND check_compiler_flag_common_patterns)
+            # Only in CMake 3.0 and above.
+            check_compiler_flag_common_patterns(COMMON_PATTERNS)
+          else()
+            set(COMMON_PATTERNS)
+          endif()
+          set(CMAKE_REQUIRED_DEFINITIONS ${SIMD_COMPILE_FLAG})
+          check_c_source_compiles(
+            "int main(void) {return 0;}" FLAG_${SIMD_COMPILE_FLAG} FAIL_REGEX
+            "warning: argument unused during compilation:" ${COMMON_PATTERNS})
+          if(NOT FLAG_${SIMD_COMPILE_FLAG})
+            unset(HAS_COMPILE_FLAG)
+            unset(HAS_COMPILE_FLAG CACHE)
+          endif()
+        endif()
+        if(HAS_COMPILE_FLAG)
+          set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${SIMD_COMPILE_FLAG}")
+        endif()
+      endif()
+    endif()
+  endif()
+  cmake_pop_check_state()
+endforeach()
diff --git a/cmake/deps.cmake b/cmake/deps.cmake
new file mode 100644
index 0000000..48c821e
--- /dev/null
+++ b/cmake/deps.cmake
@@ -0,0 +1,190 @@
+#  Copyright (c) 2021 Google LLC.
+#
+#  Use of this source code is governed by a BSD-style license
+#  that can be found in the LICENSE file in the root of the source
+#  tree. An additional intellectual property rights grant can be found
+#  in the file PATENTS.  All contributing project authors may
+#  be found in the AUTHORS file in the root of the source tree.
+
+# Generate the config.h to compile with specific intrinsics / libs.
+
+# Check for compiler options.
+include(CheckCSourceCompiles)
+check_c_source_compiles(
+  "
+    int main(void) {
+      (void)__builtin_bswap16(0);
+      return 0;
+    }
+  "
+  HAVE_BUILTIN_BSWAP16)
+check_c_source_compiles(
+  "
+    int main(void) {
+      (void)__builtin_bswap32(0);
+      return 0;
+    }
+  "
+  HAVE_BUILTIN_BSWAP32)
+check_c_source_compiles(
+  "
+    int main(void) {
+      (void)__builtin_bswap64(0);
+      return 0;
+    }
+  "
+  HAVE_BUILTIN_BSWAP64)
+
+# Check for libraries.
+if(WEBP_USE_THREAD)
+  find_package(Threads)
+  if(Threads_FOUND)
+    # work around cmake bug on QNX (https://cmake.org/Bug/view.php?id=11333)
+    if(CMAKE_USE_PTHREADS_INIT AND NOT CMAKE_SYSTEM_NAME STREQUAL "QNX")
+      set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
+    endif()
+    check_c_source_compiles(
+      "
+        #include <pthread.h>
+        int main (void) {
+          int attr = PTHREAD_PRIO_INHERIT;
+          return attr;
+        }
+      "
+      FLAG_HAVE_PTHREAD_PRIO_INHERIT)
+    set(HAVE_PTHREAD_PRIO_INHERIT ${FLAG_HAVE_PTHREAD_PRIO_INHERIT})
+    list(APPEND WEBP_DEP_LIBRARIES Threads::Threads)
+  endif()
+  set(WEBP_USE_THREAD ${Threads_FOUND})
+endif()
+
+# TODO: this seems unused, check with autotools.
+set(LT_OBJDIR ".libs/")
+
+# Only useful for vwebp, so useless for now.
+find_package(OpenGL)
+set(WEBP_HAVE_GL ${OPENGL_FOUND})
+
+# Check if we need to link to the C math library. We do not look for it as it is
+# not found when cross-compiling, while it is here.
+check_c_source_compiles(
+  "
+    #include <math.h>
+    int main(int argc, char** argv) {
+      return (int)pow(argc, 2.5);
+    }
+  "
+  HAVE_MATH_LIBRARY)
+if(NOT HAVE_MATH_LIBRARY)
+  message(STATUS "Adding -lm flag.")
+  list(APPEND SHARPYUV_DEP_LIBRARIES m)
+  list(APPEND WEBP_DEP_LIBRARIES m)
+endif()
+
+# Find the standard image libraries.
+set(WEBP_DEP_IMG_LIBRARIES)
+set(WEBP_DEP_IMG_INCLUDE_DIRS)
+foreach(I_LIB PNG JPEG TIFF)
+  # Disable tiff when compiling in static mode as it is failing on Ubuntu.
+  if(WEBP_LINK_STATIC AND ${I_LIB} STREQUAL "TIFF")
+    message("TIFF is disabled when statically linking.")
+    continue()
+  endif()
+  find_package(${I_LIB})
+  set(WEBP_HAVE_${I_LIB} ${${I_LIB}_FOUND})
+  if(${I_LIB}_FOUND)
+    list(APPEND WEBP_DEP_IMG_LIBRARIES ${${I_LIB}_LIBRARIES})
+    list(APPEND WEBP_DEP_IMG_INCLUDE_DIRS ${${I_LIB}_INCLUDE_DIR}
+         ${${I_LIB}_INCLUDE_DIRS})
+  endif()
+endforeach()
+if(WEBP_DEP_IMG_INCLUDE_DIRS)
+  list(REMOVE_DUPLICATES WEBP_DEP_IMG_INCLUDE_DIRS)
+endif()
+
+# GIF detection, gifdec isn't part of the imageio lib.
+include(CMakePushCheckState)
+set(WEBP_DEP_GIF_LIBRARIES)
+set(WEBP_DEP_GIF_INCLUDE_DIRS)
+find_package(GIF)
+set(WEBP_HAVE_GIF ${GIF_FOUND})
+if(GIF_FOUND)
+  # GIF find_package only locates the header and library, it doesn't fail
+  # compile tests when detecting the version, but falls back to 3 (as of at
+  # least cmake 3.7.2). Make sure the library links to avoid incorrect detection
+  # when cross compiling.
+  cmake_push_check_state()
+  set(CMAKE_REQUIRED_LIBRARIES ${GIF_LIBRARIES})
+  set(CMAKE_REQUIRED_INCLUDES ${GIF_INCLUDE_DIR})
+  check_c_source_compiles(
+    "
+      #include <gif_lib.h>
+      int main(void) {
+        (void)DGifOpenFileHandle;
+        return 0;
+      }
+      "
+    GIF_COMPILES)
+  cmake_pop_check_state()
+  if(GIF_COMPILES)
+    list(APPEND WEBP_DEP_GIF_LIBRARIES ${GIF_LIBRARIES})
+    list(APPEND WEBP_DEP_GIF_INCLUDE_DIRS ${GIF_INCLUDE_DIR})
+  else()
+    unset(GIF_FOUND)
+  endif()
+endif()
+
+# Check for specific headers.
+include(CheckIncludeFiles)
+check_include_files("stdlib.h;stdarg.h;string.h;float.h" STDC_HEADERS)
+check_include_files(dlfcn.h HAVE_DLFCN_H)
+check_include_files(GLUT/glut.h HAVE_GLUT_GLUT_H)
+check_include_files(GL/glut.h HAVE_GL_GLUT_H)
+check_include_files(inttypes.h HAVE_INTTYPES_H)
+check_include_files(memory.h HAVE_MEMORY_H)
+check_include_files(OpenGL/glut.h HAVE_OPENGL_GLUT_H)
+check_include_files(shlwapi.h HAVE_SHLWAPI_H)
+check_include_files(stdint.h HAVE_STDINT_H)
+check_include_files(stdlib.h HAVE_STDLIB_H)
+check_include_files(strings.h HAVE_STRINGS_H)
+check_include_files(string.h HAVE_STRING_H)
+check_include_files(sys/stat.h HAVE_SYS_STAT_H)
+check_include_files(sys/types.h HAVE_SYS_TYPES_H)
+check_include_files(unistd.h HAVE_UNISTD_H)
+check_include_files(wincodec.h HAVE_WINCODEC_H)
+check_include_files(windows.h HAVE_WINDOWS_H)
+
+# Windows specifics
+if(HAVE_WINCODEC_H)
+  list(APPEND WEBP_DEP_LIBRARIES shlwapi ole32 windowscodecs)
+endif()
+
+# Check for SIMD extensions.
+include(${CMAKE_CURRENT_LIST_DIR}/cpu.cmake)
+
+# Define extra info.
+set(PACKAGE ${PROJECT_NAME})
+set(PACKAGE_NAME ${PROJECT_NAME})
+
+# Read from configure.ac.
+file(READ ${CMAKE_CURRENT_SOURCE_DIR}/configure.ac CONFIGURE_AC)
+string(REGEX MATCHALL "\\[([0-9a-z\\.:/]*)\\]" CONFIGURE_AC_PACKAGE_INFO
+             ${CONFIGURE_AC})
+function(strip_bracket VAR)
+  string(LENGTH ${${VAR}} TMP_LEN)
+  math(EXPR TMP_LEN ${TMP_LEN}-2)
+  string(SUBSTRING ${${VAR}} 1 ${TMP_LEN} TMP_SUB)
+  set(${VAR} ${TMP_SUB} PARENT_SCOPE)
+endfunction()
+
+list(GET CONFIGURE_AC_PACKAGE_INFO 1 PACKAGE_VERSION)
+strip_bracket(PACKAGE_VERSION)
+list(GET CONFIGURE_AC_PACKAGE_INFO 2 PACKAGE_BUGREPORT)
+strip_bracket(PACKAGE_BUGREPORT)
+list(GET CONFIGURE_AC_PACKAGE_INFO 3 PACKAGE_URL)
+strip_bracket(PACKAGE_URL)
+
+# Build more info.
+set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
+set(PACKAGE_TARNAME ${PACKAGE_NAME})
+set(VERSION ${PACKAGE_VERSION})
diff --git a/codereview.settings b/codereview.settings
new file mode 100644
index 0000000..ccba2ee
--- /dev/null
+++ b/codereview.settings
@@ -0,0 +1,4 @@
+# This file is used by git cl to get repository specific information.
+GERRIT_HOST: True
+CODE_REVIEW_SERVER: chromium-review.googlesource.com
+GERRIT_SQUASH_UPLOADS: False
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..de5dfe7
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,803 @@
+AC_INIT([libwebp], [1.3.0],
+        [https://bugs.chromium.org/p/webp],,
+        [https://developers.google.com/speed/webp])
+AC_CANONICAL_HOST
+AC_PREREQ([2.60])
+AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])
+
+dnl === automake >= 1.12 requires this for 'unusual archivers' support.
+dnl === it must occur before LT_INIT (AC_PROG_LIBTOOL).
+m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
+
+dnl === AC_PROG_LIBTOOL is deprecated.
+m4_ifdef([LT_INIT], [LT_INIT], [AC_PROG_LIBTOOL])
+AC_PROG_SED
+AM_PROG_CC_C_O
+
+dnl === Enable less verbose output when building.
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+dnl == test endianness
+AC_C_BIGENDIAN
+
+dnl === SET_IF_UNSET(shell_var, value)
+dnl ===   Set the shell variable 'shell_var' to 'value' if it is unset.
+AC_DEFUN([SET_IF_UNSET], [test "${$1+set}" = "set" || $1=$2])
+
+AC_ARG_ENABLE([everything],
+              AS_HELP_STRING([--enable-everything],
+                             [Enable all optional targets. These can still be
+                              disabled with --disable-target]),
+              [SET_IF_UNSET([enable_libsharpyuv], [$enableval])
+               SET_IF_UNSET([enable_libwebpdecoder], [$enableval])
+               SET_IF_UNSET([enable_libwebpdemux], [$enableval])
+               SET_IF_UNSET([enable_libwebpextras], [$enableval])
+               SET_IF_UNSET([enable_libwebpmux], [$enableval])])
+
+dnl === Check whether libwebpmux should be built
+AC_MSG_CHECKING(whether libwebpmux is to be built)
+AC_ARG_ENABLE([libwebpmux],
+              AS_HELP_STRING([--disable-libwebpmux],
+                             [Disable libwebpmux @<:@default=no@:>@]),
+              [], [enable_libwebpmux=yes])
+AC_MSG_RESULT(${enable_libwebpmux-no})
+AM_CONDITIONAL([BUILD_MUX], [test "$enable_libwebpmux" = "yes"])
+
+dnl === Check whether libwebpdemux should be built
+AC_MSG_CHECKING(whether libwebpdemux is to be built)
+AC_ARG_ENABLE([libwebpdemux],
+              AS_HELP_STRING([--disable-libwebpdemux],
+                             [Disable libwebpdemux @<:@default=no@:>@]),
+              [], [enable_libwebpdemux=yes])
+AC_MSG_RESULT(${enable_libwebpdemux-no})
+AM_CONDITIONAL([BUILD_DEMUX], [test "$enable_libwebpdemux" = "yes"])
+
+dnl === Check whether decoder library should be built.
+AC_MSG_CHECKING(whether decoder library is to be built)
+AC_ARG_ENABLE([libwebpdecoder],
+              AS_HELP_STRING([--enable-libwebpdecoder],
+                             [Build libwebpdecoder @<:@default=no@:>@]))
+AC_MSG_RESULT(${enable_libwebpdecoder-no})
+AM_CONDITIONAL([BUILD_LIBWEBPDECODER], [test "$enable_libwebpdecoder" = "yes"])
+
+dnl === Check whether libwebpextras should be built
+AC_MSG_CHECKING(whether libwebpextras is to be built)
+AC_ARG_ENABLE([libwebpextras],
+              AS_HELP_STRING([--enable-libwebpextras],
+                             [Build libwebpextras @<:@default=no@:>@]))
+AC_MSG_RESULT(${enable_libwebpextras-no})
+AM_CONDITIONAL([BUILD_EXTRAS], [test "$enable_libwebpextras" = "yes"])
+
+dnl === If --enable-asserts is not defined, define NDEBUG
+
+AC_MSG_CHECKING(whether asserts are enabled)
+AC_ARG_ENABLE([asserts],
+              AS_HELP_STRING([--enable-asserts],
+                             [Enable assert checks]))
+if test "x${enable_asserts-no}" = "xno"; then
+  AM_CPPFLAGS="${AM_CPPFLAGS} -DNDEBUG"
+fi
+AC_MSG_RESULT(${enable_asserts-no})
+AC_SUBST([AM_CPPFLAGS])
+
+AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=DIR],
+            [Path to the pkgconfig directory @<:@LIBDIR/pkgconfig@:>@]),
+            [pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig'])
+AC_SUBST([pkgconfigdir])
+
+dnl === TEST_AND_ADD_CFLAGS(var, flag)
+dnl ===   Checks whether $CC supports 'flag' and adds it to 'var'
+dnl ===   on success.
+AC_DEFUN([TEST_AND_ADD_CFLAGS],
+         [SAVED_CFLAGS="$CFLAGS"
+          CFLAGS="-Werror $2"
+          AC_MSG_CHECKING([whether $CC supports $2])
+          dnl Note AC_LANG_PROGRAM([]) uses an old-style main definition.
+          AC_COMPILE_IFELSE([AC_LANG_SOURCE([int main(void) { return 0; }])],
+                            [AC_MSG_RESULT([yes])]
+                            dnl Simply append the variable avoiding a
+                            dnl compatibility ifdef for AS_VAR_APPEND as this
+                            dnl variable shouldn't grow all that large.
+                            [$1="${$1} $2"],
+                            [AC_MSG_RESULT([no])])
+          CFLAGS="$SAVED_CFLAGS"])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-fvisibility=hidden])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wall])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wconstant-conversion])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wdeclaration-after-statement])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wextra])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wfloat-conversion])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wformat -Wformat-nonliteral])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wformat -Wformat-security])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wmissing-declarations])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wmissing-prototypes])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wold-style-definition])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wparentheses-equality])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wshadow])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wshorten-64-to-32])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wundef])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunreachable-code-aggressive])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunreachable-code])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunused-but-set-variable])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wunused])
+TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wvla])
+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62040
+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61622
+AS_IF([test "$GCC" = "yes" ], [
+       gcc_version=`$CC -dumpversion`
+       gcc_wht_bug=""
+       case "$host_cpu" in
+         aarch64|arm64)
+          case "$gcc_version" in
+            4.9|4.9.0|4.9.1) gcc_wht_bug=yes ;;
+          esac
+       esac
+       AS_IF([test "$gcc_wht_bug" = "yes"], [
+              TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-frename-registers])])])
+# Use -flax-vector-conversions, if available, when building intrinsics with
+# older versions of gcc. The flag appeared in 4.3.x, but if backported, and
+# -fno-lax-vector-conversions is set, errors may occur with the intrinsics
+# files along with the older system includes, e.g., emmintrin.h.
+# Originally observed with cc (GCC) 4.2.1 20070831 patched [FreeBSD] (9.3).
+# https://bugs.chromium.org/p/webp/issues/detail?id=274
+AS_IF([test "$GCC" = "yes" ], [
+       case "$host_cpu" in
+         amd64|i?86|x86_64)
+           AC_COMPILE_IFELSE(
+             dnl only check for -flax-vector-conversions with older gcc, skip
+             dnl clang as it reports itself as 4.2.1, but the flag isn't needed.
+             [AC_LANG_SOURCE([#if !defined(__clang__) && defined(__GNUC__) && \
+                                  ((__GNUC__ << 8) | __GNUC_MINOR__) < 0x403
+                              #error old gcc
+                              #endif
+                              int main(void) { return 0; }
+                             ])],,
+              [TEST_AND_ADD_CFLAGS([INTRINSICS_CFLAGS],
+                                   [-flax-vector-conversions])])
+           ;;
+       esac])
+AC_SUBST([AM_CFLAGS])
+
+dnl === Check for machine specific flags
+AC_ARG_ENABLE([sse4.1],
+              AS_HELP_STRING([--disable-sse4.1],
+                             [Disable detection of SSE4.1 support
+                              @<:@default=auto@:>@]))
+
+AS_IF([test "x$enable_sse4_1" != "xno" -a "x$enable_sse2" != "xno"], [
+  SSE41_FLAGS="$INTRINSICS_CFLAGS $SSE41_FLAGS"
+  TEST_AND_ADD_CFLAGS([SSE41_FLAGS], [-msse4.1])
+  AS_IF([test -n "$SSE41_FLAGS"], [
+    SAVED_CFLAGS=$CFLAGS
+    CFLAGS="$CFLAGS $SSE41_FLAGS"
+    AC_CHECK_HEADER([smmintrin.h],
+                    [AC_DEFINE(WEBP_HAVE_SSE41, [1],
+                     [Set to 1 if SSE4.1 is supported])],
+                    [SSE41_FLAGS=""])
+    CFLAGS=$SAVED_CFLAGS])
+  AC_SUBST([SSE41_FLAGS])])
+
+AC_ARG_ENABLE([sse2],
+              AS_HELP_STRING([--disable-sse2],
+                             [Disable detection of SSE2 support
+                              @<:@default=auto@:>@]))
+
+AS_IF([test "x$enable_sse2" != "xno"], [
+  SSE2_FLAGS="$INTRINSICS_CFLAGS $SSE2_FLAGS"
+  TEST_AND_ADD_CFLAGS([SSE2_FLAGS], [-msse2])
+  AS_IF([test -n "$SSE2_FLAGS"], [
+    SAVED_CFLAGS=$CFLAGS
+    CFLAGS="$CFLAGS $SSE2_FLAGS"
+    AC_CHECK_HEADER([emmintrin.h],
+                    [AC_DEFINE(WEBP_HAVE_SSE2, [1],
+                     [Set to 1 if SSE2 is supported])],
+                    [SSE2_FLAGS=""])
+    CFLAGS=$SAVED_CFLAGS])
+  AC_SUBST([SSE2_FLAGS])])
+
+AC_ARG_ENABLE([neon],
+              AS_HELP_STRING([--disable-neon],
+                             [Disable detection of NEON support
+                              @<:@default=auto@:>@]))
+
+AC_ARG_ENABLE([neon_rtcd],
+              AS_HELP_STRING([--disable-neon-rtcd],
+                             [Disable runtime detection of NEON support via
+                              /proc/cpuinfo on Linux hosts
+                              @<:@default=auto@:>@]))
+# For ARM(7) hosts:
+# Both NEON flags unset and NEON support detected = build all modules with NEON
+# NEON detected with the use of -mfpu=neon = build only NEON modules with NEON
+AS_IF([test "x$enable_neon" != "xno"], [
+  case "$host_cpu" in
+    arm|armv7*)
+      # Test for NEON support without flags before falling back to -mfpu=neon
+      for flag in '' '-mfpu=neon'; do
+        LOCAL_NEON_FLAGS="$INTRINSICS_CFLAGS $NEON_FLAGS"
+        TEST_AND_ADD_CFLAGS([LOCAL_NEON_FLAGS], [$flag])
+        SAVED_CFLAGS=$CFLAGS
+        CFLAGS="$CFLAGS $LOCAL_NEON_FLAGS"
+
+        dnl Note AC_LANG_PROGRAM([]) uses an old-style main definition.
+        AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+          #include <arm_neon.h>
+          int main(void) {
+            int8x8_t v = vdup_n_s8(0);
+            (void)v;
+            return 0;
+          }])],
+          [NEON_FLAGS="$(echo $LOCAL_NEON_FLAGS | $SED 's/^ *//')"
+           AS_IF([test -n "$NEON_FLAGS"], [
+             AS_IF([test "${host_os%%-*}" = "linux" -o \
+                         "x$enable_neon_rtcd" = "xno"], [
+               CFLAGS=$SAVED_CFLAGS
+               AC_DEFINE(WEBP_HAVE_NEON, [1], [Set to 1 if NEON is supported])
+               break
+             ],[
+               AC_MSG_WARN(m4_normalize([NEON runtime cpu-detection is
+                                         unavailable for ${host_os%%-*}. Force
+                                         with CFLAGS=-mfpu=neon or
+                                         --disable-neon-rtcd.]))
+               enable_neon_rtcd=no
+               NEON_FLAGS=""
+             ])
+           ],[
+             CFLAGS=$SAVED_CFLAGS
+             AC_DEFINE(WEBP_HAVE_NEON, [1], [Set to 1 if NEON is supported])
+             break
+           ])])
+        CFLAGS=$SAVED_CFLAGS
+      done
+
+      AS_IF([test -n "$NEON_FLAGS"], [
+        # If NEON is available and rtcd is disabled apply NEON_FLAGS globally.
+        AS_IF([test "x$enable_neon_rtcd" = "xno"], [
+          AM_CFLAGS="$AM_CFLAGS $NEON_FLAGS"
+          NEON_FLAGS=""],
+          [AC_DEFINE(WEBP_HAVE_NEON_RTCD, [1],
+                     [Set to 1 if runtime detection of NEON is enabled])])])
+
+      case "$host_os" in
+        *android*) AC_CHECK_HEADERS([cpu-features.h]) ;;
+      esac
+      ;;
+    aarch64*|arm64*)
+      AC_DEFINE(WEBP_HAVE_NEON, [1], [Set to 1 if NEON is supported])
+      ;;
+  esac
+  AC_SUBST([NEON_FLAGS])])
+
+dnl === CLEAR_LIBVARS([var_pfx])
+dnl ===   Clears <var_pfx>_{INCLUDES,LIBS}.
+AC_DEFUN([CLEAR_LIBVARS], [$1_INCLUDES=""; $1_LIBS=""])
+
+dnl === WITHLIB_OPTION([opt_pfx], [outvar_pfx])
+dnl ===   Defines --with-<opt_pfx>{include,lib}dir options which set
+dnl ===   the variables <outvar_pfx>_{INCLUDES,LIBS}.
+AC_DEFUN([WITHLIB_OPTION],
+  [AC_ARG_WITH([$1includedir],
+               AS_HELP_STRING([--with-$1includedir=DIR],
+                              [use $2 includes from DIR]),
+               $2_INCLUDES="-I$withval")
+   AC_ARG_WITH([$1libdir],
+               AS_HELP_STRING([--with-$1libdir=DIR],
+                              [use $2 libraries from DIR]),
+               [$2_LIBS="-L$withval"])])
+
+dnl === LIBCHECK_PROLOGUE([var_pfx])
+dnl ===   Caches the current values of CPPFLAGS/LIBS in SAVED_* then
+dnl ===   prepends the current values with <var_pfx>_{INCLUDES,LIBS}.
+AC_DEFUN([LIBCHECK_PROLOGUE],
+         [SAVED_CPPFLAGS=$CPPFLAGS
+          SAVED_LIBS=$LIBS
+          CPPFLAGS="$$1_INCLUDES $CPPFLAGS"
+          LIBS="$$1_LIBS $LIBS"])
+
+dnl === LIBCHECK_EPILOGUE([var_pfx])
+dnl ===   Restores the values of CPPFLAGS/LIBS from SAVED_* and exports
+dnl ===   <var_pfx>_{INCLUDES,LIBS} with AC_SUBST.
+AC_DEFUN([LIBCHECK_EPILOGUE],
+         [AC_SUBST($1_LIBS)
+          AC_SUBST($1_INCLUDES)
+          CPPFLAGS=$SAVED_CPPFLAGS
+          LIBS=$SAVED_LIBS])
+
+dnl === Check for gcc builtins
+
+dnl === CHECK_FOR_BUILTIN([builtin], [param], [define])
+dnl ===   links a C AC_LANG_PROGRAM, with <builtin>(<param>)
+dnl ===   AC_DEFINE'ing <define> if successful.
+AC_DEFUN([CHECK_FOR_BUILTIN],
+         [AC_LANG_PUSH([C])
+          AC_MSG_CHECKING([for $1])
+          AC_LINK_IFELSE([AC_LANG_PROGRAM([], [(void)$1($2)])],
+                         [AC_MSG_RESULT([yes])
+                          AC_DEFINE([$3], [1],
+                                    [Set to 1 if $1 is available])],
+                         [AC_MSG_RESULT([no])]),
+          AC_LANG_POP])
+
+dnl AC_CHECK_FUNC doesn't work with builtin's.
+CHECK_FOR_BUILTIN([__builtin_bswap16], [1u << 15], [HAVE_BUILTIN_BSWAP16])
+CHECK_FOR_BUILTIN([__builtin_bswap32], [1u << 31], [HAVE_BUILTIN_BSWAP32])
+CHECK_FOR_BUILTIN([__builtin_bswap64], [1ull << 63], [HAVE_BUILTIN_BSWAP64])
+
+dnl === Check for pthread support
+AC_ARG_ENABLE([threading],
+              AS_HELP_STRING([--disable-threading],
+                             [Disable detection of thread support]),,
+              [enable_threading=yes])
+if test "$enable_threading" = "yes"; then
+  AC_MSG_NOTICE([checking for threading support...])
+  AX_PTHREAD([AC_DEFINE([WEBP_USE_THREAD], [1],
+                        [Undefine this to disable thread support.])
+              LIBS="$PTHREAD_LIBS $LIBS"
+              CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+              CC="$PTHREAD_CC"
+             ],
+             [AC_CHECK_FUNC([_beginthreadex],
+                            [AC_DEFINE([WEBP_USE_THREAD], [1],
+                                       [Undefine this to disable thread
+                                        support.])],
+                            [enable_threading=no])])
+fi
+AC_MSG_NOTICE([checking if threading is enabled... ${enable_threading-no}])
+
+dnl === check for OpenGL/GLUT support ===
+
+AC_ARG_ENABLE([gl], AS_HELP_STRING([--disable-gl],
+                                   [Disable detection of OpenGL support
+                                    @<:@default=auto@:>@]))
+AS_IF([test "x$enable_gl" != "xno"], [
+  CLEAR_LIBVARS([GL])
+  WITHLIB_OPTION([gl], [GL])
+
+  LIBCHECK_PROLOGUE([GL])
+
+  glut_cflags="none"
+  glut_ldflags="none"
+  case $host_os in
+    darwin*)
+      # Special case for OSX builds. Append these to give the user a chance to
+      # override with --with-gl*
+      glut_cflags="$glut_cflags|-framework GLUT -framework OpenGL"
+      glut_ldflags="$glut_ldflags|-framework GLUT -framework OpenGL"
+      # quiet deprecation warnings for glut
+      TEST_AND_ADD_CFLAGS([AM_CFLAGS], [-Wno-deprecated-declarations])
+      ;;
+  esac
+
+  GLUT_SAVED_CPPFLAGS="$CPPFLAGS"
+  SAVED_IFS="$IFS"
+  IFS="|"
+  for flag in $glut_cflags; do
+    # restore IFS immediately as the autoconf macros may need the default.
+    IFS="$SAVED_IFS"
+    unset ac_cv_header_GL_glut_h
+    unset ac_cv_header_OpenGL_glut_h
+
+    case $flag in
+      none) ;;
+      *) CPPFLAGS="$flag $CPPFLAGS";;
+    esac
+    AC_CHECK_HEADERS([GL/glut.h GLUT/glut.h OpenGL/glut.h],
+                     [glut_headers=yes;
+                      test "$flag" = "none" || GL_INCLUDES="$CPPFLAGS";
+                      break])
+    CPPFLAGS="$GLUT_SAVED_CPPFLAGS"
+    test "$glut_headers" = "yes" && break
+  done
+  IFS="$SAVED_IFS"
+
+  if test "$glut_headers" = "yes"; then
+    AC_LANG_PUSH([C])
+    GLUT_SAVED_LDFLAGS="$LDFLAGS"
+    SAVED_IFS="$IFS"
+    IFS="|"
+    for flag in $glut_ldflags; do
+      # restore IFS immediately as the autoconf macros may need the default.
+      IFS="$SAVED_IFS"
+      unset ac_cv_search_glBegin
+
+      case $flag in
+        none) ;;
+        *) LDFLAGS="$flag $LDFLAGS";;
+      esac
+
+      # find libGL
+      GL_SAVED_LIBS="$LIBS"
+      AC_SEARCH_LIBS([glBegin], [GL OpenGL opengl32])
+      LIBS="$GL_SAVED_LIBS"
+
+      # A direct link to libGL may not be necessary on e.g., linux.
+      GLUT_SAVED_LIBS="$LIBS"
+      for lib in "" "-lglut" "-lglut $ac_cv_search_glBegin"; do
+        LIBS="$lib"
+        AC_LINK_IFELSE(
+          [AC_LANG_PROGRAM([
+             #ifdef __cplusplus
+             # define EXTERN_C extern "C"
+             #else
+             # define EXTERN_C
+             #endif
+             EXTERN_C char glOrtho();
+             EXTERN_C char glutMainLoop();
+            ],[
+             glOrtho();
+             glutMainLoop();
+            ])
+          ],
+          AC_DEFINE(WEBP_HAVE_GL, [1],
+                    [Set to 1 if OpenGL is supported])
+          [glut_support=yes], []
+        )
+        if test "$glut_support" = "yes"; then
+          GL_LIBS="$LDFLAGS $lib"
+          break
+        fi
+      done
+      LIBS="$GLUT_SAVED_LIBS"
+      LDFLAGS="$GLUT_SAVED_LDFLAGS"
+      test "$glut_support" = "yes" && break
+    done
+    IFS="$SAVED_IFS"
+    AC_LANG_POP
+  fi
+
+  LIBCHECK_EPILOGUE([GL])
+
+  if test "$glut_support" = "yes" -a "$enable_libwebpdemux" = "yes"; then
+    build_vwebp=yes
+  else
+    AC_MSG_NOTICE(
+      m4_normalize([Not building vwebp.
+                    OpenGL libraries and --enable-libwebpdemux are required.]))
+  fi
+])
+AM_CONDITIONAL([BUILD_VWEBP], [test "$build_vwebp" = "yes"])
+
+dnl === check for SDL support ===
+
+AC_ARG_ENABLE([sdl],
+              AS_HELP_STRING([--disable-sdl],
+                             [Disable detection of SDL support
+                              @<:@default=auto@:>@]))
+AS_IF([test "x$enable_sdl" != "xno"], [
+  CLEAR_LIBVARS([SDL])
+  AC_PATH_PROGS([LIBSDL_CONFIG], [sdl-config])
+  if test -n "$LIBSDL_CONFIG"; then
+    SDL_INCLUDES=`$LIBSDL_CONFIG --cflags`
+    SDL_LIBS="`$LIBSDL_CONFIG --libs`"
+  fi
+
+  WITHLIB_OPTION([sdl], [SDL])
+
+  sdl_header="no"
+  LIBCHECK_PROLOGUE([SDL])
+  AC_CHECK_HEADER([SDL/SDL.h], [sdl_header="SDL/SDL.h"],
+                  [AC_CHECK_HEADER([SDL.h], [sdl_header="SDL.h"],
+                  [AC_MSG_WARN(SDL library not available - no sdl.h)])])
+  if test x"$sdl_header" != "xno"; then
+    AC_LANG_PUSH(C)
+    SDL_SAVED_LIBS="$LIBS"
+    for lib in "" "-lSDL" "-lSDLmain -lSDL"; do
+      LIBS="$SDL_SAVED_LIBS $lib"
+      # Perform a full link to ensure SDL_main is resolved if needed.
+      AC_LINK_IFELSE(
+        [AC_LANG_SOURCE([
+           #include <$sdl_header>
+           int main(int argc, char** argv) {
+             SDL_Init(0);
+             return 0;
+           }])],
+        [SDL_LIBS="$LDFLAGS $LIBS"
+         SDL_INCLUDES="$SDL_INCLUDES -DWEBP_HAVE_SDL"
+         AC_DEFINE(WEBP_HAVE_SDL, [1],
+                   [Set to 1 if SDL library is installed])
+         sdl_support=yes]
+      )
+      if test x"$sdl_support" = "xyes"; then
+        break
+      fi
+    done
+    # LIBS is restored by LIBCHECK_EPILOGUE
+    AC_LANG_POP
+    if test x"$sdl_header" = "xSDL.h"; then
+      SDL_INCLUDES="$SDL_INCLUDES -DWEBP_HAVE_JUST_SDL_H"
+    fi
+  fi
+  LIBCHECK_EPILOGUE([SDL])
+
+  if test x"$sdl_support" = "xyes"; then
+    build_vwebp_sdl=yes
+  else
+    AC_MSG_NOTICE([Not building vwebp-sdl. SDL library is required.])
+  fi
+])
+
+AM_CONDITIONAL([BUILD_VWEBP_SDL], [test "$build_vwebp_sdl" = "yes"])
+
+dnl === check for PNG support ===
+
+AC_ARG_ENABLE([png], AS_HELP_STRING([--disable-png],
+                                    [Disable detection of PNG format support
+                                     @<:@default=auto@:>@]))
+AS_IF([test "x$enable_png" != "xno"], [
+  CLEAR_LIBVARS([PNG])
+  AC_PATH_PROGS([LIBPNG_CONFIG],
+                [libpng-config libpng16-config libpng15-config libpng14-config \
+                 libpng12-config])
+  if test -n "$LIBPNG_CONFIG"; then
+    PNG_INCLUDES=`$LIBPNG_CONFIG --cflags`
+    PNG_LIBS="`$LIBPNG_CONFIG --ldflags`"
+  fi
+
+  WITHLIB_OPTION([png], [PNG])
+
+  LIBCHECK_PROLOGUE([PNG])
+  AC_CHECK_HEADER(png.h,
+    AC_SEARCH_LIBS(png_get_libpng_ver, [png],
+                   [test "$ac_cv_search_png_get_libpng_ver" = "none required" \
+                      || PNG_LIBS="$PNG_LIBS $ac_cv_search_png_get_libpng_ver"
+                    PNG_INCLUDES="$PNG_INCLUDES -DWEBP_HAVE_PNG"
+                    AC_DEFINE(WEBP_HAVE_PNG, [1],
+                              [Set to 1 if PNG library is installed])
+                    png_support=yes
+                   ],
+                   [AC_MSG_WARN(Optional png library not found)
+                    PNG_LIBS=""
+                    PNG_INCLUDES=""
+                   ],
+                   [$MATH_LIBS]),
+    [AC_MSG_WARN(png library not available - no png.h)
+     PNG_LIBS=""
+     PNG_INCLUDES=""
+    ],
+  )
+  LIBCHECK_EPILOGUE([PNG])
+])
+
+dnl === check for JPEG support ===
+
+AC_ARG_ENABLE([jpeg],
+              AS_HELP_STRING([--disable-jpeg],
+                             [Disable detection of JPEG format support
+                              @<:@default=auto@:>@]))
+AS_IF([test "x$enable_jpeg" != "xno"], [
+  CLEAR_LIBVARS([JPEG])
+  WITHLIB_OPTION([jpeg], [JPEG])
+
+  LIBCHECK_PROLOGUE([JPEG])
+  AC_CHECK_HEADER(jpeglib.h,
+    AC_CHECK_LIB(jpeg, jpeg_set_defaults,
+                 [JPEG_LIBS="$JPEG_LIBS -ljpeg"
+                  JPEG_INCLUDES="$JPEG_INCLUDES -DWEBP_HAVE_JPEG"
+                  AC_DEFINE(WEBP_HAVE_JPEG, [1],
+                            [Set to 1 if JPEG library is installed])
+                  jpeg_support=yes
+                 ],
+                 AC_MSG_WARN(Optional jpeg library not found),
+                 [$MATH_LIBS]),
+    AC_MSG_WARN(jpeg library not available - no jpeglib.h)
+  )
+  LIBCHECK_EPILOGUE([JPEG])
+])
+
+dnl === check for TIFF support ===
+
+AC_ARG_ENABLE([tiff],
+              AS_HELP_STRING([--disable-tiff],
+                             [Disable detection of TIFF format support
+                              @<:@default=auto@:>@]))
+AS_IF([test "x$enable_tiff" != "xno"], [
+  CLEAR_LIBVARS([TIFF])
+  WITHLIB_OPTION([tiff], [TIFF])
+
+  LIBCHECK_PROLOGUE([TIFF])
+  AC_CHECK_HEADER(tiffio.h,
+    AC_CHECK_LIB(tiff, TIFFGetVersion,
+                 [TIFF_LIBS="$TIFF_LIBS -ltiff"
+                  TIFF_INCLUDES="$TIFF_INCLUDES -DWEBP_HAVE_TIFF"
+                  AC_DEFINE(WEBP_HAVE_TIFF, [1],
+                            [Set to 1 if TIFF library is installed])
+                  tiff_support=yes
+                 ],
+                 AC_MSG_WARN(Optional tiff library not found),
+                 [$MATH_LIBS]),
+    AC_MSG_WARN(tiff library not available - no tiffio.h)
+  )
+  LIBCHECK_EPILOGUE([TIFF])
+])
+
+dnl === check for GIF support ===
+
+AC_ARG_ENABLE([gif], AS_HELP_STRING([--disable-gif],
+                                    [Disable detection of GIF format support
+                                     @<:@default=auto@:>@]))
+AS_IF([test "x$enable_gif" != "xno"], [
+  CLEAR_LIBVARS([GIF])
+  WITHLIB_OPTION([gif], [GIF])
+
+  LIBCHECK_PROLOGUE([GIF])
+  AC_CHECK_HEADER(gif_lib.h,
+    AC_CHECK_LIB([gif], [DGifOpenFileHandle],
+                 [GIF_LIBS="$GIF_LIBS -lgif"
+                  AC_DEFINE(WEBP_HAVE_GIF, [1],
+                            [Set to 1 if GIF library is installed])
+                  gif_support=yes
+                 ],
+                 AC_MSG_WARN(Optional gif library not found),
+                 [$MATH_LIBS]),
+    AC_MSG_WARN(gif library not available - no gif_lib.h)
+  )
+  LIBCHECK_EPILOGUE([GIF])
+
+  if test "$gif_support" = "yes" -a \
+          "$enable_libwebpdemux" = "yes"; then
+    build_anim_diff=yes
+  else
+    AC_MSG_NOTICE(
+      [Not building anim_diff. libgif and --enable-libwebpdemux are required.])
+  fi
+
+  if test "$gif_support" = "yes" -a \
+          "$enable_libwebpmux" = "yes"; then
+    build_gif2webp=yes
+  else
+    AC_MSG_NOTICE(
+      [Not building gif2webp. libgif and --enable-libwebpmux are required.])
+  fi
+])
+AM_CONDITIONAL([BUILD_ANIMDIFF], [test "${build_anim_diff}" = "yes"])
+AM_CONDITIONAL([BUILD_GIF2WEBP], [test "${build_gif2webp}" = "yes"])
+
+if test "$enable_libwebpdemux" = "yes" -a "$enable_libwebpmux" = "yes"; then
+  build_img2webp=yes
+else
+  AC_MSG_NOTICE(
+    m4_normalize([Not building img2webp.
+                  --enable-libwebpdemux & --enable-libwebpmux are required.]))
+fi
+AM_CONDITIONAL([BUILD_IMG2WEBP], [test "${build_img2webp}" = "yes"])
+
+if test "$enable_libwebpmux" = "yes"; then
+  build_webpinfo=yes
+else
+  AC_MSG_NOTICE([Not building webpinfo. --enable-libwebpdemux is required.])
+fi
+AM_CONDITIONAL([BUILD_WEBPINFO], [test "${build_webpinfo}" = "yes"])
+
+dnl === check for WIC support ===
+
+AC_ARG_ENABLE([wic],
+              AS_HELP_STRING([--disable-wic],
+                             [Disable Windows Imaging Component (WIC) detection.
+                              @<:@default=auto@:>@]),,
+              [enable_wic=yes])
+
+case $host_os in
+mingw*)
+if test "$enable_wic" = "yes"; then
+  AC_CHECK_HEADERS([wincodec.h shlwapi.h windows.h])
+  if test "$ac_cv_header_wincodec_h" = "yes"; then
+    AC_MSG_CHECKING(for Windows Imaging Component support)
+    SAVED_LIBS=$LIBS
+    LIBS="-lshlwapi -lole32 $LIBS"
+    # match include structure from [cd]webp.c
+    wic_headers="
+      #define INITGUID
+      #define CINTERFACE
+      #define COBJMACROS
+      #define _WIN32_IE 0x500
+
+      #include <shlwapi.h>
+      #include <windows.h>
+      #include <wincodec.h>
+      "
+    # test for functions from each lib and the GUID is created properly
+    wic_main="
+      int main(void) {
+        CLSID_WICImagingFactory;
+        CoInitialize(NULL);
+        SHCreateStreamOnFile(NULL, 0, NULL);
+        return 0;
+      }
+      "
+    AC_LANG_PUSH(C)
+    AC_LINK_IFELSE(
+      [AC_LANG_SOURCE([
+         $wic_headers
+         $wic_main])],
+      [wic_support=yes],
+      [wic_support=no]
+    )
+    AC_LANG_POP
+
+    test "$wic_support" = "yes" || LIBS=$SAVED_LIBS
+    AC_MSG_RESULT(${wic_support-no})
+  fi
+fi
+esac
+
+dnl === If --enable-swap-16bit-csp is defined, add -DWEBP_SWAP_16BIT_CSP=1
+
+USE_SWAP_16BIT_CSP=""
+AC_MSG_CHECKING(if --enable-swap-16bit-csp option is specified)
+AC_ARG_ENABLE([swap-16bit-csp],
+              AS_HELP_STRING([--enable-swap-16bit-csp],
+                             [Enable byte swap for 16 bit colorspaces]))
+if test "$enable_swap_16bit_csp" = "yes"; then
+  USE_SWAP_16BIT_CSP="-DWEBP_SWAP_16BIT_CSP=1"
+fi
+AC_MSG_RESULT(${enable_swap_16bit_csp-no})
+AC_SUBST(USE_SWAP_16BIT_CSP)
+
+dnl === If --disable-near-lossless is defined, add -DWEBP_NEAR_LOSSLESS=0
+
+AC_DEFINE(WEBP_NEAR_LOSSLESS, [1], [Enable near lossless encoding])
+AC_MSG_CHECKING(if --disable-near-lossless option is specified)
+AC_ARG_ENABLE([near_lossless],
+              AS_HELP_STRING([--disable-near-lossless],
+                             [Disable near lossless encoding]),
+              [], [enable_near_lossless=yes])
+if test "$enable_near_lossless" = "no"; then
+  AC_DEFINE(WEBP_NEAR_LOSSLESS, [0], [Enable near lossless encoding])
+  AC_MSG_RESULT([yes])
+else
+  AC_MSG_RESULT([no])
+fi
+
+dnl =========================
+
+dnl Add an empty webp_libname_prefix variable for use in *.pc.in.
+AC_SUBST([webp_libname_prefix])
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_HEADERS([src/webp/config.h])
+AC_CONFIG_FILES([Makefile src/Makefile man/Makefile \
+                 examples/Makefile extras/Makefile imageio/Makefile \
+                 sharpyuv/Makefile sharpyuv/libsharpyuv.pc \
+                 src/dec/Makefile src/enc/Makefile src/dsp/Makefile \
+                 src/demux/Makefile src/mux/Makefile \
+                 src/utils/Makefile \
+                 src/libwebp.pc src/libwebpdecoder.pc \
+                 src/demux/libwebpdemux.pc src/mux/libwebpmux.pc])
+
+
+AC_OUTPUT
+
+AC_MSG_NOTICE([
+WebP Configuration Summary
+--------------------------
+
+Shared libraries: ${enable_shared}
+Static libraries: ${enable_static}
+Threading support: ${enable_threading-no}
+libwebp: yes
+libwebpdecoder: ${enable_libwebpdecoder-no}
+libwebpdemux: ${enable_libwebpdemux-no}
+libwebpmux: ${enable_libwebpmux-no}
+libwebpextras: ${enable_libwebpextras-no}
+
+Tools:
+cwebp : ${enable_libwebpdemux-no}
+  Input format support
+  ====================
+  JPEG : ${jpeg_support-no}
+  PNG  : ${png_support-no}
+  TIFF : ${tiff_support-no}
+  WIC  : ${wic_support-no}
+dwebp : ${enable_libwebpdemux-no}
+  Output format support
+  =====================
+  PNG  : ${png_support-no}
+  WIC  : ${wic_support-no}
+GIF support : ${gif_support-no}
+anim_diff   : ${build_anim_diff-no}
+gif2webp    : ${build_gif2webp-no}
+img2webp    : ${build_img2webp-no}
+webpmux     : ${enable_libwebpmux-no}
+vwebp       : ${build_vwebp-no}
+webpinfo    : ${build_webpinfo-no}
+SDL support : ${sdl_support-no}
+vwebp_sdl   : ${build_vwebp_sdl-no}
+])
diff --git a/doc/TODO b/doc/TODO
new file mode 100644
index 0000000..b0a9382
--- /dev/null
+++ b/doc/TODO
@@ -0,0 +1,13 @@
+<louquillio@google.com>, 20111004
+
+* Determine that normative RFC 2119 terms (MUST, SHOULD, MAY, etc.) are
+  truly intended in all cases where capitalized.
+
+* Several passages could be made clearer.
+
+  * Overall edit for scope.  Portions are phrased as an introduction to
+    the 0.1.3 RIFF container additions, rather than a holistic guide to
+    WebP.
+
+  * To wit, suggest s/[spec|specification]/guide/g .  "Spec" can imply a
+    standards track; in any case it's too formal for a work in progress.
diff --git a/doc/api.md b/doc/api.md
new file mode 100644
index 0000000..c613ed3
--- /dev/null
+++ b/doc/api.md
@@ -0,0 +1,385 @@
+# WebP APIs
+
+## Encoding API
+
+The main encoding functions are available in the header src/webp/encode.h
+
+The ready-to-use ones are:
+
+```c
+size_t WebPEncodeRGB(const uint8_t* rgb, int width, int height, int stride,
+                     float quality_factor, uint8_t** output);
+size_t WebPEncodeBGR(const uint8_t* bgr, int width, int height, int stride,
+                     float quality_factor, uint8_t** output);
+size_t WebPEncodeRGBA(const uint8_t* rgba, int width, int height, int stride,
+                      float quality_factor, uint8_t** output);
+size_t WebPEncodeBGRA(const uint8_t* bgra, int width, int height, int stride,
+                      float quality_factor, uint8_t** output);
+```
+
+They will convert raw RGB samples to a WebP data. The only control supplied is
+the quality factor.
+
+There are some variants for using the lossless format:
+
+```c
+size_t WebPEncodeLosslessRGB(const uint8_t* rgb, int width, int height,
+                             int stride, uint8_t** output);
+size_t WebPEncodeLosslessBGR(const uint8_t* bgr, int width, int height,
+                             int stride, uint8_t** output);
+size_t WebPEncodeLosslessRGBA(const uint8_t* rgba, int width, int height,
+                              int stride, uint8_t** output);
+size_t WebPEncodeLosslessBGRA(const uint8_t* bgra, int width, int height,
+                              int stride, uint8_t** output);
+```
+
+Of course in this case, no quality factor is needed since the compression occurs
+without loss of the input values, at the expense of larger output sizes.
+
+### Advanced encoding API
+
+A more advanced API is based on the WebPConfig and WebPPicture structures.
+
+WebPConfig contains the encoding settings and is not tied to a particular
+picture. WebPPicture contains input data, on which some WebPConfig will be used
+for compression. The encoding flow looks like:
+
+```c
+#include <webp/encode.h>
+
+// Setup a config, starting form a preset and tuning some additional
+// parameters
+WebPConfig config;
+if (!WebPConfigPreset(&config, WEBP_PRESET_PHOTO, quality_factor)) {
+  return 0;   // version error
+}
+// ... additional tuning
+config.sns_strength = 90;
+config.filter_sharpness = 6;
+config_error = WebPValidateConfig(&config);  // not mandatory, but useful
+
+// Setup the input data
+WebPPicture pic;
+if (!WebPPictureInit(&pic)) {
+  return 0;  // version error
+}
+pic.width = width;
+pic.height = height;
+// allocated picture of dimension width x height
+if (!WebPPictureAlloc(&pic)) {
+  return 0;   // memory error
+}
+// at this point, 'pic' has been initialized as a container,
+// and can receive the Y/U/V samples.
+// Alternatively, one could use ready-made import functions like
+// WebPPictureImportRGB(), which will take care of memory allocation.
+// In any case, past this point, one will have to call
+// WebPPictureFree(&pic) to reclaim memory.
+
+// Set up a byte-output write method. WebPMemoryWriter, for instance.
+WebPMemoryWriter wrt;
+WebPMemoryWriterInit(&wrt);     // initialize 'wrt'
+
+pic.writer = MyFileWriter;
+pic.custom_ptr = my_opaque_structure_to_make_MyFileWriter_work;
+
+// Compress!
+int ok = WebPEncode(&config, &pic);   // ok = 0 => error occurred!
+WebPPictureFree(&pic);  // must be called independently of the 'ok' result.
+
+// output data should have been handled by the writer at that point.
+// -> compressed data is the memory buffer described by wrt.mem / wrt.size
+
+// deallocate the memory used by compressed data
+WebPMemoryWriterClear(&wrt);
+```
+
+## Decoding API
+
+This is mainly just one function to call:
+
+```c
+#include "webp/decode.h"
+uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
+                       int* width, int* height);
+```
+
+Please have a look at the file src/webp/decode.h for the details. There are
+variants for decoding in BGR/RGBA/ARGB/BGRA order, along with decoding to raw
+Y'CbCr samples. One can also decode the image directly into a pre-allocated
+buffer.
+
+To detect a WebP file and gather the picture's dimensions, the function:
+
+```c
+int WebPGetInfo(const uint8_t* data, size_t data_size,
+                int* width, int* height);
+```
+
+is supplied. No decoding is involved when using it.
+
+### Incremental decoding API
+
+In the case when data is being progressively transmitted, pictures can still be
+incrementally decoded using a slightly more complicated API. Decoder state is
+stored into an instance of the WebPIDecoder object. This object can be created
+with the purpose of decoding either RGB or Y'CbCr samples. For instance:
+
+```c
+WebPDecBuffer buffer;
+WebPInitDecBuffer(&buffer);
+buffer.colorspace = MODE_BGR;
+...
+WebPIDecoder* idec = WebPINewDecoder(&buffer);
+```
+
+As data is made progressively available, this incremental-decoder object can be
+used to decode the picture further. There are two (mutually exclusive) ways to
+pass freshly arrived data:
+
+either by appending the fresh bytes:
+
+```c
+WebPIAppend(idec, fresh_data, size_of_fresh_data);
+```
+
+or by just mentioning the new size of the transmitted data:
+
+```c
+WebPIUpdate(idec, buffer, size_of_transmitted_buffer);
+```
+
+Note that 'buffer' can be modified between each call to WebPIUpdate, in
+particular when the buffer is resized to accommodate larger data.
+
+These functions will return the decoding status: either VP8_STATUS_SUSPENDED if
+decoding is not finished yet or VP8_STATUS_OK when decoding is done. Any other
+status is an error condition.
+
+The 'idec' object must always be released (even upon an error condition) by
+calling: WebPDelete(idec).
+
+To retrieve partially decoded picture samples, one must use the corresponding
+method: WebPIDecGetRGB or WebPIDecGetYUVA. It will return the last displayable
+pixel row.
+
+Lastly, note that decoding can also be performed into a pre-allocated pixel
+buffer. This buffer must be passed when creating a WebPIDecoder, calling
+WebPINewRGB() or WebPINewYUVA().
+
+Please have a look at the src/webp/decode.h header for further details.
+
+### Advanced Decoding API
+
+WebP decoding supports an advanced API which provides on-the-fly cropping and
+rescaling, something of great usefulness on memory-constrained environments like
+mobile phones. Basically, the memory usage will scale with the output's size,
+not the input's, when one only needs a quick preview or a zoomed in portion of
+an otherwise too-large picture. Some CPU can be saved too, incidentally.
+
+```c
+// A) Init a configuration object
+WebPDecoderConfig config;
+CHECK(WebPInitDecoderConfig(&config));
+
+// B) optional: retrieve the bitstream's features.
+CHECK(WebPGetFeatures(data, data_size, &config.input) == VP8_STATUS_OK);
+
+// C) Adjust 'config' options, if needed
+config.options.no_fancy_upsampling = 1;
+config.options.use_scaling = 1;
+config.options.scaled_width = scaledWidth();
+config.options.scaled_height = scaledHeight();
+// etc.
+
+// D) Specify 'config' output options for specifying output colorspace.
+// Optionally the external image decode buffer can also be specified.
+config.output.colorspace = MODE_BGRA;
+// Optionally, the config.output can be pointed to an external buffer as
+// well for decoding the image. This externally supplied memory buffer
+// should be big enough to store the decoded picture.
+config.output.u.RGBA.rgba = (uint8_t*) memory_buffer;
+config.output.u.RGBA.stride = scanline_stride;
+config.output.u.RGBA.size = total_size_of_the_memory_buffer;
+config.output.is_external_memory = 1;
+
+// E) Decode the WebP image. There are two variants w.r.t decoding image.
+// The first one (E.1) decodes the full image and the second one (E.2) is
+// used to incrementally decode the image using small input buffers.
+// Any one of these steps can be used to decode the WebP image.
+
+// E.1) Decode full image.
+CHECK(WebPDecode(data, data_size, &config) == VP8_STATUS_OK);
+
+// E.2) Decode image incrementally.
+WebPIDecoder* const idec = WebPIDecode(NULL, NULL, &config);
+CHECK(idec != NULL);
+while (bytes_remaining > 0) {
+  VP8StatusCode status = WebPIAppend(idec, input, bytes_read);
+  if (status == VP8_STATUS_OK || status == VP8_STATUS_SUSPENDED) {
+    bytes_remaining -= bytes_read;
+  } else {
+    break;
+  }
+}
+WebPIDelete(idec);
+
+// F) Decoded image is now in config.output (and config.output.u.RGBA).
+// It can be saved, displayed or otherwise processed.
+
+// G) Reclaim memory allocated in config's object. It's safe to call
+// this function even if the memory is external and wasn't allocated
+// by WebPDecode().
+WebPFreeDecBuffer(&config.output);
+```
+
+## WebP Mux
+
+WebPMux is a set of two libraries 'Mux' and 'Demux' for creation, extraction and
+manipulation of an extended format WebP file, which can have features like color
+profile, metadata and animation. Reference command-line tools `webpmux` and
+`vwebp` as well as the WebP container specification
+'doc/webp-container-spec.txt' are also provided in this package, see the
+[tools documentation](tools.md).
+
+### Mux API
+
+The Mux API contains methods for adding data to and reading data from WebP
+files. This API currently supports XMP/EXIF metadata, ICC profile and animation.
+Other features may be added in subsequent releases.
+
+Example#1 (pseudo code): Creating a WebPMux object with image data, color
+profile and XMP metadata.
+
+```c
+int copy_data = 0;
+WebPMux* mux = WebPMuxNew();
+// ... (Prepare image data).
+WebPMuxSetImage(mux, &image, copy_data);
+// ... (Prepare ICC profile data).
+WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data);
+// ... (Prepare XMP metadata).
+WebPMuxSetChunk(mux, "XMP ", &xmp, copy_data);
+// Get data from mux in WebP RIFF format.
+WebPMuxAssemble(mux, &output_data);
+WebPMuxDelete(mux);
+// ... (Consume output_data; e.g. write output_data.bytes to file).
+WebPDataClear(&output_data);
+```
+
+Example#2 (pseudo code): Get image and color profile data from a WebP file.
+
+```c
+int copy_data = 0;
+// ... (Read data from file).
+WebPMux* mux = WebPMuxCreate(&data, copy_data);
+WebPMuxGetFrame(mux, 1, &image);
+// ... (Consume image; e.g. call WebPDecode() to decode the data).
+WebPMuxGetChunk(mux, "ICCP", &icc_profile);
+// ... (Consume icc_profile).
+WebPMuxDelete(mux);
+free(data);
+```
+
+For a detailed Mux API reference, please refer to the header file
+(src/webp/mux.h).
+
+### Demux API
+
+The Demux API enables extraction of images and extended format data from WebP
+files. This API currently supports reading of XMP/EXIF metadata, ICC profile and
+animated images. Other features may be added in subsequent releases.
+
+Code example: Demuxing WebP data to extract all the frames, ICC profile and
+EXIF/XMP metadata.
+
+```c
+WebPDemuxer* demux = WebPDemux(&webp_data);
+uint32_t width = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH);
+uint32_t height = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT);
+// ... (Get information about the features present in the WebP file).
+uint32_t flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS);
+
+// ... (Iterate over all frames).
+WebPIterator iter;
+if (WebPDemuxGetFrame(demux, 1, &iter)) {
+  do {
+    // ... (Consume 'iter'; e.g. Decode 'iter.fragment' with WebPDecode(),
+    // ... and get other frame properties like width, height, offsets etc.
+    // ... see 'struct WebPIterator' below for more info).
+  } while (WebPDemuxNextFrame(&iter));
+  WebPDemuxReleaseIterator(&iter);
+}
+
+// ... (Extract metadata).
+WebPChunkIterator chunk_iter;
+if (flags & ICCP_FLAG) WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter);
+// ... (Consume the ICC profile in 'chunk_iter.chunk').
+WebPDemuxReleaseChunkIterator(&chunk_iter);
+if (flags & EXIF_FLAG) WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter);
+// ... (Consume the EXIF metadata in 'chunk_iter.chunk').
+WebPDemuxReleaseChunkIterator(&chunk_iter);
+if (flags & XMP_FLAG) WebPDemuxGetChunk(demux, "XMP ", 1, &chunk_iter);
+// ... (Consume the XMP metadata in 'chunk_iter.chunk').
+WebPDemuxReleaseChunkIterator(&chunk_iter);
+WebPDemuxDelete(demux);
+```
+
+For a detailed Demux API reference, please refer to the header file
+(src/webp/demux.h).
+
+## AnimEncoder API
+
+The AnimEncoder API can be used to create animated WebP images.
+
+Code example:
+
+```c
+WebPAnimEncoderOptions enc_options;
+WebPAnimEncoderOptionsInit(&enc_options);
+// ... (Tune 'enc_options' as needed).
+WebPAnimEncoder* enc = WebPAnimEncoderNew(width, height, &enc_options);
+while(<there are more frames>) {
+  WebPConfig config;
+  WebPConfigInit(&config);
+  // ... (Tune 'config' as needed).
+  WebPAnimEncoderAdd(enc, frame, duration, &config);
+}
+WebPAnimEncoderAssemble(enc, webp_data);
+WebPAnimEncoderDelete(enc);
+// ... (Write the 'webp_data' to a file, or re-mux it further).
+```
+
+For a detailed AnimEncoder API reference, please refer to the header file
+(src/webp/mux.h).
+
+## AnimDecoder API
+
+This AnimDecoder API allows decoding (possibly) animated WebP images.
+
+Code Example:
+
+```c
+WebPAnimDecoderOptions dec_options;
+WebPAnimDecoderOptionsInit(&dec_options);
+// Tune 'dec_options' as needed.
+WebPAnimDecoder* dec = WebPAnimDecoderNew(webp_data, &dec_options);
+WebPAnimInfo anim_info;
+WebPAnimDecoderGetInfo(dec, &anim_info);
+for (uint32_t i = 0; i < anim_info.loop_count; ++i) {
+  while (WebPAnimDecoderHasMoreFrames(dec)) {
+    uint8_t* buf;
+    int timestamp;
+    WebPAnimDecoderGetNext(dec, &buf, &timestamp);
+    // ... (Render 'buf' based on 'timestamp').
+    // ... (Do NOT free 'buf', as it is owned by 'dec').
+  }
+  WebPAnimDecoderReset(dec);
+}
+const WebPDemuxer* demuxer = WebPAnimDecoderGetDemuxer(dec);
+// ... (Do something using 'demuxer'; e.g. get EXIF/XMP/ICC data).
+WebPAnimDecoderDelete(dec);
+```
+
+For a detailed AnimDecoder API reference, please refer to the header file
+(src/webp/demux.h).
diff --git a/doc/building.md b/doc/building.md
new file mode 100644
index 0000000..5efeab9
--- /dev/null
+++ b/doc/building.md
@@ -0,0 +1,213 @@
+# Building
+
+## Windows build
+
+By running:
+
+```batch
+nmake /f Makefile.vc CFG=release-static RTLIBCFG=static OBJDIR=output
+```
+
+the directory `output\release-static\(x64|x86)\bin` will contain the tools
+cwebp.exe and dwebp.exe. The directory `output\release-static\(x64|x86)\lib`
+will contain the libwebp static library. The target architecture (x86/x64) is
+detected by Makefile.vc from the Visual Studio compiler (cl.exe) available in
+the system path.
+
+## Unix build using makefile.unix
+
+On platforms with GNU tools installed (gcc and make), running
+
+```shell
+make -f makefile.unix
+```
+
+will build the binaries examples/cwebp and examples/dwebp, along with the static
+library src/libwebp.a. No system-wide installation is supplied, as this is a
+simple alternative to the full installation system based on the autoconf tools
+(see below). Please refer to makefile.unix for additional details and
+customizations.
+
+## Using autoconf tools
+
+Prerequisites: a compiler (e.g., gcc), make, autoconf, automake, libtool.
+
+On a Debian-like system the following should install everything you need for a
+minimal build:
+
+```shell
+$ sudo apt-get install gcc make autoconf automake libtool
+```
+
+When building from git sources, you will need to run autogen.sh to generate the
+configure script.
+
+```shell
+./configure
+make
+make install
+```
+
+should be all you need to have the following files
+
+```
+/usr/local/include/webp/decode.h
+/usr/local/include/webp/encode.h
+/usr/local/include/webp/types.h
+/usr/local/lib/libwebp.*
+/usr/local/bin/cwebp
+/usr/local/bin/dwebp
+```
+
+installed.
+
+Note: A decode-only library, libwebpdecoder, is available using the
+`--enable-libwebpdecoder` flag. The encode library is built separately and can
+be installed independently using a minor modification in the corresponding
+Makefile.am configure files (see comments there). See `./configure --help` for
+more options.
+
+## Building for MIPS Linux
+
+MIPS Linux toolchain stable available releases can be found at:
+https://community.imgtec.com/developers/mips/tools/codescape-mips-sdk/available-releases/
+
+```shell
+# Add toolchain to PATH
+export PATH=$PATH:/path/to/toolchain/bin
+
+# 32-bit build for mips32r5 (p5600)
+HOST=mips-mti-linux-gnu
+MIPS_CFLAGS="-O3 -mips32r5 -mabi=32 -mtune=p5600 -mmsa -mfp64 \
+  -msched-weight -mload-store-pairs -fPIE"
+MIPS_LDFLAGS="-mips32r5 -mabi=32 -mmsa -mfp64 -pie"
+
+# 64-bit build for mips64r6 (i6400)
+HOST=mips-img-linux-gnu
+MIPS_CFLAGS="-O3 -mips64r6 -mabi=64 -mtune=i6400 -mmsa -mfp64 \
+  -msched-weight -mload-store-pairs -fPIE"
+MIPS_LDFLAGS="-mips64r6 -mabi=64 -mmsa -mfp64 -pie"
+
+./configure --host=${HOST} --build=`config.guess` \
+  CC="${HOST}-gcc -EL" \
+  CFLAGS="$MIPS_CFLAGS" \
+  LDFLAGS="$MIPS_LDFLAGS"
+make
+make install
+```
+
+## CMake
+
+With CMake, you can compile libwebp, cwebp, dwebp, gif2webp, img2webp, webpinfo
+and the JS bindings.
+
+Prerequisites: a compiler (e.g., gcc with autotools) and CMake.
+
+On a Debian-like system the following should install everything you need for a
+minimal build:
+
+```shell
+$ sudo apt-get install build-essential cmake
+```
+
+When building from git sources, you will need to run cmake to generate the
+makefiles.
+
+```shell
+mkdir build && cd build && cmake ../
+make
+make install
+```
+
+If you also want any of the executables, you will need to enable them through
+CMake, e.g.:
+
+```shell
+cmake -DWEBP_BUILD_CWEBP=ON -DWEBP_BUILD_DWEBP=ON ../
+```
+
+or through your favorite interface (like ccmake or cmake-qt-gui).
+
+Use option `-DWEBP_UNICODE=ON` for Unicode support on Windows (with chcp 65001).
+
+Finally, once installed, you can also use WebP in your CMake project by doing:
+
+```cmake
+find_package(WebP)
+```
+
+which will define the CMake variables WebP_INCLUDE_DIRS and WebP_LIBRARIES.
+
+## Gradle
+
+The support for Gradle is minimal: it only helps you compile libwebp, cwebp and
+dwebp and webpmux_example.
+
+Prerequisites: a compiler (e.g., gcc with autotools) and gradle.
+
+On a Debian-like system the following should install everything you need for a
+minimal build:
+
+```shell
+$ sudo apt-get install build-essential gradle
+```
+
+When building from git sources, you will need to run the Gradle wrapper with the
+appropriate target, e.g. :
+
+```shell
+./gradlew buildAllExecutables
+```
+
+## SWIG bindings
+
+To generate language bindings from swig/libwebp.swig at least swig-1.3
+(http://www.swig.org) is required.
+
+Currently the following functions are mapped:
+
+Decode:
+
+```
+WebPGetDecoderVersion
+WebPGetInfo
+WebPDecodeRGBA
+WebPDecodeARGB
+WebPDecodeBGRA
+WebPDecodeBGR
+WebPDecodeRGB
+```
+
+Encode:
+
+```
+WebPGetEncoderVersion
+WebPEncodeRGBA
+WebPEncodeBGRA
+WebPEncodeRGB
+WebPEncodeBGR
+WebPEncodeLosslessRGBA
+WebPEncodeLosslessBGRA
+WebPEncodeLosslessRGB
+WebPEncodeLosslessBGR
+```
+
+See also the [swig documentation](../swig/README.md) for more detailed build
+instructions and usage examples.
+
+### Java bindings
+
+To build the swig-generated JNI wrapper code at least JDK-1.5 (or equivalent) is
+necessary for enum support. The output is intended to be a shared object / DLL
+that can be loaded via `System.loadLibrary("webp_jni")`.
+
+### Python bindings
+
+To build the swig-generated Python extension code at least Python 2.6 is
+required. Python < 2.6 may build with some minor changes to libwebp.swig or the
+generated code, but is untested.
+
+## Javascript decoder
+
+Libwebp can be compiled into a JavaScript decoder using Emscripten and CMake.
+See the [corresponding documentation](../README.md)
diff --git a/doc/specs_generation.md b/doc/specs_generation.md
new file mode 100644
index 0000000..0380d66
--- /dev/null
+++ b/doc/specs_generation.md
@@ -0,0 +1,26 @@
+# Generate libwebp Container Spec Docs from Text Source
+
+HTML generation requires [kramdown](https://kramdown.gettalong.org/), easily
+installed as a [rubygem](https://rubygems.org/). Rubygems installation should
+satisfy dependencies automatically.
+
+HTML generation can then be done from the project root:
+
+```shell
+$ kramdown doc/webp-container-spec.txt --template doc/template.html > \
+  doc/output/webp-container-spec.html
+```
+
+kramdown can optionally syntax highlight code blocks, using
+[CodeRay](https://github.com/rubychan/coderay), a dependency of kramdown that
+rubygems will install automatically. The following will apply inline CSS
+styling; an external stylesheet is not needed.
+
+```shell
+$ kramdown doc/webp-lossless-bitstream-spec.txt --template \
+  doc/template.html --coderay-css style --coderay-line-numbers ' ' \
+  --coderay-default-lang c > \
+  doc/output/webp-lossless-bitstream-spec.html
+```
+
+Optimally, use kramdown 0.13.7 or newer if syntax highlighting desired.
diff --git a/doc/template.html b/doc/template.html
new file mode 100644
index 0000000..5dbc289
--- /dev/null
+++ b/doc/template.html
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <title>WebP Container Specification</title>
+  <meta name="generator" content="kramdown <%= ::Kramdown::VERSION %>" />
+  <style type="text/css">
+  body {
+    color: #000;
+    background-color: #fff;
+    margin: 10%;
+    font-family: "Liberation Sans", "DejaVu Sans", "Bitstream Vera Sans", Arial, sans-serif;
+    line-height: 1.4;
+  }
+  h2 {
+    border-bottom: 1px solid #ccc;
+    padding-bottom: 0;
+  }
+  table {
+    border-collapse: collapse;
+  }
+  th, td {
+    border: 1px solid #999;
+    padding: .5em .7em;;
+  }
+  th {
+    color: #fff;
+    background-color: #000;
+  }
+  td {
+  }
+  hr {
+  }
+  code {
+    color: #000;
+    background-color: #f7f7f7;
+    padding: 0 3px;
+    font-family: "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", Consolata, monospace;
+  }
+  pre {
+    background-color: #f7f7f7;
+    padding: 1em;
+    border: 1px solid #ccc;
+    width: 42em;
+    overflow: auto;
+    font-family: "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", Consolata, monospace;
+  }
+  pre code {
+    background-color: #f7f7f7;
+    padding: 0; /* Only want padding on inline code, not blocks */
+  }
+  pre.terminal {
+    color: #fff;
+    background-color: #000;
+    border: 1px solid #ccc;
+    max-height: 30em;
+  }
+  pre.terminal code {
+    color: #fff;
+    background-color: #000;
+    font-size: smaller;
+  }
+  #markdown-toc ul {
+    list-style-type: disc;
+  }
+  ul#markdown-toc {
+    margin-top: -1em;
+    visibility: hidden;
+    -webkit-padding-start: 0;
+  }
+  ul#markdown-toc ul {
+    visibility: visible;
+  }
+  ul#markdown-toc ul ul{
+    visibility: visible;
+  }
+  ul#markdown-toc + hr {
+    margin-bottom: 4em;
+  }
+  ol ol { /* Format nested ordered lists */
+    list-style-type: lower-alpha;
+  }
+  dt {
+    font-style: italic;
+    font-weight: bold;
+  }
+  .caption {
+  }
+  </style>
+</head>
+<body>
+<%= @body %>
+</body>
+</html>
diff --git a/doc/tools.md b/doc/tools.md
new file mode 100644
index 0000000..853af81
--- /dev/null
+++ b/doc/tools.md
@@ -0,0 +1,512 @@
+# WebP tools
+
+## Encoding tool
+
+The examples/ directory contains tools for encoding (cwebp) and decoding (dwebp)
+images.
+
+The easiest use should look like:
+
+```shell
+cwebp input.png -q 80 -o output.webp
+```
+
+which will convert the input file to a WebP file using a quality factor of 80 on
+a 0->100 scale (0 being the lowest quality, 100 being the best. Default value is
+75).
+
+You might want to try the `-lossless` flag too, which will compress the source
+(in RGBA format) without any loss. The `-q` quality parameter will in this case
+control the amount of processing time spent trying to make the output file as
+small as possible.
+
+A longer list of options is available using the `-longhelp` command line flag:
+
+```shell
+> cwebp -longhelp
+Usage:
+ cwebp [-preset <...>] [options] in_file [-o out_file]
+```
+
+If input size (-s) for an image is not specified, it is assumed to be a PNG,
+JPEG, TIFF or WebP file. Note: Animated PNG and WebP files are not supported.
+
+Options:
+
+```
+-h / -help ............. short help
+-H / -longhelp ......... long help
+-q <float> ............. quality factor (0:small..100:big), default=75
+-alpha_q <int> ......... transparency-compression quality (0..100),
+                         default=100
+-preset <string> ....... preset setting, one of:
+                          default, photo, picture,
+                          drawing, icon, text
+   -preset must come first, as it overwrites other parameters
+-z <int> ............... activates lossless preset with given
+                         level in [0:fast, ..., 9:slowest]
+
+-m <int> ............... compression method (0=fast, 6=slowest), default=4
+-segments <int> ........ number of segments to use (1..4), default=4
+-size <int> ............ target size (in bytes)
+-psnr <float> .......... target PSNR (in dB. typically: 42)
+
+-s <int> <int> ......... input size (width x height) for YUV
+-sns <int> ............. spatial noise shaping (0:off, 100:max), default=50
+-f <int> ............... filter strength (0=off..100), default=60
+-sharpness <int> ....... filter sharpness (0:most .. 7:least sharp), default=0
+-strong ................ use strong filter instead of simple (default)
+-nostrong .............. use simple filter instead of strong
+-sharp_yuv ............. use sharper (and slower) RGB->YUV conversion
+-partition_limit <int> . limit quality to fit the 512k limit on
+                         the first partition (0=no degradation ... 100=full)
+-pass <int> ............ analysis pass number (1..10)
+-qrange <min> <max> .... specifies the permissible quality range
+                         (default: 0 100)
+-crop <x> <y> <w> <h> .. crop picture with the given rectangle
+-resize <w> <h> ........ resize picture (*after* any cropping)
+-mt .................... use multi-threading if available
+-low_memory ............ reduce memory usage (slower encoding)
+-map <int> ............. print map of extra info
+-print_psnr ............ prints averaged PSNR distortion
+-print_ssim ............ prints averaged SSIM distortion
+-print_lsim ............ prints local-similarity distortion
+-d <file.pgm> .......... dump the compressed output (PGM file)
+-alpha_method <int> .... transparency-compression method (0..1), default=1
+-alpha_filter <string> . predictive filtering for alpha plane,
+                         one of: none, fast (default) or best
+-exact ................. preserve RGB values in transparent area, default=off
+-blend_alpha <hex> ..... blend colors against background color
+                         expressed as RGB values written in
+                         hexadecimal, e.g. 0xc0e0d0 for red=0xc0
+                         green=0xe0 and blue=0xd0
+-noalpha ............... discard any transparency information
+-lossless .............. encode image losslessly, default=off
+-near_lossless <int> ... use near-lossless image
+                         preprocessing (0..100=off), default=100
+-hint <string> ......... specify image characteristics hint,
+                         one of: photo, picture or graph
+
+-metadata <string> ..... comma separated list of metadata to
+                         copy from the input to the output if present.
+                         Valid values: all, none (default), exif, icc, xmp
+
+-short ................. condense printed message
+-quiet ................. don't print anything
+-version ............... print version number and exit
+-noasm ................. disable all assembly optimizations
+-v ..................... verbose, e.g. print encoding/decoding times
+-progress .............. report encoding progress
+```
+
+Experimental Options:
+
+```
+-jpeg_like ............. roughly match expected JPEG size
+-af .................... auto-adjust filter strength
+-pre <int> ............. pre-processing filter
+```
+
+The main options you might want to try in order to further tune the visual
+quality are:
+
+-preset -sns -f -m
+
+Namely:
+
+*   `preset` will set up a default encoding configuration targeting a particular
+    type of input. It should appear first in the list of options, so that
+    subsequent options can take effect on top of this preset. Default value is
+    'default'.
+*   `sns` will progressively turn on (when going from 0 to 100) some additional
+    visual optimizations (like: segmentation map re-enforcement). This option
+    will balance the bit allocation differently. It tries to take bits from the
+    "easy" parts of the picture and use them in the "difficult" ones instead.
+    Usually, raising the sns value (at fixed -q value) leads to larger files,
+    but with better quality. Typical value is around '75'.
+*   `f` option directly links to the filtering strength used by the codec's
+    in-loop processing. The higher the value, the smoother the highly-compressed
+    area will look. This is particularly useful when aiming at very small files.
+    Typical values are around 20-30. Note that using the option
+    -strong/-nostrong will change the type of filtering. Use "-f 0" to turn
+    filtering off.
+*   `m` controls the trade-off between encoding speed and quality. Default is 4.
+    You can try -m 5 or -m 6 to explore more (time-consuming) encoding
+    possibilities. A lower value will result in faster encoding at the expense
+    of quality.
+
+## Decoding tool
+
+There is a decoding sample in examples/dwebp.c which will take a .webp file and
+decode it to a PNG image file (amongst other formats). This is simply to
+demonstrate the use of the API. You can verify the file test.webp decodes to
+exactly the same as test_ref.ppm by using:
+
+```shell
+cd examples
+./dwebp test.webp -ppm -o test.ppm
+diff test.ppm test_ref.ppm
+```
+
+The full list of options is available using -h:
+
+```shell
+> dwebp -h
+Usage: dwebp in_file [options] [-o out_file]
+```
+
+Decodes the WebP image file to PNG format [Default]. Note: Animated WebP files
+are not supported.
+
+Use following options to convert into alternate image formats:
+
+```
+-pam ......... save the raw RGBA samples as a color PAM
+-ppm ......... save the raw RGB samples as a color PPM
+-bmp ......... save as uncompressed BMP format
+-tiff ........ save as uncompressed TIFF format
+-pgm ......... save the raw YUV samples as a grayscale PGM
+               file with IMC4 layout
+-yuv ......... save the raw YUV samples in flat layout
+```
+
+Other options are:
+
+```
+-version ..... print version number and exit
+-nofancy ..... don't use the fancy YUV420 upscaler
+-nofilter .... disable in-loop filtering
+-nodither .... disable dithering
+-dither <d> .. dithering strength (in 0..100)
+-alpha_dither  use alpha-plane dithering if needed
+-mt .......... use multi-threading
+-crop <x> <y> <w> <h> ... crop output with the given rectangle
+-resize <w> <h> ......... resize output (*after* any cropping)
+-flip ........ flip the output vertically
+-alpha ....... only save the alpha plane
+-incremental . use incremental decoding (useful for tests)
+-h ........... this help message
+-v ........... verbose (e.g. print encoding/decoding times)
+-quiet ....... quiet mode, don't print anything
+-noasm ....... disable all assembly optimizations
+```
+
+## WebP file analysis tool
+
+`webpinfo` can be used to print out the chunk level structure and bitstream
+header information of WebP files. It can also check if the files are of valid
+WebP format.
+
+Usage:
+
+```shell
+webpinfo [options] in_files
+```
+
+Note: there could be multiple input files; options must come before input files.
+
+Options:
+
+```
+-version ........... Print version number and exit.
+-quiet ............. Do not show chunk parsing information.
+-diag .............. Show parsing error diagnosis.
+-summary ........... Show chunk stats summary.
+-bitstream_info .... Parse bitstream header.
+```
+
+## Visualization tool
+
+There's a little self-serve visualization tool called 'vwebp' under the
+examples/ directory. It uses OpenGL to open a simple drawing window and show a
+decoded WebP file. It's not yet integrated in the automake build system, but you
+can try to manually compile it using the recommendations below.
+
+Usage:
+
+```shell
+vwebp in_file [options]
+```
+
+Decodes the WebP image file and visualize it using OpenGL
+
+Options are:
+
+```
+-version ..... print version number and exit
+-noicc ....... don't use the icc profile if present
+-nofancy ..... don't use the fancy YUV420 upscaler
+-nofilter .... disable in-loop filtering
+-dither <int>  dithering strength (0..100), default=50
+-noalphadither disable alpha plane dithering
+-usebgcolor .. display background color
+-mt .......... use multi-threading
+-info ........ print info
+-h ........... this help message
+```
+
+Keyboard shortcuts:
+
+```
+'c' ................ toggle use of color profile
+'b' ................ toggle background color display
+'i' ................ overlay file information
+'d' ................ disable blending & disposal (debug)
+'q' / 'Q' / ESC .... quit
+```
+
+### Building
+
+Prerequisites:
+
+1.  OpenGL & OpenGL Utility Toolkit (GLUT)
+
+    Linux: `sudo apt-get install freeglut3-dev mesa-common-dev`
+
+    Mac + Xcode: These libraries should be available in the OpenGL / GLUT
+    frameworks.
+
+    Windows: http://freeglut.sourceforge.net/index.php#download
+
+2.  (Optional) qcms (Quick Color Management System)
+
+    1.  Download qcms from Mozilla / Chromium:
+        https://hg.mozilla.org/mozilla-central/file/0e7639e3bdfb/gfx/qcms
+        https://source.chromium.org/chromium/chromium/src/+/main:third_party/qcms/;drc=d4a2f8e1ed461d8fc05ed88d1ae2dc94c9773825
+    2.  Build and archive the source files as libqcms.a / qcms.lib
+    3.  Update makefile.unix / Makefile.vc
+        1.  Define WEBP_HAVE_QCMS
+        2.  Update include / library paths to reference the qcms directory.
+
+Build using makefile.unix / Makefile.vc:
+
+```shell
+$ make -f makefile.unix examples/vwebp
+> nmake /f Makefile.vc CFG=release-static \
+    ../obj/x64/release-static/bin/vwebp.exe
+```
+
+## Animation creation tool
+
+The utility `img2webp` can turn a sequence of input images (PNG, JPEG, ...) into
+an animated WebP file. It offers fine control over duration, encoding modes,
+etc.
+
+Usage:
+
+```shell
+img2webp [file_options] [[frame_options] frame_file]...
+```
+
+File-level options (only used at the start of compression):
+
+```
+-min_size ............ minimize size
+-loop <int> .......... loop count (default: 0, = infinite loop)
+-kmax <int> .......... maximum number of frame between key-frames
+                        (0=only keyframes)
+-kmin <int> .......... minimum number of frame between key-frames
+                        (0=disable key-frames altogether)
+-mixed ............... use mixed lossy/lossless automatic mode
+-v ................... verbose mode
+-h ................... this help
+-version ............. print version number and exit
+```
+
+Per-frame options (only used for subsequent images input):
+
+```
+-d <int> ............. frame duration in ms (default: 100)
+-lossless  ........... use lossless mode (default)
+-lossy ... ........... use lossy mode
+-q <float> ........... quality
+-m <int> ............. method to use
+```
+
+example: `img2webp -loop 2 in0.png -lossy in1.jpg -d 80 in2.tiff -o out.webp`
+
+Note: if a single file name is passed as the argument, the arguments will be
+tokenized from this file. The file name must not start with the character '-'.
+
+## Animated GIF conversion
+
+Animated GIF files can be converted to WebP files with animation using the
+gif2webp utility available under examples/. The files can then be viewed using
+vwebp.
+
+Usage:
+
+```shell
+gif2webp [options] gif_file -o webp_file
+```
+
+Options:
+
+```
+-h / -help ............. this help
+-lossy ................. encode image using lossy compression
+-mixed ................. for each frame in the image, pick lossy
+                         or lossless compression heuristically
+-q <float> ............. quality factor (0:small..100:big)
+-m <int> ............... compression method (0=fast, 6=slowest)
+-min_size .............. minimize output size (default:off)
+                         lossless compression by default; can be
+                         combined with -q, -m, -lossy or -mixed
+                         options
+-kmin <int> ............ min distance between key frames
+-kmax <int> ............ max distance between key frames
+-f <int> ............... filter strength (0=off..100)
+-metadata <string> ..... comma separated list of metadata to
+                         copy from the input to the output if present
+                         Valid values: all, none, icc, xmp (default)
+-loop_compatibility .... use compatibility mode for Chrome
+                         version prior to M62 (inclusive)
+-mt .................... use multi-threading if available
+
+-version ............... print version number and exit
+-v ..................... verbose
+-quiet ................. don't print anything
+```
+
+### Building
+
+With the libgif development files installed, gif2webp can be built using
+makefile.unix:
+
+```shell
+$ make -f makefile.unix examples/gif2webp
+```
+
+or using autoconf:
+
+```shell
+$ ./configure --enable-everything
+$ make
+```
+
+## Comparison of animated images
+
+Test utility anim_diff under examples/ can be used to compare two animated
+images (each can be GIF or WebP).
+
+Usage:
+
+```shell
+anim_diff <image1> <image2> [options]
+```
+
+Options:
+
+```
+-dump_frames <folder> dump decoded frames in PAM format
+-min_psnr <float> ... minimum per-frame PSNR
+-raw_comparison ..... if this flag is not used, RGB is
+                      premultiplied before comparison
+-max_diff <int> ..... maximum allowed difference per channel
+                      between corresponding pixels in subsequent
+                      frames
+-h .................. this help
+-version ............ print version number and exit
+```
+
+### Building
+
+With the libgif development files installed, anim_diff can be built using
+makefile.unix:
+
+```shell
+$ make -f makefile.unix examples/anim_diff
+```
+
+or using autoconf:
+
+```shell
+$ ./configure --enable-everything
+$ make
+```
+
+## WebP Mux tool
+
+The examples/ directory contains a tool (webpmux) for manipulating WebP files.
+The webpmux tool can be used to create an extended format WebP file and also to
+extract or strip relevant data from such a file.
+
+A list of options is available using the -help command line flag:
+
+```shell
+> webpmux -help
+Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT
+       webpmux -set SET_OPTIONS INPUT -o OUTPUT
+       webpmux -duration DURATION_OPTIONS [-duration ...]
+               INPUT -o OUTPUT
+       webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT
+       webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]
+               [-bgcolor BACKGROUND_COLOR] -o OUTPUT
+       webpmux -info INPUT
+       webpmux [-h|-help]
+       webpmux -version
+       webpmux argument_file_name
+
+GET_OPTIONS:
+ Extract relevant data:
+   icc       get ICC profile
+   exif      get EXIF metadata
+   xmp       get XMP metadata
+   frame n   get nth frame
+
+SET_OPTIONS:
+ Set color profile/metadata/parameters:
+   loop LOOP_COUNT            set the loop count
+   bgcolor BACKGROUND_COLOR   set the animation background color
+   icc  file.icc              set ICC profile
+   exif file.exif             set EXIF metadata
+   xmp  file.xmp              set XMP metadata
+   where:    'file.icc' contains the ICC profile to be set,
+             'file.exif' contains the EXIF metadata to be set
+             'file.xmp' contains the XMP metadata to be set
+
+DURATION_OPTIONS:
+ Set duration of selected frames:
+   duration            set duration for all frames
+   duration,frame      set duration of a particular frame
+   duration,start,end  set duration of frames in the
+                        interval [start,end])
+   where: 'duration' is the duration in milliseconds
+          'start' is the start frame index
+          'end' is the inclusive end frame index
+           The special 'end' value '0' means: last frame.
+
+STRIP_OPTIONS:
+ Strip color profile/metadata:
+   icc       strip ICC profile
+   exif      strip EXIF metadata
+   xmp       strip XMP metadata
+
+FRAME_OPTIONS(i):
+ Create animation:
+   file_i +di[+xi+yi[+mi[bi]]]
+   where:    'file_i' is the i'th animation frame (WebP format),
+             'di' is the pause duration before next frame,
+             'xi','yi' specify the image offset for this frame,
+             'mi' is the dispose method for this frame (0 or 1),
+             'bi' is the blending method for this frame (+b or -b)
+
+LOOP_COUNT:
+ Number of times to repeat the animation.
+ Valid range is 0 to 65535 [Default: 0 (infinite)].
+
+BACKGROUND_COLOR:
+ Background color of the canvas.
+  A,R,G,B
+  where:    'A', 'R', 'G' and 'B' are integers in the range 0 to 255 specifying
+            the Alpha, Red, Green and Blue component values respectively
+            [Default: 255,255,255,255]
+
+INPUT & OUTPUT are in WebP format.
+
+Note: The nature of EXIF, XMP and ICC data is not checked and is assumed to be
+valid.
+
+Note: if a single file name is passed as the argument, the arguments will be
+tokenized from this file. The file name must not start with the character '-'.
+```
diff --git a/doc/webp-container-spec.txt b/doc/webp-container-spec.txt
new file mode 100644
index 0000000..ffa919e
--- /dev/null
+++ b/doc/webp-container-spec.txt
@@ -0,0 +1,868 @@
+<!--
+
+Although you may be viewing an alternate representation, this document
+is sourced in Markdown, a light-duty markup scheme, and is optimized for
+the [kramdown](https://kramdown.gettalong.org/) transformer.
+
+See the accompanying specs_generation.md. External link targets are referenced
+at the end of this file.
+
+-->
+
+
+WebP Container Specification
+============================
+
+* TOC placeholder
+{:toc}
+
+
+Introduction
+------------
+
+WebP is an image format that uses either (i) the VP8 key frame encoding
+to compress image data in a lossy way, or (ii) the WebP lossless encoding
+(and possibly other encodings in the future). These encoding schemes should
+make it more efficient than currently used formats. It is optimized for fast
+image transfer over the network (e.g., for websites). The WebP format has
+feature parity (color profile, metadata, animation, etc.) with other formats as
+well. This document describes the structure of a WebP file.
+
+The WebP container (i.e., RIFF container for WebP) allows feature support over
+and above the basic use case of WebP (i.e., a file containing a single image
+encoded as a VP8 key frame). The WebP container provides additional support
+for:
+
+  * **Lossless compression.** An image can be losslessly compressed, using the
+    WebP Lossless Format.
+
+  * **Metadata.** An image may have metadata stored in Exif or XMP formats.
+
+  * **Transparency.** An image may have transparency, i.e., an alpha channel.
+
+  * **Color Profile.** An image may have an embedded ICC profile as described
+    by the [International Color Consortium][iccspec].
+
+  * **Animation.** An image may have multiple frames with pauses between them,
+    making it an animation.
+
+The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
+"SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this
+document are to be interpreted as described in BCP 14 [RFC 2119][] [RFC 8174][]
+when, and only when, they appear in all capitals, as shown here.
+
+Bit numbering in chunk diagrams starts at `0` for the most significant bit
+('MSB 0') as described in [RFC 1166][].
+
+Terminology & Basics
+--------------------
+
+A WebP file contains either a still image (i.e., an encoded matrix of pixels)
+or an [animation](#animation). Optionally, it can also contain transparency
+information, color profile and metadata. In case we need to refer only to the
+matrix of pixels, we will call it the _canvas_ of the image.
+
+Below are additional terms used throughout this document:
+
+_Reader/Writer_
+
+: Code that reads WebP files is referred to as a _reader_, while code that
+  writes them is referred to as a _writer_.
+
+_uint16_
+
+: A 16-bit, little-endian, unsigned integer.
+
+_uint24_
+
+: A 24-bit, little-endian, unsigned integer.
+
+_uint32_
+
+: A 32-bit, little-endian, unsigned integer.
+
+_FourCC_
+
+: A _FourCC_ (four-character code) is a _uint32_ created by concatenating four
+  ASCII characters in little-endian order. This means 'aaaa' (0x61616161) and
+ 'AAAA' (0x41414141) are treated as different _FourCCs_.
+
+_1-based_
+
+: An unsigned integer field storing values offset by `-1`. e.g., Such a field
+  would store value _25_ as _24_.
+
+_ChunkHeader('ABCD')_
+
+: This is used to describe the _FourCC_ and _Chunk Size_ header of individual
+  chunks, where 'ABCD' is the FourCC for the chunk. This element's size is 8
+  bytes.
+
+
+RIFF File Format
+----------------
+
+The WebP file format is based on the RIFF (Resource Interchange File Format)
+document format.
+
+The basic element of a RIFF file is a _chunk_. It consists of:
+
+     0                   1                   2                   3
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                         Chunk FourCC                          |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                          Chunk Size                           |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    :                         Chunk Payload                         :
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Chunk FourCC: 32 bits
+
+: ASCII four-character code used for chunk identification.
+
+Chunk Size: 32 bits (_uint32_)
+
+: The size of the chunk in bytes, not including this field, the chunk
+  identifier or padding.
+
+Chunk Payload: _Chunk Size_ bytes
+
+: The data payload. If _Chunk Size_ is odd, a single padding byte -- that MUST
+  be `0` to conform with RIFF -- is added.
+
+**Note:** RIFF has a convention that all-uppercase chunk FourCCs are standard
+chunks that apply to any RIFF file format, while FourCCs specific to a file
+format are all lowercase. WebP does not follow this convention.
+
+
+WebP File Header
+----------------
+
+     0                   1                   2                   3
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |      'R'      |      'I'      |      'F'      |      'F'      |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                           File Size                           |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |      'W'      |      'E'      |      'B'      |      'P'      |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+'RIFF': 32 bits
+
+: The ASCII characters 'R' 'I' 'F' 'F'.
+
+File Size: 32 bits (_uint32_)
+
+: The size of the file in bytes starting at offset 8. The maximum value of
+  this field is 2^32 minus 10 bytes and thus the size of the whole file is at
+  most 4GiB minus 2 bytes.
+
+'WEBP': 32 bits
+
+: The ASCII characters 'W' 'E' 'B' 'P'.
+
+A WebP file MUST begin with a RIFF header with the FourCC 'WEBP'. The file size
+in the header is the total size of the chunks that follow plus `4` bytes for
+the 'WEBP' FourCC. The file SHOULD NOT contain any data after the data
+specified by _File Size_. Readers MAY parse such files, ignoring the trailing
+data. As the size of any chunk is even, the size given by the RIFF header is
+also even. The contents of individual chunks will be described in the following
+sections.
+
+
+Simple File Format (Lossy)
+--------------------------
+
+This layout SHOULD be used if the image requires _lossy_ encoding and does not
+require transparency or other advanced features provided by the extended format.
+Files with this layout are smaller and supported by older software.
+
+Simple WebP (lossy) file format:
+
+     0                   1                   2                   3
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                                                               |
+    |                    WebP file header (12 bytes)                |
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    :                          VP8 chunk                            :
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+VP8 chunk:
+
+     0                   1                   2                   3
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                      ChunkHeader('VP8 ')                      |
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    :                           VP8 data                            :
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+VP8 data: _Chunk Size_ bytes
+
+: VP8 bitstream data.
+
+Note the fourth character in the 'VP8 ' FourCC is an ASCII space (0x20).
+
+The VP8 bitstream format specification can be found at [VP8 Data Format and
+Decoding Guide][vp8spec]. Note that the VP8 frame header contains the VP8 frame
+width and height. That is assumed to be the width and height of the canvas.
+
+The VP8 specification describes how to decode the image into Y'CbCr format. To
+convert to RGB, Rec. 601 SHOULD be used. Applications MAY use another
+conversion method, but visual results may differ among decoders.
+
+
+Simple File Format (Lossless)
+-----------------------------
+
+**Note:** Older readers may not support files using the lossless format.
+
+This layout SHOULD be used if the image requires _lossless_ encoding (with an
+optional transparency channel) and does not require advanced features provided
+by the extended format.
+
+Simple WebP (lossless) file format:
+
+     0                   1                   2                   3
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                                                               |
+    |                    WebP file header (12 bytes)                |
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    :                          VP8L chunk                           :
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+VP8L chunk:
+
+     0                   1                   2                   3
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                      ChunkHeader('VP8L')                      |
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    :                           VP8L data                           :
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+VP8L data: _Chunk Size_ bytes
+
+: VP8L bitstream data.
+
+The current specification of the VP8L bitstream can be found at
+[WebP Lossless Bitstream Format][webpllspec]. Note that the VP8L header
+contains the VP8L image width and height. That is assumed to be the width
+and height of the canvas.
+
+
+Extended File Format
+--------------------
+
+**Note:** Older readers may not support files using the extended format.
+
+An extended format file consists of:
+
+  * A 'VP8X' chunk with information about features used in the file.
+
+  * An optional 'ICCP' chunk with color profile.
+
+  * An optional 'ANIM' chunk with animation control data.
+
+  * Image data.
+
+  * An optional 'EXIF' chunk with Exif metadata.
+
+  * An optional 'XMP ' chunk with XMP metadata.
+
+  * An optional list of [unknown chunks](#unknown-chunks).
+
+For a _still image_, the _image data_ consists of a single frame, which is made
+up of:
+
+  * An optional [alpha subchunk](#alpha).
+
+  * A [bitstream subchunk](#bitstream-vp8vp8l).
+
+For an _animated image_, the _image data_ consists of multiple frames. More
+details about frames can be found in the [Animation](#animation) section.
+
+All chunks SHOULD be placed in the same order as listed above. If a chunk
+appears in the wrong place, the file is invalid, but readers MAY parse the
+file, ignoring the chunks that are out of order.
+
+**Rationale:** Setting the order of chunks should allow quicker file
+parsing. For example, if an 'ALPH' chunk does not appear in its required
+position, a decoder can choose to stop searching for it. The rule of
+ignoring late chunks should make programs that need to do a full search
+give the same results as the ones stopping early.
+
+Extended WebP file header:
+{:#extended_header}
+
+     0                   1                   2                   3
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                                                               |
+    |                   WebP file header (12 bytes)                 |
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                      ChunkHeader('VP8X')                      |
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |Rsv|I|L|E|X|A|R|                   Reserved                    |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |          Canvas Width Minus One               |             ...
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    ...  Canvas Height Minus One    |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Reserved (Rsv): 2 bits
+
+: MUST be `0`. Readers MUST ignore this field.
+
+ICC profile (I): 1 bit
+
+: Set if the file contains an ICC profile.
+
+Alpha (L): 1 bit
+
+: Set if any of the frames of the image contain transparency information
+  ("alpha").
+
+Exif metadata (E): 1 bit
+
+: Set if the file contains Exif metadata.
+
+XMP metadata (X): 1 bit
+
+: Set if the file contains XMP metadata.
+
+Animation (A): 1 bit
+
+: Set if this is an animated image. Data in 'ANIM' and 'ANMF' chunks should be
+  used to control the animation.
+
+Reserved (R): 1 bit
+
+: MUST be `0`. Readers MUST ignore this field.
+
+Reserved: 24 bits
+
+: MUST be `0`. Readers MUST ignore this field.
+
+Canvas Width Minus One: 24 bits
+
+: _1-based_ width of the canvas in pixels.
+  The actual canvas width is `1 + Canvas Width Minus One`.
+
+Canvas Height Minus One: 24 bits
+
+: _1-based_ height of the canvas in pixels.
+  The actual canvas height is `1 + Canvas Height Minus One`.
+
+The product of _Canvas Width_ and _Canvas Height_ MUST be at most `2^32 - 1`.
+
+Future specifications may add more fields. Unknown fields MUST be ignored.
+
+### Chunks
+
+#### Animation
+
+An animation is controlled by ANIM and ANMF chunks.
+
+ANIM Chunk:
+{:#anim_chunk}
+
+For an animated image, this chunk contains the _global parameters_ of the
+animation.
+
+     0                   1                   2                   3
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                      ChunkHeader('ANIM')                      |
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                       Background Color                        |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |          Loop Count           |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Background Color: 32 bits (_uint32_)
+
+: The default background color of the canvas in \[Blue, Green, Red, Alpha\]
+  byte order. This color MAY be used to fill the unused space on the canvas
+  around the frames, as well as the transparent pixels of the first frame.
+  Background color is also used when disposal method is `1`.
+
+**Note**:
+
+  * Background color MAY contain a non-opaque alpha value, even if the _Alpha_
+    flag in [VP8X chunk](#extended_header) is unset.
+
+  * Viewer applications SHOULD treat the background color value as a hint, and
+    are not required to use it.
+
+  * The canvas is cleared at the start of each loop. The background color MAY be
+    used to achieve this.
+
+Loop Count: 16 bits (_uint16_)
+
+: The number of times to loop the animation. `0` means infinitely.
+
+This chunk MUST appear if the _Animation_ flag in the VP8X chunk is set.
+If the _Animation_ flag is not set and this chunk is present, it MUST be
+ignored.
+
+ANMF chunk:
+
+For animated images, this chunk contains information about a _single_ frame.
+If the _Animation flag_ is not set, then this chunk SHOULD NOT be present.
+
+     0                   1                   2                   3
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                      ChunkHeader('ANMF')                      |
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                        Frame X                |             ...
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    ...          Frame Y            |   Frame Width Minus One     ...
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    ...             |           Frame Height Minus One              |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                 Frame Duration                |  Reserved |B|D|
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    :                         Frame Data                            :
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Frame X: 24 bits (_uint24_)
+
+: The X coordinate of the upper left corner of the frame is `Frame X * 2`.
+
+Frame Y: 24 bits (_uint24_)
+
+: The Y coordinate of the upper left corner of the frame is `Frame Y * 2`.
+
+Frame Width Minus One: 24 bits (_uint24_)
+
+: The _1-based_ width of the frame.
+  The frame width is `1 + Frame Width Minus One`.
+
+Frame Height Minus One: 24 bits (_uint24_)
+
+: The _1-based_ height of the frame.
+  The frame height is `1 + Frame Height Minus One`.
+
+Frame Duration: 24 bits (_uint24_)
+
+: The time to wait before displaying the next frame, in 1 millisecond units.
+  Note the interpretation of frame duration of 0 (and often <= 10) is
+  implementation defined. Many tools and browsers assign a minimum duration
+  similar to GIF.
+
+Reserved: 6 bits
+
+: MUST be `0`. Readers MUST ignore this field.
+
+Blending method (B): 1 bit
+
+: Indicates how transparent pixels of _the current frame_ are to be blended
+  with corresponding pixels of the previous canvas:
+
+    * `0`: Use alpha blending. After disposing of the previous frame, render the
+      current frame on the canvas using [alpha-blending](#alpha-blending). If
+      the current frame does not have an alpha channel, assume alpha value of
+      255, effectively replacing the rectangle.
+
+    * `1`: Do not blend. After disposing of the previous frame, render the
+      current frame on the canvas by overwriting the rectangle covered by the
+      current frame.
+
+Disposal method (D): 1 bit
+
+: Indicates how _the current frame_ is to be treated after it has been
+  displayed (before rendering the next frame) on the canvas:
+
+    * `0`: Do not dispose. Leave the canvas as is.
+
+    * `1`: Dispose to background color. Fill the _rectangle_ on the canvas
+      covered by the _current frame_ with background color specified in the
+      [ANIM chunk](#anim_chunk).
+
+**Notes**:
+
+  * The frame disposal only applies to the _frame rectangle_, that is, the
+    rectangle defined by _Frame X_, _Frame Y_, _frame width_ and _frame height_.
+    It may or may not cover the whole canvas.
+
+{:#alpha-blending}
+  * **Alpha-blending**:
+
+    Given that each of the R, G, B and A channels is 8-bit, and the RGB
+    channels are _not premultiplied_ by alpha, the formula for blending
+    'dst' onto 'src' is:
+
+~~~~~
+    blend.A = src.A + dst.A * (1 - src.A / 255)
+    if blend.A = 0 then
+      blend.RGB = 0
+    else
+      blend.RGB =
+          (src.RGB * src.A +
+           dst.RGB * dst.A * (1 - src.A / 255)) / blend.A
+~~~~~
+
+  * Alpha-blending SHOULD be done in linear color space, by taking into account
+    the [color profile](#color-profile) of the image. If the color profile is
+    not present, sRGB is to be assumed. (Note that sRGB also needs to be
+    linearized due to a gamma of ~2.2).
+
+Frame Data: _Chunk Size_ - `16` bytes
+
+: Consists of:
+
+  * An optional [alpha subchunk](#alpha) for the frame.
+
+  * A [bitstream subchunk](#bitstream-vp8vp8l) for the frame.
+
+  * An optional list of [unknown chunks](#unknown-chunks).
+
+**Note**: The 'ANMF' payload, _Frame Data_ above, consists of individual
+_padded_ chunks as described by the [RIFF file format](#riff-file-format).
+
+#### Alpha
+
+     0                   1                   2                   3
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                      ChunkHeader('ALPH')                      |
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |Rsv| P | F | C |     Alpha Bitstream...                        |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Reserved (Rsv): 2 bits
+
+: MUST be `0`. Readers MUST ignore this field.
+
+Pre-processing (P): 2 bits
+
+: These _informative_ bits are used to signal the pre-processing that has
+  been performed during compression. The decoder can use this information to
+  e.g. dither the values or smooth the gradients prior to display.
+
+    * `0`: No pre-processing.
+    * `1`: Level reduction.
+
+Filtering method (F): 2 bits
+
+: The filtering method used:
+
+    * `0`: None.
+    * `1`: Horizontal filter.
+    * `2`: Vertical filter.
+    * `3`: Gradient filter.
+
+For each pixel, filtering is performed using the following calculations.
+Assume the alpha values surrounding the current `X` position are labeled as:
+
+     C | B |
+    ---+---+
+     A | X |
+
+We seek to compute the alpha value at position `X`. First, a prediction is
+made depending on the filtering method:
+
+  * Method `0`: predictor = 0
+  * Method `1`: predictor = A
+  * Method `2`: predictor = B
+  * Method `3`: predictor = clip(A + B - C)
+
+where `clip(v)` is equal to:
+
+  * 0    if v < 0
+  * 255  if v > 255
+  * v    otherwise
+
+The final value is derived by adding the decompressed value `X` to the
+predictor and using modulo-256 arithmetic to wrap the \[256..511\] range
+into the \[0..255\] one:
+
+`alpha = (predictor + X) % 256`
+
+There are special cases for the left-most and top-most pixel positions:
+
+  * The top-left value at location (0, 0) uses 0 as predictor value. Otherwise,
+  * For horizontal or gradient filtering methods, the left-most pixels at
+    location (0, y) are predicted using the location (0, y-1) just above.
+  * For vertical or gradient filtering methods, the top-most pixels at
+    location (x, 0) are predicted using the location (x-1, 0) on the left.
+
+
+Decoders are not required to use this information in any specified way.
+
+Compression method (C): 2 bits
+
+: The compression method used:
+
+    * `0`: No compression.
+    * `1`: Compressed using the WebP lossless format.
+
+Alpha bitstream: _Chunk Size_ - `1` bytes
+
+: Encoded alpha bitstream.
+
+This optional chunk contains encoded alpha data for this frame. A frame
+containing a 'VP8L' chunk SHOULD NOT contain this chunk.
+
+**Rationale**: The transparency information is already part of the 'VP8L'
+chunk.
+
+The alpha channel data is stored as uncompressed raw data (when
+compression method is '0') or compressed using the lossless format
+(when the compression method is '1').
+
+  * Raw data: consists of a byte sequence of length width * height,
+    containing all the 8-bit transparency values in scan order.
+
+  * Lossless format compression: the byte sequence is a compressed
+    image-stream (as described in the [WebP Lossless Bitstream Format]
+    [webpllspec]) of implicit dimension width x height. That is, this
+    image-stream does NOT contain any headers describing the image dimension.
+
+    **Rationale**: the dimension is already known from other sources,
+    so storing it again would be redundant and error-prone.
+
+    Once the image-stream is decoded into ARGB color values, following
+    the process described in the lossless format specification, the
+    transparency information must be extracted from the *green* channel
+    of the ARGB quadruplet.
+
+    **Rationale**: the green channel is allowed extra transformation
+    steps in the specification -- unlike the other channels -- that can
+    improve compression.
+
+#### Bitstream (VP8/VP8L)
+
+This chunk contains compressed bitstream data for a single frame.
+
+A bitstream chunk may be either (i) a VP8 chunk, using "VP8 " (note the
+significant fourth-character space) as its tag _or_ (ii) a VP8L chunk, using
+"VP8L" as its tag.
+
+The formats of VP8 and VP8L chunks are as described in sections
+[Simple File Format (Lossy)](#simple-file-format-lossy)
+and [Simple File Format (Lossless)](#simple-file-format-lossless) respectively.
+
+#### Color Profile
+
+     0                   1                   2                   3
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                      ChunkHeader('ICCP')                      |
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    :                       Color Profile                           :
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Color Profile: _Chunk Size_ bytes
+
+: ICC profile.
+
+This chunk MUST appear before the image data.
+
+There SHOULD be at most one such chunk. If there are more such chunks, readers
+MAY ignore all except the first one.
+See the [ICC Specification][iccspec] for details.
+
+If this chunk is not present, sRGB SHOULD be assumed.
+
+#### Metadata
+
+Metadata can be stored in 'EXIF' or 'XMP ' chunks.
+
+There SHOULD be at most one chunk of each type ('EXIF' and 'XMP '). If there
+are more such chunks, readers MAY ignore all except the first one.
+
+The chunks are defined as follows:
+
+EXIF chunk:
+
+     0                   1                   2                   3
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                      ChunkHeader('EXIF')                      |
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    :                        Exif Metadata                          :
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Exif Metadata: _Chunk Size_ bytes
+
+: Image metadata in Exif format.
+
+XMP chunk:
+
+     0                   1                   2                   3
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                      ChunkHeader('XMP ')                      |
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    :                        XMP Metadata                           :
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+XMP Metadata: _Chunk Size_ bytes
+
+: Image metadata in XMP format.
+
+Note the fourth character in the 'XMP ' FourCC is an ASCII space (0x20).
+
+Additional guidance about handling metadata can be found in the
+Metadata Working Group's [Guidelines for Handling Metadata][metadata].
+
+#### Unknown Chunks
+
+A RIFF chunk (described in [this](#terminology-amp-basics) section) whose _chunk
+tag_ is different from any of the chunks described in this document, is
+considered an _unknown chunk_.
+
+**Rationale**: Allowing unknown chunks gives a provision for future extension
+of the format, and also allows storage of any application-specific data.
+
+A file MAY contain unknown chunks:
+
+  * At the end of the file as described in [Extended WebP file
+    header](#extended_header) section.
+  * At the end of ANMF chunks as described in the
+    [Animation](#animation) section.
+
+Readers SHOULD ignore these chunks. Writers SHOULD preserve them in their
+original order (unless they specifically intend to modify these chunks).
+
+### Assembling the Canvas From Frames
+
+Here we provide an overview of how a reader MUST assemble a canvas in the case
+of an animated image.
+
+The process begins with creating a canvas using the dimensions given in the
+'VP8X' chunk, `Canvas Width Minus One + 1` pixels wide by `Canvas Height Minus
+One + 1` pixels high. The `Loop Count` field from the 'ANIM' chunk controls how
+many times the animation process is repeated. This is `Loop Count - 1` for
+non-zero `Loop Count` values or infinitely if `Loop Count` is zero.
+
+At the beginning of each loop iteration the canvas is filled using the
+background color from the 'ANIM' chunk or an application defined color.
+
+'ANMF' chunks contain individual frames given in display order. Before rendering
+each frame, the previous frame's `Disposal method` is applied.
+
+The rendering of the decoded frame begins at the Cartesian coordinates (`2 *
+Frame X`, `2 * Frame Y`) using the top-left corner of the canvas as the origin.
+`Frame Width Minus One + 1` pixels wide by `Frame Height Minus One + 1` pixels
+high are rendered onto the canvas using the `Blending method`.
+
+The canvas is displayed for `Frame Duration` milliseconds. This continues until
+all frames given by 'ANMF' chunks have been displayed. A new loop iteration is
+then begun or the canvas is left in its final state if all iterations have been
+completed.
+
+The following pseudocode illustrates the rendering process. The notation
+_VP8X.field_ means the field in the 'VP8X' chunk with the same description.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+assert VP8X.flags.hasAnimation
+canvas ← new image of size VP8X.canvasWidth x VP8X.canvasHeight with
+         background color ANIM.background_color.
+loop_count ← ANIM.loopCount
+dispose_method ← Dispose to background color
+if loop_count == 0:
+  loop_count = ∞
+frame_params ← nil
+assert next chunk in image_data is ANMF
+for loop = 0..loop_count - 1
+  clear canvas to ANIM.background_color or application defined color
+  until eof or non-ANMF chunk
+    frame_params.frameX = Frame X
+    frame_params.frameY = Frame Y
+    frame_params.frameWidth = Frame Width Minus One + 1
+    frame_params.frameHeight = Frame Height Minus One + 1
+    frame_params.frameDuration = Frame Duration
+    frame_right = frame_params.frameX + frame_params.frameWidth
+    frame_bottom = frame_params.frameY + frame_params.frameHeight
+    assert VP8X.canvasWidth >= frame_right
+    assert VP8X.canvasHeight >= frame_bottom
+    for subchunk in 'Frame Data':
+      if subchunk.tag == "ALPH":
+        assert alpha subchunks not found in 'Frame Data' earlier
+        frame_params.alpha = alpha_data
+      else if subchunk.tag == "VP8 " OR subchunk.tag == "VP8L":
+        assert bitstream subchunks not found in 'Frame Data' earlier
+        frame_params.bitstream = bitstream_data
+    render frame with frame_params.alpha and frame_params.bitstream
+      on canvas with top-left corner at (frame_params.frameX,
+      frame_params.frameY), using blending method
+      frame_params.blendingMethod.
+    canvas contains the decoded image.
+    Show the contents of the canvas for
+    frame_params.frameDuration * 1ms.
+    dispose_method = frame_params.disposeMethod
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+Example File Layouts
+--------------------
+
+A lossy encoded image with alpha may look as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+RIFF/WEBP
++- VP8X (descriptions of features used)
++- ALPH (alpha bitstream)
++- VP8 (bitstream)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A losslessly encoded image may look as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+RIFF/WEBP
++- VP8X (descriptions of features used)
++- XYZW (unknown chunk)
++- VP8L (lossless bitstream)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A lossless image with ICC profile and XMP metadata may
+look as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+RIFF/WEBP
++- VP8X (descriptions of features used)
++- ICCP (color profile)
++- VP8L (lossless bitstream)
++- XMP  (metadata)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+An animated image with Exif metadata may look as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+RIFF/WEBP
++- VP8X (descriptions of features used)
++- ANIM (global animation parameters)
++- ANMF (frame1 parameters + data)
++- ANMF (frame2 parameters + data)
++- ANMF (frame3 parameters + data)
++- ANMF (frame4 parameters + data)
++- EXIF (metadata)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+[vp8spec]:  https://datatracker.ietf.org/doc/html/rfc6386
+[webpllspec]: https://chromium.googlesource.com/webm/libwebp/+/HEAD/doc/webp-lossless-bitstream-spec.txt
+[iccspec]: https://www.color.org/icc_specs2.xalter
+[metadata]: https://web.archive.org/web/20180919181934/http://www.metadataworkinggroup.org/pdf/mwg_guidance.pdf
+[rfc 1166]: https://datatracker.ietf.org/doc/html/rfc1166
+[rfc 2119]: https://datatracker.ietf.org/doc/html/rfc2119
+[rfc 8174]: https://datatracker.ietf.org/doc/html/rfc8174
diff --git a/doc/webp-lossless-bitstream-spec.txt b/doc/webp-lossless-bitstream-spec.txt
new file mode 100644
index 0000000..563426d
--- /dev/null
+++ b/doc/webp-lossless-bitstream-spec.txt
@@ -0,0 +1,1139 @@
+<!--
+
+Although you may be viewing an alternate representation, this document is
+sourced in Markdown, a light-duty markup scheme, and is optimized for the
+[kramdown](https://kramdown.gettalong.org/) transformer.
+
+See the accompanying specs_generation.md. External link targets are referenced
+at the end of this file.
+
+-->
+
+Specification for WebP Lossless Bitstream
+=========================================
+
+_Jyrki Alakuijala, Ph.D., Google, Inc., 2012-06-19_
+
+Paragraphs marked as \[AMENDED\] were amended on 2014-09-16.
+
+Paragraphs marked as \[AMENDED2\] were amended on 2022-05-13.
+
+Paragraphs marked as \[AMENDED3\] were amended on 2022-11-21.
+
+Abstract
+--------
+
+WebP lossless is an image format for lossless compression of ARGB images. The
+lossless format stores and restores the pixel values exactly, including the
+color values for pixels whose alpha value is 0. The format uses subresolution
+images, recursively embedded into the format itself, for storing statistical
+data about the images, such as the used entropy codes, spatial predictors, color
+space conversion, and color table. LZ77, prefix coding, and a color cache are
+used for compression of the bulk data. Decoding speeds faster than PNG have been
+demonstrated, as well as 25% denser compression than can be achieved using
+today's PNG format.
+
+
+* TOC placeholder
+{:toc}
+
+
+1 Introduction
+--------------
+
+This document describes the compressed data representation of a WebP lossless
+image. It is intended as a detailed reference for the WebP lossless encoder and
+decoder implementation.
+
+In this document, we extensively use C programming language syntax to describe
+the bitstream, and assume the existence of a function for reading bits,
+`ReadBits(n)`. The bytes are read in the natural order of the stream containing
+them, and bits of each byte are read in least-significant-bit-first order. When
+multiple bits are read at the same time, the integer is constructed from the
+original data in the original order. The most significant bits of the returned
+integer are also the most significant bits of the original data. Thus, the
+statement
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+b = ReadBits(2);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+is equivalent with the two statements below:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+b = ReadBits(1);
+b |= ReadBits(1) << 1;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We assume that each color component (e.g. alpha, red, blue and green) is
+represented using an 8-bit byte. We define the corresponding type as uint8. A
+whole ARGB pixel is represented by a type called uint32, an unsigned integer
+consisting of 32 bits. In the code showing the behavior of the transformations,
+alpha value is codified in bits 31..24, red in bits 23..16, green in bits 15..8
+and blue in bits 7..0, but implementations of the format are free to use another
+representation internally.
+
+Broadly, a WebP lossless image contains header data, transform information and
+actual image data. Headers contain width and height of the image. A WebP
+lossless image can go through four different types of transformation before
+being entropy encoded. The transform information in the bitstream contains the
+data required to apply the respective inverse transforms.
+
+
+2 Nomenclature
+--------------
+
+ARGB
+:   A pixel value consisting of alpha, red, green, and blue values.
+
+ARGB image
+:   A two-dimensional array containing ARGB pixels.
+
+color cache
+:   A small hash-addressed array to store recently used colors, to be able to
+    recall them with shorter codes.
+
+color indexing image
+:   A one-dimensional image of colors that can be indexed using a small integer
+    (up to 256 within WebP lossless).
+
+color transform image
+:   A two-dimensional subresolution image containing data about correlations of
+    color components.
+
+distance mapping
+:   Changes LZ77 distances to have the smallest values for pixels in 2D
+    proximity.
+
+entropy image
+:   A two-dimensional subresolution image indicating which entropy coding should
+    be used in a respective square in the image, i.e., each pixel is a meta
+    prefix code.
+
+prefix code
+:   A classic way to do entropy coding where a smaller number of bits are used
+    for more frequent codes.
+
+LZ77
+:   Dictionary-based sliding window compression algorithm that either emits
+    symbols or describes them as sequences of past symbols.
+
+meta prefix code
+:   A small integer (up to 16 bits) that indexes an element in the meta prefix
+    table.
+
+predictor image
+:   A two-dimensional subresolution image indicating which spatial predictor is
+    used for a particular square in the image.
+
+prefix coding
+:   A way to entropy code larger integers that codes a few bits of the integer
+    using an entropy code and codifies the remaining bits raw. This allows for
+    the descriptions of the entropy codes to remain relatively small even when
+    the range of symbols is large.
+
+scan-line order
+:   A processing order of pixels, left-to-right, top-to-bottom, starting from
+    the left-hand-top pixel, proceeding to the right. Once a row is completed,
+    continue from the left-hand column of the next row.
+
+3 RIFF Header
+-------------
+
+The beginning of the header has the RIFF container. This consists of the
+following 21 bytes:
+
+   1. String "RIFF"
+   2. A little-endian 32 bit value of the block length, the whole size
+      of the block controlled by the RIFF header. Normally this equals
+      the payload size (file size minus 8 bytes: 4 bytes for the 'RIFF'
+      identifier and 4 bytes for storing the value itself).
+   3. String "WEBP" (RIFF container name).
+   4. String "VP8L" (chunk tag for lossless encoded image data).
+   5. A little-endian 32-bit value of the number of bytes in the
+      lossless stream.
+   6. One byte signature 0x2f.
+
+The first 28 bits of the bitstream specify the width and height of the image.
+Width and height are decoded as 14-bit integers as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int image_width = ReadBits(14) + 1;
+int image_height = ReadBits(14) + 1;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The 14-bit dynamics for image size limit the maximum size of a WebP lossless
+image to 16384✕16384 pixels.
+
+The alpha_is_used bit is a hint only, and should not impact decoding. It should
+be set to 0 when all alpha values are 255 in the picture, and 1 otherwise.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int alpha_is_used = ReadBits(1);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The version_number is a 3 bit code that must be set to 0. Any other value should
+be treated as an error. \[AMENDED\]
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int version_number = ReadBits(3);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+4 Transformations
+-----------------
+
+Transformations are reversible manipulations of the image data that can reduce
+the remaining symbolic entropy by modeling spatial and color correlations.
+Transformations can make the final compression more dense.
+
+An image can go through four types of transformation. A 1 bit indicates the
+presence of a transform. Each transform is allowed to be used only once. The
+transformations are used only for the main level ARGB image: the subresolution
+images have no transforms, not even the 0 bit indicating the end-of-transforms.
+
+Typically, an encoder would use these transforms to reduce the Shannon entropy
+in the residual image. Also, the transform data can be decided based on entropy
+minimization.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+while (ReadBits(1)) {  // Transform present.
+  // Decode transform type.
+  enum TransformType transform_type = ReadBits(2);
+  // Decode transform data.
+  ...
+}
+
+// Decode actual image data (Section 4).
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If a transform is present then the next two bits specify the transform type.
+There are four types of transforms.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+enum TransformType {
+  PREDICTOR_TRANSFORM             = 0,
+  COLOR_TRANSFORM                 = 1,
+  SUBTRACT_GREEN_TRANSFORM        = 2,
+  COLOR_INDEXING_TRANSFORM        = 3,
+};
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The transform type is followed by the transform data. Transform data contains
+the information required to apply the inverse transform and depends on the
+transform type. Next we describe the transform data for different types.
+
+
+### 4.1 Predictor Transform
+
+The predictor transform can be used to reduce entropy by exploiting the fact
+that neighboring pixels are often correlated. In the predictor transform, the
+current pixel value is predicted from the pixels already decoded (in scan-line
+order) and only the residual value (actual - predicted) is encoded. The
+_prediction mode_ determines the type of prediction to use. We divide the image
+into squares and all the pixels in a square use the same prediction mode.
+
+The first 3 bits of prediction data define the block width and height in number
+of bits. The number of block columns, `block_xsize`, is used in indexing
+two-dimensionally.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int size_bits = ReadBits(3) + 2;
+int block_width = (1 << size_bits);
+int block_height = (1 << size_bits);
+#define DIV_ROUND_UP(num, den) ((num) + (den) - 1) / (den))
+int block_xsize = DIV_ROUND_UP(image_width, 1 << size_bits);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The transform data contains the prediction mode for each block of the image. All
+the `block_width * block_height` pixels of a block use same prediction mode. The
+prediction modes are treated as pixels of an image and encoded using the same
+techniques described in [Chapter 5](#image-data).
+
+For a pixel _x, y_, one can compute the respective filter block address by:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int block_index = (y >> size_bits) * block_xsize +
+                  (x >> size_bits);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are 14 different prediction modes. In each prediction mode, the current
+pixel value is predicted from one or more neighboring pixels whose values are
+already known.
+
+We choose the neighboring pixels (TL, T, TR, and L) of the current pixel (P) as
+follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+O    O    O    O    O    O    O    O    O    O    O
+O    O    O    O    O    O    O    O    O    O    O
+O    O    O    O    TL   T    TR   O    O    O    O
+O    O    O    O    L    P    X    X    X    X    X
+X    X    X    X    X    X    X    X    X    X    X
+X    X    X    X    X    X    X    X    X    X    X
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+where TL means top-left, T top, TR top-right, L left pixel. At the time of
+predicting a value for P, all pixels O, TL, T, TR and L have already been
+processed, and pixel P and all pixels X are unknown.
+
+Given the above neighboring pixels, the different prediction modes are defined
+as follows.
+
+| Mode   | Predicted value of each channel of the current pixel    |
+| ------ | ------------------------------------------------------- |
+|  0     | 0xff000000 (represents solid black color in ARGB)       |
+|  1     | L                                                       |
+|  2     | T                                                       |
+|  3     | TR                                                      |
+|  4     | TL                                                      |
+|  5     | Average2(Average2(L, TR), T)                            |
+|  6     | Average2(L, TL)                                         |
+|  7     | Average2(L, T)                                          |
+|  8     | Average2(TL, T)                                         |
+|  9     | Average2(T, TR)                                         |
+| 10     | Average2(Average2(L, TL), Average2(T, TR))              |
+| 11     | Select(L, T, TL)                                        |
+| 12     | ClampAddSubtractFull(L, T, TL)                          |
+| 13     | ClampAddSubtractHalf(Average2(L, T), TL)                |
+
+
+`Average2` is defined as follows for each ARGB component:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+uint8 Average2(uint8 a, uint8 b) {
+  return (a + b) / 2;
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The Select predictor is defined as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+uint32 Select(uint32 L, uint32 T, uint32 TL) {
+  // L = left pixel, T = top pixel, TL = top left pixel.
+
+  // ARGB component estimates for prediction.
+  int pAlpha = ALPHA(L) + ALPHA(T) - ALPHA(TL);
+  int pRed = RED(L) + RED(T) - RED(TL);
+  int pGreen = GREEN(L) + GREEN(T) - GREEN(TL);
+  int pBlue = BLUE(L) + BLUE(T) - BLUE(TL);
+
+  // Manhattan distances to estimates for left and top pixels.
+  int pL = abs(pAlpha - ALPHA(L)) + abs(pRed - RED(L)) +
+           abs(pGreen - GREEN(L)) + abs(pBlue - BLUE(L));
+  int pT = abs(pAlpha - ALPHA(T)) + abs(pRed - RED(T)) +
+           abs(pGreen - GREEN(T)) + abs(pBlue - BLUE(T));
+
+  // Return either left or top, the one closer to the prediction.
+  if (pL < pT) {     // \[AMENDED\]
+    return L;
+  } else {
+    return T;
+  }
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The functions `ClampAddSubtractFull` and `ClampAddSubtractHalf` are performed
+for each ARGB component as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Clamp the input value between 0 and 255.
+int Clamp(int a) {
+  return (a < 0) ? 0 : (a > 255) ?  255 : a;
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int ClampAddSubtractFull(int a, int b, int c) {
+  return Clamp(a + b - c);
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int ClampAddSubtractHalf(int a, int b) {
+  return Clamp(a + (a - b) / 2);
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are special handling rules for some border pixels. If there is a
+prediction transform, regardless of the mode \[0..13\] for these pixels, the
+predicted value for the left-topmost pixel of the image is 0xff000000, L-pixel
+for all pixels on the top row, and T-pixel for all pixels on the leftmost
+column.
+
+\[AMENDED2\] Addressing the TR-pixel for pixels on the rightmost column is
+exceptional. The pixels on the rightmost column are predicted by using the modes
+\[0..13\] just like pixels not on the border, but the leftmost pixel on the same
+row as the current pixel is instead used as the TR-pixel.
+
+
+### 4.2 Color Transform
+
+\[AMENDED2\]
+
+The goal of the color transform is to decorrelate the R, G and B values of each
+pixel. The color transform keeps the green (G) value as it is, transforms red
+(R) based on green and transforms blue (B) based on green and then based on red.
+
+As is the case for the predictor transform, first the image is divided into
+blocks and the same transform mode is used for all the pixels in a block. For
+each block there are three types of color transform elements.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+typedef struct {
+  uint8 green_to_red;
+  uint8 green_to_blue;
+  uint8 red_to_blue;
+} ColorTransformElement;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The actual color transformation is done by defining a color transform delta. The
+color transform delta depends on the `ColorTransformElement`, which is the same
+for all the pixels in a particular block. The delta is subtracted during the
+color transform. The inverse color transform then is just adding those deltas.
+
+The color transform function is defined as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void ColorTransform(uint8 red, uint8 blue, uint8 green,
+                    ColorTransformElement *trans,
+                    uint8 *new_red, uint8 *new_blue) {
+  // Transformed values of red and blue components
+  int tmp_red = red;
+  int tmp_blue = blue;
+
+  // Applying the transform is just subtracting the transform deltas
+  tmp_red  -= ColorTransformDelta(trans->green_to_red_,  green);
+  tmp_blue -= ColorTransformDelta(trans->green_to_blue_, green);
+  tmp_blue -= ColorTransformDelta(trans->red_to_blue_, red);
+
+  *new_red = tmp_red & 0xff;
+  *new_blue = tmp_blue & 0xff;
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+`ColorTransformDelta` is computed using a signed 8-bit integer representing a
+3.5-fixed-point number, and a signed 8-bit RGB color channel (c) \[-128..127\]
+and is defined as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int8 ColorTransformDelta(int8 t, int8 c) {
+  return (t * c) >> 5;
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A conversion from the 8-bit unsigned representation (uint8) to the 8-bit signed
+one (int8) is required before calling `ColorTransformDelta()`. It should be
+performed using 8-bit two's complement (that is: uint8 range \[128..255\] is
+mapped to the \[-128..-1\] range of its converted int8 value).
+
+The multiplication is to be done using more precision (with at least 16-bit
+dynamics). The sign extension property of the shift operation does not matter
+here: only the lowest 8 bits are used from the result, and there the sign
+extension shifting and unsigned shifting are consistent with each other.
+
+Now we describe the contents of color transform data so that decoding can apply
+the inverse color transform and recover the original red and blue values. The
+first 3 bits of the color transform data contain the width and height of the
+image block in number of bits, just like the predictor transform:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int size_bits = ReadBits(3) + 2;
+int block_width = 1 << size_bits;
+int block_height = 1 << size_bits;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The remaining part of the color transform data contains `ColorTransformElement`
+instances corresponding to each block of the image. `ColorTransformElement`
+instances are treated as pixels of an image and encoded using the methods
+described in [Chapter 5](#image-data).
+
+During decoding, `ColorTransformElement` instances of the blocks are decoded and
+the inverse color transform is applied on the ARGB values of the pixels. As
+mentioned earlier, that inverse color transform is just adding
+`ColorTransformElement` values to the red and blue channels. \[AMENDED3\]
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void InverseTransform(uint8 red, uint8 green, uint8 blue,
+                      ColorTransformElement *trans,
+                      uint8 *new_red, uint8 *new_blue) {
+  // Transformed values of red and blue components
+  int tmp_red = red;
+  int tmp_blue = blue;
+
+  // Applying the inverse transform is just adding the
+  // color transform deltas
+  tmp_red  += ColorTransformDelta(trans->green_to_red, green);
+  tmp_blue += ColorTransformDelta(trans->green_to_blue, green);
+  tmp_blue +=
+      ColorTransformDelta(trans->red_to_blue, tmp_red & 0xff);
+
+  *new_red = tmp_red & 0xff;
+  *new_blue = tmp_blue & 0xff;
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+### 4.3 Subtract Green Transform
+
+The subtract green transform subtracts green values from red and blue values of
+each pixel. When this transform is present, the decoder needs to add the green
+value to both red and blue. There is no data associated with this transform. The
+decoder applies the inverse transform as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void AddGreenToBlueAndRed(uint8 green, uint8 *red, uint8 *blue) {
+  *red  = (*red  + green) & 0xff;
+  *blue = (*blue + green) & 0xff;
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This transform is redundant as it can be modeled using the color transform, but
+it is still often useful. Since it can extend the dynamics of the color
+transform and there is no additional data here, the subtract green transform can
+be coded using fewer bits than a full-blown color transform.
+
+
+### 4.4 Color Indexing Transform
+
+If there are not many unique pixel values, it may be more efficient to create a
+color index array and replace the pixel values by the array's indices. The color
+indexing transform achieves this. (In the context of WebP lossless, we
+specifically do not call this a palette transform because a similar but more
+dynamic concept exists in WebP lossless encoding: color cache).
+
+The color indexing transform checks for the number of unique ARGB values in the
+image. If that number is below a threshold (256), it creates an array of those
+ARGB values, which is then used to replace the pixel values with the
+corresponding index: the green channel of the pixels are replaced with the
+index; all alpha values are set to 255; all red and blue values to 0.
+
+The transform data contains color table size and the entries in the color table.
+The decoder reads the color indexing transform data as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 8 bit value for color table size
+int color_table_size = ReadBits(8) + 1;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The color table is stored using the image storage format itself. The color table
+can be obtained by reading an image, without the RIFF header, image size, and
+transforms, assuming a height of one pixel and a width of `color_table_size`.
+The color table is always subtraction-coded to reduce image entropy. The deltas
+of palette colors contain typically much less entropy than the colors
+themselves, leading to significant savings for smaller images. In decoding,
+every final color in the color table can be obtained by adding the previous
+color component values by each ARGB component separately, and storing the least
+significant 8 bits of the result.
+
+The inverse transform for the image is simply replacing the pixel values (which
+are indices to the color table) with the actual color table values. The indexing
+is done based on the green component of the ARGB color.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Inverse transform
+argb = color_table[GREEN(argb)];
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If the index is equal or larger than `color_table_size`, the argb color value
+should be set to 0x00000000 (transparent black). \[AMENDED\]
+
+When the color table is small (equal to or less than 16 colors), several pixels
+are bundled into a single pixel. The pixel bundling packs several (2, 4, or 8)
+pixels into a single pixel, reducing the image width respectively. Pixel
+bundling allows for a more efficient joint distribution entropy coding of
+neighboring pixels, and gives some arithmetic coding-like benefits to the
+entropy code, but it can only be used when there are 16 or fewer unique values.
+
+`color_table_size` specifies how many pixels are combined:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int width_bits;
+if (color_table_size <= 2) {
+  width_bits = 3;
+} else if (color_table_size <= 4) {
+  width_bits = 2;
+} else if (color_table_size <= 16) {
+  width_bits = 1;
+} else {
+  width_bits = 0;
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+`width_bits` has a value of 0, 1, 2 or 3. A value of 0 indicates no pixel
+bundling is to be done for the image. A value of 1 indicates that two pixels are
+combined, and each pixel has a range of \[0..15\]. A value of 2 indicates that
+four pixels are combined, and each pixel has a range of \[0..3\]. A value of 3
+indicates that eight pixels are combined and each pixel has a range of \[0..1\],
+i.e., a binary value.
+
+The values are packed into the green component as follows:
+
+  * `width_bits` = 1: for every x value where x ≡ 0 (mod 2), a green
+    value at x is positioned into the 4 least-significant bits of the
+    green value at x / 2, a green value at x + 1 is positioned into the
+    4 most-significant bits of the green value at x / 2.
+  * `width_bits` = 2: for every x value where x ≡ 0 (mod 4), a green
+    value at x is positioned into the 2 least-significant bits of the
+    green value at x / 4, green values at x + 1 to x + 3 are positioned in order
+    to the more significant bits of the green value at x / 4.
+  * `width_bits` = 3: for every x value where x ≡ 0 (mod 8), a green
+    value at x is positioned into the least-significant bit of the green
+    value at x / 8, green values at x + 1 to x + 7 are positioned in order to
+    the more significant bits of the green value at x / 8.
+
+
+5 Image Data
+------------
+
+Image data is an array of pixel values in scan-line order.
+
+### 5.1 Roles of Image Data
+
+We use image data in five different roles:
+
+  1. ARGB image: Stores the actual pixels of the image.
+  1. Entropy image: Stores the
+     [meta prefix codes](#decoding-of-meta-prefix-codes). The red and green
+     components of a pixel define the meta prefix code used in a particular
+     block of the ARGB image.
+  1. Predictor image: Stores the metadata for
+     [Predictor Transform](#predictor-transform). The green component of a pixel
+     defines which of the 14 predictors is used within a particular block of the
+     ARGB image.
+  1. Color transform image. It is created by `ColorTransformElement` values
+     (defined in [Color Transform](#color-transform)) for different blocks of
+     the image. Each `ColorTransformElement` `'cte'` is treated as a pixel whose
+     alpha component is `255`, red component is `cte.red_to_blue`, green
+     component is `cte.green_to_blue` and blue component is `cte.green_to_red`.
+  1. Color indexing image: An array of size `color_table_size` (up to 256
+     ARGB values) storing the metadata for the
+     [Color Indexing Transform](#color-indexing-transform). This is stored as an
+     image of width `color_table_size` and height `1`.
+
+### 5.2 Encoding of Image Data
+
+The encoding of image data is independent of its role.
+
+The image is first divided into a set of fixed-size blocks (typically 16x16
+blocks). Each of these blocks are modeled using their own entropy codes. Also,
+several blocks may share the same entropy codes.
+
+**Rationale:** Storing an entropy code incurs a cost. This cost can be minimized
+if statistically similar blocks share an entropy code, thereby storing that code
+only once. For example, an encoder can find similar blocks by clustering them
+using their statistical properties, or by repeatedly joining a pair of randomly
+selected clusters when it reduces the overall amount of bits needed to encode
+the image.
+
+Each pixel is encoded using one of the three possible methods:
+
+  1. Prefix coded literal: each channel (green, red, blue and alpha) is
+     entropy-coded independently;
+  2. LZ77 backward reference: a sequence of pixels are copied from elsewhere
+     in the image; or
+  3. Color cache code: using a short multiplicative hash code (color cache
+     index) of a recently seen color.
+
+The following subsections describe each of these in detail.
+
+#### 5.2.1 Prefix Coded Literals
+
+The pixel is stored as prefix coded values of green, red, blue and alpha (in
+that order). See [this section](#decoding-entropy-coded-image-data) for details.
+
+#### 5.2.2 LZ77 Backward Reference
+
+Backward references are tuples of _length_ and _distance code_:
+
+  * Length indicates how many pixels in scan-line order are to be copied.
+  * Distance code is a number indicating the position of a previously seen
+    pixel, from which the pixels are to be copied. The exact mapping is
+    described [below](#distance-mapping).
+
+The length and distance values are stored using **LZ77 prefix coding**.
+
+LZ77 prefix coding divides large integer values into two parts: the _prefix
+code_ and the _extra bits_: the prefix code is stored using an entropy code,
+while the extra bits are stored as they are (without an entropy code).
+
+**Rationale**: This approach reduces the storage requirement for the entropy
+code. Also, large values are usually rare, and so extra bits would be used for
+very few values in the image. Thus, this approach results in better compression
+overall.
+
+The following table denotes the prefix codes and extra bits used for storing
+different ranges of values.
+
+Note: The maximum backward reference length is limited to 4096. Hence, only the
+first 24 prefix codes (with the respective extra bits) are meaningful for length
+values. For distance values, however, all the 40 prefix codes are valid.
+
+| Value range     | Prefix code | Extra bits |
+| --------------- | ----------- | ---------- |
+| 1               | 0           | 0          |
+| 2               | 1           | 0          |
+| 3               | 2           | 0          |
+| 4               | 3           | 0          |
+| 5..6            | 4           | 1          |
+| 7..8            | 5           | 1          |
+| 9..12           | 6           | 2          |
+| 13..16          | 7           | 2          |
+| ...             | ...         | ...        |
+| 3072..4096      | 23          | 10         |
+| ...             | ...         | ...        |
+| 524289..786432  | 38          | 18         |
+| 786433..1048576 | 39          | 18         |
+
+The pseudocode to obtain a (length or distance) value from the prefix code is as
+follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+if (prefix_code < 4) {
+  return prefix_code + 1;
+}
+int extra_bits = (prefix_code - 2) >> 1;
+int offset = (2 + (prefix_code & 1)) << extra_bits;
+return offset + ReadBits(extra_bits) + 1;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**Distance Mapping:**
+{:#distance-mapping}
+
+As noted previously, a distance code is a number indicating the position of a
+previously seen pixel, from which the pixels are to be copied. This subsection
+defines the mapping between a distance code and the position of a previous
+pixel.
+
+Distance codes larger than 120 denote the pixel-distance in scan-line order,
+offset by 120.
+
+The smallest distance codes \[1..120\] are special, and are reserved for a close
+neighborhood of the current pixel. This neighborhood consists of 120 pixels:
+
+  * Pixels that are 1 to 7 rows above the current pixel, and are up to 8 columns
+    to the left or up to 7 columns to the right of the current pixel. \[Total
+    such pixels = `7 * (8 + 1 + 7) = 112`\].
+  * Pixels that are in same row as the current pixel, and are up to 8 columns to
+    the left of the current pixel. \[`8` such pixels\].
+
+The mapping between distance code `i` and the neighboring pixel offset
+`(xi, yi)` is as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+(0, 1),  (1, 0),  (1, 1),  (-1, 1), (0, 2),  (2, 0),  (1, 2),
+(-1, 2), (2, 1),  (-2, 1), (2, 2),  (-2, 2), (0, 3),  (3, 0),
+(1, 3),  (-1, 3), (3, 1),  (-3, 1), (2, 3),  (-2, 3), (3, 2),
+(-3, 2), (0, 4),  (4, 0),  (1, 4),  (-1, 4), (4, 1),  (-4, 1),
+(3, 3),  (-3, 3), (2, 4),  (-2, 4), (4, 2),  (-4, 2), (0, 5),
+(3, 4),  (-3, 4), (4, 3),  (-4, 3), (5, 0),  (1, 5),  (-1, 5),
+(5, 1),  (-5, 1), (2, 5),  (-2, 5), (5, 2),  (-5, 2), (4, 4),
+(-4, 4), (3, 5),  (-3, 5), (5, 3),  (-5, 3), (0, 6),  (6, 0),
+(1, 6),  (-1, 6), (6, 1),  (-6, 1), (2, 6),  (-2, 6), (6, 2),
+(-6, 2), (4, 5),  (-4, 5), (5, 4),  (-5, 4), (3, 6),  (-3, 6),
+(6, 3),  (-6, 3), (0, 7),  (7, 0),  (1, 7),  (-1, 7), (5, 5),
+(-5, 5), (7, 1),  (-7, 1), (4, 6),  (-4, 6), (6, 4),  (-6, 4),
+(2, 7),  (-2, 7), (7, 2),  (-7, 2), (3, 7),  (-3, 7), (7, 3),
+(-7, 3), (5, 6),  (-5, 6), (6, 5),  (-6, 5), (8, 0),  (4, 7),
+(-4, 7), (7, 4),  (-7, 4), (8, 1),  (8, 2),  (6, 6),  (-6, 6),
+(8, 3),  (5, 7),  (-5, 7), (7, 5),  (-7, 5), (8, 4),  (6, 7),
+(-6, 7), (7, 6),  (-7, 6), (8, 5),  (7, 7),  (-7, 7), (8, 6),
+(8, 7)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For example, the distance code `1` indicates an offset of `(0, 1)` for the
+neighboring pixel, that is, the pixel above the current pixel (0 pixel
+difference in the X-direction and 1 pixel difference in the Y-direction).
+Similarly, the distance code `3` indicates the left-top pixel.
+
+The decoder can convert a distance code `i` to a scan-line order distance `dist`
+as follows:
+
+\[AMENDED3\]
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+(xi, yi) = distance_map[i - 1]
+dist = xi + yi * xsize
+if (dist < 1) {
+  dist = 1
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+where `distance_map` is the mapping noted above and `xsize` is the width of the
+image in pixels.
+
+
+#### 5.2.3 Color Cache Coding
+{:#color-cache-code}
+
+Color cache stores a set of colors that have been recently used in the image.
+
+**Rationale:** This way, the recently used colors can sometimes be referred to
+more efficiently than emitting them using the other two methods (described in
+[5.2.1](#prefix-coded-literals) and [5.2.2](#lz77-backward-reference)).
+
+Color cache codes are stored as follows. First, there is a 1-bit value that
+indicates if the color cache is used. If this bit is 0, no color cache codes
+exist, and they are not transmitted in the prefix code that decodes the green
+symbols and the length prefix codes. However, if this bit is 1, the color cache
+size is read next:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int color_cache_code_bits = ReadBits(4);
+int color_cache_size = 1 << color_cache_code_bits;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+`color_cache_code_bits` defines the size of the color_cache by (1 <<
+`color_cache_code_bits`). The range of allowed values for
+`color_cache_code_bits` is \[1..11\]. Compliant decoders must indicate a
+corrupted bitstream for other values.
+
+A color cache is an array of size `color_cache_size`. Each entry stores one ARGB
+color. Colors are looked up by indexing them by (0x1e35a7bd * `color`) >> (32 -
+`color_cache_code_bits`). Only one lookup is done in a color cache; there is no
+conflict resolution.
+
+In the beginning of decoding or encoding of an image, all entries in all color
+cache values are set to zero. The color cache code is converted to this color at
+decoding time. The state of the color cache is maintained by inserting every
+pixel, be it produced by backward referencing or as literals, into the cache in
+the order they appear in the stream.
+
+
+6 Entropy Code
+--------------
+
+### 6.1 Overview
+
+Most of the data is coded using a [canonical prefix code][canonical_huff].
+Hence, the codes are transmitted by sending the _prefix code lengths_, as
+opposed to the actual _prefix codes_.
+
+In particular, the format uses **spatially-variant prefix coding**. In other
+words, different blocks of the image can potentially use different entropy
+codes.
+
+**Rationale**: Different areas of the image may have different characteristics.
+So, allowing them to use different entropy codes provides more flexibility and
+potentially better compression.
+
+### 6.2 Details
+
+The encoded image data consists of several parts:
+
+  1. Decoding and building the prefix codes \[AMENDED2\]
+  1. Meta prefix codes
+  1. Entropy-coded image data
+
+#### 6.2.1 Decoding and Building the Prefix Codes
+
+There are several steps in decoding the prefix codes.
+
+**Decoding the Code Lengths:**
+{:#decoding-the-code-lengths}
+
+This section describes how to read the prefix code lengths from the bitstream.
+
+The prefix code lengths can be coded in two ways. The method used is specified
+by a 1-bit value.
+
+  * If this bit is 1, it is a _simple code length code_, and
+  * If this bit is 0, it is a _normal code length code_.
+
+In both cases, there can be unused code lengths that are still part of the
+stream. This may be inefficient, but it is allowed by the format.
+
+**(i) Simple Code Length Code:**
+
+\[AMENDED2\]
+
+This variant is used in the special case when only 1 or 2 prefix symbols are in
+the range \[0..255\] with code length `1`. All other prefix code lengths are
+implicitly zeros.
+
+The first bit indicates the number of symbols:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int num_symbols = ReadBits(1) + 1;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Following are the symbol values.
+
+This first symbol is coded using 1 or 8 bits depending on the value of
+`is_first_8bits`. The range is \[0..1\] or \[0..255\], respectively. The second
+symbol, if present, is always assumed to be in the range \[0..255\] and coded
+using 8 bits.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int is_first_8bits = ReadBits(1);
+symbol0 = ReadBits(1 + 7 * is_first_8bits);
+code_lengths[symbol0] = 1;
+if (num_symbols == 2) {
+  symbol1 = ReadBits(8);
+  code_lengths[symbol1] = 1;
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**Note:** Another special case is when _all_ prefix code lengths are _zeros_ (an
+empty prefix code). For example, a prefix code for distance can be empty if
+there are no backward references. Similarly, prefix codes for alpha, red, and
+blue can be empty if all pixels within the same meta prefix code are produced
+using the color cache. However, this case doesn't need special handling, as
+empty prefix codes can be coded as those containing a single symbol `0`.
+
+**(ii) Normal Code Length Code:**
+
+The code lengths of the prefix code fit in 8 bits and are read as follows.
+First, `num_code_lengths` specifies the number of code lengths.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int num_code_lengths = 4 + ReadBits(4);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If `num_code_lengths` is > 19, the bitstream is invalid. \[AMENDED3\]
+
+The code lengths are themselves encoded using prefix codes: lower level code
+lengths, `code_length_code_lengths`, first have to be read. The rest of those
+`code_length_code_lengths` (according to the order in `kCodeLengthCodeOrder`)
+are zeros.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int kCodeLengthCodes = 19;
+int kCodeLengthCodeOrder[kCodeLengthCodes] = {
+  17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+};
+int code_length_code_lengths[kCodeLengthCodes] = { 0 };  // All zeros
+for (i = 0; i < num_code_lengths; ++i) {
+  code_length_code_lengths[kCodeLengthCodeOrder[i]] = ReadBits(3);
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Next, if `ReadBits(1) == 0`, the maximum number of different read symbols is
+`num_code_lengths`. Otherwise, it is defined as:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int length_nbits = 2 + 2 * ReadBits(3);
+int max_symbol = 2 + ReadBits(length_nbits);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A prefix table is then built from `code_length_code_lengths` and used to read up
+to `max_symbol` code lengths.
+
+  * Code \[0..15\] indicates literal code lengths.
+    * Value 0 means no symbols have been coded.
+    * Values \[1..15\] indicate the bit length of the respective code.
+  * Code 16 repeats the previous non-zero value \[3..6\] times, i.e.,
+    `3 + ReadBits(2)` times. If code 16 is used before a non-zero
+    value has been emitted, a value of 8 is repeated.
+  * Code 17 emits a streak of zeros \[3..10\], i.e., `3 + ReadBits(3)`
+    times.
+  * Code 18 emits a streak of zeros of length \[11..138\], i.e.,
+    `11 + ReadBits(7)` times.
+
+Once code lengths are read, a prefix code for each symbol type (A, R, G, B,
+distance) is formed using their respective alphabet sizes:
+
+  * G channel: 256 + 24 + `color_cache_size`
+  * other literals (A,R,B): 256
+  * distance code: 40
+
+#### 6.2.2 Decoding of Meta Prefix Codes
+
+As noted earlier, the format allows the use of different prefix codes for
+different blocks of the image. _Meta prefix codes_ are indexes identifying which
+prefix codes to use in different parts of the image.
+
+Meta prefix codes may be used _only_ when the image is being used in the
+[role](#roles-of-image-data) of an _ARGB image_.
+
+There are two possibilities for the meta prefix codes, indicated by a 1-bit
+value:
+
+  * If this bit is zero, there is only one meta prefix code used everywhere in
+    the image. No more data is stored.
+  * If this bit is one, the image uses multiple meta prefix codes. These meta
+    prefix codes are stored as an _entropy image_ (described below).
+
+**Entropy image:**
+
+The entropy image defines which prefix codes are used in different parts of the
+image, as described below.
+
+The first 3-bits contain the `prefix_bits` value. The dimensions of the entropy
+image are derived from `prefix_bits`.
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int prefix_bits = ReadBits(3) + 2;
+int prefix_xsize = DIV_ROUND_UP(xsize, 1 << prefix_bits);
+int prefix_ysize = DIV_ROUND_UP(ysize, 1 << prefix_bits);
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+where `DIV_ROUND_UP` is as defined [earlier](#predictor-transform).
+
+The next bits contain an entropy image of width `prefix_xsize` and height
+`prefix_ysize`.
+
+**Interpretation of Meta Prefix Codes:**
+
+For any given pixel (x, y), there is a set of five prefix codes associated with
+it. These codes are (in bitstream order):
+
+  * **Prefix code #1**: used for green channel, backward-reference length and
+    color cache.
+  * **Prefix code #2, #3 and #4**: used for red, blue and alpha channels
+    respectively.
+  * **Prefix code #5**: used for backward-reference distance.
+
+From here on, we refer to this set as a **prefix code group**.
+
+The number of prefix code groups in the ARGB image can be obtained by finding
+the _largest meta prefix code_ from the entropy image:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int num_prefix_groups = max(entropy image) + 1;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+where `max(entropy image)` indicates the largest prefix code stored in the
+entropy image.
+
+As each prefix code group contains five prefix codes, the total number of prefix
+codes is:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int num_prefix_codes = 5 * num_prefix_groups;
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Given a pixel (x, y) in the ARGB image, we can obtain the corresponding prefix
+codes to be used as follows:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int position =
+    (y >> prefix_bits) * prefix_xsize + (x >> prefix_bits);
+int meta_prefix_code = (entropy_image[pos] >> 8) & 0xffff;
+PrefixCodeGroup prefix_group = prefix_code_groups[meta_prefix_code];
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+where, we have assumed the existence of `PrefixCodeGroup` structure, which
+represents a set of five prefix codes. Also, `prefix_code_groups` is an array of
+`PrefixCodeGroup` (of size `num_prefix_groups`).
+
+The decoder then uses prefix code group `prefix_group` to decode the pixel
+(x, y) as explained in the [next section](#decoding-entropy-coded-image-data).
+
+#### 6.2.3 Decoding Entropy-coded Image Data
+
+\[AMENDED2\]
+
+For the current position (x, y) in the image, the decoder first identifies the
+corresponding prefix code group (as explained in the last section). Given the
+prefix code group, the pixel is read and decoded as follows:
+
+Read the next symbol S from the bitstream using prefix code #1. Note that S is
+any integer in the range `0` to
+`(256 + 24 + ` [`color_cache_size`](#color-cache-code)` - 1)`.
+
+The interpretation of S depends on its value:
+
+  1. if S < 256
+     1. Use S as the green component.
+     1. Read red from the bitstream using prefix code #2.
+     1. Read blue from the bitstream using prefix code #3.
+     1. Read alpha from the bitstream using prefix code #4.
+  1. if S >= 256 && S < 256 + 24
+     1. Use S - 256 as a length prefix code.
+     1. Read extra bits for length from the bitstream.
+     1. Determine backward-reference length L from length prefix code and the
+        extra bits read.
+     1. Read distance prefix code from the bitstream using prefix code #5.
+     1. Read extra bits for distance from the bitstream.
+     1. Determine backward-reference distance D from distance prefix code and
+        the extra bits read.
+     1. Copy the L pixels (in scan-line order) from the sequence of pixels
+        prior to them by D pixels.
+  1. if S >= 256 + 24
+     1. Use S - (256 + 24) as the index into the color cache.
+     1. Get ARGB color from the color cache at that index.
+
+
+7 Overall Structure of the Format
+---------------------------------
+
+Below is a view into the format in Augmented Backus-Naur Form ([ABNF]). It does
+not cover all details. End-of-image (EOI) is only implicitly coded into the
+number of pixels (xsize * ysize).
+
+
+#### 7.1 Basic Structure
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+format       = RIFF-header image-size image-stream
+RIFF-header  = "RIFF" 4OCTET "WEBP" "VP8L" 4OCTET %x2F
+image-size   = 14BIT 14BIT ; width - 1, height - 1
+image-stream = optional-transform spatially-coded-image
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+#### 7.2 Structure of Transforms
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+optional-transform   =  (%b1 transform optional-transform) / %b0
+transform            =  predictor-tx / color-tx / subtract-green-tx
+transform            =/ color-indexing-tx
+
+predictor-tx         =  %b00 predictor-image
+predictor-image      =  3BIT ; sub-pixel code
+                        entropy-coded-image
+
+color-tx             =  %b01 color-image
+color-image          =  3BIT ; sub-pixel code
+                        entropy-coded-image
+
+subtract-green-tx    =  %b10
+
+color-indexing-tx    =  %b11 color-indexing-image
+color-indexing-image =  8BIT ; color count
+                        entropy-coded-image
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+#### 7.3 Structure of the Image Data
+
+\[AMENDED2\]
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+spatially-coded-image =  color-cache-info meta-prefix data
+entropy-coded-image   =  color-cache-info data
+
+color-cache-info      =  %b0
+color-cache-info      =/ (%b1 4BIT) ; 1 followed by color cache size
+
+meta-prefix           =  %b0 / (%b1 entropy-image)
+
+data                  =  prefix-codes lz77-coded-image
+entropy-image         =  3BIT ; subsample value
+                         entropy-coded-image
+
+prefix-codes          =  prefix-code-group *prefix-codes
+prefix-code-group     =
+    5prefix-code ; See "Interpretation of Meta Prefix Codes" to
+                 ; understand what each of these five prefix
+                 ; codes are for.
+
+prefix-code           =  simple-prefix-code / normal-prefix-code
+simple-prefix-code    =  ; see "Simple Code Length Code" for details
+normal-prefix-code    =  code-length-code encoded-code-lengths
+code-length-code      =  ; see section "Normal Code Length Code"
+
+lz77-coded-image      =
+    *((argb-pixel / lz77-copy / color-cache-code) lz77-coded-image)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A possible example sequence:
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+RIFF-header image-size %b1 subtract-green-tx
+%b1 predictor-tx %b0 color-cache-info
+%b0 prefix-codes lz77-coded-image
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+[ABNF]: https://www.rfc-editor.org/rfc/rfc5234
+[canonical_huff]: https://en.wikipedia.org/wiki/Canonical_Huffman_code
diff --git a/examples/Android.mk b/examples/Android.mk
new file mode 100644
index 0000000..ba3c458
--- /dev/null
+++ b/examples/Android.mk
@@ -0,0 +1,114 @@
+# Ignore this file during non-NDK builds.
+ifdef NDK_ROOT
+LOCAL_PATH := $(call my-dir)
+
+################################################################################
+# libexample_util
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    example_util.c \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
+
+LOCAL_MODULE := example_util
+
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-BSD
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../COPYING $(LOCAL_PATH)/../NOTICE $(LOCAL_PATH)/../PATENTS
+include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+# cwebp
+
+include $(CLEAR_VARS)
+
+# Note: to enable jpeg/png encoding the sources from AOSP can be used with
+# minor modification to their Android.mk files.
+LOCAL_SRC_FILES := \
+    cwebp.c \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_STATIC_LIBRARIES := example_util imageio_util imagedec webpdemux webp
+
+LOCAL_MODULE := cwebp
+
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-BSD
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../COPYING $(LOCAL_PATH)/../NOTICE $(LOCAL_PATH)/../PATENTS
+include $(BUILD_EXECUTABLE)
+
+################################################################################
+# dwebp
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    dwebp.c \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_STATIC_LIBRARIES := example_util imagedec imageenc webpdemux webp
+LOCAL_MODULE := dwebp
+
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-BSD
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../COPYING $(LOCAL_PATH)/../NOTICE $(LOCAL_PATH)/../PATENTS
+include $(BUILD_EXECUTABLE)
+
+################################################################################
+# webpmux
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    webpmux.c \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_STATIC_LIBRARIES := example_util imageio_util webpmux webp
+
+LOCAL_MODULE := webpmux_example
+
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-BSD
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../COPYING $(LOCAL_PATH)/../NOTICE $(LOCAL_PATH)/../PATENTS
+include $(BUILD_EXECUTABLE)
+
+################################################################################
+# img2webp
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    img2webp.c \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_STATIC_LIBRARIES := example_util imageio_util imagedec webpmux webpdemux \
+                          webp
+
+LOCAL_MODULE := img2webp_example
+
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-BSD
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../COPYING $(LOCAL_PATH)/../NOTICE $(LOCAL_PATH)/../PATENTS
+include $(BUILD_EXECUTABLE)
+
+################################################################################
+# webpinfo
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    webpinfo.c \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_STATIC_LIBRARIES := example_util imageio_util webp
+
+LOCAL_MODULE := webpinfo_example
+
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-BSD
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../COPYING $(LOCAL_PATH)/../NOTICE $(LOCAL_PATH)/../PATENTS
+include $(BUILD_EXECUTABLE)
+endif  # NDK_ROOT
diff --git a/examples/Makefile.am b/examples/Makefile.am
new file mode 100644
index 0000000..72aa9f9
--- /dev/null
+++ b/examples/Makefile.am
@@ -0,0 +1,119 @@
+AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src
+
+bin_PROGRAMS =
+if BUILD_DEMUX
+  bin_PROGRAMS += dwebp cwebp
+endif
+if BUILD_ANIMDIFF
+  noinst_PROGRAMS = anim_diff anim_dump
+endif
+if BUILD_GIF2WEBP
+  bin_PROGRAMS += gif2webp
+endif
+if BUILD_IMG2WEBP
+  bin_PROGRAMS += img2webp
+endif
+if BUILD_MUX
+  bin_PROGRAMS += webpmux
+endif
+if BUILD_VWEBP
+  bin_PROGRAMS += vwebp
+endif
+if BUILD_WEBPINFO
+  bin_PROGRAMS += webpinfo
+endif
+
+noinst_LTLIBRARIES = libexample_util.la
+
+libexample_util_la_SOURCES = example_util.c example_util.h
+libexample_util_la_LIBADD = ../src/libwebp.la
+
+anim_diff_SOURCES = anim_diff.c anim_util.c anim_util.h gifdec.c gifdec.h
+anim_diff_CPPFLAGS = $(AM_CPPFLAGS) $(GIF_INCLUDES)
+anim_diff_LDADD  =
+anim_diff_LDADD += ../src/demux/libwebpdemux.la
+anim_diff_LDADD += libexample_util.la
+anim_diff_LDADD += ../imageio/libimageio_util.la
+anim_diff_LDADD += $(GIF_LIBS) -lm
+
+anim_dump_SOURCES = anim_dump.c anim_util.c anim_util.h gifdec.c gifdec.h
+anim_dump_CPPFLAGS = $(AM_CPPFLAGS) $(PNG_INCLUDES)
+anim_dump_CPPFLAGS += $(GIF_INCLUDES)
+anim_dump_LDADD  =
+anim_dump_LDADD += ../src/demux/libwebpdemux.la
+anim_dump_LDADD += libexample_util.la
+anim_dump_LDADD += ../imageio/libimageio_util.la
+anim_dump_LDADD += ../imageio/libimageenc.la
+anim_dump_LDADD += $(PNG_LIBS) $(GIF_LIBS) $(TIFF_LIBS) -lm
+
+cwebp_SOURCES  = cwebp.c stopwatch.h
+cwebp_CPPFLAGS  = $(AM_CPPFLAGS) -I$(top_srcdir)
+cwebp_LDADD  =
+cwebp_LDADD += libexample_util.la
+cwebp_LDADD += ../imageio/libimageio_util.la
+cwebp_LDADD += ../imageio/libimagedec.la
+cwebp_LDADD += ../src/libwebp.la
+cwebp_LDADD += $(JPEG_LIBS) $(PNG_LIBS) $(TIFF_LIBS)
+
+dwebp_SOURCES = dwebp.c stopwatch.h
+dwebp_CPPFLAGS  = $(AM_CPPFLAGS)
+dwebp_CPPFLAGS += $(JPEG_INCLUDES) $(PNG_INCLUDES)
+dwebp_LDADD  =
+dwebp_LDADD += libexample_util.la
+dwebp_LDADD += ../imageio/libimagedec.la
+dwebp_LDADD += ../imageio/libimageenc.la
+dwebp_LDADD += ../imageio/libimageio_util.la
+dwebp_LDADD += ../src/libwebp.la
+dwebp_LDADD +=$(PNG_LIBS) $(JPEG_LIBS)
+
+gif2webp_SOURCES = gif2webp.c gifdec.c gifdec.h
+gif2webp_CPPFLAGS = $(AM_CPPFLAGS) $(GIF_INCLUDES)
+gif2webp_LDADD  =
+gif2webp_LDADD += libexample_util.la
+gif2webp_LDADD += ../imageio/libimageio_util.la
+gif2webp_LDADD += ../src/mux/libwebpmux.la
+gif2webp_LDADD += ../src/libwebp.la
+gif2webp_LDADD += $(GIF_LIBS)
+
+vwebp_SOURCES = vwebp.c
+vwebp_CPPFLAGS = $(AM_CPPFLAGS) $(GL_INCLUDES)
+vwebp_LDADD  =
+vwebp_LDADD += libexample_util.la
+vwebp_LDADD += ../imageio/libimageio_util.la
+vwebp_LDADD += ../src/demux/libwebpdemux.la
+vwebp_LDADD += $(GL_LIBS)
+
+webpmux_SOURCES = webpmux.c
+webpmux_CPPFLAGS = $(AM_CPPFLAGS)
+webpmux_LDADD  =
+webpmux_LDADD += libexample_util.la
+webpmux_LDADD += ../imageio/libimageio_util.la
+webpmux_LDADD += ../src/mux/libwebpmux.la
+webpmux_LDADD += ../src/libwebp.la
+
+img2webp_SOURCES = img2webp.c
+img2webp_CPPFLAGS = $(AM_CPPFLAGS)
+img2webp_LDADD  =
+img2webp_LDADD += libexample_util.la
+img2webp_LDADD += ../imageio/libimageio_util.la
+img2webp_LDADD += ../imageio/libimagedec.la
+img2webp_LDADD += ../src/mux/libwebpmux.la
+img2webp_LDADD += ../src/libwebp.la
+img2webp_LDADD += $(PNG_LIBS) $(JPEG_LIBS) $(TIFF_LIBS)
+
+webpinfo_SOURCES = webpinfo.c
+webpinfo_CPPFLAGS = $(AM_CPPFLAGS)
+webpinfo_LDADD  =
+webpinfo_LDADD += libexample_util.la
+webpinfo_LDADD += ../imageio/libimageio_util.la
+webpinfo_LDADD += ../src/libwebp.la
+
+if BUILD_LIBWEBPDECODER
+  anim_diff_LDADD += ../src/libwebpdecoder.la
+  anim_dump_LDADD += ../src/libwebpdecoder.la
+  vwebp_LDADD += ../src/libwebpdecoder.la
+else
+  anim_diff_LDADD += ../src/libwebp.la
+  anim_dump_LDADD += ../src/libwebp.la
+  vwebp_LDADD += ../src/libwebp.la
+endif
diff --git a/examples/anim_diff.c b/examples/anim_diff.c
new file mode 100644
index 0000000..7ffabc8
--- /dev/null
+++ b/examples/anim_diff.c
@@ -0,0 +1,317 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Checks if given pair of animated GIF/WebP images are identical:
+// That is: their reconstructed canvases match pixel-by-pixel and their other
+// animation properties (loop count etc) also match.
+//
+// example: anim_diff foo.gif bar.webp
+
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>  // for 'strtod'.
+#include <string.h>  // for 'strcmp'.
+
+#include "./anim_util.h"
+#include "./example_util.h"
+#include "./unicode.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf _snprintf
+#endif
+
+// Returns true if 'a + b' will overflow.
+static int AdditionWillOverflow(int a, int b) {
+  return (b > 0) && (a > INT_MAX - b);
+}
+
+static int FramesAreEqual(const uint8_t* const rgba1,
+                          const uint8_t* const rgba2, int width, int height) {
+  const int stride = width * 4;  // Always true for 'DecodedFrame.rgba'.
+  return !memcmp(rgba1, rgba2, stride * height);
+}
+
+static WEBP_INLINE int PixelsAreSimilar(uint32_t src, uint32_t dst,
+                                        int max_allowed_diff) {
+  const int src_a = (src >> 24) & 0xff;
+  const int src_r = (src >> 16) & 0xff;
+  const int src_g = (src >> 8) & 0xff;
+  const int src_b = (src >> 0) & 0xff;
+  const int dst_a = (dst >> 24) & 0xff;
+  const int dst_r = (dst >> 16) & 0xff;
+  const int dst_g = (dst >> 8) & 0xff;
+  const int dst_b = (dst >> 0) & 0xff;
+
+  return (abs(src_r * src_a - dst_r * dst_a) <= (max_allowed_diff * 255)) &&
+         (abs(src_g * src_a - dst_g * dst_a) <= (max_allowed_diff * 255)) &&
+         (abs(src_b * src_a - dst_b * dst_a) <= (max_allowed_diff * 255)) &&
+         (abs(src_a - dst_a) <= max_allowed_diff);
+}
+
+static int FramesAreSimilar(const uint8_t* const rgba1,
+                            const uint8_t* const rgba2,
+                            int width, int height, int max_allowed_diff) {
+  int i, j;
+  assert(max_allowed_diff > 0);
+  for (j = 0; j < height; ++j) {
+    for (i = 0; i < width; ++i) {
+      const int stride = width * 4;
+      const size_t offset = j * stride + i;
+      if (!PixelsAreSimilar(rgba1[offset], rgba2[offset], max_allowed_diff)) {
+        return 0;
+      }
+    }
+  }
+  return 1;
+}
+
+// Minimize number of frames by combining successive frames that have at max
+// 'max_diff' difference per channel between corresponding pixels.
+static void MinimizeAnimationFrames(AnimatedImage* const img, int max_diff) {
+  uint32_t i;
+  for (i = 1; i < img->num_frames; ++i) {
+    DecodedFrame* const frame1 = &img->frames[i - 1];
+    DecodedFrame* const frame2 = &img->frames[i];
+    const uint8_t* const rgba1 = frame1->rgba;
+    const uint8_t* const rgba2 = frame2->rgba;
+    int should_merge_frames = 0;
+    // If merging frames will result in integer overflow for 'duration',
+    // skip merging.
+    if (AdditionWillOverflow(frame1->duration, frame2->duration)) continue;
+    if (max_diff > 0) {
+      should_merge_frames = FramesAreSimilar(rgba1, rgba2, img->canvas_width,
+                                             img->canvas_height, max_diff);
+    } else {
+      should_merge_frames =
+          FramesAreEqual(rgba1, rgba2, img->canvas_width, img->canvas_height);
+    }
+    if (should_merge_frames) {  // Merge 'i+1'th frame into 'i'th frame.
+      frame1->duration += frame2->duration;
+      if (i + 1 < img->num_frames) {
+        memmove(&img->frames[i], &img->frames[i + 1],
+                (img->num_frames - i - 1) * sizeof(*img->frames));
+      }
+      --img->num_frames;
+      --i;
+    }
+  }
+}
+
+static int CompareValues(uint32_t a, uint32_t b, const char* output_str) {
+  if (a != b) {
+    fprintf(stderr, "%s: %d vs %d\n", output_str, a, b);
+    return 0;
+  }
+  return 1;
+}
+
+static int CompareBackgroundColor(uint32_t bg1, uint32_t bg2, int premultiply) {
+  if (premultiply) {
+    const int alpha1 = (bg1 >> 24) & 0xff;
+    const int alpha2 = (bg2 >> 24) & 0xff;
+    if (alpha1 == 0 && alpha2 == 0) return 1;
+  }
+  if (bg1 != bg2) {
+    fprintf(stderr, "Background color mismatch: 0x%08x vs 0x%08x\n",
+            bg1, bg2);
+    return 0;
+  }
+  return 1;
+}
+
+// Note: As long as frame durations and reconstructed frames are identical, it
+// is OK for other aspects like offsets, dispose/blend method to vary.
+static int CompareAnimatedImagePair(const AnimatedImage* const img1,
+                                    const AnimatedImage* const img2,
+                                    int premultiply,
+                                    double min_psnr) {
+  int ok = 1;
+  const int is_multi_frame_image = (img1->num_frames > 1);
+  uint32_t i;
+
+  ok = CompareValues(img1->canvas_width, img2->canvas_width,
+                     "Canvas width mismatch") && ok;
+  ok = CompareValues(img1->canvas_height, img2->canvas_height,
+                     "Canvas height mismatch") && ok;
+  ok = CompareValues(img1->num_frames, img2->num_frames,
+                     "Frame count mismatch") && ok;
+  if (!ok) return 0;  // These are fatal failures, can't proceed.
+
+  if (is_multi_frame_image) {  // Checks relevant for multi-frame images only.
+    int max_loop_count_workaround = 0;
+    // Transcodes to webp increase the gif loop count by 1 for compatibility.
+    // When the gif has the maximum value the webp value will be off by one.
+    if ((img1->format == ANIM_GIF && img1->loop_count == 65536 &&
+         img2->format == ANIM_WEBP && img2->loop_count == 65535) ||
+        (img1->format == ANIM_WEBP && img1->loop_count == 65535 &&
+         img2->format == ANIM_GIF && img2->loop_count == 65536)) {
+      max_loop_count_workaround = 1;
+    }
+    ok = (max_loop_count_workaround ||
+          CompareValues(img1->loop_count, img2->loop_count,
+                        "Loop count mismatch")) && ok;
+    ok = CompareBackgroundColor(img1->bgcolor, img2->bgcolor,
+                                premultiply) && ok;
+  }
+
+  for (i = 0; i < img1->num_frames; ++i) {
+    // Pixel-by-pixel comparison.
+    const uint8_t* const rgba1 = img1->frames[i].rgba;
+    const uint8_t* const rgba2 = img2->frames[i].rgba;
+    int max_diff;
+    double psnr;
+    if (is_multi_frame_image) {  // Check relevant for multi-frame images only.
+      const char format[] = "Frame #%d, duration mismatch";
+      char tmp[sizeof(format) + 8];
+      ok = ok && (snprintf(tmp, sizeof(tmp), format, i) >= 0);
+      ok = ok && CompareValues(img1->frames[i].duration,
+                               img2->frames[i].duration, tmp);
+    }
+    GetDiffAndPSNR(rgba1, rgba2, img1->canvas_width, img1->canvas_height,
+                   premultiply, &max_diff, &psnr);
+    if (min_psnr > 0.) {
+      if (psnr < min_psnr) {
+        fprintf(stderr, "Frame #%d, psnr = %.2lf (min_psnr = %f)\n", i,
+                psnr, min_psnr);
+        ok = 0;
+      }
+    } else {
+      if (max_diff != 0) {
+        fprintf(stderr, "Frame #%d, max pixel diff: %d\n", i, max_diff);
+        ok = 0;
+      }
+    }
+  }
+  return ok;
+}
+
+static void Help(void) {
+  printf("Usage: anim_diff <image1> <image2> [options]\n");
+  printf("\nOptions:\n");
+  printf("  -dump_frames <folder> dump decoded frames in PAM format\n");
+  printf("  -min_psnr <float> ... minimum per-frame PSNR\n");
+  printf("  -raw_comparison ..... if this flag is not used, RGB is\n");
+  printf("                        premultiplied before comparison\n");
+  printf("  -max_diff <int> ..... maximum allowed difference per channel\n"
+         "                        between corresponding pixels in subsequent\n"
+         "                        frames\n");
+  printf("  -h .................. this help\n");
+  printf("  -version ............ print version number and exit\n");
+}
+
+int main(int argc, const char* argv[]) {
+  int return_code = -1;
+  int dump_frames = 0;
+  const char* dump_folder = NULL;
+  double min_psnr = 0.;
+  int got_input1 = 0;
+  int got_input2 = 0;
+  int premultiply = 1;
+  int max_diff = 0;
+  int i, c;
+  const char* files[2] = { NULL, NULL };
+  AnimatedImage images[2];
+
+  INIT_WARGV(argc, argv);
+
+  for (c = 1; c < argc; ++c) {
+    int parse_error = 0;
+    if (!strcmp(argv[c], "-dump_frames")) {
+      if (c < argc - 1) {
+        dump_frames = 1;
+        dump_folder = (const char*)GET_WARGV(argv, ++c);
+      } else {
+        parse_error = 1;
+      }
+    } else if (!strcmp(argv[c], "-min_psnr")) {
+      if (c < argc - 1) {
+        min_psnr = ExUtilGetFloat(argv[++c], &parse_error);
+      } else {
+        parse_error = 1;
+      }
+    } else if (!strcmp(argv[c], "-raw_comparison")) {
+      premultiply = 0;
+    } else if (!strcmp(argv[c], "-max_diff")) {
+      if (c < argc - 1) {
+        max_diff = ExUtilGetInt(argv[++c], 0, &parse_error);
+      } else {
+        parse_error = 1;
+      }
+    } else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
+      Help();
+      FREE_WARGV_AND_RETURN(0);
+    } else if (!strcmp(argv[c], "-version")) {
+      int dec_version, demux_version;
+      GetAnimatedImageVersions(&dec_version, &demux_version);
+      printf("WebP Decoder version: %d.%d.%d\nWebP Demux version: %d.%d.%d\n",
+             (dec_version >> 16) & 0xff, (dec_version >> 8) & 0xff,
+             (dec_version >> 0) & 0xff,
+             (demux_version >> 16) & 0xff, (demux_version >> 8) & 0xff,
+             (demux_version >> 0) & 0xff);
+      FREE_WARGV_AND_RETURN(0);
+    } else {
+      if (!got_input1) {
+        files[0] = (const char*)GET_WARGV(argv, c);
+        got_input1 = 1;
+      } else if (!got_input2) {
+        files[1] = (const char*)GET_WARGV(argv, c);
+        got_input2 = 1;
+      } else {
+        parse_error = 1;
+      }
+    }
+    if (parse_error) {
+      Help();
+      FREE_WARGV_AND_RETURN(-1);
+    }
+  }
+  if (argc < 3) {
+    Help();
+    FREE_WARGV_AND_RETURN(-1);
+  }
+
+
+  if (!got_input2) {
+    Help();
+    FREE_WARGV_AND_RETURN(-1);
+  }
+
+  if (dump_frames) {
+    WPRINTF("Dumping decoded frames in: %s\n", (const W_CHAR*)dump_folder);
+  }
+
+  memset(images, 0, sizeof(images));
+  for (i = 0; i < 2; ++i) {
+    WPRINTF("Decoding file: %s\n", (const W_CHAR*)files[i]);
+    if (!ReadAnimatedImage(files[i], &images[i], dump_frames, dump_folder)) {
+      WFPRINTF(stderr, "Error decoding file: %s\n Aborting.\n",
+               (const W_CHAR*)files[i]);
+      return_code = -2;
+      goto End;
+    } else {
+      MinimizeAnimationFrames(&images[i], max_diff);
+    }
+  }
+
+  if (!CompareAnimatedImagePair(&images[0], &images[1],
+                                premultiply, min_psnr)) {
+    WFPRINTF(stderr, "\nFiles %s and %s differ.\n", (const W_CHAR*)files[0],
+             (const W_CHAR*)files[1]);
+    return_code = -3;
+  } else {
+    WPRINTF("\nFiles %s and %s are identical.\n", (const W_CHAR*)files[0],
+            (const W_CHAR*)files[1]);
+    return_code = 0;
+  }
+ End:
+  ClearAnimatedImage(&images[0]);
+  ClearAnimatedImage(&images[1]);
+  FREE_WARGV_AND_RETURN(return_code);
+}
diff --git a/examples/anim_dump.c b/examples/anim_dump.c
new file mode 100644
index 0000000..e447338
--- /dev/null
+++ b/examples/anim_dump.c
@@ -0,0 +1,121 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Decodes an animated WebP file and dumps the decoded frames as PNG or TIFF.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <stdio.h>
+#include <string.h>  // for 'strcmp'.
+
+#include "./anim_util.h"
+#include "webp/decode.h"
+#include "../imageio/image_enc.h"
+#include "./unicode.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf _snprintf
+#endif
+
+static void Help(void) {
+  printf("Usage: anim_dump [options] files...\n");
+  printf("\nOptions:\n");
+  printf("  -folder <string> .... dump folder (default: '.')\n");
+  printf("  -prefix <string> .... prefix for dumped frames "
+                                  "(default: 'dump_')\n");
+  printf("  -tiff ............... save frames as TIFF\n");
+  printf("  -pam ................ save frames as PAM\n");
+  printf("  -h .................. this help\n");
+  printf("  -version ............ print version number and exit\n");
+}
+
+int main(int argc, const char* argv[]) {
+  int error = 0;
+  const W_CHAR* dump_folder = TO_W_CHAR(".");
+  const W_CHAR* prefix = TO_W_CHAR("dump_");
+  const W_CHAR* suffix = TO_W_CHAR("png");
+  WebPOutputFileFormat format = PNG;
+  int c;
+
+  INIT_WARGV(argc, argv);
+
+  if (argc < 2) {
+    Help();
+    FREE_WARGV_AND_RETURN(-1);
+  }
+
+  for (c = 1; !error && c < argc; ++c) {
+    if (!strcmp(argv[c], "-folder")) {
+      if (c + 1 == argc) {
+        fprintf(stderr, "missing argument after option '%s'\n", argv[c]);
+        error = 1;
+        break;
+      }
+      dump_folder = GET_WARGV(argv, ++c);
+    } else if (!strcmp(argv[c], "-prefix")) {
+      if (c + 1 == argc) {
+        fprintf(stderr, "missing argument after option '%s'\n", argv[c]);
+        error = 1;
+        break;
+      }
+      prefix = GET_WARGV(argv, ++c);
+    } else if (!strcmp(argv[c], "-tiff")) {
+      format = TIFF;
+      suffix = TO_W_CHAR("tiff");
+    } else if (!strcmp(argv[c], "-pam")) {
+      format = PAM;
+      suffix = TO_W_CHAR("pam");
+    } else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
+      Help();
+      FREE_WARGV_AND_RETURN(0);
+    } else if (!strcmp(argv[c], "-version")) {
+      int dec_version, demux_version;
+      GetAnimatedImageVersions(&dec_version, &demux_version);
+      printf("WebP Decoder version: %d.%d.%d\nWebP Demux version: %d.%d.%d\n",
+             (dec_version >> 16) & 0xff, (dec_version >> 8) & 0xff,
+             (dec_version >> 0) & 0xff,
+             (demux_version >> 16) & 0xff, (demux_version >> 8) & 0xff,
+             (demux_version >> 0) & 0xff);
+      FREE_WARGV_AND_RETURN(0);
+    } else {
+      uint32_t i;
+      AnimatedImage image;
+      const W_CHAR* const file = GET_WARGV(argv, c);
+      memset(&image, 0, sizeof(image));
+      WPRINTF("Decoding file: %s as %s/%sxxxx.%s\n",
+              file, dump_folder, prefix, suffix);
+      if (!ReadAnimatedImage((const char*)file, &image, 0, NULL)) {
+        WFPRINTF(stderr, "Error decoding file: %s\n Aborting.\n", file);
+        error = 1;
+        break;
+      }
+      for (i = 0; !error && i < image.num_frames; ++i) {
+        W_CHAR out_file[1024];
+        WebPDecBuffer buffer;
+        WebPInitDecBuffer(&buffer);
+        buffer.colorspace = MODE_RGBA;
+        buffer.is_external_memory = 1;
+        buffer.width = image.canvas_width;
+        buffer.height = image.canvas_height;
+        buffer.u.RGBA.rgba = image.frames[i].rgba;
+        buffer.u.RGBA.stride = buffer.width * sizeof(uint32_t);
+        buffer.u.RGBA.size = buffer.u.RGBA.stride * buffer.height;
+        WSNPRINTF(out_file, sizeof(out_file), "%s/%s%.4d.%s",
+                  dump_folder, prefix, i, suffix);
+        if (!WebPSaveImage(&buffer, format, (const char*)out_file)) {
+          WFPRINTF(stderr, "Error while saving image '%s'\n", out_file);
+          error = 1;
+        }
+        WebPFreeDecBuffer(&buffer);
+      }
+      ClearAnimatedImage(&image);
+    }
+  }
+  FREE_WARGV_AND_RETURN(error ? 1 : 0);
+}
diff --git a/examples/anim_util.c b/examples/anim_util.c
new file mode 100644
index 0000000..cf7da4c
--- /dev/null
+++ b/examples/anim_util.c
@@ -0,0 +1,782 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Utilities for animated images
+
+#include "./anim_util.h"
+
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#if defined(WEBP_HAVE_GIF)
+#include <gif_lib.h>
+#endif
+#include "webp/format_constants.h"
+#include "webp/decode.h"
+#include "webp/demux.h"
+#include "../imageio/imageio_util.h"
+#include "./gifdec.h"
+#include "./unicode.h"
+#include "./unicode_gif.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf _snprintf
+#endif
+
+static const int kNumChannels = 4;
+
+// -----------------------------------------------------------------------------
+// Common utilities.
+
+#if defined(WEBP_HAVE_GIF)
+// Returns true if the frame covers the full canvas.
+static int IsFullFrame(int width, int height,
+                       int canvas_width, int canvas_height) {
+  return (width == canvas_width && height == canvas_height);
+}
+#endif // WEBP_HAVE_GIF
+
+static int CheckSizeForOverflow(uint64_t size) {
+  return (size == (size_t)size);
+}
+
+static int AllocateFrames(AnimatedImage* const image, uint32_t num_frames) {
+  uint32_t i;
+  uint8_t* mem = NULL;
+  DecodedFrame* frames = NULL;
+  const uint64_t rgba_size =
+      (uint64_t)image->canvas_width * kNumChannels * image->canvas_height;
+  const uint64_t total_size = (uint64_t)num_frames * rgba_size * sizeof(*mem);
+  const uint64_t total_frame_size = (uint64_t)num_frames * sizeof(*frames);
+  if (!CheckSizeForOverflow(total_size) ||
+      !CheckSizeForOverflow(total_frame_size)) {
+    return 0;
+  }
+  mem = (uint8_t*)WebPMalloc((size_t)total_size);
+  frames = (DecodedFrame*)WebPMalloc((size_t)total_frame_size);
+
+  if (mem == NULL || frames == NULL) {
+    WebPFree(mem);
+    WebPFree(frames);
+    return 0;
+  }
+  WebPFree(image->raw_mem);
+  image->num_frames = num_frames;
+  image->frames = frames;
+  for (i = 0; i < num_frames; ++i) {
+    frames[i].rgba = mem + i * rgba_size;
+    frames[i].duration = 0;
+    frames[i].is_key_frame = 0;
+  }
+  image->raw_mem = mem;
+  return 1;
+}
+
+void ClearAnimatedImage(AnimatedImage* const image) {
+  if (image != NULL) {
+    WebPFree(image->raw_mem);
+    WebPFree(image->frames);
+    image->num_frames = 0;
+    image->frames = NULL;
+    image->raw_mem = NULL;
+  }
+}
+
+#if defined(WEBP_HAVE_GIF)
+// Clear the canvas to transparent.
+static void ZeroFillCanvas(uint8_t* rgba,
+                           uint32_t canvas_width, uint32_t canvas_height) {
+  memset(rgba, 0, canvas_width * kNumChannels * canvas_height);
+}
+
+// Clear given frame rectangle to transparent.
+static void ZeroFillFrameRect(uint8_t* rgba, int rgba_stride, int x_offset,
+                              int y_offset, int width, int height) {
+  int j;
+  assert(width * kNumChannels <= rgba_stride);
+  rgba += y_offset * rgba_stride + x_offset * kNumChannels;
+  for (j = 0; j < height; ++j) {
+    memset(rgba, 0, width * kNumChannels);
+    rgba += rgba_stride;
+  }
+}
+
+// Copy width * height pixels from 'src' to 'dst'.
+static void CopyCanvas(const uint8_t* src, uint8_t* dst,
+                       uint32_t width, uint32_t height) {
+  assert(src != NULL && dst != NULL);
+  memcpy(dst, src, width * kNumChannels * height);
+}
+
+// Copy pixels in the given rectangle from 'src' to 'dst' honoring the 'stride'.
+static void CopyFrameRectangle(const uint8_t* src, uint8_t* dst, int stride,
+                               int x_offset, int y_offset,
+                               int width, int height) {
+  int j;
+  const int width_in_bytes = width * kNumChannels;
+  const size_t offset = y_offset * stride + x_offset * kNumChannels;
+  assert(width_in_bytes <= stride);
+  src += offset;
+  dst += offset;
+  for (j = 0; j < height; ++j) {
+    memcpy(dst, src, width_in_bytes);
+    src += stride;
+    dst += stride;
+  }
+}
+#endif // WEBP_HAVE_GIF
+
+// Canonicalize all transparent pixels to transparent black to aid comparison.
+static void CleanupTransparentPixels(uint32_t* rgba,
+                                     uint32_t width, uint32_t height) {
+  const uint32_t* const rgba_end = rgba + width * height;
+  while (rgba < rgba_end) {
+    const uint8_t alpha = (*rgba >> 24) & 0xff;
+    if (alpha == 0) {
+      *rgba = 0;
+    }
+    ++rgba;
+  }
+}
+
+// Dump frame to a PAM file. Returns true on success.
+static int DumpFrame(const char filename[], const char dump_folder[],
+                     uint32_t frame_num, const uint8_t rgba[],
+                     int canvas_width, int canvas_height) {
+  int ok = 0;
+  size_t max_len;
+  int y;
+  const W_CHAR* base_name = NULL;
+  W_CHAR* file_name = NULL;
+  FILE* f = NULL;
+  const char* row;
+
+  if (dump_folder == NULL) dump_folder = (const char*)TO_W_CHAR(".");
+
+  base_name = WSTRRCHR(filename, '/');
+  base_name = (base_name == NULL) ? (const W_CHAR*)filename : base_name + 1;
+  max_len = WSTRLEN(dump_folder) + 1 + WSTRLEN(base_name)
+          + strlen("_frame_") + strlen(".pam") + 8;
+  file_name = (W_CHAR*)WebPMalloc(max_len * sizeof(*file_name));
+  if (file_name == NULL) goto End;
+
+  if (WSNPRINTF(file_name, max_len, "%s/%s_frame_%d.pam",
+                (const W_CHAR*)dump_folder, base_name, frame_num) < 0) {
+    fprintf(stderr, "Error while generating file name\n");
+    goto End;
+  }
+
+  f = WFOPEN(file_name, "wb");
+  if (f == NULL) {
+    WFPRINTF(stderr, "Error opening file for writing: %s\n", file_name);
+    ok = 0;
+    goto End;
+  }
+  if (fprintf(f, "P7\nWIDTH %d\nHEIGHT %d\n"
+              "DEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n",
+              canvas_width, canvas_height) < 0) {
+    WFPRINTF(stderr, "Write error for file %s\n", file_name);
+    goto End;
+  }
+  row = (const char*)rgba;
+  for (y = 0; y < canvas_height; ++y) {
+    if (fwrite(row, canvas_width * kNumChannels, 1, f) != 1) {
+      WFPRINTF(stderr, "Error writing to file: %s\n", file_name);
+      goto End;
+    }
+    row += canvas_width * kNumChannels;
+  }
+  ok = 1;
+ End:
+  if (f != NULL) fclose(f);
+  WebPFree(file_name);
+  return ok;
+}
+
+// -----------------------------------------------------------------------------
+// WebP Decoding.
+
+// Returns true if this is a valid WebP bitstream.
+static int IsWebP(const WebPData* const webp_data) {
+  return (WebPGetInfo(webp_data->bytes, webp_data->size, NULL, NULL) != 0);
+}
+
+// Read animated WebP bitstream 'webp_data' into 'AnimatedImage' struct.
+static int ReadAnimatedWebP(const char filename[],
+                            const WebPData* const webp_data,
+                            AnimatedImage* const image, int dump_frames,
+                            const char dump_folder[]) {
+  int ok = 0;
+  int dump_ok = 1;
+  uint32_t frame_index = 0;
+  int prev_frame_timestamp = 0;
+  WebPAnimDecoder* dec;
+  WebPAnimInfo anim_info;
+
+  memset(image, 0, sizeof(*image));
+
+  dec = WebPAnimDecoderNew(webp_data, NULL);
+  if (dec == NULL) {
+    WFPRINTF(stderr, "Error parsing image: %s\n", (const W_CHAR*)filename);
+    goto End;
+  }
+
+  if (!WebPAnimDecoderGetInfo(dec, &anim_info)) {
+    fprintf(stderr, "Error getting global info about the animation\n");
+    goto End;
+  }
+
+  // Animation properties.
+  image->canvas_width = anim_info.canvas_width;
+  image->canvas_height = anim_info.canvas_height;
+  image->loop_count = anim_info.loop_count;
+  image->bgcolor = anim_info.bgcolor;
+
+  // Allocate frames.
+  if (!AllocateFrames(image, anim_info.frame_count)) goto End;
+
+  // Decode frames.
+  while (WebPAnimDecoderHasMoreFrames(dec)) {
+    DecodedFrame* curr_frame;
+    uint8_t* curr_rgba;
+    uint8_t* frame_rgba;
+    int timestamp;
+
+    if (!WebPAnimDecoderGetNext(dec, &frame_rgba, &timestamp)) {
+      fprintf(stderr, "Error decoding frame #%u\n", frame_index);
+      goto End;
+    }
+    assert(frame_index < anim_info.frame_count);
+    curr_frame = &image->frames[frame_index];
+    curr_rgba = curr_frame->rgba;
+    curr_frame->duration = timestamp - prev_frame_timestamp;
+    curr_frame->is_key_frame = 0;  // Unused.
+    memcpy(curr_rgba, frame_rgba,
+           image->canvas_width * kNumChannels * image->canvas_height);
+
+    // Needed only because we may want to compare with GIF later.
+    CleanupTransparentPixels((uint32_t*)curr_rgba,
+                             image->canvas_width, image->canvas_height);
+
+    if (dump_frames && dump_ok) {
+      dump_ok = DumpFrame(filename, dump_folder, frame_index, curr_rgba,
+                          image->canvas_width, image->canvas_height);
+      if (!dump_ok) {  // Print error once, but continue decode loop.
+        fprintf(stderr, "Error dumping frames to %s\n", dump_folder);
+      }
+    }
+
+    ++frame_index;
+    prev_frame_timestamp = timestamp;
+  }
+  ok = dump_ok;
+  if (ok) image->format = ANIM_WEBP;
+
+ End:
+  WebPAnimDecoderDelete(dec);
+  return ok;
+}
+
+// -----------------------------------------------------------------------------
+// GIF Decoding.
+
+#if defined(WEBP_HAVE_GIF)
+
+// Returns true if this is a valid GIF bitstream.
+static int IsGIF(const WebPData* const data) {
+  return data->size > GIF_STAMP_LEN &&
+         (!memcmp(GIF_STAMP, data->bytes, GIF_STAMP_LEN) ||
+          !memcmp(GIF87_STAMP, data->bytes, GIF_STAMP_LEN) ||
+          !memcmp(GIF89_STAMP, data->bytes, GIF_STAMP_LEN));
+}
+
+// GIFLIB_MAJOR is only defined in libgif >= 4.2.0.
+#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR)
+# define LOCAL_GIF_VERSION ((GIFLIB_MAJOR << 8) | GIFLIB_MINOR)
+# define LOCAL_GIF_PREREQ(maj, min) \
+    (LOCAL_GIF_VERSION >= (((maj) << 8) | (min)))
+#else
+# define LOCAL_GIF_VERSION 0
+# define LOCAL_GIF_PREREQ(maj, min) 0
+#endif
+
+#if !LOCAL_GIF_PREREQ(5, 0)
+
+// Added in v5.0
+typedef struct {
+  int DisposalMode;
+#define DISPOSAL_UNSPECIFIED      0       // No disposal specified
+#define DISPOSE_DO_NOT            1       // Leave image in place
+#define DISPOSE_BACKGROUND        2       // Set area to background color
+#define DISPOSE_PREVIOUS          3       // Restore to previous content
+  int 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;
+
+static 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 = GifExtension[1] | (GifExtension[2] << 8);
+  if (GifExtension[0] & 0x01) {
+    gcb->TransparentColor = (int)GifExtension[3];
+  } else {
+    gcb->TransparentColor = NO_TRANSPARENT_COLOR;
+  }
+  return GIF_OK;
+}
+
+static int DGifSavedExtensionToGCB(GifFileType* GifFile, int ImageIndex,
+                                   GraphicsControlBlock* gcb) {
+  int i;
+  if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) {
+    return GIF_ERROR;
+  }
+  gcb->DisposalMode = DISPOSAL_UNSPECIFIED;
+  gcb->UserInputFlag = 0;
+  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, (const GifByteType*)ep->Bytes, gcb);
+    }
+  }
+  return GIF_ERROR;
+}
+
+#define CONTINUE_EXT_FUNC_CODE 0x00
+
+// Signature was changed in v5.0
+#define DGifOpenFileName(a, b) DGifOpenFileName(a)
+
+#endif  // !LOCAL_GIF_PREREQ(5, 0)
+
+// Signature changed in v5.1
+#if !LOCAL_GIF_PREREQ(5, 1)
+#define DGifCloseFile(a, b) DGifCloseFile(a)
+#endif
+
+static int IsKeyFrameGIF(const GifImageDesc* prev_desc, int prev_dispose,
+                         const DecodedFrame* const prev_frame,
+                         int canvas_width, int canvas_height) {
+  if (prev_frame == NULL) return 1;
+  if (prev_dispose == DISPOSE_BACKGROUND) {
+    if (IsFullFrame(prev_desc->Width, prev_desc->Height,
+                    canvas_width, canvas_height)) {
+      return 1;
+    }
+    if (prev_frame->is_key_frame) return 1;
+  }
+  return 0;
+}
+
+static int GetTransparentIndexGIF(GifFileType* gif) {
+  GraphicsControlBlock first_gcb;
+  memset(&first_gcb, 0, sizeof(first_gcb));
+  DGifSavedExtensionToGCB(gif, 0, &first_gcb);
+  return first_gcb.TransparentColor;
+}
+
+static uint32_t GetBackgroundColorGIF(GifFileType* gif) {
+  const int transparent_index = GetTransparentIndexGIF(gif);
+  const ColorMapObject* const color_map = gif->SColorMap;
+  if (transparent_index != NO_TRANSPARENT_COLOR &&
+      gif->SBackGroundColor == transparent_index) {
+    return 0x00000000;  // Special case: transparent black.
+  } else if (color_map == NULL || color_map->Colors == NULL
+             || gif->SBackGroundColor >= color_map->ColorCount) {
+    return 0xffffffff;  // Invalid: assume white.
+  } else {
+    const GifColorType color = color_map->Colors[gif->SBackGroundColor];
+    return (0xffu << 24) |
+           (color.Red << 16) |
+           (color.Green << 8) |
+           (color.Blue << 0);
+  }
+}
+
+// Find appropriate app extension and get loop count from the next extension.
+// We use Chrome's interpretation of the 'loop_count' semantics:
+//   if not present -> loop once
+//   if present and loop_count == 0, return 0 ('infinite').
+//   if present and loop_count != 0, it's the number of *extra* loops
+//     so we need to return loop_count + 1 as total loop number.
+static uint32_t GetLoopCountGIF(const GifFileType* const gif) {
+  int i;
+  for (i = 0; i < gif->ImageCount; ++i) {
+    const SavedImage* const image = &gif->SavedImages[i];
+    int j;
+    for (j = 0; (j + 1) < image->ExtensionBlockCount; ++j) {
+      const ExtensionBlock* const eb1 = image->ExtensionBlocks + j;
+      const ExtensionBlock* const eb2 = image->ExtensionBlocks + j + 1;
+      const char* const signature = (const char*)eb1->Bytes;
+      const int signature_is_ok =
+          (eb1->Function == APPLICATION_EXT_FUNC_CODE) &&
+          (eb1->ByteCount == 11) &&
+          (!memcmp(signature, "NETSCAPE2.0", 11) ||
+           !memcmp(signature, "ANIMEXTS1.0", 11));
+      if (signature_is_ok &&
+          eb2->Function == CONTINUE_EXT_FUNC_CODE && eb2->ByteCount >= 3 &&
+          eb2->Bytes[0] == 1) {
+        const uint32_t extra_loop = ((uint32_t)(eb2->Bytes[2]) << 8) +
+                                    ((uint32_t)(eb2->Bytes[1]) << 0);
+        return (extra_loop > 0) ? extra_loop + 1 : 0;
+      }
+    }
+  }
+  return 1;  // Default.
+}
+
+// Get duration of 'n'th frame in milliseconds.
+static int GetFrameDurationGIF(GifFileType* gif, int n) {
+  GraphicsControlBlock gcb;
+  memset(&gcb, 0, sizeof(gcb));
+  DGifSavedExtensionToGCB(gif, n, &gcb);
+  return gcb.DelayTime * 10;
+}
+
+// Returns true if frame 'target' completely covers 'covered'.
+static int CoversFrameGIF(const GifImageDesc* const target,
+                          const GifImageDesc* const covered) {
+  return target->Left <= covered->Left &&
+         covered->Left + covered->Width <= target->Left + target->Width &&
+         target->Top <= covered->Top &&
+         covered->Top + covered->Height <= target->Top + target->Height;
+}
+
+static void RemapPixelsGIF(const uint8_t* const src,
+                           const ColorMapObject* const cmap,
+                           int transparent_color, int len, uint8_t* dst) {
+  int i;
+  for (i = 0; i < len; ++i) {
+    if (src[i] != transparent_color) {
+      // If a pixel in the current frame is transparent, we don't modify it, so
+      // that we can see-through the corresponding pixel from an earlier frame.
+      const GifColorType c = cmap->Colors[src[i]];
+      dst[4 * i + 0] = c.Red;
+      dst[4 * i + 1] = c.Green;
+      dst[4 * i + 2] = c.Blue;
+      dst[4 * i + 3] = 0xff;
+    }
+  }
+}
+
+static int ReadFrameGIF(const SavedImage* const gif_image,
+                        const ColorMapObject* cmap, int transparent_color,
+                        int out_stride, uint8_t* const dst) {
+  const GifImageDesc* image_desc = &gif_image->ImageDesc;
+  const uint8_t* in;
+  uint8_t* out;
+  int j;
+
+  if (image_desc->ColorMap) cmap = image_desc->ColorMap;
+
+  if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) {
+    fprintf(stderr, "Potentially corrupt color map.\n");
+    return 0;
+  }
+
+  in = (const uint8_t*)gif_image->RasterBits;
+  out = dst + image_desc->Top * out_stride + image_desc->Left * kNumChannels;
+
+  for (j = 0; j < image_desc->Height; ++j) {
+    RemapPixelsGIF(in, cmap, transparent_color, image_desc->Width, out);
+    in += image_desc->Width;
+    out += out_stride;
+  }
+  return 1;
+}
+
+// Read animated GIF bitstream from 'filename' into 'AnimatedImage' struct.
+static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
+                           int dump_frames, const char dump_folder[]) {
+  uint32_t frame_count;
+  uint32_t canvas_width, canvas_height;
+  uint32_t i;
+  int gif_error;
+  GifFileType* gif;
+
+  gif = DGifOpenFileUnicode((const W_CHAR*)filename, NULL);
+  if (gif == NULL) {
+    WFPRINTF(stderr, "Could not read file: %s.\n", (const W_CHAR*)filename);
+    return 0;
+  }
+
+  gif_error = DGifSlurp(gif);
+  if (gif_error != GIF_OK) {
+    WFPRINTF(stderr, "Could not parse image: %s.\n", (const W_CHAR*)filename);
+    GIFDisplayError(gif, gif_error);
+    DGifCloseFile(gif, NULL);
+    return 0;
+  }
+
+  // Animation properties.
+  image->canvas_width = (uint32_t)gif->SWidth;
+  image->canvas_height = (uint32_t)gif->SHeight;
+  if (image->canvas_width > MAX_CANVAS_SIZE ||
+      image->canvas_height > MAX_CANVAS_SIZE) {
+    fprintf(stderr, "Invalid canvas dimension: %d x %d\n",
+            image->canvas_width, image->canvas_height);
+    DGifCloseFile(gif, NULL);
+    return 0;
+  }
+  image->loop_count = GetLoopCountGIF(gif);
+  image->bgcolor = GetBackgroundColorGIF(gif);
+
+  frame_count = (uint32_t)gif->ImageCount;
+  if (frame_count == 0) {
+    DGifCloseFile(gif, NULL);
+    return 0;
+  }
+
+  if (image->canvas_width == 0 || image->canvas_height == 0) {
+    image->canvas_width = gif->SavedImages[0].ImageDesc.Width;
+    image->canvas_height = gif->SavedImages[0].ImageDesc.Height;
+    gif->SavedImages[0].ImageDesc.Left = 0;
+    gif->SavedImages[0].ImageDesc.Top = 0;
+    if (image->canvas_width == 0 || image->canvas_height == 0) {
+      fprintf(stderr, "Invalid canvas size in GIF.\n");
+      DGifCloseFile(gif, NULL);
+      return 0;
+    }
+  }
+  // Allocate frames.
+  if (!AllocateFrames(image, frame_count)) {
+    DGifCloseFile(gif, NULL);
+    return 0;
+  }
+
+  canvas_width = image->canvas_width;
+  canvas_height = image->canvas_height;
+
+  // Decode and reconstruct frames.
+  for (i = 0; i < frame_count; ++i) {
+    const int canvas_width_in_bytes = canvas_width * kNumChannels;
+    const SavedImage* const curr_gif_image = &gif->SavedImages[i];
+    GraphicsControlBlock curr_gcb;
+    DecodedFrame* curr_frame;
+    uint8_t* curr_rgba;
+
+    memset(&curr_gcb, 0, sizeof(curr_gcb));
+    DGifSavedExtensionToGCB(gif, i, &curr_gcb);
+
+    curr_frame = &image->frames[i];
+    curr_rgba = curr_frame->rgba;
+    curr_frame->duration = GetFrameDurationGIF(gif, i);
+    // Force frames with a small or no duration to 100ms to be consistent
+    // with web browsers and other transcoding tools (like gif2webp itself).
+    if (curr_frame->duration <= 10) curr_frame->duration = 100;
+
+    if (i == 0) {  // Initialize as transparent.
+      curr_frame->is_key_frame = 1;
+      ZeroFillCanvas(curr_rgba, canvas_width, canvas_height);
+    } else {
+      DecodedFrame* const prev_frame = &image->frames[i - 1];
+      const GifImageDesc* const prev_desc = &gif->SavedImages[i - 1].ImageDesc;
+      GraphicsControlBlock prev_gcb;
+      memset(&prev_gcb, 0, sizeof(prev_gcb));
+      DGifSavedExtensionToGCB(gif, i - 1, &prev_gcb);
+
+      curr_frame->is_key_frame =
+          IsKeyFrameGIF(prev_desc, prev_gcb.DisposalMode, prev_frame,
+                        canvas_width, canvas_height);
+
+      if (curr_frame->is_key_frame) {  // Initialize as transparent.
+        ZeroFillCanvas(curr_rgba, canvas_width, canvas_height);
+      } else {
+        int prev_frame_disposed, curr_frame_opaque;
+        int prev_frame_completely_covered;
+        // Initialize with previous canvas.
+        uint8_t* const prev_rgba = image->frames[i - 1].rgba;
+        CopyCanvas(prev_rgba, curr_rgba, canvas_width, canvas_height);
+
+        // Dispose previous frame rectangle.
+        prev_frame_disposed =
+            (prev_gcb.DisposalMode == DISPOSE_BACKGROUND ||
+             prev_gcb.DisposalMode == DISPOSE_PREVIOUS);
+        curr_frame_opaque =
+            (curr_gcb.TransparentColor == NO_TRANSPARENT_COLOR);
+        prev_frame_completely_covered =
+            curr_frame_opaque &&
+            CoversFrameGIF(&curr_gif_image->ImageDesc, prev_desc);
+
+        if (prev_frame_disposed && !prev_frame_completely_covered) {
+          switch (prev_gcb.DisposalMode) {
+            case DISPOSE_BACKGROUND: {
+              ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes,
+                                prev_desc->Left, prev_desc->Top,
+                                prev_desc->Width, prev_desc->Height);
+              break;
+            }
+            case DISPOSE_PREVIOUS: {
+              int src_frame_num = i - 2;
+              while (src_frame_num >= 0) {
+                GraphicsControlBlock src_frame_gcb;
+                memset(&src_frame_gcb, 0, sizeof(src_frame_gcb));
+                DGifSavedExtensionToGCB(gif, src_frame_num, &src_frame_gcb);
+                if (src_frame_gcb.DisposalMode != DISPOSE_PREVIOUS) break;
+                --src_frame_num;
+              }
+              if (src_frame_num >= 0) {
+                // Restore pixels inside previous frame rectangle to
+                // corresponding pixels in source canvas.
+                uint8_t* const src_frame_rgba =
+                    image->frames[src_frame_num].rgba;
+                CopyFrameRectangle(src_frame_rgba, curr_rgba,
+                                   canvas_width_in_bytes,
+                                   prev_desc->Left, prev_desc->Top,
+                                   prev_desc->Width, prev_desc->Height);
+              } else {
+                // Source canvas doesn't exist. So clear previous frame
+                // rectangle to background.
+                ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes,
+                                  prev_desc->Left, prev_desc->Top,
+                                  prev_desc->Width, prev_desc->Height);
+              }
+              break;
+            }
+            default:
+              break;  // Nothing to do.
+          }
+        }
+      }
+    }
+
+    // Decode current frame.
+    if (!ReadFrameGIF(curr_gif_image, gif->SColorMap, curr_gcb.TransparentColor,
+                      canvas_width_in_bytes, curr_rgba)) {
+      DGifCloseFile(gif, NULL);
+      return 0;
+    }
+
+    if (dump_frames) {
+      if (!DumpFrame(filename, dump_folder, i, curr_rgba,
+                     canvas_width, canvas_height)) {
+        DGifCloseFile(gif, NULL);
+        return 0;
+      }
+    }
+  }
+  image->format = ANIM_GIF;
+  DGifCloseFile(gif, NULL);
+  return 1;
+}
+
+#else
+
+static int IsGIF(const WebPData* const data) {
+  (void)data;
+  return 0;
+}
+
+static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
+                           int dump_frames, const char dump_folder[]) {
+  (void)filename;
+  (void)image;
+  (void)dump_frames;
+  (void)dump_folder;
+  fprintf(stderr, "GIF support not compiled. Please install the libgif-dev "
+          "package before building.\n");
+  return 0;
+}
+
+#endif  // WEBP_HAVE_GIF
+
+// -----------------------------------------------------------------------------
+
+int ReadAnimatedImage(const char filename[], AnimatedImage* const image,
+                      int dump_frames, const char dump_folder[]) {
+  int ok = 0;
+  WebPData webp_data;
+
+  WebPDataInit(&webp_data);
+  memset(image, 0, sizeof(*image));
+
+  if (!ImgIoUtilReadFile(filename, &webp_data.bytes, &webp_data.size)) {
+    WFPRINTF(stderr, "Error reading file: %s\n", (const W_CHAR*)filename);
+    return 0;
+  }
+
+  if (IsWebP(&webp_data)) {
+    ok = ReadAnimatedWebP(filename, &webp_data, image, dump_frames,
+                          dump_folder);
+  } else if (IsGIF(&webp_data)) {
+    ok = ReadAnimatedGIF(filename, image, dump_frames, dump_folder);
+  } else {
+    WFPRINTF(stderr,
+             "Unknown file type: %s. Supported file types are WebP and GIF\n",
+             (const W_CHAR*)filename);
+    ok = 0;
+  }
+  if (!ok) ClearAnimatedImage(image);
+  WebPDataClear(&webp_data);
+  return ok;
+}
+
+static void Accumulate(double v1, double v2, double* const max_diff,
+                       double* const sse) {
+  const double diff = fabs(v1 - v2);
+  if (diff > *max_diff) *max_diff = diff;
+  *sse += diff * diff;
+}
+
+void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[],
+                    uint32_t width, uint32_t height, int premultiply,
+                    int* const max_diff, double* const psnr) {
+  const uint32_t stride = width * kNumChannels;
+  const int kAlphaChannel = kNumChannels - 1;
+  double f_max_diff = 0.;
+  double sse = 0.;
+  uint32_t x, y;
+  for (y = 0; y < height; ++y) {
+    for (x = 0; x < stride; x += kNumChannels) {
+      int k;
+      const size_t offset = (size_t)y * stride + x;
+      const int alpha1 = rgba1[offset + kAlphaChannel];
+      const int alpha2 = rgba2[offset + kAlphaChannel];
+      Accumulate(alpha1, alpha2, &f_max_diff, &sse);
+      if (!premultiply) {
+        for (k = 0; k < kAlphaChannel; ++k) {
+          Accumulate(rgba1[offset + k], rgba2[offset + k], &f_max_diff, &sse);
+        }
+      } else {
+        // premultiply R/G/B channels with alpha value
+        for (k = 0; k < kAlphaChannel; ++k) {
+          Accumulate(rgba1[offset + k] * alpha1 / 255.,
+                     rgba2[offset + k] * alpha2 / 255.,
+                     &f_max_diff, &sse);
+        }
+      }
+    }
+  }
+  *max_diff = (int)f_max_diff;
+  if (*max_diff == 0) {
+    *psnr = 99.;  // PSNR when images are identical.
+  } else {
+    sse /= stride * height;
+    *psnr = 4.3429448 * log(255. * 255. / sse);
+  }
+}
+
+void GetAnimatedImageVersions(int* const decoder_version,
+                              int* const demux_version) {
+  *decoder_version = WebPGetDecoderVersion();
+  *demux_version = WebPGetDemuxVersion();
+}
diff --git a/examples/anim_util.h b/examples/anim_util.h
new file mode 100644
index 0000000..574e032
--- /dev/null
+++ b/examples/anim_util.h
@@ -0,0 +1,73 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Utilities for animated images
+
+#ifndef WEBP_EXAMPLES_ANIM_UTIL_H_
+#define WEBP_EXAMPLES_ANIM_UTIL_H_
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include "webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+  ANIM_GIF,
+  ANIM_WEBP
+} AnimatedFileFormat;
+
+typedef struct {
+  uint8_t* rgba;         // Decoded and reconstructed full frame.
+  int duration;          // Frame duration in milliseconds.
+  int is_key_frame;      // True if this frame is a key-frame.
+} DecodedFrame;
+
+typedef struct {
+  AnimatedFileFormat format;
+  uint32_t canvas_width;
+  uint32_t canvas_height;
+  uint32_t bgcolor;
+  uint32_t loop_count;
+  DecodedFrame* frames;
+  uint32_t num_frames;
+  void* raw_mem;
+} AnimatedImage;
+
+// Deallocate everything in 'image' (but not the object itself).
+void ClearAnimatedImage(AnimatedImage* const image);
+
+// Read animated image file into 'AnimatedImage' struct.
+// If 'dump_frames' is true, dump frames to 'dump_folder'.
+// Previous content of 'image' is obliterated.
+// Upon successful return, content of 'image' must be deleted by
+// calling 'ClearAnimatedImage'.
+int ReadAnimatedImage(const char filename[], AnimatedImage* const image,
+                      int dump_frames, const char dump_folder[]);
+
+// Given two RGBA buffers, calculate max pixel difference and PSNR.
+// If 'premultiply' is true, R/G/B values will be pre-multiplied by the
+// transparency before comparison.
+void GetDiffAndPSNR(const uint8_t rgba1[], const uint8_t rgba2[],
+                    uint32_t width, uint32_t height, int premultiply,
+                    int* const max_diff, double* const psnr);
+
+// Return library versions used by anim_util.
+void GetAnimatedImageVersions(int* const decoder_version,
+                              int* const demux_version);
+
+#ifdef __cplusplus
+}    // extern "C"
+#endif
+
+#endif  // WEBP_EXAMPLES_ANIM_UTIL_H_
diff --git a/examples/cwebp.c b/examples/cwebp.c
new file mode 100644
index 0000000..9adb31e
--- /dev/null
+++ b/examples/cwebp.c
@@ -0,0 +1,1246 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  simple command line calling the WebPEncode function.
+//  Encodes a raw .YUV into WebP bitstream
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include "../examples/example_util.h"
+#include "../imageio/image_dec.h"
+#include "../imageio/imageio_util.h"
+#include "../imageio/webpdec.h"
+#include "./stopwatch.h"
+#include "./unicode.h"
+#include "sharpyuv/sharpyuv.h"
+#include "webp/encode.h"
+
+#ifndef WEBP_DLL
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void* VP8GetCPUInfo;   // opaque forward declaration.
+
+#ifdef __cplusplus
+}    // extern "C"
+#endif
+#endif  // WEBP_DLL
+
+//------------------------------------------------------------------------------
+
+static int verbose = 0;
+
+static int ReadYUV(const uint8_t* const data, size_t data_size,
+                   WebPPicture* const pic) {
+  const int use_argb = pic->use_argb;
+  const int uv_width = (pic->width + 1) / 2;
+  const int uv_height = (pic->height + 1) / 2;
+  const int y_plane_size = pic->width * pic->height;
+  const int uv_plane_size = uv_width * uv_height;
+  const size_t expected_data_size = y_plane_size + 2 * uv_plane_size;
+
+  if (data_size != expected_data_size) {
+    fprintf(stderr,
+            "input data doesn't have the expected size (%d instead of %d)\n",
+            (int)data_size, (int)expected_data_size);
+    return 0;
+  }
+
+  pic->use_argb = 0;
+  if (!WebPPictureAlloc(pic)) return 0;
+  ImgIoUtilCopyPlane(data, pic->width, pic->y, pic->y_stride,
+                     pic->width, pic->height);
+  ImgIoUtilCopyPlane(data + y_plane_size, uv_width,
+                     pic->u, pic->uv_stride, uv_width, uv_height);
+  ImgIoUtilCopyPlane(data + y_plane_size + uv_plane_size, uv_width,
+                     pic->v, pic->uv_stride, uv_width, uv_height);
+  return use_argb ? WebPPictureYUVAToARGB(pic) : 1;
+}
+
+#ifdef HAVE_WINCODEC_H
+
+static int ReadPicture(const char* const filename, WebPPicture* const pic,
+                       int keep_alpha, Metadata* const metadata) {
+  int ok = 0;
+  const uint8_t* data = NULL;
+  size_t data_size = 0;
+  if (pic->width != 0 && pic->height != 0) {
+    ok = ImgIoUtilReadFile(filename, &data, &data_size);
+    ok = ok && ReadYUV(data, data_size, pic);
+  } else {
+    // If no size specified, try to decode it using WIC.
+    ok = ReadPictureWithWIC(filename, pic, keep_alpha, metadata);
+    if (!ok) {
+      ok = ImgIoUtilReadFile(filename, &data, &data_size);
+      ok = ok && ReadWebP(data, data_size, pic, keep_alpha, metadata);
+    }
+  }
+  if (!ok) {
+    WFPRINTF(stderr, "Error! Could not process file %s\n",
+             (const W_CHAR*)filename);
+  }
+  WebPFree((void*)data);
+  return ok;
+}
+
+#else  // !HAVE_WINCODEC_H
+
+static int ReadPicture(const char* const filename, WebPPicture* const pic,
+                       int keep_alpha, Metadata* const metadata) {
+  const uint8_t* data = NULL;
+  size_t data_size = 0;
+  int ok = 0;
+
+  ok = ImgIoUtilReadFile(filename, &data, &data_size);
+  if (!ok) goto End;
+
+  if (pic->width == 0 || pic->height == 0) {
+    WebPImageReader reader = WebPGuessImageReader(data, data_size);
+    ok = reader(data, data_size, pic, keep_alpha, metadata);
+  } else {
+    // If image size is specified, infer it as YUV format.
+    ok = ReadYUV(data, data_size, pic);
+  }
+ End:
+  if (!ok) {
+    WFPRINTF(stderr, "Error! Could not process file %s\n",
+             (const W_CHAR*)filename);
+  }
+  WebPFree((void*)data);
+  return ok;
+}
+
+#endif  // !HAVE_WINCODEC_H
+
+static void AllocExtraInfo(WebPPicture* const pic) {
+  const int mb_w = (pic->width + 15) / 16;
+  const int mb_h = (pic->height + 15) / 16;
+  pic->extra_info =
+      (uint8_t*)WebPMalloc(mb_w * mb_h * sizeof(*pic->extra_info));
+}
+
+static void PrintByteCount(const int bytes[4], int total_size,
+                           int* const totals) {
+  int s;
+  int total = 0;
+  for (s = 0; s < 4; ++s) {
+    fprintf(stderr, "| %7d ", bytes[s]);
+    total += bytes[s];
+    if (totals) totals[s] += bytes[s];
+  }
+  fprintf(stderr, "| %7d  (%.1f%%)\n", total, 100.f * total / total_size);
+}
+
+static void PrintPercents(const int counts[4]) {
+  int s;
+  const int total = counts[0] + counts[1] + counts[2] + counts[3];
+  for (s = 0; s < 4; ++s) {
+    fprintf(stderr, "|     %3d%%", (int)(100. * counts[s] / total + .5));
+  }
+  fprintf(stderr, "| %7d\n", total);
+}
+
+static void PrintValues(const int values[4]) {
+  int s;
+  for (s = 0; s < 4; ++s) {
+    fprintf(stderr, "| %7d ", values[s]);
+  }
+  fprintf(stderr, "|\n");
+}
+
+static void PrintFullLosslessInfo(const WebPAuxStats* const stats,
+                                  const char* const description) {
+  fprintf(stderr, "Lossless-%s compressed size: %d bytes\n",
+          description, stats->lossless_size);
+  fprintf(stderr, "  * Header size: %d bytes, image data size: %d\n",
+          stats->lossless_hdr_size, stats->lossless_data_size);
+  if (stats->lossless_features) {
+    fprintf(stderr, "  * Lossless features used:");
+    if (stats->lossless_features & 1) fprintf(stderr, " PREDICTION");
+    if (stats->lossless_features & 2) fprintf(stderr, " CROSS-COLOR-TRANSFORM");
+    if (stats->lossless_features & 4) fprintf(stderr, " SUBTRACT-GREEN");
+    if (stats->lossless_features & 8) fprintf(stderr, " PALETTE");
+    fprintf(stderr, "\n");
+  }
+  fprintf(stderr, "  * Precision Bits: histogram=%d transform=%d cache=%d\n",
+          stats->histogram_bits, stats->transform_bits, stats->cache_bits);
+  if (stats->palette_size > 0) {
+    fprintf(stderr, "  * Palette size:   %d\n", stats->palette_size);
+  }
+}
+
+static void PrintExtraInfoLossless(const WebPPicture* const pic,
+                                   int short_output,
+                                   const char* const file_name) {
+  const WebPAuxStats* const stats = pic->stats;
+  if (short_output) {
+    fprintf(stderr, "%7d %2.2f\n", stats->coded_size, stats->PSNR[3]);
+  } else {
+    WFPRINTF(stderr, "File:      %s\n", (const W_CHAR*)file_name);
+    fprintf(stderr, "Dimension: %d x %d\n", pic->width, pic->height);
+    fprintf(stderr, "Output:    %d bytes (%.2f bpp)\n", stats->coded_size,
+            8.f * stats->coded_size / pic->width / pic->height);
+    PrintFullLosslessInfo(stats, "ARGB");
+  }
+}
+
+static void PrintExtraInfoLossy(const WebPPicture* const pic, int short_output,
+                                int full_details,
+                                const char* const file_name) {
+  const WebPAuxStats* const stats = pic->stats;
+  if (short_output) {
+    fprintf(stderr, "%7d %2.2f\n", stats->coded_size, stats->PSNR[3]);
+  } else {
+    const int num_i4 = stats->block_count[0];
+    const int num_i16 = stats->block_count[1];
+    const int num_skip = stats->block_count[2];
+    const int total = num_i4 + num_i16;
+    WFPRINTF(stderr, "File:      %s\n", (const W_CHAR*)file_name);
+    fprintf(stderr, "Dimension: %d x %d%s\n",
+            pic->width, pic->height,
+            stats->alpha_data_size ? " (with alpha)" : "");
+    fprintf(stderr, "Output:    "
+            "%d bytes Y-U-V-All-PSNR %2.2f %2.2f %2.2f   %2.2f dB\n"
+            "           (%.2f bpp)\n",
+            stats->coded_size,
+            stats->PSNR[0], stats->PSNR[1], stats->PSNR[2], stats->PSNR[3],
+            8.f * stats->coded_size / pic->width / pic->height);
+    if (total > 0) {
+      int totals[4] = { 0, 0, 0, 0 };
+      fprintf(stderr, "block count:  intra4:     %6d  (%.2f%%)\n"
+                      "              intra16:    %6d  (%.2f%%)\n"
+                      "              skipped:    %6d  (%.2f%%)\n",
+              num_i4, 100.f * num_i4 / total,
+              num_i16, 100.f * num_i16 / total,
+              num_skip, 100.f * num_skip / total);
+      fprintf(stderr, "bytes used:  header:         %6d  (%.1f%%)\n"
+                      "             mode-partition: %6d  (%.1f%%)\n",
+              stats->header_bytes[0],
+              100.f * stats->header_bytes[0] / stats->coded_size,
+              stats->header_bytes[1],
+              100.f * stats->header_bytes[1] / stats->coded_size);
+      if (stats->alpha_data_size > 0) {
+        fprintf(stderr, "             transparency:   %6d (%.1f dB)\n",
+                stats->alpha_data_size, stats->PSNR[4]);
+      }
+      fprintf(stderr, " Residuals bytes  "
+                      "|segment 1|segment 2|segment 3"
+                      "|segment 4|  total\n");
+      if (full_details) {
+        fprintf(stderr, "  intra4-coeffs:  ");
+        PrintByteCount(stats->residual_bytes[0], stats->coded_size, totals);
+        fprintf(stderr, " intra16-coeffs:  ");
+        PrintByteCount(stats->residual_bytes[1], stats->coded_size, totals);
+        fprintf(stderr, "  chroma coeffs:  ");
+        PrintByteCount(stats->residual_bytes[2], stats->coded_size, totals);
+      }
+      fprintf(stderr, "    macroblocks:  ");
+      PrintPercents(stats->segment_size);
+      fprintf(stderr, "      quantizer:  ");
+      PrintValues(stats->segment_quant);
+      fprintf(stderr, "   filter level:  ");
+      PrintValues(stats->segment_level);
+      if (full_details) {
+        fprintf(stderr, "------------------+---------");
+        fprintf(stderr, "+---------+---------+---------+-----------------\n");
+        fprintf(stderr, " segments total:  ");
+        PrintByteCount(totals, stats->coded_size, NULL);
+      }
+    }
+    if (stats->lossless_size > 0) {
+      PrintFullLosslessInfo(stats, "alpha");
+    }
+  }
+}
+
+static void PrintMapInfo(const WebPPicture* const pic) {
+  if (pic->extra_info != NULL) {
+    const int mb_w = (pic->width + 15) / 16;
+    const int mb_h = (pic->height + 15) / 16;
+    const int type = pic->extra_info_type;
+    int x, y;
+    for (y = 0; y < mb_h; ++y) {
+      for (x = 0; x < mb_w; ++x) {
+        const int c = pic->extra_info[x + y * mb_w];
+        if (type == 1) {   // intra4/intra16
+          fprintf(stderr, "%c", "+."[c]);
+        } else if (type == 2) {    // segments
+          fprintf(stderr, "%c", ".-*X"[c]);
+        } else if (type == 3) {    // quantizers
+          fprintf(stderr, "%.2d ", c);
+        } else if (type == 6 || type == 7) {
+          fprintf(stderr, "%3d ", c);
+        } else {
+          fprintf(stderr, "0x%.2x ", c);
+        }
+      }
+      fprintf(stderr, "\n");
+    }
+  }
+}
+
+//------------------------------------------------------------------------------
+
+static int MyWriter(const uint8_t* data, size_t data_size,
+                    const WebPPicture* const pic) {
+  FILE* const out = (FILE*)pic->custom_ptr;
+  return data_size ? (fwrite(data, data_size, 1, out) == 1) : 1;
+}
+
+// Dumps a picture as a PGM file using the IMC4 layout.
+static int DumpPicture(const WebPPicture* const picture, const char* PGM_name) {
+  int y;
+  const int uv_width = (picture->width + 1) / 2;
+  const int uv_height = (picture->height + 1) / 2;
+  const int stride = (picture->width + 1) & ~1;
+  const uint8_t* src_y = picture->y;
+  const uint8_t* src_u = picture->u;
+  const uint8_t* src_v = picture->v;
+  const uint8_t* src_a = picture->a;
+  const int alpha_height =
+      WebPPictureHasTransparency(picture) ? picture->height : 0;
+  const int height = picture->height + uv_height + alpha_height;
+  FILE* const f = WFOPEN(PGM_name, "wb");
+  if (f == NULL) return 0;
+  fprintf(f, "P5\n%d %d\n255\n", stride, height);
+  for (y = 0; y < picture->height; ++y) {
+    if (fwrite(src_y, picture->width, 1, f) != 1) return 0;
+    if (picture->width & 1) fputc(0, f);  // pad
+    src_y += picture->y_stride;
+  }
+  for (y = 0; y < uv_height; ++y) {
+    if (fwrite(src_u, uv_width, 1, f) != 1) return 0;
+    if (fwrite(src_v, uv_width, 1, f) != 1) return 0;
+    src_u += picture->uv_stride;
+    src_v += picture->uv_stride;
+  }
+  for (y = 0; y < alpha_height; ++y) {
+    if (fwrite(src_a, picture->width, 1, f) != 1) return 0;
+    if (picture->width & 1) fputc(0, f);  // pad
+    src_a += picture->a_stride;
+  }
+  fclose(f);
+  return 1;
+}
+
+// -----------------------------------------------------------------------------
+// Metadata writing.
+
+enum {
+  METADATA_EXIF = (1 << 0),
+  METADATA_ICC  = (1 << 1),
+  METADATA_XMP  = (1 << 2),
+  METADATA_ALL  = METADATA_EXIF | METADATA_ICC | METADATA_XMP
+};
+
+static const int kChunkHeaderSize = 8;
+static const int kTagSize = 4;
+
+static void PrintMetadataInfo(const Metadata* const metadata,
+                              int metadata_written) {
+  if (metadata == NULL || metadata_written == 0) return;
+
+  fprintf(stderr, "Metadata:\n");
+  if (metadata_written & METADATA_ICC) {
+    fprintf(stderr, "  * ICC profile:  %6d bytes\n", (int)metadata->iccp.size);
+  }
+  if (metadata_written & METADATA_EXIF) {
+    fprintf(stderr, "  * EXIF data:    %6d bytes\n", (int)metadata->exif.size);
+  }
+  if (metadata_written & METADATA_XMP) {
+    fprintf(stderr, "  * XMP data:     %6d bytes\n", (int)metadata->xmp.size);
+  }
+}
+
+// Outputs, in little endian, 'num' bytes from 'val' to 'out'.
+static int WriteLE(FILE* const out, uint32_t val, int num) {
+  uint8_t buf[4];
+  int i;
+  for (i = 0; i < num; ++i) {
+    buf[i] = (uint8_t)(val & 0xff);
+    val >>= 8;
+  }
+  return (fwrite(buf, num, 1, out) == 1);
+}
+
+static int WriteLE24(FILE* const out, uint32_t val) {
+  return WriteLE(out, val, 3);
+}
+
+static int WriteLE32(FILE* const out, uint32_t val) {
+  return WriteLE(out, val, 4);
+}
+
+static int WriteMetadataChunk(FILE* const out, const char fourcc[4],
+                              const MetadataPayload* const payload) {
+  const uint8_t zero = 0;
+  const size_t need_padding = payload->size & 1;
+  int ok = (fwrite(fourcc, kTagSize, 1, out) == 1);
+  ok = ok && WriteLE32(out, (uint32_t)payload->size);
+  ok = ok && (fwrite(payload->bytes, payload->size, 1, out) == 1);
+  return ok && (fwrite(&zero, need_padding, need_padding, out) == need_padding);
+}
+
+// Sets 'flag' in 'vp8x_flags' and updates 'metadata_size' with the size of the
+// chunk if there is metadata and 'keep' is true.
+static int UpdateFlagsAndSize(const MetadataPayload* const payload,
+                              int keep, int flag,
+                              uint32_t* vp8x_flags, uint64_t* metadata_size) {
+  if (keep && payload->bytes != NULL && payload->size > 0) {
+    *vp8x_flags |= flag;
+    *metadata_size += kChunkHeaderSize + payload->size + (payload->size & 1);
+    return 1;
+  }
+  return 0;
+}
+
+// Writes a WebP file using the image contained in 'memory_writer' and the
+// metadata from 'metadata'. Metadata is controlled by 'keep_metadata' and the
+// availability in 'metadata'. Returns true on success.
+// For details see doc/webp-container-spec.txt#extended-file-format.
+static int WriteWebPWithMetadata(FILE* const out,
+                                 const WebPPicture* const picture,
+                                 const WebPMemoryWriter* const memory_writer,
+                                 const Metadata* const metadata,
+                                 int keep_metadata,
+                                 int* const metadata_written) {
+  const char kVP8XHeader[] = "VP8X\x0a\x00\x00\x00";
+  const int kAlphaFlag = 0x10;
+  const int kEXIFFlag  = 0x08;
+  const int kICCPFlag  = 0x20;
+  const int kXMPFlag   = 0x04;
+  const size_t kRiffHeaderSize = 12;
+  const size_t kMaxChunkPayload = ~0 - kChunkHeaderSize - 1;
+  const size_t kMinSize = kRiffHeaderSize + kChunkHeaderSize;
+  uint32_t flags = 0;
+  uint64_t metadata_size = 0;
+  const int write_exif = UpdateFlagsAndSize(&metadata->exif,
+                                            !!(keep_metadata & METADATA_EXIF),
+                                            kEXIFFlag, &flags, &metadata_size);
+  const int write_iccp = UpdateFlagsAndSize(&metadata->iccp,
+                                            !!(keep_metadata & METADATA_ICC),
+                                            kICCPFlag, &flags, &metadata_size);
+  const int write_xmp  = UpdateFlagsAndSize(&metadata->xmp,
+                                            !!(keep_metadata & METADATA_XMP),
+                                            kXMPFlag, &flags, &metadata_size);
+  uint8_t* webp = memory_writer->mem;
+  size_t webp_size = memory_writer->size;
+
+  *metadata_written = 0;
+
+  if (webp_size < kMinSize) return 0;
+  if (webp_size - kChunkHeaderSize + metadata_size > kMaxChunkPayload) {
+    fprintf(stderr, "Error! Addition of metadata would exceed "
+                    "container size limit.\n");
+    return 0;
+  }
+
+  if (metadata_size > 0) {
+    const int kVP8XChunkSize = 18;
+    const int has_vp8x = !memcmp(webp + kRiffHeaderSize, "VP8X", kTagSize);
+    const uint32_t riff_size = (uint32_t)(webp_size - kChunkHeaderSize +
+                                          (has_vp8x ? 0 : kVP8XChunkSize) +
+                                          metadata_size);
+    // RIFF
+    int ok = (fwrite(webp, kTagSize, 1, out) == 1);
+    // RIFF size (file header size is not recorded)
+    ok = ok && WriteLE32(out, riff_size);
+    webp += kChunkHeaderSize;
+    webp_size -= kChunkHeaderSize;
+    // WEBP
+    ok = ok && (fwrite(webp, kTagSize, 1, out) == 1);
+    webp += kTagSize;
+    webp_size -= kTagSize;
+    if (has_vp8x) {  // update the existing VP8X flags
+      webp[kChunkHeaderSize] |= (uint8_t)(flags & 0xff);
+      ok = ok && (fwrite(webp, kVP8XChunkSize, 1, out) == 1);
+      webp += kVP8XChunkSize;
+      webp_size -= kVP8XChunkSize;
+    } else {
+      const int is_lossless = !memcmp(webp, "VP8L", kTagSize);
+      if (is_lossless) {
+        // Presence of alpha is stored in the 37th bit (29th after the
+        // signature) of VP8L data.
+        if (webp[kChunkHeaderSize + 4] & (1 << 4)) flags |= kAlphaFlag;
+      }
+      ok = ok && (fwrite(kVP8XHeader, kChunkHeaderSize, 1, out) == 1);
+      ok = ok && WriteLE32(out, flags);
+      ok = ok && WriteLE24(out, picture->width - 1);
+      ok = ok && WriteLE24(out, picture->height - 1);
+    }
+    if (write_iccp) {
+      ok = ok && WriteMetadataChunk(out, "ICCP", &metadata->iccp);
+      *metadata_written |= METADATA_ICC;
+    }
+    // Image
+    ok = ok && (fwrite(webp, webp_size, 1, out) == 1);
+    if (write_exif) {
+      ok = ok && WriteMetadataChunk(out, "EXIF", &metadata->exif);
+      *metadata_written |= METADATA_EXIF;
+    }
+    if (write_xmp) {
+      ok = ok && WriteMetadataChunk(out, "XMP ", &metadata->xmp);
+      *metadata_written |= METADATA_XMP;
+    }
+    return ok;
+  }
+
+  // No metadata, just write the original image file.
+  return (fwrite(webp, webp_size, 1, out) == 1);
+}
+
+//------------------------------------------------------------------------------
+
+static int ProgressReport(int percent, const WebPPicture* const picture) {
+  fprintf(stderr, "[%s]: %3d %%      \r",
+          (char*)picture->user_data, percent);
+  return 1;  // all ok
+}
+
+//------------------------------------------------------------------------------
+
+static void HelpShort(void) {
+  printf("Usage:\n\n");
+  printf("   cwebp [options] -q quality input.png -o output.webp\n\n");
+  printf("where quality is between 0 (poor) to 100 (very good).\n");
+  printf("Typical value is around 80.\n\n");
+  printf("Try -longhelp for an exhaustive list of advanced options.\n");
+}
+
+static void HelpLong(void) {
+  printf("Usage:\n");
+  printf(" cwebp [-preset <...>] [options] in_file [-o out_file]\n\n");
+  printf("If input size (-s) for an image is not specified, it is\n"
+         "assumed to be a PNG, JPEG, TIFF or WebP file.\n");
+  printf("Note: Animated PNG and WebP files are not supported.\n");
+#ifdef HAVE_WINCODEC_H
+  printf("Windows builds can take as input any of the files handled by WIC.\n");
+#endif
+  printf("\nOptions:\n");
+  printf("  -h / -help ............. short help\n");
+  printf("  -H / -longhelp ......... long help\n");
+  printf("  -q <float> ............. quality factor (0:small..100:big), "
+         "default=75\n");
+  printf("  -alpha_q <int> ......... transparency-compression quality (0..100),"
+         "\n                           default=100\n");
+  printf("  -preset <string> ....... preset setting, one of:\n");
+  printf("                            default, photo, picture,\n");
+  printf("                            drawing, icon, text\n");
+  printf("     -preset must come first, as it overwrites other parameters\n");
+  printf("  -z <int> ............... activates lossless preset with given\n"
+         "                           level in [0:fast, ..., 9:slowest]\n");
+  printf("\n");
+  printf("  -m <int> ............... compression method (0=fast, 6=slowest), "
+         "default=4\n");
+  printf("  -segments <int> ........ number of segments to use (1..4), "
+         "default=4\n");
+  printf("  -size <int> ............ target size (in bytes)\n");
+  printf("  -psnr <float> .......... target PSNR (in dB. typically: 42)\n");
+  printf("\n");
+  printf("  -s <int> <int> ......... input size (width x height) for YUV\n");
+  printf("  -sns <int> ............. spatial noise shaping (0:off, 100:max), "
+         "default=50\n");
+  printf("  -f <int> ............... filter strength (0=off..100), "
+         "default=60\n");
+  printf("  -sharpness <int> ....... "
+         "filter sharpness (0:most .. 7:least sharp), default=0\n");
+  printf("  -strong ................ use strong filter instead "
+                                     "of simple (default)\n");
+  printf("  -nostrong .............. use simple filter instead of strong\n");
+  printf("  -sharp_yuv ............. use sharper (and slower) RGB->YUV "
+                                     "conversion\n");
+  printf("  -partition_limit <int> . limit quality to fit the 512k limit on\n");
+  printf("                           "
+         "the first partition (0=no degradation ... 100=full)\n");
+  printf("  -pass <int> ............ analysis pass number (1..10)\n");
+  printf("  -qrange <min> <max> .... specifies the permissible quality range\n"
+         "                           (default: 0 100)\n");
+  printf("  -crop <x> <y> <w> <h> .. crop picture with the given rectangle\n");
+  printf("  -resize <w> <h> ........ resize picture (*after* any cropping)\n");
+  printf("  -mt .................... use multi-threading if available\n");
+  printf("  -low_memory ............ reduce memory usage (slower encoding)\n");
+  printf("  -map <int> ............. print map of extra info\n");
+  printf("  -print_psnr ............ prints averaged PSNR distortion\n");
+  printf("  -print_ssim ............ prints averaged SSIM distortion\n");
+  printf("  -print_lsim ............ prints local-similarity distortion\n");
+  printf("  -d <file.pgm> .......... dump the compressed output (PGM file)\n");
+  printf("  -alpha_method <int> .... transparency-compression method (0..1), "
+         "default=1\n");
+  printf("  -alpha_filter <string> . predictive filtering for alpha plane,\n");
+  printf("                           one of: none, fast (default) or best\n");
+  printf("  -exact ................. preserve RGB values in transparent area, "
+         "default=off\n");
+  printf("  -blend_alpha <hex> ..... blend colors against background color\n"
+         "                           expressed as RGB values written in\n"
+         "                           hexadecimal, e.g. 0xc0e0d0 for red=0xc0\n"
+         "                           green=0xe0 and blue=0xd0\n");
+  printf("  -noalpha ............... discard any transparency information\n");
+  printf("  -lossless .............. encode image losslessly, default=off\n");
+  printf("  -near_lossless <int> ... use near-lossless image\n"
+         "                           preprocessing (0..100=off), "
+         "default=100\n");
+  printf("  -hint <string> ......... specify image characteristics hint,\n");
+  printf("                           one of: photo, picture or graph\n");
+
+  printf("\n");
+  printf("  -metadata <string> ..... comma separated list of metadata to\n");
+  printf("                           ");
+  printf("copy from the input to the output if present.\n");
+  printf("                           "
+         "Valid values: all, none (default), exif, icc, xmp\n");
+
+  printf("\n");
+  printf("  -short ................. condense printed message\n");
+  printf("  -quiet ................. don't print anything\n");
+  printf("  -version ............... print version number and exit\n");
+#ifndef WEBP_DLL
+  printf("  -noasm ................. disable all assembly optimizations\n");
+#endif
+  printf("  -v ..................... verbose, e.g. print encoding/decoding "
+         "times\n");
+  printf("  -progress .............. report encoding progress\n");
+  printf("\n");
+  printf("Experimental Options:\n");
+  printf("  -jpeg_like ............. roughly match expected JPEG size\n");
+  printf("  -af .................... auto-adjust filter strength\n");
+  printf("  -pre <int> ............. pre-processing filter\n");
+  printf("\n");
+  printf("Supported input formats:\n  %s\n", WebPGetEnabledInputFileFormats());
+}
+
+//------------------------------------------------------------------------------
+// Error messages
+
+static const char* const kErrorMessages[VP8_ENC_ERROR_LAST] = {
+  "OK",
+  "OUT_OF_MEMORY: Out of memory allocating objects",
+  "BITSTREAM_OUT_OF_MEMORY: Out of memory re-allocating byte buffer",
+  "NULL_PARAMETER: NULL parameter passed to function",
+  "INVALID_CONFIGURATION: configuration is invalid",
+  "BAD_DIMENSION: Bad picture dimension. Maximum width and height "
+  "allowed is 16383 pixels.",
+  "PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k.\n"
+  "To reduce the size of this partition, try using less segments "
+  "with the -segments option, and eventually reduce the number of "
+  "header bits using -partition_limit. More details are available "
+  "in the manual (`man cwebp`)",
+  "PARTITION_OVERFLOW: Partition is too big to fit 16M",
+  "BAD_WRITE: Picture writer returned an I/O error",
+  "FILE_TOO_BIG: File would be too big to fit in 4G",
+  "USER_ABORT: encoding abort requested by user"
+};
+
+//------------------------------------------------------------------------------
+
+int main(int argc, const char* argv[]) {
+  int return_value = -1;
+  const char* in_file = NULL, *out_file = NULL, *dump_file = NULL;
+  FILE* out = NULL;
+  int c;
+  int short_output = 0;
+  int quiet = 0;
+  int keep_alpha = 1;
+  int blend_alpha = 0;
+  uint32_t background_color = 0xffffffu;
+  int crop = 0, crop_x = 0, crop_y = 0, crop_w = 0, crop_h = 0;
+  int resize_w = 0, resize_h = 0;
+  int lossless_preset = 6;
+  int use_lossless_preset = -1;  // -1=unset, 0=don't use, 1=use it
+  int show_progress = 0;
+  int keep_metadata = 0;
+  int metadata_written = 0;
+  WebPPicture picture;
+  int print_distortion = -1;        // -1=off, 0=PSNR, 1=SSIM, 2=LSIM
+  WebPPicture original_picture;    // when PSNR or SSIM is requested
+  WebPConfig config;
+  WebPAuxStats stats;
+  WebPMemoryWriter memory_writer;
+  int use_memory_writer;
+  Metadata metadata;
+  Stopwatch stop_watch;
+
+  INIT_WARGV(argc, argv);
+
+  MetadataInit(&metadata);
+  WebPMemoryWriterInit(&memory_writer);
+  if (!WebPPictureInit(&picture) ||
+      !WebPPictureInit(&original_picture) ||
+      !WebPConfigInit(&config)) {
+    fprintf(stderr, "Error! Version mismatch!\n");
+    FREE_WARGV_AND_RETURN(-1);
+  }
+
+  if (argc == 1) {
+    HelpShort();
+    FREE_WARGV_AND_RETURN(0);
+  }
+
+  for (c = 1; c < argc; ++c) {
+    int parse_error = 0;
+    if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
+      HelpShort();
+      FREE_WARGV_AND_RETURN(0);
+    } else if (!strcmp(argv[c], "-H") || !strcmp(argv[c], "-longhelp")) {
+      HelpLong();
+      FREE_WARGV_AND_RETURN(0);
+    } else if (!strcmp(argv[c], "-o") && c + 1 < argc) {
+      out_file = (const char*)GET_WARGV(argv, ++c);
+    } else if (!strcmp(argv[c], "-d") && c + 1 < argc) {
+      dump_file = (const char*)GET_WARGV(argv, ++c);
+      config.show_compressed = 1;
+    } else if (!strcmp(argv[c], "-print_psnr")) {
+      config.show_compressed = 1;
+      print_distortion = 0;
+    } else if (!strcmp(argv[c], "-print_ssim")) {
+      config.show_compressed = 1;
+      print_distortion = 1;
+    } else if (!strcmp(argv[c], "-print_lsim")) {
+      config.show_compressed = 1;
+      print_distortion = 2;
+    } else if (!strcmp(argv[c], "-short")) {
+      ++short_output;
+    } else if (!strcmp(argv[c], "-s") && c + 2 < argc) {
+      picture.width = ExUtilGetInt(argv[++c], 0, &parse_error);
+      picture.height = ExUtilGetInt(argv[++c], 0, &parse_error);
+      if (picture.width > WEBP_MAX_DIMENSION || picture.width < 0 ||
+          picture.height > WEBP_MAX_DIMENSION ||  picture.height < 0) {
+        fprintf(stderr,
+                "Specified dimension (%d x %d) is out of range.\n",
+                picture.width, picture.height);
+        goto Error;
+      }
+    } else if (!strcmp(argv[c], "-m") && c + 1 < argc) {
+      config.method = ExUtilGetInt(argv[++c], 0, &parse_error);
+      use_lossless_preset = 0;   // disable -z option
+    } else if (!strcmp(argv[c], "-q") && c + 1 < argc) {
+      config.quality = ExUtilGetFloat(argv[++c], &parse_error);
+      use_lossless_preset = 0;   // disable -z option
+    } else if (!strcmp(argv[c], "-z") && c + 1 < argc) {
+      lossless_preset = ExUtilGetInt(argv[++c], 0, &parse_error);
+      if (use_lossless_preset != 0) use_lossless_preset = 1;
+    } else if (!strcmp(argv[c], "-alpha_q") && c + 1 < argc) {
+      config.alpha_quality = ExUtilGetInt(argv[++c], 0, &parse_error);
+    } else if (!strcmp(argv[c], "-alpha_method") && c + 1 < argc) {
+      config.alpha_compression = ExUtilGetInt(argv[++c], 0, &parse_error);
+    } else if (!strcmp(argv[c], "-alpha_cleanup")) {
+      // This flag is obsolete, does opposite of -exact.
+      config.exact = 0;
+    } else if (!strcmp(argv[c], "-exact")) {
+      config.exact = 1;
+    } else if (!strcmp(argv[c], "-blend_alpha") && c + 1 < argc) {
+      blend_alpha = 1;
+      // background color is given in hex with an optional '0x' prefix
+      background_color = ExUtilGetInt(argv[++c], 16, &parse_error);
+      background_color = background_color & 0x00ffffffu;
+    } else if (!strcmp(argv[c], "-alpha_filter") && c + 1 < argc) {
+      ++c;
+      if (!strcmp(argv[c], "none")) {
+        config.alpha_filtering = 0;
+      } else if (!strcmp(argv[c], "fast")) {
+        config.alpha_filtering = 1;
+      } else if (!strcmp(argv[c], "best")) {
+        config.alpha_filtering = 2;
+      } else {
+        fprintf(stderr, "Error! Unrecognized alpha filter: %s\n", argv[c]);
+        goto Error;
+      }
+    } else if (!strcmp(argv[c], "-noalpha")) {
+      keep_alpha = 0;
+    } else if (!strcmp(argv[c], "-lossless")) {
+      config.lossless = 1;
+    } else if (!strcmp(argv[c], "-near_lossless") && c + 1 < argc) {
+      config.near_lossless = ExUtilGetInt(argv[++c], 0, &parse_error);
+      config.lossless = 1;  // use near-lossless only with lossless
+    } else if (!strcmp(argv[c], "-hint") && c + 1 < argc) {
+      ++c;
+      if (!strcmp(argv[c], "photo")) {
+        config.image_hint = WEBP_HINT_PHOTO;
+      } else if (!strcmp(argv[c], "picture")) {
+        config.image_hint = WEBP_HINT_PICTURE;
+      } else if (!strcmp(argv[c], "graph")) {
+        config.image_hint = WEBP_HINT_GRAPH;
+      } else {
+        fprintf(stderr, "Error! Unrecognized image hint: %s\n", argv[c]);
+        goto Error;
+      }
+    } else if (!strcmp(argv[c], "-size") && c + 1 < argc) {
+      config.target_size = ExUtilGetInt(argv[++c], 0, &parse_error);
+    } else if (!strcmp(argv[c], "-psnr") && c + 1 < argc) {
+      config.target_PSNR = ExUtilGetFloat(argv[++c], &parse_error);
+    } else if (!strcmp(argv[c], "-sns") && c + 1 < argc) {
+      config.sns_strength = ExUtilGetInt(argv[++c], 0, &parse_error);
+    } else if (!strcmp(argv[c], "-f") && c + 1 < argc) {
+      config.filter_strength = ExUtilGetInt(argv[++c], 0, &parse_error);
+    } else if (!strcmp(argv[c], "-af")) {
+      config.autofilter = 1;
+    } else if (!strcmp(argv[c], "-jpeg_like")) {
+      config.emulate_jpeg_size = 1;
+    } else if (!strcmp(argv[c], "-mt")) {
+      ++config.thread_level;  // increase thread level
+    } else if (!strcmp(argv[c], "-low_memory")) {
+      config.low_memory = 1;
+    } else if (!strcmp(argv[c], "-strong")) {
+      config.filter_type = 1;
+    } else if (!strcmp(argv[c], "-nostrong")) {
+      config.filter_type = 0;
+    } else if (!strcmp(argv[c], "-sharpness") && c + 1 < argc) {
+      config.filter_sharpness = ExUtilGetInt(argv[++c], 0, &parse_error);
+    } else if (!strcmp(argv[c], "-sharp_yuv")) {
+      config.use_sharp_yuv = 1;
+    } else if (!strcmp(argv[c], "-pass") && c + 1 < argc) {
+      config.pass = ExUtilGetInt(argv[++c], 0, &parse_error);
+    } else if (!strcmp(argv[c], "-qrange") && c + 2 < argc) {
+      config.qmin = ExUtilGetInt(argv[++c], 0, &parse_error);
+      config.qmax = ExUtilGetInt(argv[++c], 0, &parse_error);
+      if (config.qmin < 0) config.qmin = 0;
+      if (config.qmax > 100) config.qmax = 100;
+    } else if (!strcmp(argv[c], "-pre") && c + 1 < argc) {
+      config.preprocessing = ExUtilGetInt(argv[++c], 0, &parse_error);
+    } else if (!strcmp(argv[c], "-segments") && c + 1 < argc) {
+      config.segments = ExUtilGetInt(argv[++c], 0, &parse_error);
+    } else if (!strcmp(argv[c], "-partition_limit") && c + 1 < argc) {
+      config.partition_limit = ExUtilGetInt(argv[++c], 0, &parse_error);
+    } else if (!strcmp(argv[c], "-map") && c + 1 < argc) {
+      picture.extra_info_type = ExUtilGetInt(argv[++c], 0, &parse_error);
+    } else if (!strcmp(argv[c], "-crop") && c + 4 < argc) {
+      crop = 1;
+      crop_x = ExUtilGetInt(argv[++c], 0, &parse_error);
+      crop_y = ExUtilGetInt(argv[++c], 0, &parse_error);
+      crop_w = ExUtilGetInt(argv[++c], 0, &parse_error);
+      crop_h = ExUtilGetInt(argv[++c], 0, &parse_error);
+    } else if (!strcmp(argv[c], "-resize") && c + 2 < argc) {
+      resize_w = ExUtilGetInt(argv[++c], 0, &parse_error);
+      resize_h = ExUtilGetInt(argv[++c], 0, &parse_error);
+#ifndef WEBP_DLL
+    } else if (!strcmp(argv[c], "-noasm")) {
+      VP8GetCPUInfo = NULL;
+#endif
+    } else if (!strcmp(argv[c], "-version")) {
+      const int version = WebPGetEncoderVersion();
+      const int sharpyuv_version = SharpYuvGetVersion();
+      printf("%d.%d.%d\n",
+             (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
+      printf("libsharpyuv: %d.%d.%d\n",
+             (sharpyuv_version >> 24) & 0xff, (sharpyuv_version >> 16) & 0xffff,
+             sharpyuv_version & 0xff);
+      FREE_WARGV_AND_RETURN(0);
+    } else if (!strcmp(argv[c], "-progress")) {
+      show_progress = 1;
+    } else if (!strcmp(argv[c], "-quiet")) {
+      quiet = 1;
+    } else if (!strcmp(argv[c], "-preset") && c + 1 < argc) {
+      WebPPreset preset;
+      ++c;
+      if (!strcmp(argv[c], "default")) {
+        preset = WEBP_PRESET_DEFAULT;
+      } else if (!strcmp(argv[c], "photo")) {
+        preset = WEBP_PRESET_PHOTO;
+      } else if (!strcmp(argv[c], "picture")) {
+        preset = WEBP_PRESET_PICTURE;
+      } else if (!strcmp(argv[c], "drawing")) {
+        preset = WEBP_PRESET_DRAWING;
+      } else if (!strcmp(argv[c], "icon")) {
+        preset = WEBP_PRESET_ICON;
+      } else if (!strcmp(argv[c], "text")) {
+        preset = WEBP_PRESET_TEXT;
+      } else {
+        fprintf(stderr, "Error! Unrecognized preset: %s\n", argv[c]);
+        goto Error;
+      }
+      if (!WebPConfigPreset(&config, preset, config.quality)) {
+        fprintf(stderr, "Error! Could initialize configuration with preset.\n");
+        goto Error;
+      }
+    } else if (!strcmp(argv[c], "-metadata") && c + 1 < argc) {
+      static const struct {
+        const char* option;
+        int flag;
+      } kTokens[] = {
+        { "all",  METADATA_ALL },
+        { "none", 0 },
+        { "exif", METADATA_EXIF },
+        { "icc",  METADATA_ICC },
+        { "xmp",  METADATA_XMP },
+      };
+      const size_t kNumTokens = sizeof(kTokens) / sizeof(kTokens[0]);
+      const char* start = argv[++c];
+      const char* const end = start + strlen(start);
+
+      while (start < end) {
+        size_t i;
+        const char* token = strchr(start, ',');
+        if (token == NULL) token = end;
+
+        for (i = 0; i < kNumTokens; ++i) {
+          if ((size_t)(token - start) == strlen(kTokens[i].option) &&
+              !strncmp(start, kTokens[i].option, strlen(kTokens[i].option))) {
+            if (kTokens[i].flag != 0) {
+              keep_metadata |= kTokens[i].flag;
+            } else {
+              keep_metadata = 0;
+            }
+            break;
+          }
+        }
+        if (i == kNumTokens) {
+          fprintf(stderr, "Error! Unknown metadata type '%.*s'\n",
+                  (int)(token - start), start);
+          FREE_WARGV_AND_RETURN(-1);
+        }
+        start = token + 1;
+      }
+#ifdef HAVE_WINCODEC_H
+      if (keep_metadata != 0 && keep_metadata != METADATA_ICC) {
+        // TODO(jzern): remove when -metadata is supported on all platforms.
+        fprintf(stderr, "Warning: only ICC profile extraction is currently"
+                        " supported on this platform!\n");
+      }
+#endif
+    } else if (!strcmp(argv[c], "-v")) {
+      verbose = 1;
+    } else if (!strcmp(argv[c], "--")) {
+      if (c + 1 < argc) in_file = (const char*)GET_WARGV(argv, ++c);
+      break;
+    } else if (argv[c][0] == '-') {
+      fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]);
+      HelpLong();
+      FREE_WARGV_AND_RETURN(-1);
+    } else {
+      in_file = (const char*)GET_WARGV(argv, c);
+    }
+
+    if (parse_error) {
+      HelpLong();
+      FREE_WARGV_AND_RETURN(-1);
+    }
+  }
+  if (in_file == NULL) {
+    fprintf(stderr, "No input file specified!\n");
+    HelpShort();
+    goto Error;
+  }
+
+  if (use_lossless_preset == 1) {
+    if (!WebPConfigLosslessPreset(&config, lossless_preset)) {
+      fprintf(stderr, "Invalid lossless preset (-z %d)\n", lossless_preset);
+      goto Error;
+    }
+  }
+
+  // Check for unsupported command line options for lossless mode and log
+  // warning for such options.
+  if (!quiet && config.lossless == 1) {
+    if (config.target_size > 0 || config.target_PSNR > 0) {
+      fprintf(stderr, "Encoding for specified size or PSNR is not supported"
+                      " for lossless encoding. Ignoring such option(s)!\n");
+    }
+    if (config.partition_limit > 0) {
+      fprintf(stderr, "Partition limit option is not required for lossless"
+                      " encoding. Ignoring this option!\n");
+    }
+  }
+  // If a target size or PSNR was given, but somehow the -pass option was
+  // omitted, force a reasonable value.
+  if (config.target_size > 0 || config.target_PSNR > 0) {
+    if (config.pass == 1) config.pass = 6;
+  }
+
+  if (!WebPValidateConfig(&config)) {
+    fprintf(stderr, "Error! Invalid configuration.\n");
+    goto Error;
+  }
+
+  // Read the input. We need to decide if we prefer ARGB or YUVA
+  // samples, depending on the expected compression mode (this saves
+  // some conversion steps).
+  picture.use_argb = (config.lossless || config.use_sharp_yuv ||
+                      config.preprocessing > 0 ||
+                      crop || (resize_w | resize_h) > 0);
+  if (verbose) {
+    StopwatchReset(&stop_watch);
+  }
+  if (!ReadPicture(in_file, &picture, keep_alpha,
+                   (keep_metadata == 0) ? NULL : &metadata)) {
+    WFPRINTF(stderr, "Error! Cannot read input picture file '%s'\n",
+             (const W_CHAR*)in_file);
+    goto Error;
+  }
+  picture.progress_hook = (show_progress && !quiet) ? ProgressReport : NULL;
+
+  if (blend_alpha) {
+    WebPBlendAlpha(&picture, background_color);
+  }
+
+  if (verbose) {
+    const double read_time = StopwatchReadAndReset(&stop_watch);
+    fprintf(stderr, "Time to read input: %.3fs\n", read_time);
+  }
+  // The bitstream should be kept in memory when metadata must be appended
+  // before writing it to a file/stream, and/or when the near-losslessly encoded
+  // bitstream must be decoded for distortion computation (lossy will modify the
+  // 'picture' but not the lossless pipeline).
+  // Otherwise directly write the bitstream to a file.
+  use_memory_writer = (out_file != NULL && keep_metadata) ||
+                      (!quiet && print_distortion >= 0 && config.lossless &&
+                       config.near_lossless < 100);
+
+  // Open the output
+  if (out_file != NULL) {
+    const int use_stdout = !WSTRCMP(out_file, "-");
+    out = use_stdout ? ImgIoUtilSetBinaryMode(stdout) : WFOPEN(out_file, "wb");
+    if (out == NULL) {
+      WFPRINTF(stderr, "Error! Cannot open output file '%s'\n",
+               (const W_CHAR*)out_file);
+      goto Error;
+    } else {
+      if (!short_output && !quiet) {
+        WFPRINTF(stderr, "Saving file '%s'\n", (const W_CHAR*)out_file);
+      }
+    }
+    if (use_memory_writer) {
+      picture.writer = WebPMemoryWrite;
+      picture.custom_ptr = (void*)&memory_writer;
+    } else {
+      picture.writer = MyWriter;
+      picture.custom_ptr = (void*)out;
+    }
+  } else {
+    out = NULL;
+    if (use_memory_writer) {
+      picture.writer = WebPMemoryWrite;
+      picture.custom_ptr = (void*)&memory_writer;
+    }
+    if (!quiet && !short_output) {
+      fprintf(stderr, "No output file specified (no -o flag). Encoding will\n");
+      fprintf(stderr, "be performed, but its results discarded.\n\n");
+    }
+  }
+  if (!quiet) {
+    picture.stats = &stats;
+    picture.user_data = (void*)in_file;
+  }
+
+  // Crop & resize.
+  if (verbose) {
+    StopwatchReset(&stop_watch);
+  }
+  if (crop != 0) {
+    // We use self-cropping using a view.
+    if (!WebPPictureView(&picture, crop_x, crop_y, crop_w, crop_h, &picture)) {
+      fprintf(stderr, "Error! Cannot crop picture\n");
+      goto Error;
+    }
+  }
+  if ((resize_w | resize_h) > 0) {
+    WebPPicture picture_no_alpha;
+    if (config.exact) {
+      // If -exact, we can't premultiply RGB by A otherwise RGB is lost if A=0.
+      // We rescale an opaque copy and assemble scaled A and non-premultiplied
+      // RGB channels. This is slower but it's a very uncommon use case. Color
+      // leak at sharp alpha edges is possible.
+      if (!WebPPictureCopy(&picture, &picture_no_alpha)) {
+        fprintf(stderr, "Error! Cannot copy temporary picture\n");
+        goto Error;
+      }
+
+      // We enforced picture.use_argb = 1 above. Now, remove the alpha values.
+      {
+        int x, y;
+        uint32_t* argb_no_alpha = picture_no_alpha.argb;
+        for (y = 0; y < picture_no_alpha.height; ++y) {
+          for (x = 0; x < picture_no_alpha.width; ++x) {
+            argb_no_alpha[x] |= 0xff000000;  // Opaque copy.
+          }
+          argb_no_alpha += picture_no_alpha.argb_stride;
+        }
+      }
+
+      if (!WebPPictureRescale(&picture_no_alpha, resize_w, resize_h)) {
+        fprintf(stderr, "Error! Cannot resize temporary picture\n");
+        goto Error;
+      }
+    }
+
+    if (!WebPPictureRescale(&picture, resize_w, resize_h)) {
+      fprintf(stderr, "Error! Cannot resize picture\n");
+      goto Error;
+    }
+
+    if (config.exact) {  // Put back the alpha information.
+      int x, y;
+      uint32_t* argb_no_alpha = picture_no_alpha.argb;
+      uint32_t* argb = picture.argb;
+      for (y = 0; y < picture_no_alpha.height; ++y) {
+        for (x = 0; x < picture_no_alpha.width; ++x) {
+          argb[x] = (argb[x] & 0xff000000) | (argb_no_alpha[x] & 0x00ffffff);
+        }
+        argb_no_alpha += picture_no_alpha.argb_stride;
+        argb += picture.argb_stride;
+      }
+      WebPPictureFree(&picture_no_alpha);
+    }
+  }
+  if (verbose && (crop != 0 || (resize_w | resize_h) > 0)) {
+    const double preproc_time = StopwatchReadAndReset(&stop_watch);
+    fprintf(stderr, "Time to crop/resize picture: %.3fs\n", preproc_time);
+  }
+
+  if (picture.extra_info_type > 0) {
+    AllocExtraInfo(&picture);
+  }
+  // Save original picture for later comparison. Only for lossy as lossless does
+  // not modify 'picture' (even near-lossless).
+  if (print_distortion >= 0 && !config.lossless &&
+      !WebPPictureCopy(&picture, &original_picture)) {
+    fprintf(stderr, "Error! Cannot copy temporary picture\n");
+    goto Error;
+  }
+
+  // Compress.
+  if (verbose) {
+    StopwatchReset(&stop_watch);
+  }
+  if (!WebPEncode(&config, &picture)) {
+    fprintf(stderr, "Error! Cannot encode picture as WebP\n");
+    fprintf(stderr, "Error code: %d (%s)\n",
+            picture.error_code, kErrorMessages[picture.error_code]);
+    goto Error;
+  }
+  if (verbose) {
+    const double encode_time = StopwatchReadAndReset(&stop_watch);
+    fprintf(stderr, "Time to encode picture: %.3fs\n", encode_time);
+  }
+
+  // Get the decompressed image for the lossless pipeline.
+  if (!quiet && print_distortion >= 0 && config.lossless) {
+    if (config.near_lossless == 100) {
+      // Pure lossless: image was not modified, make 'original_picture' a view
+      // of 'picture' by copying all members except the freeable pointers.
+      original_picture = picture;
+      original_picture.memory_ = original_picture.memory_argb_ = NULL;
+    } else {
+      // Decode the bitstream stored in 'memory_writer' to get the altered image
+      // to 'picture'; save the 'original_picture' beforehand.
+      assert(use_memory_writer);
+      original_picture = picture;
+      if (!WebPPictureInit(&picture)) {  // Do not free 'picture'.
+        fprintf(stderr, "Error! Version mismatch!\n");
+        goto Error;
+      }
+
+      picture.use_argb = 1;
+      if (!ReadWebP(
+              memory_writer.mem, memory_writer.size, &picture,
+              /*keep_alpha=*/WebPPictureHasTransparency(&original_picture),
+              /*metadata=*/NULL)) {
+        fprintf(stderr, "Error! Cannot decode encoded WebP bitstream\n");
+        fprintf(stderr, "Error code: %d (%s)\n", picture.error_code,
+                kErrorMessages[picture.error_code]);
+        goto Error;
+      }
+      picture.stats = original_picture.stats;
+    }
+    original_picture.stats = NULL;
+  }
+
+  // Write the YUV planes to a PGM file. Only available for lossy.
+  if (dump_file) {
+    if (picture.use_argb) {
+      fprintf(stderr, "Warning: can't dump file (-d option) "
+                      "in lossless mode.\n");
+    } else if (!DumpPicture(&picture, dump_file)) {
+      WFPRINTF(stderr, "Warning, couldn't dump picture %s\n",
+               (const W_CHAR*)dump_file);
+    }
+  }
+
+  if (use_memory_writer && out != NULL &&
+      !WriteWebPWithMetadata(out, &picture, &memory_writer, &metadata,
+                             keep_metadata, &metadata_written)) {
+    fprintf(stderr, "Error writing WebP file!\n");
+    goto Error;
+  }
+
+  if (out == NULL && keep_metadata) {
+    // output is disabled, just display the metadata stats.
+    const struct {
+      const MetadataPayload* const payload;
+      int flag;
+    } *iter, info[] = {{&metadata.exif, METADATA_EXIF},
+                       {&metadata.iccp, METADATA_ICC},
+                       {&metadata.xmp, METADATA_XMP},
+                       {NULL, 0}};
+    uint32_t unused1 = 0;
+    uint64_t unused2 = 0;
+
+    for (iter = info; iter->payload != NULL; ++iter) {
+      if (UpdateFlagsAndSize(iter->payload, !!(keep_metadata & iter->flag),
+                             /*flag=*/0, &unused1, &unused2)) {
+        metadata_written |= iter->flag;
+      }
+    }
+  }
+
+  if (!quiet) {
+    if (!short_output || print_distortion < 0) {
+      if (config.lossless) {
+        PrintExtraInfoLossless(&picture, short_output, in_file);
+      } else {
+        PrintExtraInfoLossy(&picture, short_output, config.low_memory, in_file);
+      }
+    }
+    if (!short_output && picture.extra_info_type > 0) {
+      PrintMapInfo(&picture);
+    }
+    if (print_distortion >= 0) {    // print distortion
+      static const char* distortion_names[] = { "PSNR", "SSIM", "LSIM" };
+      float values[5];
+      if (!WebPPictureDistortion(&picture, &original_picture,
+                                 print_distortion, values)) {
+        fprintf(stderr, "Error while computing the distortion.\n");
+        goto Error;
+      }
+      if (!short_output) {
+        fprintf(stderr, "%s: ", distortion_names[print_distortion]);
+        fprintf(stderr, "B:%.2f G:%.2f R:%.2f A:%.2f  Total:%.2f\n",
+                values[0], values[1], values[2], values[3], values[4]);
+      } else {
+        fprintf(stderr, "%7d %.4f\n", picture.stats->coded_size, values[4]);
+      }
+    }
+    if (!short_output) {
+      PrintMetadataInfo(&metadata, metadata_written);
+    }
+  }
+  return_value = 0;
+
+ Error:
+  WebPMemoryWriterClear(&memory_writer);
+  WebPFree(picture.extra_info);
+  MetadataFree(&metadata);
+  WebPPictureFree(&picture);
+  WebPPictureFree(&original_picture);
+  if (out != NULL && out != stdout) {
+    fclose(out);
+  }
+
+  FREE_WARGV_AND_RETURN(return_value);
+}
+
+//------------------------------------------------------------------------------
diff --git a/examples/dwebp.c b/examples/dwebp.c
new file mode 100644
index 0000000..652de6a
--- /dev/null
+++ b/examples/dwebp.c
@@ -0,0 +1,421 @@
+// Copyright 2010 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  Command-line tool for decoding a WebP image.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include "../examples/example_util.h"
+#include "../imageio/image_enc.h"
+#include "../imageio/webpdec.h"
+#include "./stopwatch.h"
+#include "./unicode.h"
+
+static int verbose = 0;
+static int quiet = 0;
+#ifndef WEBP_DLL
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void* VP8GetCPUInfo;   // opaque forward declaration.
+
+#ifdef __cplusplus
+}    // extern "C"
+#endif
+#endif  // WEBP_DLL
+
+
+static int SaveOutput(const WebPDecBuffer* const buffer,
+                      WebPOutputFileFormat format, const char* const out_file) {
+  const int use_stdout = (out_file != NULL) && !WSTRCMP(out_file, "-");
+  int ok = 1;
+  Stopwatch stop_watch;
+
+  if (verbose) {
+    StopwatchReset(&stop_watch);
+  }
+  ok = WebPSaveImage(buffer, format, out_file);
+
+  if (ok) {
+    if (!quiet) {
+      if (use_stdout) {
+        fprintf(stderr, "Saved to stdout\n");
+      } else {
+        WFPRINTF(stderr, "Saved file %s\n", (const W_CHAR*)out_file);
+      }
+    }
+    if (verbose) {
+      const double write_time = StopwatchReadAndReset(&stop_watch);
+      fprintf(stderr, "Time to write output: %.3fs\n", write_time);
+    }
+  } else {
+    if (use_stdout) {
+      fprintf(stderr, "Error writing to stdout !!\n");
+    } else {
+      WFPRINTF(stderr, "Error writing file %s !!\n", (const W_CHAR*)out_file);
+    }
+  }
+  return ok;
+}
+
+static void Help(void) {
+  printf("Usage: dwebp in_file [options] [-o out_file]\n\n"
+         "Decodes the WebP image file to PNG format [Default].\n"
+         "Note: Animated WebP files are not supported.\n\n"
+         "Use following options to convert into alternate image formats:\n"
+         "  -pam ......... save the raw RGBA samples as a color PAM\n"
+         "  -ppm ......... save the raw RGB samples as a color PPM\n"
+         "  -bmp ......... save as uncompressed BMP format\n"
+         "  -tiff ........ save as uncompressed TIFF format\n"
+         "  -pgm ......... save the raw YUV samples as a grayscale PGM\n"
+         "                 file with IMC4 layout\n"
+         "  -yuv ......... save the raw YUV samples in flat layout\n"
+         "\n"
+         " Other options are:\n"
+         "  -version ..... print version number and exit\n"
+         "  -nofancy ..... don't use the fancy YUV420 upscaler\n"
+         "  -nofilter .... disable in-loop filtering\n"
+         "  -nodither .... disable dithering\n"
+         "  -dither <d> .. dithering strength (in 0..100)\n"
+         "  -alpha_dither  use alpha-plane dithering if needed\n"
+         "  -mt .......... use multi-threading\n"
+         "  -crop <x> <y> <w> <h> ... crop output with the given rectangle\n"
+         "  -resize <w> <h> ......... resize output (*after* any cropping)\n"
+         "  -flip ........ flip the output vertically\n"
+         "  -alpha ....... only save the alpha plane\n"
+         "  -incremental . use incremental decoding (useful for tests)\n"
+         "  -h ........... this help message\n"
+         "  -v ........... verbose (e.g. print encoding/decoding times)\n"
+         "  -quiet ....... quiet mode, don't print anything\n"
+#ifndef WEBP_DLL
+         "  -noasm ....... disable all assembly optimizations\n"
+#endif
+        );
+}
+
+static const char* const kFormatType[] = {
+  "unspecified", "lossy", "lossless"
+};
+
+static uint8_t* AllocateExternalBuffer(WebPDecoderConfig* config,
+                                       WebPOutputFileFormat format,
+                                       int use_external_memory) {
+  uint8_t* external_buffer = NULL;
+  WebPDecBuffer* const output_buffer = &config->output;
+  int w = config->input.width;
+  int h = config->input.height;
+  if (config->options.use_scaling) {
+    w = config->options.scaled_width;
+    h = config->options.scaled_height;
+  } else if (config->options.use_cropping) {
+    w = config->options.crop_width;
+    h = config->options.crop_height;
+  }
+  if (format >= RGB && format <= rgbA_4444) {
+    const int bpp = (format == RGB || format == BGR) ? 3
+                  : (format == RGBA_4444 || format == rgbA_4444 ||
+                     format == RGB_565) ? 2
+                  : 4;
+    uint32_t stride = bpp * w + 7;   // <- just for exercising
+    external_buffer = (uint8_t*)WebPMalloc(stride * h);
+    if (external_buffer == NULL) return NULL;
+    output_buffer->u.RGBA.stride = stride;
+    output_buffer->u.RGBA.size = stride * h;
+    output_buffer->u.RGBA.rgba = external_buffer;
+  } else {    // YUV and YUVA
+    const int has_alpha = WebPIsAlphaMode(output_buffer->colorspace);
+    uint8_t* tmp;
+    uint32_t stride = w + 3;
+    uint32_t uv_stride = (w + 1) / 2 + 13;
+    uint32_t total_size = stride * h * (has_alpha ? 2 : 1)
+                        + 2 * uv_stride * (h + 1) / 2;
+    assert(format >= YUV && format <= YUVA);
+    external_buffer = (uint8_t*)WebPMalloc(total_size);
+    if (external_buffer == NULL) return NULL;
+    tmp = external_buffer;
+    output_buffer->u.YUVA.y = tmp;
+    output_buffer->u.YUVA.y_stride = stride;
+    output_buffer->u.YUVA.y_size = stride * h;
+    tmp += output_buffer->u.YUVA.y_size;
+    if (has_alpha) {
+      output_buffer->u.YUVA.a = tmp;
+      output_buffer->u.YUVA.a_stride = stride;
+      output_buffer->u.YUVA.a_size = stride * h;
+      tmp += output_buffer->u.YUVA.a_size;
+    } else {
+      output_buffer->u.YUVA.a = NULL;
+      output_buffer->u.YUVA.a_stride = 0;
+    }
+    output_buffer->u.YUVA.u = tmp;
+    output_buffer->u.YUVA.u_stride = uv_stride;
+    output_buffer->u.YUVA.u_size = uv_stride * (h + 1) / 2;
+    tmp += output_buffer->u.YUVA.u_size;
+
+    output_buffer->u.YUVA.v = tmp;
+    output_buffer->u.YUVA.v_stride = uv_stride;
+    output_buffer->u.YUVA.v_size = uv_stride * (h + 1) / 2;
+    tmp += output_buffer->u.YUVA.v_size;
+    assert(tmp <= external_buffer + total_size);
+  }
+  output_buffer->is_external_memory = use_external_memory;
+  return external_buffer;
+}
+
+int main(int argc, const char* argv[]) {
+  int ok = 0;
+  const char* in_file = NULL;
+  const char* out_file = NULL;
+
+  WebPDecoderConfig config;
+  WebPDecBuffer* const output_buffer = &config.output;
+  WebPBitstreamFeatures* const bitstream = &config.input;
+  WebPOutputFileFormat format = PNG;
+  uint8_t* external_buffer = NULL;
+  int use_external_memory = 0;
+  const uint8_t* data = NULL;
+
+  int incremental = 0;
+  int c;
+
+  INIT_WARGV(argc, argv);
+
+  if (!WebPInitDecoderConfig(&config)) {
+    fprintf(stderr, "Library version mismatch!\n");
+    FREE_WARGV_AND_RETURN(-1);
+  }
+
+  for (c = 1; c < argc; ++c) {
+    int parse_error = 0;
+    if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
+      Help();
+      FREE_WARGV_AND_RETURN(0);
+    } else if (!strcmp(argv[c], "-o") && c < argc - 1) {
+      out_file = (const char*)GET_WARGV(argv, ++c);
+    } else if (!strcmp(argv[c], "-alpha")) {
+      format = ALPHA_PLANE_ONLY;
+    } else if (!strcmp(argv[c], "-nofancy")) {
+      config.options.no_fancy_upsampling = 1;
+    } else if (!strcmp(argv[c], "-nofilter")) {
+      config.options.bypass_filtering = 1;
+    } else if (!strcmp(argv[c], "-pam")) {
+      format = PAM;
+    } else if (!strcmp(argv[c], "-ppm")) {
+      format = PPM;
+    } else if (!strcmp(argv[c], "-bmp")) {
+      format = BMP;
+    } else if (!strcmp(argv[c], "-tiff")) {
+      format = TIFF;
+    } else if (!strcmp(argv[c], "-quiet")) {
+      quiet = 1;
+    } else if (!strcmp(argv[c], "-version")) {
+      const int version = WebPGetDecoderVersion();
+      printf("%d.%d.%d\n",
+             (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
+      FREE_WARGV_AND_RETURN(0);
+    } else if (!strcmp(argv[c], "-pgm")) {
+      format = PGM;
+    } else if (!strcmp(argv[c], "-yuv")) {
+      format = RAW_YUV;
+    } else if (!strcmp(argv[c], "-pixel_format") && c < argc - 1) {
+      const char* const fmt = argv[++c];
+      if      (!strcmp(fmt, "RGB"))  format = RGB;
+      else if (!strcmp(fmt, "RGBA")) format = RGBA;
+      else if (!strcmp(fmt, "BGR"))  format = BGR;
+      else if (!strcmp(fmt, "BGRA")) format = BGRA;
+      else if (!strcmp(fmt, "ARGB")) format = ARGB;
+      else if (!strcmp(fmt, "RGBA_4444")) format = RGBA_4444;
+      else if (!strcmp(fmt, "RGB_565")) format = RGB_565;
+      else if (!strcmp(fmt, "rgbA")) format = rgbA;
+      else if (!strcmp(fmt, "bgrA")) format = bgrA;
+      else if (!strcmp(fmt, "Argb")) format = Argb;
+      else if (!strcmp(fmt, "rgbA_4444")) format = rgbA_4444;
+      else if (!strcmp(fmt, "YUV"))  format = YUV;
+      else if (!strcmp(fmt, "YUVA")) format = YUVA;
+      else {
+        fprintf(stderr, "Can't parse pixel_format %s\n", fmt);
+        parse_error = 1;
+      }
+    } else if (!strcmp(argv[c], "-external_memory") && c < argc - 1) {
+      use_external_memory = ExUtilGetInt(argv[++c], 0, &parse_error);
+      parse_error |= (use_external_memory > 2 || use_external_memory < 0);
+      if (parse_error) {
+        fprintf(stderr, "Can't parse 'external_memory' value %s\n", argv[c]);
+      }
+    } else if (!strcmp(argv[c], "-mt")) {
+      config.options.use_threads = 1;
+    } else if (!strcmp(argv[c], "-alpha_dither")) {
+      config.options.alpha_dithering_strength = 100;
+    } else if (!strcmp(argv[c], "-nodither")) {
+      config.options.dithering_strength = 0;
+    } else if (!strcmp(argv[c], "-dither") && c < argc - 1) {
+      config.options.dithering_strength =
+          ExUtilGetInt(argv[++c], 0, &parse_error);
+    } else if (!strcmp(argv[c], "-crop") && c < argc - 4) {
+      config.options.use_cropping = 1;
+      config.options.crop_left   = ExUtilGetInt(argv[++c], 0, &parse_error);
+      config.options.crop_top    = ExUtilGetInt(argv[++c], 0, &parse_error);
+      config.options.crop_width  = ExUtilGetInt(argv[++c], 0, &parse_error);
+      config.options.crop_height = ExUtilGetInt(argv[++c], 0, &parse_error);
+    } else if ((!strcmp(argv[c], "-scale") || !strcmp(argv[c], "-resize")) &&
+               c < argc - 2) {  // '-scale' is left for compatibility
+      config.options.use_scaling = 1;
+      config.options.scaled_width  = ExUtilGetInt(argv[++c], 0, &parse_error);
+      config.options.scaled_height = ExUtilGetInt(argv[++c], 0, &parse_error);
+    } else if (!strcmp(argv[c], "-flip")) {
+      config.options.flip = 1;
+    } else if (!strcmp(argv[c], "-v")) {
+      verbose = 1;
+#ifndef WEBP_DLL
+    } else if (!strcmp(argv[c], "-noasm")) {
+      VP8GetCPUInfo = NULL;
+#endif
+    } else if (!strcmp(argv[c], "-incremental")) {
+      incremental = 1;
+    } else if (!strcmp(argv[c], "--")) {
+      if (c < argc - 1) in_file = (const char*)GET_WARGV(argv, ++c);
+      break;
+    } else if (argv[c][0] == '-') {
+      fprintf(stderr, "Unknown option '%s'\n", argv[c]);
+      Help();
+      FREE_WARGV_AND_RETURN(-1);
+    } else {
+      in_file = (const char*)GET_WARGV(argv, c);
+    }
+
+    if (parse_error) {
+      Help();
+      FREE_WARGV_AND_RETURN(-1);
+    }
+  }
+
+  if (in_file == NULL) {
+    fprintf(stderr, "missing input file!!\n");
+    Help();
+    FREE_WARGV_AND_RETURN(-1);
+  }
+
+  if (quiet) verbose = 0;
+
+  {
+    VP8StatusCode status = VP8_STATUS_OK;
+    size_t data_size = 0;
+    if (!LoadWebP(in_file, &data, &data_size, bitstream)) {
+      FREE_WARGV_AND_RETURN(-1);
+    }
+
+    switch (format) {
+      case PNG:
+#ifdef HAVE_WINCODEC_H
+        output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR;
+#else
+        output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB;
+#endif
+        break;
+      case PAM:
+        output_buffer->colorspace = MODE_RGBA;
+        break;
+      case PPM:
+        output_buffer->colorspace = MODE_RGB;  // drops alpha for PPM
+        break;
+      case BMP:
+        output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR;
+        break;
+      case TIFF:
+        output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB;
+        break;
+      case PGM:
+      case RAW_YUV:
+        output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV;
+        break;
+      case ALPHA_PLANE_ONLY:
+        output_buffer->colorspace = MODE_YUVA;
+        break;
+      // forced modes:
+      case RGB: output_buffer->colorspace = MODE_RGB; break;
+      case RGBA: output_buffer->colorspace = MODE_RGBA; break;
+      case BGR: output_buffer->colorspace = MODE_BGR; break;
+      case BGRA: output_buffer->colorspace = MODE_BGRA; break;
+      case ARGB: output_buffer->colorspace = MODE_ARGB; break;
+      case RGBA_4444: output_buffer->colorspace = MODE_RGBA_4444; break;
+      case RGB_565: output_buffer->colorspace = MODE_RGB_565; break;
+      case rgbA: output_buffer->colorspace = MODE_rgbA; break;
+      case bgrA: output_buffer->colorspace = MODE_bgrA; break;
+      case Argb: output_buffer->colorspace = MODE_Argb; break;
+      case rgbA_4444: output_buffer->colorspace = MODE_rgbA_4444; break;
+      case YUV: output_buffer->colorspace = MODE_YUV; break;
+      case YUVA: output_buffer->colorspace = MODE_YUVA; break;
+      default: goto Exit;
+    }
+
+    if (use_external_memory > 0 && format >= RGB) {
+      external_buffer = AllocateExternalBuffer(&config, format,
+                                               use_external_memory);
+      if (external_buffer == NULL) goto Exit;
+    }
+
+    {
+      Stopwatch stop_watch;
+      if (verbose) StopwatchReset(&stop_watch);
+
+      if (incremental) {
+        status = DecodeWebPIncremental(data, data_size, &config);
+      } else {
+        status = DecodeWebP(data, data_size, &config);
+      }
+      if (verbose) {
+        const double decode_time = StopwatchReadAndReset(&stop_watch);
+        fprintf(stderr, "Time to decode picture: %.3fs\n", decode_time);
+      }
+    }
+
+    ok = (status == VP8_STATUS_OK);
+    if (!ok) {
+      PrintWebPError(in_file, status);
+      goto Exit;
+    }
+  }
+
+  if (out_file != NULL) {
+    if (!quiet) {
+      WFPRINTF(stderr, "Decoded %s.", (const W_CHAR*)in_file);
+      fprintf(stderr, " Dimensions: %d x %d %s. Format: %s. Now saving...\n",
+              output_buffer->width, output_buffer->height,
+              bitstream->has_alpha ? " (with alpha)" : "",
+              kFormatType[bitstream->format]);
+    }
+    ok = SaveOutput(output_buffer, format, out_file);
+  } else {
+    if (!quiet) {
+      WFPRINTF(stderr, "File %s can be decoded ", (const W_CHAR*)in_file);
+      fprintf(stderr, "(dimensions: %d x %d %s. Format: %s).\n",
+              output_buffer->width, output_buffer->height,
+              bitstream->has_alpha ? " (with alpha)" : "",
+              kFormatType[bitstream->format]);
+      fprintf(stderr, "Nothing written; "
+                      "use -o flag to save the result as e.g. PNG.\n");
+    }
+  }
+ Exit:
+  WebPFreeDecBuffer(output_buffer);
+  WebPFree((void*)external_buffer);
+  WebPFree((void*)data);
+  FREE_WARGV_AND_RETURN(ok ? 0 : -1);
+}
+
+//------------------------------------------------------------------------------
diff --git a/examples/example_util.c b/examples/example_util.c
new file mode 100644
index 0000000..fa38d3c
--- /dev/null
+++ b/examples/example_util.c
@@ -0,0 +1,139 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  Utility functions used by the example programs.
+//
+
+#include "./example_util.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "webp/mux_types.h"
+#include "../imageio/imageio_util.h"
+
+//------------------------------------------------------------------------------
+// String parsing
+
+uint32_t ExUtilGetUInt(const char* const v, int base, int* const error) {
+  char* end = NULL;
+  const uint32_t n = (v != NULL) ? (uint32_t)strtoul(v, &end, base) : 0u;
+  if (end == v && error != NULL && !*error) {
+    *error = 1;
+    fprintf(stderr, "Error! '%s' is not an integer.\n",
+            (v != NULL) ? v : "(null)");
+  }
+  return n;
+}
+
+int ExUtilGetInt(const char* const v, int base, int* const error) {
+  return (int)ExUtilGetUInt(v, base, error);
+}
+
+int ExUtilGetInts(const char* v, int base, int max_output, int output[]) {
+  int n, error = 0;
+  for (n = 0; v != NULL && n < max_output; ++n) {
+    const int value = ExUtilGetInt(v, base, &error);
+    if (error) return -1;
+    output[n] = value;
+    v = strchr(v, ',');
+    if (v != NULL) ++v;   // skip over the trailing ','
+  }
+  return n;
+}
+
+float ExUtilGetFloat(const char* const v, int* const error) {
+  char* end = NULL;
+  const float f = (v != NULL) ? (float)strtod(v, &end) : 0.f;
+  if (end == v && error != NULL && !*error) {
+    *error = 1;
+    fprintf(stderr, "Error! '%s' is not a floating point number.\n",
+            (v != NULL) ? v : "(null)");
+  }
+  return f;
+}
+
+//------------------------------------------------------------------------------
+
+static void ResetCommandLineArguments(int argc, const char* argv[],
+                                      CommandLineArguments* const args) {
+  assert(args != NULL);
+  args->argc_ = argc;
+  args->argv_ = argv;
+  args->own_argv_ = 0;
+  WebPDataInit(&args->argv_data_);
+}
+
+void ExUtilDeleteCommandLineArguments(CommandLineArguments* const args) {
+  if (args != NULL) {
+    if (args->own_argv_) {
+      WebPFree((void*)args->argv_);
+      WebPDataClear(&args->argv_data_);
+    }
+    ResetCommandLineArguments(0, NULL, args);
+  }
+}
+
+#define MAX_ARGC 16384
+int ExUtilInitCommandLineArguments(int argc, const char* argv[],
+                                   CommandLineArguments* const args) {
+  if (args == NULL || argv == NULL) return 0;
+  ResetCommandLineArguments(argc, argv, args);
+  if (argc == 1 && argv[0][0] != '-') {
+    char* cur;
+    const char sep[] = " \t\r\n\f\v";
+
+#if defined(_WIN32) && defined(_UNICODE)
+    fprintf(stderr,
+            "Error: Reading arguments from a file is a feature unavailable "
+            "with Unicode binaries.\n");
+    return 0;
+#endif
+
+    if (!ExUtilReadFileToWebPData(argv[0], &args->argv_data_)) {
+      return 0;
+    }
+    args->own_argv_ = 1;
+    args->argv_ = (const char**)WebPMalloc(MAX_ARGC * sizeof(*args->argv_));
+    if (args->argv_ == NULL) {
+      ExUtilDeleteCommandLineArguments(args);
+      return 0;
+    }
+
+    argc = 0;
+    for (cur = strtok((char*)args->argv_data_.bytes, sep);
+         cur != NULL;
+         cur = strtok(NULL, sep)) {
+      if (argc == MAX_ARGC) {
+        fprintf(stderr, "ERROR: Arguments limit %d reached\n", MAX_ARGC);
+        ExUtilDeleteCommandLineArguments(args);
+        return 0;
+      }
+      assert(strlen(cur) != 0);
+      args->argv_[argc++] = cur;
+    }
+    args->argc_ = argc;
+  }
+  return 1;
+}
+
+//------------------------------------------------------------------------------
+
+int ExUtilReadFileToWebPData(const char* const filename,
+                             WebPData* const webp_data) {
+  const uint8_t* data;
+  size_t size;
+  if (webp_data == NULL) return 0;
+  if (!ImgIoUtilReadFile(filename, &data, &size)) return 0;
+  webp_data->bytes = data;
+  webp_data->size = size;
+  return 1;
+}
diff --git a/examples/example_util.h b/examples/example_util.h
new file mode 100644
index 0000000..fe762a4
--- /dev/null
+++ b/examples/example_util.h
@@ -0,0 +1,70 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  Utility functions used by the example programs.
+//
+
+#ifndef WEBP_EXAMPLES_EXAMPLE_UTIL_H_
+#define WEBP_EXAMPLES_EXAMPLE_UTIL_H_
+
+#include "webp/types.h"
+#include "webp/mux_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//------------------------------------------------------------------------------
+// String parsing
+
+// Parses 'v' using strto(ul|l|d)(). If error is non-NULL, '*error' is set to
+// true on failure while on success it is left unmodified to allow chaining of
+// calls. An error is only printed on the first occurrence.
+uint32_t ExUtilGetUInt(const char* const v, int base, int* const error);
+int ExUtilGetInt(const char* const v, int base, int* const error);
+float ExUtilGetFloat(const char* const v, int* const error);
+
+// This variant of ExUtilGetInt() will parse multiple integers from a
+// comma-separated list. Up to 'max_output' integers are parsed.
+// The result is placed in the output[] array, and the number of integers
+// actually parsed is returned, or -1 if an error occurred.
+int ExUtilGetInts(const char* v, int base, int max_output, int output[]);
+
+// Reads a file named 'filename' into a WebPData structure. The content of
+// webp_data is overwritten. Returns false in case of error.
+int ExUtilReadFileToWebPData(const char* const filename,
+                             WebPData* const webp_data);
+
+//------------------------------------------------------------------------------
+// Command-line arguments
+
+typedef struct {
+  int argc_;
+  const char** argv_;
+  WebPData argv_data_;
+  int own_argv_;
+} CommandLineArguments;
+
+// Initializes the structure from the command-line parameters. If there is
+// only one parameter and it does not start with a '-', then it is assumed to
+// be a file name. This file will be read and tokenized into command-line
+// arguments. The content of 'args' is overwritten.
+// Returns false in case of error (memory allocation failure, non
+// existing file, too many arguments, ...).
+int ExUtilInitCommandLineArguments(int argc, const char* argv[],
+                                   CommandLineArguments* const args);
+
+// Deallocate all memory and reset 'args'.
+void ExUtilDeleteCommandLineArguments(CommandLineArguments* const args);
+
+#ifdef __cplusplus
+}    // extern "C"
+#endif
+
+#endif  // WEBP_EXAMPLES_EXAMPLE_UTIL_H_
diff --git a/examples/gif2webp.c b/examples/gif2webp.c
new file mode 100644
index 0000000..cc9b25d
--- /dev/null
+++ b/examples/gif2webp.c
@@ -0,0 +1,609 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  simple tool to convert animated GIFs to WebP
+//
+// Authors: Skal (pascal.massimino@gmail.com)
+//          Urvang (urvang@google.com)
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#ifdef WEBP_HAVE_GIF
+
+#if defined(HAVE_UNISTD_H) && HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <gif_lib.h>
+#include "webp/encode.h"
+#include "webp/mux.h"
+#include "../examples/example_util.h"
+#include "../imageio/imageio_util.h"
+#include "./gifdec.h"
+#include "./unicode.h"
+#include "./unicode_gif.h"
+
+#if !defined(STDIN_FILENO)
+#define STDIN_FILENO 0
+#endif
+
+//------------------------------------------------------------------------------
+
+static int transparent_index = GIF_INDEX_INVALID;  // Opaque by default.
+
+static const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = {
+  "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
+  "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
+};
+
+static const char* ErrorString(WebPMuxError err) {
+  assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
+  return kErrorMessages[-err];
+}
+
+enum {
+  METADATA_ICC  = (1 << 0),
+  METADATA_XMP  = (1 << 1),
+  METADATA_ALL  = METADATA_ICC | METADATA_XMP
+};
+
+//------------------------------------------------------------------------------
+
+static void Help(void) {
+  printf("Usage:\n");
+  printf(" gif2webp [options] gif_file -o webp_file\n");
+  printf("Options:\n");
+  printf("  -h / -help ............. this help\n");
+  printf("  -lossy ................. encode image using lossy compression\n");
+  printf("  -mixed ................. for each frame in the image, pick lossy\n"
+         "                           or lossless compression heuristically\n");
+  printf("  -q <float> ............. quality factor (0:small..100:big)\n");
+  printf("  -m <int> ............... compression method (0=fast, 6=slowest)\n");
+  printf("  -min_size .............. minimize output size (default:off)\n"
+         "                           lossless compression by default; can be\n"
+         "                           combined with -q, -m, -lossy or -mixed\n"
+         "                           options\n");
+  printf("  -kmin <int> ............ min distance between key frames\n");
+  printf("  -kmax <int> ............ max distance between key frames\n");
+  printf("  -f <int> ............... filter strength (0=off..100)\n");
+  printf("  -metadata <string> ..... comma separated list of metadata to\n");
+  printf("                           ");
+  printf("copy from the input to the output if present\n");
+  printf("                           ");
+  printf("Valid values: all, none, icc, xmp (default)\n");
+  printf("  -loop_compatibility .... use compatibility mode for Chrome\n");
+  printf("                           version prior to M62 (inclusive)\n");
+  printf("  -mt .................... use multi-threading if available\n");
+  printf("\n");
+  printf("  -version ............... print version number and exit\n");
+  printf("  -v ..................... verbose\n");
+  printf("  -quiet ................. don't print anything\n");
+  printf("\n");
+}
+
+//------------------------------------------------------------------------------
+
+int main(int argc, const char* argv[]) {
+  int verbose = 0;
+  int gif_error = GIF_ERROR;
+  WebPMuxError err = WEBP_MUX_OK;
+  int ok = 0;
+  const W_CHAR* in_file = NULL, *out_file = NULL;
+  GifFileType* gif = NULL;
+  int frame_duration = 0;
+  int frame_timestamp = 0;
+  GIFDisposeMethod orig_dispose = GIF_DISPOSE_NONE;
+
+  WebPPicture frame;                // Frame rectangle only (not disposed).
+  WebPPicture curr_canvas;          // Not disposed.
+  WebPPicture prev_canvas;          // Disposed.
+
+  WebPAnimEncoder* enc = NULL;
+  WebPAnimEncoderOptions enc_options;
+  WebPConfig config;
+
+  int frame_number = 0;     // Whether we are processing the first frame.
+  int done;
+  int c;
+  int quiet = 0;
+  WebPData webp_data;
+
+  int keep_metadata = METADATA_XMP;  // ICC not output by default.
+  WebPData icc_data;
+  int stored_icc = 0;         // Whether we have already stored an ICC profile.
+  WebPData xmp_data;
+  int stored_xmp = 0;         // Whether we have already stored an XMP profile.
+  int loop_count = 0;         // default: infinite
+  int stored_loop_count = 0;  // Whether we have found an explicit loop count.
+  int loop_compatibility = 0;
+  WebPMux* mux = NULL;
+
+  int default_kmin = 1;  // Whether to use default kmin value.
+  int default_kmax = 1;
+
+  INIT_WARGV(argc, argv);
+
+  if (!WebPConfigInit(&config) || !WebPAnimEncoderOptionsInit(&enc_options) ||
+      !WebPPictureInit(&frame) || !WebPPictureInit(&curr_canvas) ||
+      !WebPPictureInit(&prev_canvas)) {
+    fprintf(stderr, "Error! Version mismatch!\n");
+    FREE_WARGV_AND_RETURN(-1);
+  }
+  config.lossless = 1;  // Use lossless compression by default.
+
+  WebPDataInit(&webp_data);
+  WebPDataInit(&icc_data);
+  WebPDataInit(&xmp_data);
+
+  if (argc == 1) {
+    Help();
+    FREE_WARGV_AND_RETURN(0);
+  }
+
+  for (c = 1; c < argc; ++c) {
+    int parse_error = 0;
+    if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
+      Help();
+      FREE_WARGV_AND_RETURN(0);
+    } else if (!strcmp(argv[c], "-o") && c < argc - 1) {
+      out_file = GET_WARGV(argv, ++c);
+    } else if (!strcmp(argv[c], "-lossy")) {
+      config.lossless = 0;
+    } else if (!strcmp(argv[c], "-mixed")) {
+      enc_options.allow_mixed = 1;
+      config.lossless = 0;
+    } else if (!strcmp(argv[c], "-loop_compatibility")) {
+      loop_compatibility = 1;
+    } else if (!strcmp(argv[c], "-q") && c < argc - 1) {
+      config.quality = ExUtilGetFloat(argv[++c], &parse_error);
+    } else if (!strcmp(argv[c], "-m") && c < argc - 1) {
+      config.method = ExUtilGetInt(argv[++c], 0, &parse_error);
+    } else if (!strcmp(argv[c], "-min_size")) {
+      enc_options.minimize_size = 1;
+    } else if (!strcmp(argv[c], "-kmax") && c < argc - 1) {
+      enc_options.kmax = ExUtilGetInt(argv[++c], 0, &parse_error);
+      default_kmax = 0;
+    } else if (!strcmp(argv[c], "-kmin") && c < argc - 1) {
+      enc_options.kmin = ExUtilGetInt(argv[++c], 0, &parse_error);
+      default_kmin = 0;
+    } else if (!strcmp(argv[c], "-f") && c < argc - 1) {
+      config.filter_strength = ExUtilGetInt(argv[++c], 0, &parse_error);
+    } else if (!strcmp(argv[c], "-metadata") && c < argc - 1) {
+      static const struct {
+        const char* option;
+        int flag;
+      } kTokens[] = {
+        { "all",  METADATA_ALL },
+        { "none", 0 },
+        { "icc",  METADATA_ICC },
+        { "xmp",  METADATA_XMP },
+      };
+      const size_t kNumTokens = sizeof(kTokens) / sizeof(*kTokens);
+      const char* start = argv[++c];
+      const char* const end = start + strlen(start);
+
+      keep_metadata = 0;
+      while (start < end) {
+        size_t i;
+        const char* token = strchr(start, ',');
+        if (token == NULL) token = end;
+
+        for (i = 0; i < kNumTokens; ++i) {
+          if ((size_t)(token - start) == strlen(kTokens[i].option) &&
+              !strncmp(start, kTokens[i].option, strlen(kTokens[i].option))) {
+            if (kTokens[i].flag != 0) {
+              keep_metadata |= kTokens[i].flag;
+            } else {
+              keep_metadata = 0;
+            }
+            break;
+          }
+        }
+        if (i == kNumTokens) {
+          fprintf(stderr, "Error! Unknown metadata type '%.*s'\n",
+                  (int)(token - start), start);
+          Help();
+          FREE_WARGV_AND_RETURN(-1);
+        }
+        start = token + 1;
+      }
+    } else if (!strcmp(argv[c], "-mt")) {
+      ++config.thread_level;
+    } else if (!strcmp(argv[c], "-version")) {
+      const int enc_version = WebPGetEncoderVersion();
+      const int mux_version = WebPGetMuxVersion();
+      printf("WebP Encoder version: %d.%d.%d\nWebP Mux version: %d.%d.%d\n",
+             (enc_version >> 16) & 0xff, (enc_version >> 8) & 0xff,
+             enc_version & 0xff, (mux_version >> 16) & 0xff,
+             (mux_version >> 8) & 0xff, mux_version & 0xff);
+      FREE_WARGV_AND_RETURN(0);
+    } else if (!strcmp(argv[c], "-quiet")) {
+      quiet = 1;
+      enc_options.verbose = 0;
+    } else if (!strcmp(argv[c], "-v")) {
+      verbose = 1;
+      enc_options.verbose = 1;
+    } else if (!strcmp(argv[c], "--")) {
+      if (c < argc - 1) in_file = GET_WARGV(argv, ++c);
+      break;
+    } else if (argv[c][0] == '-') {
+      fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]);
+      Help();
+      FREE_WARGV_AND_RETURN(-1);
+    } else {
+      in_file = GET_WARGV(argv, c);
+    }
+
+    if (parse_error) {
+      Help();
+      FREE_WARGV_AND_RETURN(-1);
+    }
+  }
+
+  // Appropriate default kmin, kmax values for lossy and lossless.
+  if (default_kmin) {
+    enc_options.kmin = config.lossless ? 9 : 3;
+  }
+  if (default_kmax) {
+    enc_options.kmax = config.lossless ? 17 : 5;
+  }
+
+  if (!WebPValidateConfig(&config)) {
+    fprintf(stderr, "Error! Invalid configuration.\n");
+    goto End;
+  }
+
+  if (in_file == NULL) {
+    fprintf(stderr, "No input file specified!\n");
+    Help();
+    goto End;
+  }
+
+  // Start the decoder object
+  gif = DGifOpenFileUnicode(in_file, &gif_error);
+  if (gif == NULL) goto End;
+
+  // Loop over GIF images
+  done = 0;
+  do {
+    GifRecordType type;
+    if (DGifGetRecordType(gif, &type) == GIF_ERROR) goto End;
+
+    switch (type) {
+      case IMAGE_DESC_RECORD_TYPE: {
+        GIFFrameRect gif_rect;
+        GifImageDesc* const image_desc = &gif->Image;
+
+        if (!DGifGetImageDesc(gif)) goto End;
+
+        if (frame_number == 0) {
+          if (verbose) {
+            printf("Canvas screen: %d x %d\n", gif->SWidth, gif->SHeight);
+          }
+          // Fix some broken GIF global headers that report
+          // 0 x 0 screen dimension.
+          if (gif->SWidth == 0 || gif->SHeight == 0) {
+            image_desc->Left = 0;
+            image_desc->Top = 0;
+            gif->SWidth = image_desc->Width;
+            gif->SHeight = image_desc->Height;
+            if (gif->SWidth <= 0 || gif->SHeight <= 0) {
+              goto End;
+            }
+            if (verbose) {
+              printf("Fixed canvas screen dimension to: %d x %d\n",
+                     gif->SWidth, gif->SHeight);
+            }
+          }
+          // Allocate current buffer.
+          frame.width = gif->SWidth;
+          frame.height = gif->SHeight;
+          frame.use_argb = 1;
+          if (!WebPPictureAlloc(&frame)) goto End;
+          GIFClearPic(&frame, NULL);
+          if (!(WebPPictureCopy(&frame, &curr_canvas) &&
+                WebPPictureCopy(&frame, &prev_canvas))) {
+            fprintf(stderr, "Error allocating canvas.\n");
+            goto End;
+          }
+
+          // Background color.
+          GIFGetBackgroundColor(gif->SColorMap, gif->SBackGroundColor,
+                                transparent_index,
+                                &enc_options.anim_params.bgcolor);
+
+          // Initialize encoder.
+          enc = WebPAnimEncoderNew(curr_canvas.width, curr_canvas.height,
+                                   &enc_options);
+          if (enc == NULL) {
+            fprintf(stderr,
+                    "Error! Could not create encoder object. Possibly due to "
+                    "a memory error.\n");
+            goto End;
+          }
+        }
+
+        // Some even more broken GIF can have sub-rect with zero width/height.
+        if (image_desc->Width == 0 || image_desc->Height == 0) {
+          image_desc->Width = gif->SWidth;
+          image_desc->Height = gif->SHeight;
+        }
+
+        if (!GIFReadFrame(gif, transparent_index, &gif_rect, &frame)) {
+          goto End;
+        }
+        // Blend frame rectangle with previous canvas to compose full canvas.
+        // Note that 'curr_canvas' is same as 'prev_canvas' at this point.
+        GIFBlendFrames(&frame, &gif_rect, &curr_canvas);
+
+        if (!WebPAnimEncoderAdd(enc, &curr_canvas, frame_timestamp, &config)) {
+          fprintf(stderr, "Error while adding frame #%d: %s\n", frame_number,
+                  WebPAnimEncoderGetError(enc));
+          goto End;
+        } else {
+          ++frame_number;
+        }
+
+        // Update canvases.
+        GIFDisposeFrame(orig_dispose, &gif_rect, &prev_canvas, &curr_canvas);
+        GIFCopyPixels(&curr_canvas, &prev_canvas);
+
+        // Force frames with a small or no duration to 100ms to be consistent
+        // with web browsers and other transcoding tools. This also avoids
+        // incorrect durations between frames when padding frames are
+        // discarded.
+        if (frame_duration <= 10) {
+          frame_duration = 100;
+        }
+
+        // Update timestamp (for next frame).
+        frame_timestamp += frame_duration;
+
+        // In GIF, graphic control extensions are optional for a frame, so we
+        // may not get one before reading the next frame. To handle this case,
+        // we reset frame properties to reasonable defaults for the next frame.
+        orig_dispose = GIF_DISPOSE_NONE;
+        frame_duration = 0;
+        transparent_index = GIF_INDEX_INVALID;
+        break;
+      }
+      case EXTENSION_RECORD_TYPE: {
+        int extension;
+        GifByteType* data = NULL;
+        if (DGifGetExtension(gif, &extension, &data) == GIF_ERROR) {
+          goto End;
+        }
+        if (data == NULL) continue;
+
+        switch (extension) {
+          case COMMENT_EXT_FUNC_CODE: {
+            break;  // Do nothing for now.
+          }
+          case GRAPHICS_EXT_FUNC_CODE: {
+            if (!GIFReadGraphicsExtension(data, &frame_duration, &orig_dispose,
+                                          &transparent_index)) {
+              goto End;
+            }
+            break;
+          }
+          case PLAINTEXT_EXT_FUNC_CODE: {
+            break;
+          }
+          case APPLICATION_EXT_FUNC_CODE: {
+            if (data[0] != 11) break;    // Chunk is too short
+            if (!memcmp(data + 1, "NETSCAPE2.0", 11) ||
+                !memcmp(data + 1, "ANIMEXTS1.0", 11)) {
+              if (!GIFReadLoopCount(gif, &data, &loop_count)) {
+                goto End;
+              }
+              if (verbose) {
+                fprintf(stderr, "Loop count: %d\n", loop_count);
+              }
+              stored_loop_count = loop_compatibility ? (loop_count != 0) : 1;
+            } else {  // An extension containing metadata.
+              // We only store the first encountered chunk of each type, and
+              // only if requested by the user.
+              const int is_xmp = (keep_metadata & METADATA_XMP) &&
+                                 !stored_xmp &&
+                                 !memcmp(data + 1, "XMP DataXMP", 11);
+              const int is_icc = (keep_metadata & METADATA_ICC) &&
+                                 !stored_icc &&
+                                 !memcmp(data + 1, "ICCRGBG1012", 11);
+              if (is_xmp || is_icc) {
+                if (!GIFReadMetadata(gif, &data,
+                                     is_xmp ? &xmp_data : &icc_data)) {
+                  goto End;
+                }
+                if (is_icc) {
+                  stored_icc = 1;
+                } else if (is_xmp) {
+                  stored_xmp = 1;
+                }
+              }
+            }
+            break;
+          }
+          default: {
+            break;  // skip
+          }
+        }
+        while (data != NULL) {
+          if (DGifGetExtensionNext(gif, &data) == GIF_ERROR) goto End;
+        }
+        break;
+      }
+      case TERMINATE_RECORD_TYPE: {
+        done = 1;
+        break;
+      }
+      default: {
+        if (verbose) {
+          fprintf(stderr, "Skipping over unknown record type %d\n", type);
+        }
+        break;
+      }
+    }
+  } while (!done);
+
+  // Last NULL frame.
+  if (!WebPAnimEncoderAdd(enc, NULL, frame_timestamp, NULL)) {
+    fprintf(stderr, "Error flushing WebP muxer.\n");
+    fprintf(stderr, "%s\n", WebPAnimEncoderGetError(enc));
+  }
+
+  if (!WebPAnimEncoderAssemble(enc, &webp_data)) {
+    fprintf(stderr, "%s\n", WebPAnimEncoderGetError(enc));
+    goto End;
+  }
+  // If there's only one frame, we don't need to handle loop count.
+  if (frame_number == 1) {
+    loop_count = 0;
+  } else if (!loop_compatibility) {
+    if (!stored_loop_count) {
+      // if no loop-count element is seen, the default is '1' (loop-once)
+      // and we need to signal it explicitly in WebP. Note however that
+      // in case there's a single frame, we still don't need to store it.
+      if (frame_number > 1) {
+        stored_loop_count = 1;
+        loop_count = 1;
+      }
+    } else if (loop_count > 0 && loop_count < 65535) {
+      // adapt GIF's semantic to WebP's (except in the infinite-loop case)
+      loop_count += 1;
+    }
+  }
+  // loop_count of 0 is the default (infinite), so no need to signal it
+  if (loop_count == 0) stored_loop_count = 0;
+
+  if (stored_loop_count || stored_icc || stored_xmp) {
+    // Re-mux to add loop count and/or metadata as needed.
+    mux = WebPMuxCreate(&webp_data, 1);
+    if (mux == NULL) {
+      fprintf(stderr, "ERROR: Could not re-mux to add loop count/metadata.\n");
+      goto End;
+    }
+    WebPDataClear(&webp_data);
+
+    if (stored_loop_count) {  // Update loop count.
+      WebPMuxAnimParams new_params;
+      err = WebPMuxGetAnimationParams(mux, &new_params);
+      if (err != WEBP_MUX_OK) {
+        fprintf(stderr, "ERROR (%s): Could not fetch loop count.\n",
+                ErrorString(err));
+        goto End;
+      }
+      new_params.loop_count = loop_count;
+      err = WebPMuxSetAnimationParams(mux, &new_params);
+      if (err != WEBP_MUX_OK) {
+        fprintf(stderr, "ERROR (%s): Could not update loop count.\n",
+                ErrorString(err));
+        goto End;
+      }
+    }
+
+    if (stored_icc) {   // Add ICCP chunk.
+      err = WebPMuxSetChunk(mux, "ICCP", &icc_data, 1);
+      if (verbose) {
+        fprintf(stderr, "ICC size: %d\n", (int)icc_data.size);
+      }
+      if (err != WEBP_MUX_OK) {
+        fprintf(stderr, "ERROR (%s): Could not set ICC chunk.\n",
+                ErrorString(err));
+        goto End;
+      }
+    }
+
+    if (stored_xmp) {   // Add XMP chunk.
+      err = WebPMuxSetChunk(mux, "XMP ", &xmp_data, 1);
+      if (verbose) {
+        fprintf(stderr, "XMP size: %d\n", (int)xmp_data.size);
+      }
+      if (err != WEBP_MUX_OK) {
+        fprintf(stderr, "ERROR (%s): Could not set XMP chunk.\n",
+                ErrorString(err));
+        goto End;
+      }
+    }
+
+    err = WebPMuxAssemble(mux, &webp_data);
+    if (err != WEBP_MUX_OK) {
+      fprintf(stderr, "ERROR (%s): Could not assemble when re-muxing to add "
+              "loop count/metadata.\n", ErrorString(err));
+      goto End;
+    }
+  }
+
+  if (out_file != NULL) {
+    if (!ImgIoUtilWriteFile((const char*)out_file, webp_data.bytes,
+                            webp_data.size)) {
+      WFPRINTF(stderr, "Error writing output file: %s\n", out_file);
+      goto End;
+    }
+    if (!quiet) {
+      if (!WSTRCMP(out_file, "-")) {
+        fprintf(stderr, "Saved %d bytes to STDIO\n",
+                (int)webp_data.size);
+      } else {
+        WFPRINTF(stderr, "Saved output file (%d bytes): %s\n",
+                 (int)webp_data.size, out_file);
+      }
+    }
+  } else {
+    if (!quiet) {
+      fprintf(stderr, "Nothing written; use -o flag to save the result "
+                      "(%d bytes).\n", (int)webp_data.size);
+    }
+  }
+
+  // All OK.
+  ok = 1;
+  gif_error = GIF_OK;
+
+ End:
+  WebPDataClear(&icc_data);
+  WebPDataClear(&xmp_data);
+  WebPMuxDelete(mux);
+  WebPDataClear(&webp_data);
+  WebPPictureFree(&frame);
+  WebPPictureFree(&curr_canvas);
+  WebPPictureFree(&prev_canvas);
+  WebPAnimEncoderDelete(enc);
+
+  if (gif_error != GIF_OK) {
+    GIFDisplayError(gif, gif_error);
+  }
+  if (gif != NULL) {
+#if LOCAL_GIF_PREREQ(5,1)
+    DGifCloseFile(gif, &gif_error);
+#else
+    DGifCloseFile(gif);
+#endif
+  }
+
+  FREE_WARGV_AND_RETURN(!ok);
+}
+
+#else  // !WEBP_HAVE_GIF
+
+int main(int argc, const char* argv[]) {
+  fprintf(stderr, "GIF support not enabled in %s.\n", argv[0]);
+  (void)argc;
+  return 0;
+}
+
+#endif
+
+//------------------------------------------------------------------------------
diff --git a/examples/gifdec.c b/examples/gifdec.c
new file mode 100644
index 0000000..99ee996
--- /dev/null
+++ b/examples/gifdec.c
@@ -0,0 +1,416 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// GIF decode.
+
+#include "./gifdec.h"
+
+#include <stdio.h>
+
+#ifdef WEBP_HAVE_GIF
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "webp/encode.h"
+#include "webp/mux_types.h"
+
+#define GIF_TRANSPARENT_COLOR 0x00000000u
+#define GIF_WHITE_COLOR       0xffffffffu
+#define GIF_TRANSPARENT_MASK  0x01
+#define GIF_DISPOSE_MASK      0x07
+#define GIF_DISPOSE_SHIFT     2
+
+// from utils/utils.h
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern void WebPCopyPlane(const uint8_t* src, int src_stride,
+                          uint8_t* dst, int dst_stride,
+                          int width, int height);
+extern void WebPCopyPixels(const WebPPicture* const src,
+                           WebPPicture* const dst);
+#ifdef __cplusplus
+}
+#endif
+
+void GIFGetBackgroundColor(const ColorMapObject* const color_map,
+                           int bgcolor_index, int transparent_index,
+                           uint32_t* const bgcolor) {
+  if (transparent_index != GIF_INDEX_INVALID &&
+      bgcolor_index == transparent_index) {
+    *bgcolor = GIF_TRANSPARENT_COLOR;  // Special case.
+  } else if (color_map == NULL || color_map->Colors == NULL
+             || bgcolor_index >= color_map->ColorCount) {
+    *bgcolor = GIF_WHITE_COLOR;
+    fprintf(stderr,
+            "GIF decode warning: invalid background color index. Assuming "
+            "white background.\n");
+  } else {
+    const GifColorType color = color_map->Colors[bgcolor_index];
+    *bgcolor = (0xffu       << 24)
+             | (color.Red   << 16)
+             | (color.Green <<  8)
+             | (color.Blue  <<  0);
+  }
+}
+
+int GIFReadGraphicsExtension(const GifByteType* const buf, int* const duration,
+                             GIFDisposeMethod* const dispose,
+                             int* const transparent_index) {
+  const int flags = buf[1];
+  const int dispose_raw = (flags >> GIF_DISPOSE_SHIFT) & GIF_DISPOSE_MASK;
+  const int duration_raw = buf[2] | (buf[3] << 8);  // In 10 ms units.
+  if (buf[0] != 4) return 0;
+  *duration = duration_raw * 10;  // Duration is in 1 ms units.
+  switch (dispose_raw) {
+    case 3:
+      *dispose = GIF_DISPOSE_RESTORE_PREVIOUS;
+      break;
+    case 2:
+      *dispose = GIF_DISPOSE_BACKGROUND;
+      break;
+    case 1:
+    case 0:
+    default:
+      *dispose = GIF_DISPOSE_NONE;
+      break;
+  }
+  *transparent_index =
+      (flags & GIF_TRANSPARENT_MASK) ? buf[4] : GIF_INDEX_INVALID;
+  return 1;
+}
+
+static int Remap(const GifFileType* const gif, const uint8_t* const src,
+                 int len, int transparent_index, uint32_t* dst) {
+  int i;
+  const GifColorType* colors;
+  const ColorMapObject* const cmap =
+      gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap;
+  if (cmap == NULL) return 1;
+  if (cmap->Colors == NULL || cmap->ColorCount <= 0) return 0;
+  colors = cmap->Colors;
+
+  for (i = 0; i < len; ++i) {
+    if (src[i] == transparent_index) {
+      dst[i] = GIF_TRANSPARENT_COLOR;
+    } else if (src[i] < cmap->ColorCount) {
+      const GifColorType c = colors[src[i]];
+      dst[i] = c.Blue | (c.Green << 8) | (c.Red << 16) | (0xffu << 24);
+    } else {
+      return 0;
+    }
+  }
+  return 1;
+}
+
+int GIFReadFrame(GifFileType* const gif, int transparent_index,
+                 GIFFrameRect* const gif_rect, WebPPicture* const picture) {
+  WebPPicture sub_image;
+  const GifImageDesc* const image_desc = &gif->Image;
+  uint32_t* dst = NULL;
+  uint8_t* tmp = NULL;
+  const GIFFrameRect rect = {
+      image_desc->Left, image_desc->Top, image_desc->Width, image_desc->Height
+  };
+  const uint64_t memory_needed = 4 * rect.width * (uint64_t)rect.height;
+  int ok = 0;
+  *gif_rect = rect;
+
+  if (memory_needed != (size_t)memory_needed || memory_needed > (4ULL << 32)) {
+    fprintf(stderr, "Image is too large (%d x %d).", rect.width, rect.height);
+    return 0;
+  }
+
+  // Use a view for the sub-picture:
+  if (!WebPPictureView(picture, rect.x_offset, rect.y_offset,
+                       rect.width, rect.height, &sub_image)) {
+    fprintf(stderr, "Sub-image %dx%d at position %d,%d is invalid!\n",
+            rect.width, rect.height, rect.x_offset, rect.y_offset);
+    return 0;
+  }
+  dst = sub_image.argb;
+
+  tmp = (uint8_t*)WebPMalloc(rect.width * sizeof(*tmp));
+  if (tmp == NULL) goto End;
+
+  if (image_desc->Interlace) {  // Interlaced image.
+    // We need 4 passes, with the following offsets and jumps.
+    const int interlace_offsets[] = { 0, 4, 2, 1 };
+    const int interlace_jumps[]   = { 8, 8, 4, 2 };
+    int pass;
+    for (pass = 0; pass < 4; ++pass) {
+      const size_t stride = (size_t)sub_image.argb_stride;
+      int y = interlace_offsets[pass];
+      uint32_t* row = dst + y * stride;
+      const size_t jump = interlace_jumps[pass] * stride;
+      for (; y < rect.height; y += interlace_jumps[pass], row += jump) {
+        if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End;
+        if (!Remap(gif, tmp, rect.width, transparent_index, row)) goto End;
+      }
+    }
+  } else {  // Non-interlaced image.
+    int y;
+    uint32_t* ptr = dst;
+    for (y = 0; y < rect.height; ++y, ptr += sub_image.argb_stride) {
+      if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End;
+      if (!Remap(gif, tmp, rect.width, transparent_index, ptr)) goto End;
+    }
+  }
+  ok = 1;
+
+ End:
+  if (!ok) picture->error_code = sub_image.error_code;
+  WebPPictureFree(&sub_image);
+  WebPFree(tmp);
+  return ok;
+}
+
+int GIFReadLoopCount(GifFileType* const gif, GifByteType** const buf,
+                     int* const loop_count) {
+  assert(!memcmp(*buf + 1, "NETSCAPE2.0", 11) ||
+         !memcmp(*buf + 1, "ANIMEXTS1.0", 11));
+  if (DGifGetExtensionNext(gif, buf) == GIF_ERROR) {
+    return 0;
+  }
+  if (*buf == NULL) {
+    return 0;  // Loop count sub-block missing.
+  }
+  if ((*buf)[0] < 3 || (*buf)[1] != 1) {
+    return 0;   // wrong size/marker
+  }
+  *loop_count = (*buf)[2] | ((*buf)[3] << 8);
+  return 1;
+}
+
+int GIFReadMetadata(GifFileType* const gif, GifByteType** const buf,
+                    WebPData* const metadata) {
+  const int is_xmp = !memcmp(*buf + 1, "XMP DataXMP", 11);
+  const int is_icc = !memcmp(*buf + 1, "ICCRGBG1012", 11);
+  assert(is_xmp || is_icc);
+  (void)is_icc;  // silence unused warning.
+  // Construct metadata from sub-blocks.
+  // Usual case (including ICC profile): In each sub-block, the
+  // first byte specifies its size in bytes (0 to 255) and the
+  // rest of the bytes contain the data.
+  // Special case for XMP data: In each sub-block, the first byte
+  // is also part of the XMP payload. XMP in GIF also has a 257
+  // byte padding data. See the XMP specification for details.
+  while (1) {
+    WebPData subblock;
+    const uint8_t* tmp;
+    if (DGifGetExtensionNext(gif, buf) == GIF_ERROR) {
+      return 0;
+    }
+    if (*buf == NULL) break;  // Finished.
+    subblock.size = is_xmp ? (*buf)[0] + 1 : (*buf)[0];
+    assert(subblock.size > 0);
+    subblock.bytes = is_xmp ? *buf : *buf + 1;
+    // Note: We store returned value in 'tmp' first, to avoid
+    // leaking old memory in metadata->bytes on error.
+    tmp = (uint8_t*)realloc((void*)metadata->bytes,
+                            metadata->size + subblock.size);
+    if (tmp == NULL) {
+      return 0;
+    }
+    memcpy((void*)(tmp + metadata->size),
+           subblock.bytes, subblock.size);
+    metadata->bytes = tmp;
+    metadata->size += subblock.size;
+  }
+  if (is_xmp) {
+    // XMP padding data is 0x01, 0xff, 0xfe ... 0x01, 0x00.
+    const size_t xmp_pading_size = 257;
+    if (metadata->size > xmp_pading_size) {
+      metadata->size -= xmp_pading_size;
+    }
+  }
+  return 1;
+}
+
+static void ClearRectangle(WebPPicture* const picture,
+                           int left, int top, int width, int height) {
+  int i, j;
+  const size_t stride = picture->argb_stride;
+  uint32_t* dst = picture->argb + top * stride + left;
+  for (j = 0; j < height; ++j, dst += stride) {
+    for (i = 0; i < width; ++i) dst[i] = GIF_TRANSPARENT_COLOR;
+  }
+}
+
+void GIFClearPic(WebPPicture* const pic, const GIFFrameRect* const rect) {
+  if (rect != NULL) {
+    ClearRectangle(pic, rect->x_offset, rect->y_offset,
+                   rect->width, rect->height);
+  } else {
+    ClearRectangle(pic, 0, 0, pic->width, pic->height);
+  }
+}
+
+void GIFCopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
+  WebPCopyPixels(src, dst);
+}
+
+void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect,
+                     const WebPPicture* const prev_canvas,
+                     WebPPicture* const curr_canvas) {
+  assert(rect != NULL);
+  if (dispose == GIF_DISPOSE_BACKGROUND) {
+    GIFClearPic(curr_canvas, rect);
+  } else if (dispose == GIF_DISPOSE_RESTORE_PREVIOUS) {
+    const size_t src_stride = prev_canvas->argb_stride;
+    const uint32_t* const src = prev_canvas->argb + rect->x_offset
+                              + rect->y_offset * src_stride;
+    const size_t dst_stride = curr_canvas->argb_stride;
+    uint32_t* const dst = curr_canvas->argb + rect->x_offset
+                        + rect->y_offset * dst_stride;
+    assert(prev_canvas != NULL);
+    WebPCopyPlane((uint8_t*)src, (int)(4 * src_stride),
+                  (uint8_t*)dst, (int)(4 * dst_stride),
+                  4 * rect->width, rect->height);
+  }
+}
+
+void GIFBlendFrames(const WebPPicture* const src,
+                    const GIFFrameRect* const rect, WebPPicture* const dst) {
+  int i, j;
+  const size_t src_stride = src->argb_stride;
+  const size_t dst_stride = dst->argb_stride;
+  assert(src->width == dst->width && src->height == dst->height);
+  for (j = rect->y_offset; j < rect->y_offset + rect->height; ++j) {
+    for (i = rect->x_offset; i < rect->x_offset + rect->width; ++i) {
+      const uint32_t src_pixel = src->argb[j * src_stride + i];
+      const int src_alpha = src_pixel >> 24;
+      if (src_alpha != 0) {
+        dst->argb[j * dst_stride + i] = src_pixel;
+      }
+    }
+  }
+}
+
+void GIFDisplayError(const GifFileType* const gif, int gif_error) {
+  // libgif 4.2.0 has retired PrintGifError() and added GifErrorString().
+#if LOCAL_GIF_PREREQ(4,2)
+#if LOCAL_GIF_PREREQ(5,0)
+  // Static string actually, hence the const char* cast.
+  const char* error_str = (const char*)GifErrorString(
+      (gif == NULL) ? gif_error : gif->Error);
+#else
+  const char* error_str = (const char*)GifErrorString();
+  (void)gif;
+#endif
+  if (error_str == NULL) error_str = "Unknown error";
+  fprintf(stderr, "GIFLib Error %d: %s\n", gif_error, error_str);
+#else
+  (void)gif;
+  fprintf(stderr, "GIFLib Error %d: ", gif_error);
+  PrintGifError();
+  fprintf(stderr, "\n");
+#endif
+}
+
+#else  // !WEBP_HAVE_GIF
+
+static void ErrorGIFNotAvailable() {
+  fprintf(stderr, "GIF support not compiled. Please install the libgif-dev "
+          "package before building.\n");
+}
+
+void GIFGetBackgroundColor(const struct ColorMapObject* const color_map,
+                           int bgcolor_index, int transparent_index,
+                           uint32_t* const bgcolor) {
+  (void)color_map;
+  (void)bgcolor_index;
+  (void)transparent_index;
+  (void)bgcolor;
+  ErrorGIFNotAvailable();
+}
+
+int GIFReadGraphicsExtension(const GifByteType* const data, int* const duration,
+                             GIFDisposeMethod* const dispose,
+                             int* const transparent_index) {
+  (void)data;
+  (void)duration;
+  (void)dispose;
+  (void)transparent_index;
+  ErrorGIFNotAvailable();
+  return 0;
+}
+
+int GIFReadFrame(struct GifFileType* const gif, int transparent_index,
+                 GIFFrameRect* const gif_rect,
+                 struct WebPPicture* const picture) {
+  (void)gif;
+  (void)transparent_index;
+  (void)gif_rect;
+  (void)picture;
+  ErrorGIFNotAvailable();
+  return 0;
+}
+
+int GIFReadLoopCount(struct GifFileType* const gif, GifByteType** const buf,
+                     int* const loop_count) {
+  (void)gif;
+  (void)buf;
+  (void)loop_count;
+  ErrorGIFNotAvailable();
+  return 0;
+}
+
+int GIFReadMetadata(struct GifFileType* const gif, GifByteType** const buf,
+                    struct WebPData* const metadata) {
+  (void)gif;
+  (void)buf;
+  (void)metadata;
+  ErrorGIFNotAvailable();
+  return 0;
+}
+
+void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect,
+                     const struct WebPPicture* const prev_canvas,
+                     struct WebPPicture* const curr_canvas) {
+  (void)dispose;
+  (void)rect;
+  (void)prev_canvas;
+  (void)curr_canvas;
+  ErrorGIFNotAvailable();
+}
+
+void GIFBlendFrames(const struct WebPPicture* const src,
+                    const GIFFrameRect* const rect,
+                    struct WebPPicture* const dst) {
+  (void)src;
+  (void)rect;
+  (void)dst;
+  ErrorGIFNotAvailable();
+}
+
+void GIFDisplayError(const struct GifFileType* const gif, int gif_error) {
+  (void)gif;
+  (void)gif_error;
+  ErrorGIFNotAvailable();
+}
+
+void GIFClearPic(struct WebPPicture* const pic,
+                 const GIFFrameRect* const rect) {
+  (void)pic;
+  (void)rect;
+  ErrorGIFNotAvailable();
+}
+
+void GIFCopyPixels(const struct WebPPicture* const src,
+                   struct WebPPicture* const dst) {
+  (void)src;
+  (void)dst;
+  ErrorGIFNotAvailable();
+}
+
+#endif  // WEBP_HAVE_GIF
+
+// -----------------------------------------------------------------------------
diff --git a/examples/gifdec.h b/examples/gifdec.h
new file mode 100644
index 0000000..5eba9dd
--- /dev/null
+++ b/examples/gifdec.h
@@ -0,0 +1,116 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// GIF decode.
+
+#ifndef WEBP_EXAMPLES_GIFDEC_H_
+#define WEBP_EXAMPLES_GIFDEC_H_
+
+#include <stdio.h>
+#include "webp/types.h"
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#ifdef WEBP_HAVE_GIF
+#include <gif_lib.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// GIFLIB_MAJOR is only defined in libgif >= 4.2.0.
+#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR)
+# define LOCAL_GIF_VERSION ((GIFLIB_MAJOR << 8) | GIFLIB_MINOR)
+# define LOCAL_GIF_PREREQ(maj, min) \
+    (LOCAL_GIF_VERSION >= (((maj) << 8) | (min)))
+#else
+# define LOCAL_GIF_VERSION 0
+# define LOCAL_GIF_PREREQ(maj, min) 0
+#endif
+
+#define GIF_INDEX_INVALID (-1)
+
+typedef enum GIFDisposeMethod {
+  GIF_DISPOSE_NONE,
+  GIF_DISPOSE_BACKGROUND,
+  GIF_DISPOSE_RESTORE_PREVIOUS
+} GIFDisposeMethod;
+
+typedef struct {
+  int x_offset, y_offset, width, height;
+} GIFFrameRect;
+
+struct WebPData;
+struct WebPPicture;
+
+#ifndef WEBP_HAVE_GIF
+struct ColorMapObject;
+struct GifFileType;
+typedef unsigned char GifByteType;
+#endif
+
+// Given the index of background color and transparent color, returns the
+// corresponding background color (in BGRA format) in 'bgcolor'.
+void GIFGetBackgroundColor(const struct ColorMapObject* const color_map,
+                           int bgcolor_index, int transparent_index,
+                           uint32_t* const bgcolor);
+
+// Parses the given graphics extension data to get frame duration (in 1ms
+// units), dispose method and transparent color index.
+// Returns true on success.
+int GIFReadGraphicsExtension(const GifByteType* const buf, int* const duration,
+                             GIFDisposeMethod* const dispose,
+                             int* const transparent_index);
+
+// Reads the next GIF frame from 'gif' into 'picture'. Also, returns the GIF
+// frame dimensions and offsets in 'rect'.
+// Returns true on success.
+int GIFReadFrame(struct GifFileType* const gif, int transparent_index,
+                 GIFFrameRect* const gif_rect,
+                 struct WebPPicture* const picture);
+
+// Parses loop count from the given Netscape extension data.
+int GIFReadLoopCount(struct GifFileType* const gif, GifByteType** const buf,
+                     int* const loop_count);
+
+// Parses the given ICC or XMP extension data and stores it into 'metadata'.
+// Returns true on success.
+int GIFReadMetadata(struct GifFileType* const gif, GifByteType** const buf,
+                    struct WebPData* const metadata);
+
+// Dispose the pixels within 'rect' of 'curr_canvas' based on 'dispose' method
+// and 'prev_canvas'.
+void GIFDisposeFrame(GIFDisposeMethod dispose, const GIFFrameRect* const rect,
+                     const struct WebPPicture* const prev_canvas,
+                     struct WebPPicture* const curr_canvas);
+
+// Given 'src' picture and its frame rectangle 'rect', blend it into 'dst'.
+void GIFBlendFrames(const struct WebPPicture* const src,
+                    const GIFFrameRect* const rect,
+                    struct WebPPicture* const dst);
+
+// Prints an error string based on 'gif_error'.
+void GIFDisplayError(const struct GifFileType* const gif, int gif_error);
+
+// In the given 'pic', clear the pixels in 'rect' to transparent color.
+void GIFClearPic(struct WebPPicture* const pic, const GIFFrameRect* const rect);
+
+// Copy pixels from 'src' to 'dst' honoring strides. 'src' and 'dst' are assumed
+// to be already allocated.
+void GIFCopyPixels(const struct WebPPicture* const src,
+                   struct WebPPicture* const dst);
+
+#ifdef __cplusplus
+}    // extern "C"
+#endif
+
+#endif  // WEBP_EXAMPLES_GIFDEC_H_
diff --git a/examples/img2webp.c b/examples/img2webp.c
new file mode 100644
index 0000000..bfb1bfc
--- /dev/null
+++ b/examples/img2webp.c
@@ -0,0 +1,325 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  generate an animated WebP out of a sequence of images
+//  (PNG, JPEG, ...)
+//
+//  Example usage:
+//     img2webp -o out.webp -q 40 -mixed -duration 40 input??.png
+//
+// Author: skal@google.com (Pascal Massimino)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include "../examples/example_util.h"
+#include "../imageio/image_dec.h"
+#include "../imageio/imageio_util.h"
+#include "./stopwatch.h"
+#include "./unicode.h"
+#include "webp/encode.h"
+#include "webp/mux.h"
+
+//------------------------------------------------------------------------------
+
+static void Help(void) {
+  printf("Usage:\n\n");
+  printf("  img2webp [file_options] [[frame_options] frame_file]...\n");
+  printf("\n");
+
+  printf("File-level options (only used at the start of compression):\n");
+  printf(" -min_size ............ minimize size\n");
+  printf(" -loop <int> .......... loop count (default: 0, = infinite loop)\n");
+  printf(" -kmax <int> .......... maximum number of frame between key-frames\n"
+         "                        (0=only keyframes)\n");
+  printf(" -kmin <int> .......... minimum number of frame between key-frames\n"
+         "                        (0=disable key-frames altogether)\n");
+  printf(" -mixed ............... use mixed lossy/lossless automatic mode\n");
+  printf(" -v ................... verbose mode\n");
+  printf(" -h ................... this help\n");
+  printf(" -version ............. print version number and exit\n");
+  printf("\n");
+
+  printf("Per-frame options (only used for subsequent images input):\n");
+  printf(" -d <int> ............. frame duration in ms (default: 100)\n");
+  printf(" -lossless  ........... use lossless mode (default)\n");
+  printf(" -lossy ... ........... use lossy mode\n");
+  printf(" -q <float> ........... quality\n");
+  printf(" -m <int> ............. method to use\n");
+
+  printf("\n");
+  printf("example: img2webp -loop 2 in0.png -lossy in1.jpg\n"
+         "                  -d 80 in2.tiff -o out.webp\n");
+  printf("\nNote: if a single file name is passed as the argument, the "
+         "arguments will be\n");
+  printf("tokenized from this file. The file name must not start with "
+         "the character '-'.\n");
+  printf("\nSupported input formats:\n  %s\n",
+         WebPGetEnabledInputFileFormats());
+}
+
+//------------------------------------------------------------------------------
+
+static int ReadImage(const char filename[], WebPPicture* const pic) {
+  const uint8_t* data = NULL;
+  size_t data_size = 0;
+  WebPImageReader reader;
+  int ok;
+#ifdef HAVE_WINCODEC_H
+  // Try to decode the file using WIC falling back to the other readers for
+  // e.g., WebP.
+  ok = ReadPictureWithWIC(filename, pic, 1, NULL);
+  if (ok) return 1;
+#endif
+  if (!ImgIoUtilReadFile(filename, &data, &data_size)) return 0;
+  reader = WebPGuessImageReader(data, data_size);
+  ok = reader(data, data_size, pic, 1, NULL);
+  WebPFree((void*)data);
+  return ok;
+}
+
+static int SetLoopCount(int loop_count, WebPData* const webp_data) {
+  int ok = 1;
+  WebPMuxError err;
+  uint32_t features;
+  WebPMuxAnimParams new_params;
+  WebPMux* const mux = WebPMuxCreate(webp_data, 1);
+  if (mux == NULL) return 0;
+
+  err = WebPMuxGetFeatures(mux, &features);
+  ok = (err == WEBP_MUX_OK);
+  if (!ok || !(features & ANIMATION_FLAG)) goto End;
+
+  err = WebPMuxGetAnimationParams(mux, &new_params);
+  ok = (err == WEBP_MUX_OK);
+  if (ok) {
+    new_params.loop_count = loop_count;
+    err = WebPMuxSetAnimationParams(mux, &new_params);
+    ok = (err == WEBP_MUX_OK);
+  }
+  if (ok) {
+    WebPDataClear(webp_data);
+    err = WebPMuxAssemble(mux, webp_data);
+    ok = (err == WEBP_MUX_OK);
+  }
+
+ End:
+  WebPMuxDelete(mux);
+  if (!ok) {
+    fprintf(stderr, "Error during loop-count setting\n");
+  }
+  return ok;
+}
+
+//------------------------------------------------------------------------------
+
+int main(int argc, const char* argv[]) {
+  const char* output = NULL;
+  WebPAnimEncoder* enc = NULL;
+  int verbose = 0;
+  int pic_num = 0;
+  int duration = 100;
+  int timestamp_ms = 0;
+  int loop_count = 0;
+  int width = 0, height = 0;
+  WebPAnimEncoderOptions anim_config;
+  WebPConfig config;
+  WebPPicture pic;
+  WebPData webp_data;
+  int c;
+  int have_input = 0;
+  CommandLineArguments cmd_args;
+  int ok;
+
+  INIT_WARGV(argc, argv);
+
+  ok = ExUtilInitCommandLineArguments(argc - 1, argv + 1, &cmd_args);
+  if (!ok) FREE_WARGV_AND_RETURN(1);
+
+  argc = cmd_args.argc_;
+  argv = cmd_args.argv_;
+
+  WebPDataInit(&webp_data);
+  if (!WebPAnimEncoderOptionsInit(&anim_config) ||
+      !WebPConfigInit(&config) ||
+      !WebPPictureInit(&pic)) {
+    fprintf(stderr, "Library version mismatch!\n");
+    ok = 0;
+    goto End;
+  }
+
+  // 1st pass of option parsing
+  for (c = 0; ok && c < argc; ++c) {
+    if (argv[c][0] == '-') {
+      int parse_error = 0;
+      if (!strcmp(argv[c], "-o") && c + 1 < argc) {
+        argv[c] = NULL;
+        output = (const char*)GET_WARGV_SHIFTED(argv, ++c);
+      } else if (!strcmp(argv[c], "-kmin") && c + 1 < argc) {
+        argv[c] = NULL;
+        anim_config.kmin = ExUtilGetInt(argv[++c], 0, &parse_error);
+      } else if (!strcmp(argv[c], "-kmax") && c + 1 < argc) {
+        argv[c] = NULL;
+        anim_config.kmax = ExUtilGetInt(argv[++c], 0, &parse_error);
+      } else if (!strcmp(argv[c], "-loop") && c + 1 < argc) {
+        argv[c] = NULL;
+        loop_count = ExUtilGetInt(argv[++c], 0, &parse_error);
+        if (loop_count < 0) {
+          fprintf(stderr, "Invalid non-positive loop-count (%d)\n", loop_count);
+          parse_error = 1;
+        }
+      } else if (!strcmp(argv[c], "-min_size")) {
+        anim_config.minimize_size = 1;
+      } else if (!strcmp(argv[c], "-mixed")) {
+        anim_config.allow_mixed = 1;
+        config.lossless = 0;
+      } else if (!strcmp(argv[c], "-v")) {
+        verbose = 1;
+      } else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
+        Help();
+        FREE_WARGV_AND_RETURN(0);
+      } else if (!strcmp(argv[c], "-version")) {
+        const int enc_version = WebPGetEncoderVersion();
+        const int mux_version = WebPGetMuxVersion();
+        printf("WebP Encoder version: %d.%d.%d\nWebP Mux version: %d.%d.%d\n",
+               (enc_version >> 16) & 0xff, (enc_version >> 8) & 0xff,
+               enc_version & 0xff, (mux_version >> 16) & 0xff,
+               (mux_version >> 8) & 0xff, mux_version & 0xff);
+        goto End;
+      } else {
+        continue;
+      }
+      ok = !parse_error;
+      if (!ok) goto End;
+      argv[c] = NULL;   // mark option as 'parsed' during 1st pass
+    } else {
+      have_input |= 1;
+    }
+  }
+  if (!have_input) {
+    fprintf(stderr, "No input file(s) for generating animation!\n");
+    goto End;
+  }
+
+  // image-reading pass
+  pic_num = 0;
+  config.lossless = 1;
+  for (c = 0; ok && c < argc; ++c) {
+    if (argv[c] == NULL) continue;
+    if (argv[c][0] == '-') {    // parse local options
+      int parse_error = 0;
+      if (!strcmp(argv[c], "-lossy")) {
+        if (!anim_config.allow_mixed) config.lossless = 0;
+      } else if (!strcmp(argv[c], "-lossless")) {
+        if (!anim_config.allow_mixed) config.lossless = 1;
+      } else if (!strcmp(argv[c], "-q") && c + 1 < argc) {
+        config.quality = ExUtilGetFloat(argv[++c], &parse_error);
+      } else if (!strcmp(argv[c], "-m") && c + 1 < argc) {
+        config.method = ExUtilGetInt(argv[++c], 0, &parse_error);
+      } else if (!strcmp(argv[c], "-d") && c + 1 < argc) {
+        duration = ExUtilGetInt(argv[++c], 0, &parse_error);
+        if (duration <= 0) {
+          fprintf(stderr, "Invalid negative duration (%d)\n", duration);
+          parse_error = 1;
+        }
+      } else {
+        parse_error = 1;   // shouldn't be here.
+        fprintf(stderr, "Unknown option [%s]\n", argv[c]);
+      }
+      ok = !parse_error;
+      if (!ok) goto End;
+      continue;
+    }
+
+    if (ok) {
+      ok = WebPValidateConfig(&config);
+      if (!ok) {
+        fprintf(stderr, "Invalid configuration.\n");
+        goto End;
+      }
+    }
+
+    // read next input image
+    pic.use_argb = 1;
+    ok = ReadImage((const char*)GET_WARGV_SHIFTED(argv, c), &pic);
+    if (!ok) goto End;
+
+    if (enc == NULL) {
+      width  = pic.width;
+      height = pic.height;
+      enc = WebPAnimEncoderNew(width, height, &anim_config);
+      ok = (enc != NULL);
+      if (!ok) {
+        fprintf(stderr, "Could not create WebPAnimEncoder object.\n");
+      }
+    }
+
+    if (ok) {
+      ok = (width == pic.width && height == pic.height);
+      if (!ok) {
+        fprintf(stderr, "Frame #%d dimension mismatched! "
+                        "Got %d x %d. Was expecting %d x %d.\n",
+                pic_num, pic.width, pic.height, width, height);
+      }
+    }
+
+    if (ok) {
+      ok = WebPAnimEncoderAdd(enc, &pic, timestamp_ms, &config);
+      if (!ok) {
+        fprintf(stderr, "Error while adding frame #%d\n", pic_num);
+      }
+    }
+    WebPPictureFree(&pic);
+    if (!ok) goto End;
+
+    if (verbose) {
+      WFPRINTF(stderr, "Added frame #%3d at time %4d (file: %s)\n",
+               pic_num, timestamp_ms, GET_WARGV_SHIFTED(argv, c));
+    }
+    timestamp_ms += duration;
+    ++pic_num;
+  }
+
+  // add a last fake frame to signal the last duration
+  ok = ok && WebPAnimEncoderAdd(enc, NULL, timestamp_ms, NULL);
+  ok = ok && WebPAnimEncoderAssemble(enc, &webp_data);
+  if (!ok) {
+    fprintf(stderr, "Error during final animation assembly.\n");
+  }
+
+ End:
+  // free resources
+  WebPAnimEncoderDelete(enc);
+
+  if (ok && loop_count > 0) {  // Re-mux to add loop count.
+    ok = SetLoopCount(loop_count, &webp_data);
+  }
+
+  if (ok) {
+    if (output != NULL) {
+      ok = ImgIoUtilWriteFile(output, webp_data.bytes, webp_data.size);
+      if (ok) WFPRINTF(stderr, "output file: %s     ", (const W_CHAR*)output);
+    } else {
+      fprintf(stderr, "[no output file specified]   ");
+    }
+  }
+
+  if (ok) {
+    fprintf(stderr, "[%d frames, %u bytes].\n",
+            pic_num, (unsigned int)webp_data.size);
+  }
+  WebPDataClear(&webp_data);
+  ExUtilDeleteCommandLineArguments(&cmd_args);
+  FREE_WARGV_AND_RETURN(ok ? 0 : 1);
+}
diff --git a/examples/stopwatch.h b/examples/stopwatch.h
new file mode 100644
index 0000000..f1b0fac
--- /dev/null
+++ b/examples/stopwatch.h
@@ -0,0 +1,63 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  Helper functions to measure elapsed time.
+//
+// Author: Mikolaj Zalewski (mikolajz@google.com)
+
+#ifndef WEBP_EXAMPLES_STOPWATCH_H_
+#define WEBP_EXAMPLES_STOPWATCH_H_
+
+#include "webp/types.h"
+
+#if defined _WIN32 && !defined __GNUC__
+#include <windows.h>
+
+typedef LARGE_INTEGER Stopwatch;
+
+static WEBP_INLINE void StopwatchReset(Stopwatch* watch) {
+  QueryPerformanceCounter(watch);
+}
+
+static WEBP_INLINE double StopwatchReadAndReset(Stopwatch* watch) {
+  const LARGE_INTEGER old_value = *watch;
+  LARGE_INTEGER freq;
+  if (!QueryPerformanceCounter(watch))
+    return 0.0;
+  if (!QueryPerformanceFrequency(&freq))
+    return 0.0;
+  if (freq.QuadPart == 0)
+    return 0.0;
+  return (watch->QuadPart - old_value.QuadPart) / (double)freq.QuadPart;
+}
+
+
+#else    /* !_WIN32 */
+#include <string.h>  // memcpy
+#include <sys/time.h>
+
+typedef struct timeval Stopwatch;
+
+static WEBP_INLINE void StopwatchReset(Stopwatch* watch) {
+  gettimeofday(watch, NULL);
+}
+
+static WEBP_INLINE double StopwatchReadAndReset(Stopwatch* watch) {
+  struct timeval old_value;
+  double delta_sec, delta_usec;
+  memcpy(&old_value, watch, sizeof(old_value));
+  gettimeofday(watch, NULL);
+  delta_sec = (double)watch->tv_sec - old_value.tv_sec;
+  delta_usec = (double)watch->tv_usec - old_value.tv_usec;
+  return delta_sec + delta_usec / 1000000.0;
+}
+
+#endif   /* _WIN32 */
+
+#endif  // WEBP_EXAMPLES_STOPWATCH_H_
diff --git a/examples/test.webp b/examples/test.webp
new file mode 100644
index 0000000..3e4bca1
--- /dev/null
+++ b/examples/test.webp
Binary files differ
diff --git a/examples/test_ref.ppm b/examples/test_ref.ppm
new file mode 100644
index 0000000..97719f0
--- /dev/null
+++ b/examples/test_ref.ppm
@@ -0,0 +1,4 @@
+P6
+128 128
+255
+ËáûËáûËáûËáûËáûËáûËáûËáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãûÎãûÎãûÎãûÏäýÏäýÏäýÏäýÐãýÐãýÐãýÐãýÒäþÒäþÒäþÒäþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÒäþÒäþÒäþÒäþÓåÿÓåÿÓåÿÓåÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèýÔèýÔèýÔèýÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷ËáûËáûËáûËáûËáûËáûËáûËáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãûÎãûÎãûÎãûÏäýÏäýÏäýÏäýÏäýÐãýÐãýÐãýÒäþÒäþÒäþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÒäþÒäþÒäþÒäþÓåÿÓåÿÓåÿÓåÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèýÔèýÔèýÔèýÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷ËáûËáûËáûËáûËáûËáûËáûËáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãûÎãûÎãûÎãûÏäýÏäýÏäýÏäýÏäûÏäûÏäûÏäûÐåýÐåýÐåýÐåýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÒäþÒäþÒäþÒäþÓåÿÓåÿÓåÿÓåÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèýÔèýÔèýÔèýÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷ËáûËáûËáûËáûËáûËáûËáûËáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÌáûÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãûÎãûÎãûÎãûÏäýÏäýÏäýÏäýÎåûÎåûÎåûÎåûÏæýÏæýÏæýÏæýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÒäþÒäþÒäþÒäþÓåÿÓåÿÓåÿÓåÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèýÔèýÔèýÔèýÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷Ïå÷ËáûËáûËáûËáûÌâýÌâýÌâýÌâýÍâýÍâýÍâýÍâýÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãþÏäÿÏäÿÏäÿÏäÿÏäÿÏäÿÏäÿÏäÿÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåþÎåûÎåùÎåùÎåùÏæúÏæúÏæúÏæýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÓåÿÓåÿÓåÿÓåÿÔæÿÔæÿÔæÿÔæÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÖêÿÖêÿÖêÿÖêÿÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÒèùÒèùÒèùÒèùÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷ËáûËáûËáûËáûÌâýÌâýÌâýÌâýÍâýÍâýÍâýÍâýÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãþÏäÿÏäÿÏäÿÏäÿÏäÿÏäÿÏäÿÏäÿÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåýÒèûÒèûÒèûÒèûÐæúÐæúÐæúÐæúÐåýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÓåÿÓåÿÓåÿÓåÿÔæÿÔæÿÔæÿÔæÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÖêÿÖêÿÖêÿÖêÿÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÒèùÒèùÒèùÒèùÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷ËáûËáûËáûËáûÌâýÌâýÌâýÌâýÍâýÍâýÍâýÍâýÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãþÏäÿÏäÿÏäÿÏäÿÏäÿÏäÿÏäÿÏäÿÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåýÐæúÒåøÒåøÒåøÒåøÒåøÒåøÐæúÐåýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÓåÿÓåÿÓåÿÓåÿÔæÿÔæÿÔæÿÔæÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÖêÿÖêÿÖêÿÖêÿÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÒèùÒèùÒèùÒèùÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷ËáûËáûËáûËáûÌâýÌâýÌâýÌâýÍâýÍâýÍâýÍâýÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãþÎãþÎãþÎãþÏäÿÏäÿÏäÿÏäÿÏäÿÐãÿÐãÿÐãÿÏäýÏäýÏäýÐãýÒäþÒåýÒåýÒåýÏáóÏáóÏáóÏáñÔåöÔåöÔåöÔåøÒåúÒåýÒåýÐåýÐåýÐåýÐåýÐåþÓåÿÓåÿÓåÿÓåÿÔæÿÔæÿÔæÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÔèÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéÿÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÖêÿÖêÿÖêÿÖêÿÔêûÔêûÔêûÔêûÔêûÔêûÔêûÔêûÓéúÓéúÓéúÓéúÒèùÒèùÒèùÒèùÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷ÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãûÎãûÎãûÎãûÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÒæÿÐåþÒäþÐãýÎáúÒâûÐáúÕåÿÎáýÔæÿÔæÿÓãýÏßøÉÚð·Æ܇–ª‰—©›­±»ÎŸª»©³ÅÌÖèÖáòÓáñÖèøÔåö×éûÒåøÒåúÖêÿÐæúÒæþÓæþÓæþÓæþÓæþÔèÿÔèÿÔèÿÔèýÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÕëÿÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëý×ëþ×ëþ×ëþ×ëþÖêýÖêýÖêýÖêýÕéûÕéûÕéûÕéûÔèúÔèúÔèúÔèúÓæùÓæùÓæùÓæùÒåøÒåøÒåøÒåøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷ÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãûÎãûÎãûÎãûÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÒäþÒäþÓãý×èÿÕãþÒßúÕäÿÌÛøÈÙò«Åm{•dqˆhtŒlvŒs{…‹y‘‡Ÿ‘—¨¤ªº°¶Æ½ÆÕÎÜêÓäóÕæ÷ÔåöÖèúÓæùÐäùÔêþÓæûÓæûÓæûÓæûÔèýÔèýÔèýÔèýÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëý×ëþ×ëþ×ëþ×ëþÖêýÖêýÖêýÖêýÕéûÕéûÕéûÕéûÔèúÔèúÔèúÔèúÓæùÓæùÓæùÓæùÒåøÒåøÒåøÒåøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷ÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãûÎãûÎãûÎãûÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÌáùÎãûÓåÿÕèÿÒâûÓãýÔâýÙæÿÓáû©·Òlz•\j„mz”hqŒmw‚‹¡†‰ž¥‚–‡‹ž‚”°³Å¸½Ë•›©¿ËÙÓãòÖæö×æøÔåöÖèúÕéûÐä÷ÓæûÓæûÓæûÓæûÔèýÔèýÔèýÔèýÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëý×ëþ×ëþ×ëþ×ëþÖêýÖêýÖêýÖêýÕéûÕéûÕéûÕéûÔèúÔèúÔèúÔèúÓæùÓæùÓæùÓæùÒåøÒåøÒåøÒåøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷ÍâýÍâýÍâýÍâýÎãþÎãþÎãþÎãþÎãûÎãûÎãûÎãûÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÓèÿÒæÿÐãýÏâûÓãýÕåÿ±¿Úªr€›Wd~\i‚~‹¤˜¢½jq‡Žªz˜„…›œ²‚„š…†›‚…–—š©²´Á¯³¿–Ÿ¬±½ËÆÔäÔâòÙèùÔãôÖèúÕæùÓæûÓæûÓæûÓæûÔèýÔèýÔèýÔèýÕéþÕéþÕéþÕéþÕéþÕéþÕéþÕéþÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëýÕëý×ëþ×ëþ×ëþ×ëþÖêýÖêýÖêýÖêýÕéûÕéûÕéûÕéûÔèúÔèúÔèúÔèúÓæùÓæùÓæùÓæùÒåøÒåøÒåøÒåøÐæøÐæøÐæøÐæøÏå÷Ïå÷Ïå÷Ïå÷ÎãþÎãþÎãþÎãþÏäÿÏäÿÏäÿÏäÿÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÓèÿÉÞ÷ÚìÿÔæÿÈÙòy‰£M[vXfVc}[deo‰ipŒ‡Žªfj‡Œ¬xz—}}˜~{•qo†‘¥—–©‰ˆ˜——¤ž¡­ÆÌÚ½Æ՘¢±¯¹ËÖáò×å÷Úèù×æúÖæýÔèýÔèýÔèýÔèýÔèýÔèýÔèýÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþ×íÿ×íÿ×íÿ×íÿ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþÖêýÕéûÔèúÕéûÕéûÕéûÕéûÓæùÓæùÓæùÓæùÓæùÓæùÓæùÓæùÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÎãþÎãþÎãþÎãþÏäÿÏäÿÏäÿÏäÿÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÒæÿÒäþÅ×ñfwM]wLZtMZsOXq_f€jq~žrv“rs“ˆ‰©……¤ss‘vs}–ž²›š¬€€šš¥¤¨²·»Ç‹ž‘—¨ˆ‘¡­¶ÈÓÝïÚä÷ÛéúÖèúÔèýÔèýÔèýÔèýÔèýÔèýÔèýÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþ×íÿ×íÿ×íÿ×íÿ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþÖêýÕéûÔèúÕéûÕéûÕéûÕéûÓæùÓæùÓæùÓæùÓæùÓæùÓæùÓæùÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÎãþÎãþÎãþÎãþÏäÿÏäÿÏäÿÏäÿÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÒæÿÏäý½ÏéXk…IZsL\vP^yN[tbi‚sx‘Ž“¬€‚Ÿ~€‚‚¢©¨Ë¡}}›qq~—œ²œž­‚…‘±´¿¤¨²„¨¬º{€Ž‰Ÿ˜ž¯³»Î×ßò×å÷ÖèúÔèýÔèýÔèýÔèýÔèýÔèýÔèýÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþ×íÿ×íÿ×íÿ×íÿ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþÖêýÕéûÔèúÕéûÕéûÕéûÕéûÓæùÓæùÓæùÓæùÓæùÓæùÓæùÓæùÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÎãþÎãþÎãþÎãþÏäÿÏäÿÏäÿÏäÿÏäýÏäýÏäýÏäýÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåýÐåýÐåþÐåþÍßù[m‡GWqLZtIWrFQmox“›Ÿ´¡¤¸‘•ªyzss¤¤Â½¹Ù²°Ì‰‹£lm„z{vx‰œž««¯¹ÌÏڏ“›Œš›ž©˜›¨„‘•£ª°ÀÔÚê×âó×æúÔèýÔèýÔèýÔèýÔèýÔèýÔèýÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖêÿÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþÖìþ×íÿ×íÿ×íÿ×íÿ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþÖêýÕéûÔèúÕéûÕéûÕéûÕéûÓæùÓæùÓæùÓæùÓæùÓæùÓæùÓæùÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐæøÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÏåÿÏåÿÏæþÏæþÎãúÏåùÖêÿÒåýºÍæk~šJXvLWvLTqFNkQXtlqˆÆÈÚ£¥²áäﷷą„–£¢·ÎÍâÝÜïìëû¸¸Å£¢²yy†——£²²»ººÆˆˆ‘•–œž¤­­·•˜£‚…‘¡¥±¬±¿ÕÞíÖå÷×éûÔèúÓæùÔêþÔêþÔéÿÕéþÖêÿÙêýÙêýÙêýÙêýÙêýÙêýÙêýÖêýÖêýÖêýÖêý×ëþ×ëþ×ëþ×ëþ×íÿ×íý×íý×íý×íý×íý×íý×íý×íý×íý×íý×íý×íý×íý×íý×íýÙíýÙíýÙíýÙíý×ìû×ìû×ìû×ìû×íýÖìûÕëúÔêùÔêùÔêùÔêùÔêùÓéøÓéøÓéøÓéøÒè÷Òè÷Òè÷Òè÷ÐæöÐæöÐæöÐæöÐæöÐæöÐæöÐæöÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÏåÿÏåÿÏæþÐåýÕêÿÐäùÏãø¬ÀÕVi‚IXvN\yHPpLSpHOlei†¨“•¢²²»ÚÛáÒÒۂ‘”“£º¹ÉÌÌÙ°°»——£ŒŒ˜yy†··À££¬žžª‹›š¡¯­´ªª³»»Åª­¸œŸªšžª£©·ÌÚêÒãóÚëûÚíÿÕéûÔêþÔéÿÕéþÙêýÙêýÙêýÙêýÙêýÙêýÙêýÙêýÖêýÖêýÖêýÖêý×ëþ×ëþ×ëþ×ëþ×íý×íý×íý×íý×íý×íý×íý×íý×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìú×ïûÖíúÕìùÔëøÔëøÔëøÔëøÔëøÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÐèôÐèôÐèôÐèôÐèôÐèôÐèôÐèôÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÏæþÏæþÐåþÐåýÐäûÐäû½ÍãSc{FVpIWtS^}MUtLSpHOllp“•­ˆ‹š¡©Ž‘—‰•jly…‡–wy†“Ÿikxjlysv…ˆ‹šÉÍׅˆ“xz‡œ¦¥¯©¨±šš£­­·²¶À•˜£¦«¶‡Ž˜¦²ÀÈÖæ×è÷×éùÙêýÕéþÕéþÕéþÙêýÙêýÙêýÙêýÙêýÙêýÙêýÙêýÖêýÖêýÖêýÖêý×ëþ×ëþ×ëþ×ëþ×íý×íý×íý×íý×íý×íý×íý×íý×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìú×ïûÖíúÕìùÔëøÔëøÔëøÔëøÔëøÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÐèôÐèôÐèôÐèôÐèôÐèôÐèôÐèôÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÏæþÐåýÐåýÒåýÒåý£³ÌTd}IWqIWrQ]{P\zOWwLTqFMjcf„z–il~x{†y}…z~ˆ]_o~€¥¨´kmzikxehw^artwˆŽ~€‡‰˜sv…iit£¢«ÂÂ̕•ž‰—}€‹‘–¡†‹•—ž©ÌÕâÖâðÝëûÜëý×éûÕéûÕéþÙêýÙêýÙêýÙêýÙêýÙêýÙêýÙêýÖêýÖêýÖêýÖêý×ëþ×ëþ×ëþ×ëþ×íý×íý×íý×íý×íý×íý×íý×íý×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìú×ïûÖíúÕìùÔëøÔëøÔëøÔëøÔëøÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÐèôÐèôÐèôÐèôÐèôÐèôÐèôÐèôÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÓèÿÎãúÕéÿÈÜóqšIZrJXrIWrO[yJVtPXxT\{FNkV]zv}˜osX\pqv„fkw‚‡“fj{ˆŒ†‘sx„†‹˜y}Žhkei}X]kty‡‡‹œcewbbo†„°°»½½È±´½ÇËÓ·½Ä¡¦­·»Æ¶ºÅÀÇÒ×ãïÙæ÷ÙèùÖèúÙìÿÖêýÖêýÖêýÖêý×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþÙìÿÙìÿÙìÿÙìÿ×íý×íý×íý×íý×íý×íý×íý×íý×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìúÕìùÕìùÕìùÕìùÔëøÔëøÔëøÔëøÔëøÔëøÔëøÔëøÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÐèôÐèôÐèôÐèôÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåýÎäøÓæûÄ×ï^o‡HXqM[vDQlP\xNZxIUsLTsMUtHPm]e‚krŽSZsSWoW[pfj{qt†txŒjm]arZ^lmq‚^bv[^sdh}fj{dhyil€ehy~~‹˜–£¢¢­©©´¢¥­»¿Çª°·±·¾ÁÅËÌÏ׶ºÅÁÈÓÚåóÚèøÛìýÖêýÖêýÖêýÖêýÖêý×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþ×ëþÙìÿÙìÿÙìÿÙìÿ×íý×íý×íý×íý×íý×íý×íý×íý×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìúÕìùÕìùÕìùÕìùÔëøÔëøÔëøÔëøÔëøÔëøÔëøÔëøÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÐèôÐèôÐèôÐèôÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåýÕéþ²ÆÛTdzEUmM[tAOjWcMXwJSrS[zIQqJSr[c€^f„NWrLSoPTqINhW\sim‚X]tae}V\oX^qU[m]bwW\s]bykq„flV[pTWkqp€•“¡ªª¶˜˜¤ˆŒ”Œ—…‹²¶»·¸»ÌËÏÚÝå½Á̹ÂÏÔâòÛêûÕéûÖêýÕëýÕëýÕëýÖìþÖìþÖìþÖìþ×ëþ×ëþ×ëþ×ëþÙìÿÙìÿÙìÿÙìÿ×íý×íý×íý×íý×íý×íý×íý×íý×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìúÕìùÕìùÕìùÕìùÔëøÔëøÔëøÔëøÔëøÔëøÔëøÔëøÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÐèôÐèôÐèôÐèôÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÐåþÒåý³ÄÚN^tIZpIWqHVqQ_zJXvLWvNZxEPoLWvQZyV^{W_}U\yOVsQUsTWt{€š\azSWqPUoPUlOTiV[pmr‡TXrOTmmr‡x}‘_d{X\qz}Œœœ¨›––¢z~ˆ†‰”{‰ww€Œ‹‘ÆÅÌÌÌÕ¥ª´°·ÁÅÐÞ×åöÛëúÙêúÖêýÖêýÕëýÖìþÖìþÖìþÖìþ×ëþ×ëþ×ëþ×ëþÙìÿÙìÿÙìÿÙìÿ×íý×íý×íý×íý×íý×íý×íý×íý×ïû×ïû×ïû×ïû×ïû×ïû×ïû×ïûÙíûÙíûÙíûÙíû×ìú×ìú×ìú×ìúÕìùÕìùÕìùÕìùÔëøÔëøÔëøÔëøÔëøÔëøÔëøÔëøÓê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÐèôÐèôÐèôÐèôÒæþÒæþÒæþÒæþÓèÿÓèÿÓèÿÓèÿÒäþÔæÿÕèÿÐãýÐãýÕèÿÕèÿ¨¸ÒZhN[tN[tO]xQ_zM[vLZtL\vJZwBQoP^{Q]{W_W]~W[{UXwQUs\_}hl†TXrLOmIMkDGdDH_EI^‚‡œlq‹TXr~‚š„›TWtaeqv„¦ª´‡‹•~€qp€‡†–Œœ‚€‰‡”¡Ÿ©ÅÅг·Á¡¥°¤©³ÅÌÕÙâìÜêúÞíÿÚëûÖëúÖìû×íý×ðþÙñÿ×íýÙíýÙíýÙíýÙíýÙíýÙíýÙíý×íý×ïû×ïû×ïûÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðúÙðúÙðúÙðú×ïù×ïù×ïù×ïùÖíúÖíúÖíúÖíúÕìùÕìùÕìùÕìùÕìùÔëøÔëøÔëøÓê÷Óê÷ÒéöÒéöÓê÷ÒéöÒéöÒéöÒéöÐèôÐèôÐèôÒæþÒæþÒæþÒæþÓèÿÓèÿÓèÿÓèÿÒäþÖéÿÒäþÓåÿÐãýÐãý}©O_yNZvNVsLWsQ]yLWsN\wN\wHXrIZsHWtSa~S^}[abe†hi‹WXxW[yVZwZ^xTWtPTrOSqNQoBGaINc{€•^c}[_y–›²€…œX\yQVpjo}ˆ—¢¤±­°¿wv†‰ˆ›‰†šš–ª‰‡•rp}——£¿¿Ë„‡¢¥­»ÁÆÉÐÚÝé÷ÙæøÜëýÝïÿÚïþÙïþÖïýÕíû×íýÙíýÙíýÙíýÙíýÙíýÙíýÙíý×ïû×ïû×ïû×ïûÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðúÙðúÙðúÙðú×ïù×ïù×ïù×ïùÖíúÖíúÖíúÖíúÕìùÕìùÕìùÕìùÕìùÕìùÕìùÔëøÓê÷Óê÷Óê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÐèôÐèôÒæþÒæþÒæþÒæþÓèÿÓèÿÓèÿÓèÿÔæÿÓãýÖæÿÕåÿÛëÿw‡¡M]wDQlLWsLTqS[xV^{O[wS^zLWsBPkLZtO]xUa}Zbls_copž^bUXv\azW\vGJiHLjMPmFJbIObX^qZ^vy~•Ÿ¥¸w{UZq[_tty‡ty…‡‰–ž…„”‰ˆ˜£¡±¬ªº»»Èvv‚‹ŸŸ«¦ª²©¬´¤¨°¹¾ÈÛæôÛéùÛëúÙêùÛðþÙðýÖïûÕíú×íýÙíýÙíýÙíýÙíýÙíýÙíýÙíý×ïû×ïû×ïû×ïûÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðúÙðúÙðúÙðú×ïù×ïù×ïù×ïùÖíúÖíúÖíúÖíúÕìùÕìùÕìùÕìùÕìùÕìùÕìùÕìùÔëøÓê÷Óê÷Óê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÐèôÒæþÒæþÒæþÒæþÓèÿÓèÿÓèÿÔèÿÖæÿÔäþÓãý×èÿ~Ž¨HXrHXrIWrIUqMUrQZwNVsQZwPXvNVsJVrS_yO\vU^y^eVZw^bsv“†ˆ¥y}šUZs]byX]wPTqPTqMQkNSjPViLQdSWo~‚—ž¤·jp‚]bw[_trv‡UZhZ\kkm}€šš¦±¯½À¾Ì©©¶ffsxx…‰‰•Œš–š¢˜œ¤ž£­ÆÏÞÝëûÜêúÛëúÙíûÖíúÙðýÚñþÙíýÙíýÙíýÙíýÙíýÙíýÙíýÙíý×ïû×ïû×ïû×ïûÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðúÙðúÙðúÙðú×ïù×ïù×ïù×ïùÖíúÖíúÖíúÖíúÕìùÕìùÕìùÕìùÕìùÕìùÕìùÕìùÔëøÔëøÔëøÓê÷Óê÷Óê÷Óê÷Óê÷ÒéöÒéöÒéöÒéöÒæþÒæþÒæþÒæþÒæþÒæþÒæþÓæþÚêÿÏÝøÎÜ÷hvHVqHVqHVqGUpHTpOWtU]zLTqLTqOVsLSpS\wOXqV]wU\v]b{ptŽjoˆkmˆz}—kp‰X]wLPhNSjTWtOSpW\vLPhMQfPUjV[rae}U[mU[mkp…X]r^bv^bvehybdseerŸŸ«©¦³¢Ÿ¬‹‰škm}fixtw„Œ˜˜¢¤±Ÿ¥³ÎÚèÜêøÙéöÞñýÙíùÙðúÕìùÚïþÚïþÚïþÚïþÚïþÚïþÚïþÚïþÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÚñþÚñþÚñþÚñþÚñþÚñþÚñþÚñþÚñûÚñûÚñûÚñûÙðúÙðúÙðúÙðú×ïû×ïû×ïû×ïûÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÕìùÔëøÔëøÔëøÔëøÔëøÔëøÓê÷ÒéöÒéöÒéöÒæþÒæþÒæþÒæþÒæþÒæþÒæþÓæþÕåÿÎÜ÷drFToIWrESmFToMXtS^zU]zHPmLSpJQoU\y\c€sw”[_w\ax^czdi€chchdf_bzSWoNSjX]r\ax\azOTmPUlae}QVmHMdLPjINhMQfdj}y~“ejVZojm‚€–‹œŽ‡‡“¨¦°žž¨y{‹…‡˜€‚‘z}Œ}{Œœ‘‘ž¨ª·•›©ÀÉÙÙäòÙæôÚê÷ÜïúÙíùÛòýÚïýÚïþÚïþÚïþÚïþÚïþÚïþÚïþÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÚñþÚñþÚñþÚñþÚñþÚñþÚñþÚñþÚñûÚñûÚñûÚñûÙðúÙðúÙðúÙðú×ïû×ïû×ïû×ïûÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÕìùÔëøÔëøÔëøÔëøÔëøÓê÷ÒéöÒéöÒæþÒæþÒæþÒæþÒæþÒæþÒæþÓæþÕãþw‚žHTpMXtO[wLWsNZvT_{P\xLTqIQoLSpOSq\_~il‹„¡mpˆSVkdh}…ˆW[p[^sUZoNShV[r_h~^fzw“qx‘PWqPXockDJdGNjNUqFMiXawt}€ˆœV[rSUmtw‘¦”–¨ª››¤±°·‰‹xz‡‚”„†—‹ž}{Œ”“£œš¨´´Á‘–¤¦°½ËÖâÜëöÝíøÝðù×íöÚïúÚïýÚïþÚïþÚïþÚïþÚïþÚïþÚïþÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÚñþÚñþÚñþÚñþÚñþÚñþÚñþÚñþÚñûÚñûÚñûÚñûÙðúÙðúÙðúÙðú×ïû×ïû×ïû×ïûÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÔëøÔëøÔëøÔëøÔëøÔëøÓê÷ÒéöÒæÿÒæþÒæþÒæþÒæþÓæþÓæþÕåþ}‹¥DOkIUqMXtJVrMXtP\xS^zLWvHPpGNkELiNQodh…Œª¦©Ä›ž³[^r{“ilSVkQUjMQfGLaJSf_h{ltˆW_vT[t^eJSiGNhNUqLSoFMjFMiIPjrzV^tLPhOQlxz“˜š°„…š‡†–˜¸·¾•”‚‚„†—„†—xw‰ts„Ÿ››¨´´Á«­º—œ¨©°ºÍÙâÛêòÚêôßòûÜòúÚïýÚïþÚïþÚïþÚïþÚïþÚïþÚïýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÙðýÚñþÚñþÚñþÚñþÚñþÚñþÚñþÚñûÚñûÚñûÚñûÚñûÙðúÙðúÙðúÙðú×ïù×ïû×ïû×ïûÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÖíúÔëøÔëøÔëøÔëøÔëøÔëøÔëøÓê÷ÓèÿÐåþÓåÿÓåÿÒåýÓãûÔäúm{•LWsJSpJSpNVsT\yZbXa~NVvGOqFLoGMmIPlQXrbj~š¢´´ºÍˆ¢fk€im‚\axNSjJOfQVmLPeV[p^cxchFIfMTqHOlFOjNUqZa}FIfQUrMPmTWtej„MQkMQkWZt}šxz“[\rbasfev˜–¤›‹˜ŽŒœŸœ°]\oa_r‹‰šŸŸ¬ÄÄл»Ç¤¤­¡¤¯¦­·Ë×ÞÝìôÝð÷ÝðùÜïúÜíýÜíýÜíýÜñÿÜñÿÛòÿÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÜóþÜóþÜóþÜóþÜóþÜóþÜóþÜóûÛòúÛòúÚñùÚñùÚñùÚñùÚñùÚñùÙðøÙðúÙðúÙðú×ïù×ïù×ïù×ïù×ïù×ïù×ïù×ïùÖíøÖíøÖíøÖíøÕì÷Õì÷Õì÷Õì÷ÔëöÔëöÔëöÓêôÐãýÖéÿÓæþÔäýÔäý×åÿp~—TazNVsLTsLTsNVvV^~V^~NVvEMlGMpNTwLSpFMi[cypxŒ¦¶«´Ä^cx_d{\axW\sTXpNSjNSj\avioZ^sdi‚FIfGNkIOpGOlSZvfk…^c}}›[_yPTqei†OTmNSlbdª]_x]^t{zzyŒœšªŽŒœ•Ÿ—“¤ž›¯]Zm[Zlkj}›¬³³¿º¹Â¾½Ä¬¬¶¨­´±»ÄÐÝäßñøÚêôÝíúÝíúÜïúÜïúÜñýÜñýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöÕìôÕìôÕìôÕìôÔëóÔëóÔëóÓêò×éûÒãöÕåû×æýÖäþ„ªIVpW_}PXxMUwMUwPXzU]NVxGOqDLmNTwIOpBIfJQmLTjmv‰¯·Épx‹W\schZ^vMQiSWoOTkPUjaez\btioae}AEbLSpLSpGOlJQkW\sswŒ£¥¾twX[vik†JMjQTopršMOh\]syx‹‰ˆ˜£¡±‹˜˜¨ˆšsp„ro‚a_r^]p‰Œ›¯¯º³²»·¶½ÀÀɝ£ª¯¶¿¿ÌÓÛêòÜì÷ÝíúÝíúÜïúÜïúÜñýÜñýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöÕìôÕìôÕìôÕìôÔëóÔëóÔëóÓêòÕåô×è÷Öå÷×äùŒ˜°PZtS\wLTqQZyNVxNVxQZ{OWyHPrFNpIQqQWxW^{LSoMTmHOiS[qz‚–[cwX]wV[tHMf@E\INebf~PUjrx‹iodjzPUjHMfOVrHOlMUrFMiHMdX\qjm‚ei~š€‚›PSkik„wy‘lo‡TWl_av{zŽ¢Ÿ­Žœ¡œ«“ŽŸŒŸ¡rq„ZXkmp¡¡¬–•žº¹ÀËËԛž¦ž¥¯¬·¿ËÖßÛêôÝíúÝíúÜïúÜïúÜñýÜñýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÛòýÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÜóûÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöÕìôÕìôÕìôÕìôÔëóÔëóÔëóÓêò•¤¯¯¾È½ËÙz…–S\rV]wQXtV]zS[zT\{PXxLTsFNmIQqOWwT\{W^zQXtJQkELeELeNUoU]sGNhGLePTqNSlUZqMQipt‰[_tz€jq{}„ŽX^oHMdT[wOVsNVvSZwUWrHI_TUkrsˆ€•wxhi~st‰UXmMPeUXmst‰Œœ••¢­«¹•“Ÿ¤Ÿ¯ª¥·¨¤¸¢ž²¡Ÿ²qp‚mp››¦¡Ÿ©¬«²´´¾³·¿Ÿ¤¯«²»ª±»ÙäðáïýÜìùáóÿáóÿÜñýÜñýÛòýÛòýÜóþÜóþÜóþÜóþÜóþÜóþÜóþÜóþÝôýÝôýÝôýÝôýÝôýÝôýÝôýÝôýÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöÖíöÖíöÖíöÖíöÕìôÕìôÕìôÔëóeqzit~NZeOXhPXlX]tX]w[^{QXvS[xS[xPXvNVsQZwT\yT\yMTmMTmBIcAHbMTmELeBIc@GcIMjOSpOTmjoˆrwŽhldj}MScPWaahrMScV[pT[tQXvOWtT[wVXqVWlijqs…actqs…ŒŽvx‰SVjTWlW[ohj{‘¡¦¦³œš¦›¨¶±À£ž°Ž‹žŒˆœ¢š˜«“¢›š˜¢Œ“¹¹ÂÅÅΦª´¯³¾¦«·¶¿ÌáïýÞïûáóÿáóÿÜñýÜñýÛòýÛòýÜóþÜóþÜóþÜóþÜóþÜóþÜóþÜóþÝôýÝôýÝôýÝôýÝôýÝôýÝôýÝôýÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöÖíöÖíöÖíöÖíöÕìôÕìôÕìôÔë󀉔y‚bkxNTdV\oX\qW[p^aySZsU^yU^yQ[vS\wU^yS\wNWpFNdAI_BJa>E^GNhGNj7>Z;B_?Ba?B_BGaX]wINech}‰¢GM]DIWOUeafy_dyJSiMTmWawdl€[]ofevsr‚hfwfevml}š˜«ž¡²qtˆ_cxadx[]o€¦¦³›¨œš¦­©¸–‘£}yzw‹{z†…—•—¦œŽ–Œ‹‘©©²ÀÀɲ¶À¸»Æ´·Äœ¢°ÁÏÝÜìùáóÿÞñýÜñýÜñýÛòýÛòýÜóþÜóþÜóþÜóþÜóþÜóþÜóþÜóþÝôýÝôýÝôýÝôýÝôýÝôýÝôýÝôýÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöÖíöÖíöÖíöÖíöÕìôÕìôÕìôÔë󅌖t{†pt€lqhlzehyTVh_avZ^sXatU]sPXoQXrT[tNUoELeAHbDH_AF_@GaELh=Da=Da@Gd>A_>A^JNkHMfAF]PUjOTiOUhX^qsyŒejOTiS[qFNdLViT\pX\pZ\m^arihzdcvjiy‹‰š£¢´rsˆy}~“xz‰wy†„„——£‰‡”~{‰‡…“‰‡—‰†…—‹‰š”–£ŒŒ—š˜¢¸·¾»»Å¾¾Ç¿¿Ë½½È¶¶Áª¯º›¦ÓãðáñþÜïúÞñýÜñýÜñýÛòýÜóþÜóþÜóþÜóþÜóþÜóþÜóþÜóþÝôýÝôýÝôýÝôýÝôýÝôýÝôýÝôýÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷ÖíöÖíöÖíöÖíöÖíöÖíöÖíöÖíöÕìôÕìôÕìôÔë󏓝•˜£ˆŒ–‹Ž˜ÆÉÔ±±½šš¥››¨sv‚hjyps…VZmUZqPUoLOlFIhFIhGHhEFeAEc@Db@Dd>Dd>Eb>Eb?FbHOkFMiAHbAHbBIcNVlXatS[m]eydl€U]sLSlJQkLSlTWtHJhOQj]^tcdyxz‰±±¾‡†–mp}~“‘”£ž¡­–˜¥‚…‘Žvv‚Œss€‹‹—Žž€……‘““Ÿ©œ›¤­¬¶º¹Â¾½Æ¤£¬¹¸Á¾½Æ²¶À—ž©œ¨³ßïùáñûÛíùßòþÝòÿÚïúÞóÿÞôýÞôýÞôýÞôýÞôýÞôýÞôýÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÝöúÝöúÝöúÝöúÜôùÜôùÜôùÜôùÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷ÕïöÕïöÕïöÕïöÔíôÔíôÔíôÓì󏐖‰‹›œ¢¢£©³´ºÙÚßÈÉϬ­³½¾Ä··À¨ª·[]obc{bdMNmIMkJLkLMlIJjFGi@Dd@Dd>Dd>Eb?Fb>EaGNjDJfBIeAHd?FbFMfDLb@H\IQeckQZpIPjFMfOVrMPoLOmPSm]_x]^spr„……‘~}“~€‘vx‡‡‰–¢¥°£¥²prvx‡Ž……‘ss€ŽŽ›~~‹šªš—¤œ›¤¤£¬ÓÒÛ¤£¬­¬¶³²»°°¹±¶À‚Œ–ÒÝéßïùáñþáóÿßôÿÝòþÞôýÞôýÞôýÞôýÞôýÞôýÞôýÞôýÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÝöúÝöúÝöúÝöúÜôùÜôùÜôùÜôùÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷ÕïöÕïöÕïöÕïöÔíôÔíôÔíôÓìóÁÂƤ¥©Œ”•˜—˜œÞßãÎÏÓÅÆÉ¿ÀÄ´¶»··Â{zcd}hh†VWyLMoILiJMjHJhEFe@Db@Db>Eb>Eb?Fb>EaELhAHdAHdAHd>Ea@GcELh>E^ELe]e{JSiGOcNVjV^rPUoNSlOQjX\qQShbdvom€{z{}‘mpQTc[]j…ˆ“‹š†ˆ—ŒŽ††“ddqbboyy†ˆˆ•ŒŒ˜œ““Ÿ›¨›š£ž¦²±ºž¦¦¥¯¡Ÿ©š´¹Ä˜Ÿª¬¶ÀâíùáñûÞñýÛðûÝòþÞôýÞôýÞôýÞôýÞôýÞôýÞôýÞôýÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÝöúÝöúÝöúÝöúÜôùÜôùÜôùÜôùÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷ÕïöÕïöÕïöÕïöÔíôÔíôÔíôÓìóÉËÍ´¶¸¦¨ª‘“•‹ŒŽ·¸ºÜÝßÚÛݸ¹»²³·ÇÇЪ©¹zxdd€__NOoDFaDFaAD^?A^@Da@Db>Eb>Eb@Gc>EaDJf?Fb=D_@Gc>Ea@GcFMi@GcJQkXawPXoJSfXasPXlGLcQVm\_tQUjSTi\^pts„vt‡‚—ik}UWfikxx{†…‡”‡‰˜‘¡^^kzz‡‚‚‰‰–‡‡”‘‘ž””¡”‘ž£¢«¡Ÿ©±°¹†…Ž£¢«¢¡ªŸŸ©²¶À­²½‹‘œÚãíãòýáóýÞóÿáöÿÞôýÞôýÞôýÞôýÞôýÞôýÞôýÞôýÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÝöúÝöúÝöúÝöúÜôùÜôùÜôùÜôùÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ï÷×ï÷×ï÷×ï÷ÕïöÕïöÕïöÕïöÔíôÔíôÔíôÓì󩫪¾À¿‘”“ˆ‹‰mpoz}{ËÍÌßâáÖÖÖÒÒÔÁÀÇÀ¾Ë²±Ä–”«de~OOkHLaGJ_HJcFHa@E^=@]:A]=D_>Ea?Fb>Ea;B^>Ea?Fb@GcAHdMTmSZsHPfFNdS[qOWmQZpLTjJOi_d{JNc_cw]_qjl{llyŒy{VXjWZirtlpz€‚•—¦¢¤³””¡ww„}}‰‹‹—††“””¡¡¡­““Ÿ›˜¥”“œ›š£ÆÅΣ¢«ž¦ª©²°¯¸ßß黻ǔ˜£ºÄÎÝìôáóýßöþÛòúÞôýÞôýÞôýÞôýßöþßöþßöþßöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞ÷ûÞ÷ûÞ÷ûÞ÷ûÝöúÝöúÝöúÝöúÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ñø×ñø×ñø×ñøÖð÷Öð÷Öð÷Õïö~±²´‰‹‚…deh‘”¯°²ÎÎθ¶·ÓÐÔÜÚÞÒÐ×°­ºª©¹wvˆWXmOSdNQeNQeLOd@E\>B\:A]>Ea?Fb>Ea;B^=D_@Gc?Fb>Ea=D]=E[FNdDLbMUkS[qQZpT[tU\vV[rejTWlX\pcewjlyyy…ŽŽ›y{\^plo~y{ˆ‹Ž˜”–£vx‡~€••¢‘‘ž‚‚““Ÿ••¢‰‰–––£˜˜¥Ÿª¡Ÿ©¦¥¯Ÿž¨±°¹£¢«©¨±ª©²±°¹´³½¬°º‹‘›ßëôÛíôÞôýß÷ÿÞôýÞôýÞôýÞôýßöþßöþßöþßöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞ÷ûÞ÷ûÞ÷ûÞ÷ûÝöúÝöúÝöúÝöúÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ñø×ñø×ñø×ñøÖð÷Öð÷Öð÷Õïö¡¢¥”•˜‚„‡{}€dei…†‰˜šššœ›˜šÅÀÁÛÙÜÓÐÔÍÌÐÆÅÌ°°¹eisV[fTXfQUfOSfAF]>B\;B^?Fb>Ea;B^=D_=D_?Fb>Ea=D_;B\BJaEMa@H^MUkIQhJSiNUoV^tdi~{”W[odhysv…z}Œ¢¢¯qp€VXjxzŒrt„vx…€„Ž“•¢fixdfvŽŽ›ww„‡‡”££°¾¾Ë——¤››¨œš¦š˜¢¯­·ÁÀÉ¥¤­´³½­¬¶°¯¸¦£«ÍÉÔ½½Æž£­©¶½Þð÷ÞôúÝöúÞôýÞôýÞôýÞôýßöþßöþßöþßöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞöþÞ÷ûÞ÷ûÞ÷ûÞ÷ûÝöúÝöúÝöúÝöúÜóûÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ñø×ñø×ñø×ñøÖð÷Öð÷Öð÷Õïö°±´°±´¥¦ªŽ“{}€efjmor_^c¯¬°ÕÓ֗•˜¦¤¨”Œ‹•–œ‰‰“^blV[fSWePTeBF[?D[?B_>Ea;B^=D_=D_>Ea:A];B^=D_>EaFNdAI_;DZJQkHOiBIc;B\AHbV[pafy]arhlzy{‹ž­jiywy‹bdvjl{}Œfiv~€oq~zz‡˜˜¤ŽŽšœ‡‡“~~‰““Ÿ««¸““žœ›¤¡Ÿ©˜—¡£¢«œ¥ª©²Ÿž¨¶´»¥¢ªÒÐ×ÀÁǯ´»¥°¶Úéðßñ÷ãöýÞôúÞôýÞôýÞôýßöþßöûßöûßöûÞöþÞöþÞöþÞöþÞöþÞ÷ûÞ÷ûÞ÷ûÞ÷ûÞ÷ûÞ÷ûÞ÷ûÝöúÝöúÝöúÝöúÜôùÜóûÜóûÜóûÛòúÛòúÛòúÛòúÚñùÚñùÚñùÚñùÙðøÙðøÙðøÙðø×ñø×ñø×ñø×ñøÖð÷Öð÷Öð÷Õïö¶´¹¨¦«Ÿž£š˜~}zy~‹‰Žtsxqpt«ª¯ÄÂɍŒ“VU^WV_ljwyy…lpzTWbSVaUWfGJ^GIb@Da>Eb?Fc>Gb=Fa;E];E];E_;E_;E_?Ha?Ha?Ha?Hc@Id>Gb>GbAHdMQiNSh_ctqv„‡‰–Žvt…sr‚zy‰kjzvt…~Žyy†••¢““Ÿ––¢±°¹©¨¯ž¦•”€‰”‘žŽ›¡ž«¢¡ª—–Ÿ‹‰£¢©ÐÏÖª©°¥¤«°¯³±°´ª«¯³¸º²¶»°¶½´»ÂÖáæßïöáóúÝöúÞ÷ûÞ÷ûßøýáùýâøýá÷ûá÷ýá÷ýá÷ýá÷ýâøþâøýâøýâøýáùýáùýßøûÞ÷úÞ÷úÞ÷úÞ÷úÞ÷úÝöùÝöúÝöúÝöúÜôùÜôùÜôùÜôùÛóøÛóøÛóøÛóøÚò÷Úò÷Úò÷Úò÷×ñö×ñö×ñö×ñöÖðôÖðôÖðôÕïórqv¥¤©›šžœ›Ÿ{z“‘–„‚‡wvz€†¥¤«º¹Â˜—¡a^k_]jb_mdbpss\_hNQ\\^kNQeGIbDGd>A_?Fc>Fc=Fa;E];E];E_;Da;Da>Gb>G_>Gb>Gb=Fa=Fa?GdELhUZsOTi]ar€…“‚…‘sv‚yxˆkjzom~dcs~Ž‰ˆ˜~~‹}}‰šš¥““œ°¯¶¬«°¨¦­›š¡•”¬«´”‘ž”‘ž¥¤­–•ž”“š¢¡¨´³¸ÅÄÈÓÒÖ½»À¬­°ª«­º»¿»¿Å¤¨°ÀÆ͞¤«äï÷âôûÞ÷ûÝöúÜôùâúþÞ÷úß÷øãùþá÷ýá÷ýá÷ýá÷ýâøýâøýâùúâùúáùýáùýßøûÞ÷úÞ÷úÞ÷úÞ÷úÞ÷úÝöùÝöùÝöùÝöùÜôøÜôøÜôøÜôøÛóøÛóøÛóøÛóøÚò÷Úò÷Úò÷Úò÷×òô×òô×òô×òôÖñóÖñóÖñóÕðò}{€wvzrqvvty€…°¯³¡Ÿ¤”‹‰‰ˆ‰ˆ‘š˜¢„ŽŒ‰–a^lZWe^^j[^iZ]h]_lPTeJNcGLe@Db?Fc>Fc=Fa;E_;E_;E_;Da;Da=Fa=F^=Fa=Fa=Fa=Fa?GdELh\azOTicfx€…“jly\^k{z‹_^oVUebaqqp€~Ž}}‰ss€˜–©¨¯«ª¯¯­´œ£¡Ÿ©ÆÅΨ¥²Ž›­¬¶‘šž¤¥¤«¡Ÿ¤ÂÁÆëêïÌËÏÆÆȱ±³ÅÆɾ¿Å¯²º½Àȓ˜ŸÁÌÔßñøßöûÞôúßöûßöúãùþâúûÞ÷úá÷ýá÷ýá÷ýá÷ýâøýâøýâùúâùúáùýáùýßøûÞ÷úÞ÷úÞ÷úÞ÷úÞ÷úÝöùÝöùÝöùÝöùÜôøÜôøÜôøÜôøÛóøÛóøÛóøÛóøÚò÷Úò÷Úò÷Úò÷×òô×òô×òô×òôÖñóÖñóÖñóÕðò¿¾Â‰ˆ\[_edi„‚‡±°´¶´¹—–›”“š‹‰„‚Œ[Zc…‚¿½É“žcao]]iVZdkoy_boNQcNQfGLeGJh?Fb>Fc=Fa;E_;E_;E_;Da;E_=F^=F^=Fa=Fa@Id>Gb>FcAHdGLeX]rcfxdiw]_leht~Žbaq\[ksr‚xw‡{z‹‚‚yy†‰‰•••ž£¢©ª©­³²¹ž¤”“œª©²¡ž«¤¢¯·¶¿Œ‹”¤£ªª©°¦¥ª©¨¬ÇÆËÁÀŽ½¿¿¿Áº¹¾ÆÇ͹¹Â¾ÁɾÁɍ”ÒáéÞñøä÷þäúÿâøýá÷ûßøùáùýá÷ýá÷ýá÷ýá÷ýâøýâøýâùúâùúáùýáùýßøûÞ÷úÞ÷úÞ÷úÞ÷úÞ÷úÝöùÝöùÝöùÝöùÜôøÜôøÜôøÜôøÛóøÛóøÛóøÛóøÚò÷Úò÷Úò÷Úò÷×òô×òô×òô×òôÖñóÖñóÖñóÕðò©¨¬´³¸edi~‚¡Ÿ¤—–›­¬±¬«°š˜Ÿ–•œž¦~}†WUb¬ª·ÍËÙhesbbmps~Z]ecfq_drMPdINeJOiAHd=Fa=Eb;Da;Da;Da;E_;E_;E];E];E_;E_>Gb;E_AIfJQmJOiW\qZ]olq_boz}‰€ml}ihxrq~}vt…tttt””Ÿ¦¬«²¬«°ª©°š˜Ÿ“‘›ž¦¥£°½ºÇ¦¥¯–•žŒ“š˜Ÿœ›Ÿ¦¥ª¿¾ÂÆÅÉÀ¾ÁÅÂÆ¿¾ÂÔÓÚ²²»»»Å¾Áɟ¤¯…šÉÙáäöýÜïöá÷ûãùþãûýäýÿâøþâøþâøþâøþâøýâøýâùúâùúáùýáùýáùýáùýßøûßøûßøûßøûÞ÷úÞ÷úÞ÷úÞ÷úÝöùÝöùÝöùÝöùÛó÷Ûó÷Ûó÷Ûó÷ÚòöÚòöÚòöÚòö×òô×òô×òô×òôÖñóÖñóÖñóÕðò»º¿ÂÁÆ¡Ÿ¤Œ‹©¨¬£¢¦–•š”“—¨¦­œ›¢ž¦°¯¸‚€[Xe^\j¸¶Ä]]iX\f_ck\_jlq}fj{DH]GLcDJd>Gb=Eb;Da;Da;Da;E_;E_;E];E];E_;E_=Fa;E_?GdGNjPUoQVkSVhlqvx…y{ˆedtedtxw‡xw‡sr‚„‚“‰‰–qq~‰‰•¨¨±ÆÅ̽»À±°·½»ÂŸž¨Ž—“{y†€ˆ˜¤£ª“‘˜”¦¥ª»º¿ËÉÎÈÆÉËÈÌÂÀÅÀ¿Æ»ºÄ¿¿ÈÂÂÌ°´¿}†œ¥ßï÷èùÿáôùá÷ûáùúáùýâøþâøþâøþâøþâøýâøýâùúâùúáùýáùýáùýáùýßøûßøûßøûßøûÞ÷úÞ÷úÞ÷úÞ÷úÝöùÝöùÝöùÝöùÛó÷Ûó÷Ûó÷Ûó÷ÚòöÚòöÚòöÚòö×òô×òô×òô×òôÖñóÖñóÖñóÕðòÐÏÔ¯­²¥¤©¦¥ª—–››šžœ¡‘•‘—ˆ‡Ž–•ž¸·ÀÆÄÐ^\ib_mqo}SS^Z]h[^fadohlx}FL^=AVFMf@Id=Eb=Eb;Da;Da;E_;E];E];E];E_;E_:D^:D^=EbAHdFJdNShMPb]bp„‘ž„‚“‡†–xw‡€yxˆ€šŒtt€ŽŽ—±°·º¹¾¦¥¬¹¸¿­¬¶–•ž¥£°š˜—¡š˜¢•”›š˜ŸŽ‘©¨¬»º¿ËÉÎâßãÔÎÓÄÁÆÌÈÐÏÎ×ÎÍÖ»»Å¦ª´£ª´y‚¬¸Áè÷ÿåùþßöúßøùßøûâøþâøþâøþâøþâøýâøýâùúâùúáùýáùýáùýáùýßøûßøûßøûßøûÞ÷úÞ÷úÞ÷úÞ÷úÝöùÝöùÝöùÝöùÛóôÛóôÛóôÛóôÚòóÚòóÚòóÚòó×òô×òô×òô×òôÖñóÖñóÖñóÕðòÈÇ̪©­–•š¯­²©¨¬£¢¦³²·­¬±£¢¦{z‡†œ›¤¤¢¯ljwVTbVVcNP]Z]hilwbepcfqimyrv‡FL^BJaDJd?Fb>Eb=Da;Da;E_;E];E];E_;E_;E_9B]:D^;Da=D_@Ga?D[FI]ps…ŒŽœž«——¤‚‘‚‘xw‡~Ž€€‡‡”„„vv††¿¾Å²±¶ª©°–•œ‰ˆ‘¡Ÿ©¸·À©¨±»ºÄ­¬³¸·¾¡Ÿ¦—–›­¬±¾½ÁÄÂÇÍËÎÍËι·»½º¿°¯¶³²»ÁÁËÁÁ˾ÁÌ¢¦²‚‰”Ë×ÞåôûèûÿâøýßøûáùýáùþâøþâøýâøýâøýáùúáùúáùúáùýáùýáùýßøûßøûßøûßøûÞ÷úÞ÷úÞ÷úÞ÷úÝöùÝöùÝöùÝöùÛóôÛóôÛóôÛóôÚòóÚòóÚòóÚòó×òô×òô×òô×òôÖñóÖñóÖñóÕðòÇÇÉÏÏÒÀÀÂ¥¥¨““•¨¨ª··¹¯¯±›››œœœ‚…rsy˜˜¢hhtNM]NP_SUdacrllxffrllxrt}ŽfizAEZHJcJNkDGd>A_9@];B^@Ga?Fb?Fc>Eb>Eb=D_;B^;B^=D_;B\AF_DHb@B[LOcmpŽ˜›¨‹‹—zz‡Žrr~xx„tt€‹‚‚Œ¶·½¹ºÀ°±·¥¦¬•–œ«¬²³´º±²¸¯­´²±¶·¶ºº¹¾Ÿž£¡Ÿ¤½»ÀÇÆËÁÀŲ±¶´³¸º¹¾³²·ÐÏÖÆÅÌÄÂɾ½Æ´´ÀŽ‘œ•œ¥Ýêñè÷ÿåøÿá÷ýáûþáûþâúþãúûäûýãûýâýýâýýâúûâúûâúûâúûáùúáùúáùúáùúßøùßøùßøùßøùÞ÷øÞ÷øÞ÷øÞ÷øÛööÛööÛööÛööÚôôÚôôÚôôÚôôÙñòÙñòÙñòÙñò×ðñ×ðñ×ðñÖïðÞÞáÈÈ˽½¿³³¶••—““•ÀÀÂÇÇÉ«««¯¯¯º»¾‰‹Ž[^fTVcPSbX[lVXjbdsoo{±±½››¦~~‰hhtkjz^_tHI_HJeMOlFIf?B_9@\;B^>Eb>Eb>Eb>Eb>Ea=D_;B^;B^;B\@Ga>A^@E^QUjNQcUWd…••¡……‡‡“iitffrllx€€Œ‰‰•°°¹´¶»±²¸£¤ª…†Œ¯°¶¦¨­²³¹¹¸½²±¶¸·»ÁÀŹ¸½›šž¢¡¥»º¿ÉÈ͹¸½¬«°·¶º´³¸Ù×ÜÄÂÇÈÇÎÀ½Ç¥¤­²²»‡Œ–¥¯¹ßëôæ÷ÿäúÿáûþáûûâúûãúûäûýãûýâýýâýýâûùâûùâûùâûùáúøáúøáúøáúøßøùßøùßøùßøùÞ÷øÞ÷øÞ÷øÞ÷øÛööÛööÛööÛööÚôôÚôôÚôôÚôôÙñòÙñòÙñòÙñò×ðñ×ðñ×ðñÖïðßßâÒÒԚšœŸ££¥““•­­°³³¶¯¯¯ŸŸŸ—˜›¯°³z~†jlyNP_QTewy‹hjyzz‡””¡ÈÈÔ\\hllyts„y{PQfEG_HJeGJhAEb=D_>Ea>Eb>Eb=Da=Da>Ea>Ea=D_;B^=D]9@Z@Da?D]PTi^bsdfswz…]]immy‰‰•{{‡jjvzz†††‘““ž¢¢«³´º¹ºÀ²³¹–—‘“˜³´º©ª°¸·»²±¶­¬±¯­²¾½Á¸·»±°´¥¤©¾½ÁÇÆ˺¹¾”“—½»À·¶ºÀ¿ÄÎÍÔÇÆÏ°¯¸½½Æšž©x‰±½Æéøÿä÷þâúþáûûâúûâúûãûýãûýâýýâýýâûùâûùâûùâûùáúøáúøáúøáúøßøùßøùßøùßøùÞ÷øÞ÷øÞ÷øÞ÷øÛööÛööÛööÛööÚôôÚôôÚôôÚôôÙñòÙñòÙñòÙñò×ðñ×ðñ×ðñÖïðÌÌÎ××Ú³³¶ªª¬°°²¬¬¯ššœ””–¡¡¡¥¥¥£¤¦¤¥©˜œ¤‚…‘WZiTVhpr„\^m~Žffs““ŸZZe‰‰–¡¡­¤¦¶twˆEH]FHaLPjGJh?Fb;B^=Da=Da=Da=Da=D_>Ea>Ea=D_>E^:A[;?\@E^NQfcfx…‡”£¦±ŒŒ—€€Œww‚„„{{‡‹œŽŽš˜˜¢´¶»³´º³´º¤¥«‚ˆ¤¥«¡¢¨´³¸³²·±°´·¶ºÌËϽ»À¾½Á¹¸½¿¾Â»º¿ÆÅɘ—œÌËÏÂÁÆìëðáßæÄÂÌÇÆ϶¶¿°³¾y€‹‰“ë÷ÿãôûãùþáûûâúûâúûãûýãûýâýýâýýâûùâûùâûùâûùáúøáúøáúøáúøßøùßøùßøùßøùÞ÷øÞ÷øÞ÷øÞ÷øÛööÛööÛööÛööÚôôÚôôÚôôÚôôÙñòÙñòÙñòÙñò×ðñ×ðñ×ðñÖïðÄÄÆÌÌÎÏÏÒ¸¸ººº½³³¶ÂÂŬ¬¯˜˜˜“”–—˜œœŸ¨‡‰–LN]IL]qs‚lo~kjzNM]FFSOO\eeq~~‹y{ˆ…‡–_cw>@XOTmFIfAHd>Ea>Eb>Eb>Eb>Eb9@\:A]>Ea>Ea=D]=D];?\HMfHLa\_q„¬°º¯¯º““ž††‘‰‰•ŽŽšˆˆ”——£ˆˆ”¦¥¦¬¯°¶²³¹¸¹¿š›¡Ž”°±·´³¸¹¸½±°´º¹¾ÅÄÈÇÆ˽»À¹¸½ÅÄÈÈÇÌÅÄȤ£¨¨¦«ÖÕÚãâæÝÜãÔÓÜËÉÓÀÀÉÍÐۖ›¦—¢ÈÎÜë÷ÿãùþãûýâýýâýýâýýâýýãûýãûýãýúâûùáúøáúøáúøáúøáúøáúøßøùßøùßøùßøùÞ÷øÞ÷øÞ÷øÞ÷øÜ÷÷Ü÷÷Ü÷÷Ü÷÷ÛööÛööÛööÛööÙñòÙñòÙñòÙñò×ðñ×ðñ×ðñÖïðÀÀ··¹ËË;¾À¨¨ª¶¶¸ÈÈËÇÇɲ²²ªªª¬­°Ÿ¡¤˜œ¤›ªGIXIL]SUdehw„‚“XWhML\^^k]]ipp{Ž‘œ¯±À“–ªILdINhHLiAHdAHd?Fc>Eb>Eb>Eb:A]:A]=D_>Ea>E^@Ga>A^AF_AEZ]arrt{‰}}ˆˆˆ”……›——£¥¥±ŸŸ«¦¦°ª«±¸¹¿¶·½­¯´±²¸†‡ª«±»º¿ÌËÏÂÁÆ«ª¯¹¸½ÍÌÐÄÂǺ¹¾·¶º½»À¾½Á±°´¿¾ÂßÞãÄÂÇÝÜãíìöÂÁËÇÇÐÐÐÜÅÇԏ”Ÿª¯½ëôÿåùþâúûâýýâýýâýýâýýãûýãûýãýúâûùáúøáúøáúøáúøáúøáúøßøùßøùßøùßøùÞ÷øÞ÷øÞ÷øÞ÷øÜ÷÷Ü÷÷Ü÷÷Ü÷÷ÛööÛööÛööÛööÙñòÙñòÙñòÙñò×ðñ×ðñ×ðñÖïðÁÁĺº½ÅÅǾ¾À“¥¥¨»»¾ÁÁÄÅÅž¾¾³´·°±´¬°¸”–£LN]OQc\^macr…„”ji{TScbboZZejjs¯²ºº½É_ctVZo@E^>A^FMiBIeBIf?Fc>Eb>Eb:A];B^=D_=D_?F_>E^@Da@E^GJ_\_q{~‹z~ˆss††‘……‹‹‹–˜˜¤““ž›¤¤­ÍÎÔ»½Â³´º¤¥«Ÿ¡¦­¯´ž¤±°´¿¾ÂÏÎÓ­¬±±°´ž¢¯­²ÂÁÆ¿¾Â³²·¸·»«ª¯º¹¾¿¾ÂÍÌÐ÷öýÙ×áÄÂ̽½ÆÆÆÒ××䚜©“¢ÉÐÛèùÿâúûâýýßþýßþýâýýãûýäûýãýúâûùáúøáúøáúøáúøáúøáúøßøùßøùßøùßøùÞ÷øÞ÷øÞ÷øÞ÷øÜ÷÷Ü÷÷Ü÷÷Ü÷÷ÛööÛööÛööÛööÙñòÙñòÙñòÙñò×ðñ×ðñ×ðñÖïðÇÇÉÄÄƽ½¿½½¿¦¦©¹¹»±±³³³³ÁÁ¿ÇÇDz³¶°±´ªª³¡¡­NM_IL]UWiik}PObqp‚edt^^kaaljjs­­·©¬·}Ž{}‘SUmFHcHLiGJhDJfAJe>Gb>Ea:A];B^>Ea=D_?F_9@Z@DaFJd=?WLOc~‚Žwz…ooz‚‚Ž——£‚‚Žˆˆ”––¢……–—•–œ³´º¿Àƌ“¡¢¨½½Æ¦¨­¨¦«ÅÄÈÎÍÒ´³¸¬«°·¶ºÀ¿Ä²±¶»º¿ÅÄÈÇÆ˶´¹À¿ÄÎÍÒÜÛßÔÓ××ÖÝãâé¿ÀƬ¬¶ÀÀÌ´´ÁŒœ¡¬áð÷åùûâúûâýýâýýâýýãûýãýúãýúâûùáúøáúøáúøáúøáúøáúøßøùßøùßøùßøùÞ÷øÞ÷øÞ÷øÞ÷øÜ÷÷Ü÷÷Ü÷÷Ü÷÷ÛööÛööÛööÛööÙñòÙñòÙñòÙñò×ðñ×ðñ×ðñÖï𑑑ÀÀÀ«««ËË˾¾¾ÁÁÁ¹¹¹½½½ÄÄÁ¿¿¿ÎÎЭ¬³¯¬¹Ÿ­PMaNMbHF]qo†\[pzyŒ€\\i‚‚Žllv}~„‚‚Œ˜˜¤­¬½›²NLeNLhJMjJTlGTkANe=F^=D]:A[;?\=@]?D]?D]?B_?B_AF_?DXfmx~„‹swvvŽŽ—~~‡ww„žž«‡‡“–¥¤«‹‰“£¢«“‘˜œ›¤¯¬¹¡Ÿ©¢¡¥²²´ÏÏÒÀÀÂÆÆȱ±³££¥±±³½½¿ÈÈËÇÇÉÂÂÅÄÄÆÇÇÉÍÍÏÌÌÎñðôöôùÈÇÌ­¬³¿¾ÅÅÄ͑œ–š¤¸ÂËæöýäöùëÿÿãýúãþûÞýùâþøäþùãýøâû÷âû÷áúöáúöáúöáúøßù÷ßù÷ßù÷ßù÷ÞøöÞøöÞøöÞøöÝ÷ôÝ÷ôÝ÷ôÝ÷ôÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòðÙòðÙòð×ñïxxxÌÌÌÒÒÒ¶¶¶ÌÌ̜œœ‡‡‡°°°¦¦¤¢¢¢¸¸º²±¸°­º¢Ÿ­OL_LH^VTkWUlMLahfy˜˜¥ddpœœ¥ss}iho…„‹¥¤­±¯½ÉÆډ†XToTTpDMeN[rDPh>G_:A[:A[>A^>A^?D]?D]?B_?B_BFc=AXZ_my~ˆtx‚ppyˆˆ‘‰‰•““Ÿ‹‹—zz‡””ŸŽ—Ÿž¥–•žŒ••”Ž—¶³À»ºÄš˜²²´ÁÁĸ¸º¿¿ÁÇÇɬ¬¯¡¡£»»¾ÆÆȾ¾ÀÁÁÄÀÀÂÅÅÇÌÌÎÇÇÉããåÂÂÅÒÐÕ·¶ºÀ¿Æ¿¾Å«ª³ŽŽ—¢©²áëóåöùéúþåýûáúøâÿúÞý÷ãÿùãýøâû÷âû÷áúöáúöáúöáúößù÷ßù÷ßù÷ßù÷ÞøöÞøöÞøöÞøöÝ÷ôÝ÷ôÝ÷ôÝ÷ôÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòðÙòðÙòð×ñïllj––”ÏÏÍ¿¿½¸¸¶~~{……‚¿¿½¡¡žžžœ££¥±°´©¨±«©¶HFTOM]XWjXWjSQb\\ittww‚{{…fhmomt–•œ¯­·¾»È½ºË²¯Â[WmVWpBIcQ[vISmBIe=D_>A^?B_>A^?D]?D]?D]?D]?B_BG^MSasx„xz‡zz‡Œ––£{{‡šš£……Ž‚‚Žzx…˜–£œ¥—–Ÿ—–Ÿ–•žª©°ÇÆÍ°¯³³³¶««­±±³²²´««­¿¿ÁÆÆȳ³¶ÁÁÄÇÇÉÁÁĹ¹»½½¿ÈÈËÈÈ˽½¿ÇÇɸ·»ÁÀŹ¸¿ÉÈϹ¸Á‹‹”Œ›ÆÐÙíúÿèùýåùùåÿýßûöáÿùãÿùãýøâû÷âû÷áúöáúöáúöáúößù÷ßù÷ßù÷ßù÷ÞøöÞøöÞøöÞøöÝ÷ôÝ÷ôÝ÷ôÝ÷ôÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòðÙòðÙòð×ñïaa^yywÙÙÖËËÈÈÈƅ…‚wwt¡¡žŸ¡œ´´²ŸŸ¢¦¥¬œ›¤VTaLIVNN[UUbPP\EEPaalMMVMNThiokjož¢½»Â£¢«ÄÁÏ»¹Éws‡QPeFJdMTpNUqHLiEHeAEb>A^=A[?D[?D[?D]?D]?B_DHbBHXqvsv‚qq~wv†——¤……Ž‹Œ‘˜””Ÿqo}Œ‰–¥¤­¨¦°›¨›š£–»º¿¸¸º¸¸º»»¾ÄÄÆ»»¾´´·²²´íí𸸺ÅÅÇÂÂÅÅÅǽ½¿´´···¹¹¹»¨¨ªÉÉÌÄÂǺ¹¾±°·¸·¾ÈÇОž¨‚‡‘“š£Üéíâòöæúúãúùãÿùâþøäþùãýøâû÷âû÷áúöáúöáúöáúößù÷ßù÷ßù÷ßù÷ÞøöÞøöÞøöÞøöÝ÷ôÝ÷ôÝ÷ôÝ÷ôÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòðÙòðÙòð×ñïVWSopk½¾¹½¾¹Œvwr€}”•‚„²³¯³³±„„„¡¡£Œ‹–•œkjqddmTT]TT]OOXZ[aTU[NOSdeiomrˆ‡Œ¥¤©£¢©ÂÁ˦¤±qo}edtSVk?D]OTmOTmHLiBFc;?\>B\?DX?DX>BZ>B\AEb>B\?EWzsv‚oo{ml}š––Ÿ‚„‰¦““ž›rp}Ž—±°¹°­ºž¦Œ‹‘Ž“±±³ÁÁÄÄÄƺº½ÆÆÈÆÆÈÉÉÌÌÌθ¸ºÌÌÎÇÇÉÅÅÇÀÀÂÀÀºº½²²´ºº½èèêÜÛß¿¾Â½»Â¸·¾ÍÌÕÈÈ҇‹•y€‰©³¹íúÿä÷÷åýûäþùáý÷åÿúäþùãýøãýøâû÷âû÷âû÷âû÷ßù÷ßù÷ßù÷ßù÷ÞøöÞøöÞøöÞøöÜöóÜöóÜöóÜöóÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòðÙòðÙòð×ñïTUPbc^}~y«¬¨¢£žbc^‡ˆ„stpklh‹Œ‡‹‹ˆ€€~€€€‡‡‰°°²°¯³ijm]^bXZ]deiZ[^\]aTUXefjooqwwy¹¹»œ›Ÿ¹¸¿Œ‹”°¯¸††“cewLOdPTiPSkOQlIMj?B_>B\?DX@EZ?D[?D]?B_?D]BH[jo}€‚{{ˆddqxx…œœ¨††‘‘›˜˜¤£¡­“xw€Ž—°¯¸±°¹˜—ž†…Œ«ª¯»»¾ÄÄÆÉÉ̾¾ÀÌÌÎÈÈËââäÍÍϸ¸ºËËÍÉÉÌÇÇÉÂÂÅÁÁÄÂÂÅ»»¾ÀÀÂÄÂÇÈÇÌÇÆͨ¦­ÒÐÚÅÅΟ£­{€‹†”Ôáååøøåùùãýøãÿùåÿúäþùãýøãýøâû÷âû÷âû÷âû÷ßù÷ßù÷ßù÷ßù÷ÞøöÞøöÞøöÞøöÜöóÜöóÜöóÜöóÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòðÙòðÙòð×ñïUVOef_lmf©ª£‰‹„WXQ„…~mohde^stpjkfddbaa^eeeŸŸŸŽŽŽijljkm^_b]^aefi„…‡LMOijlrrtkkkžžž‹‹——šjimfel…„}}ˆfivUWiPQfPSkOTmDGd;B\@EZAGZ@E\?D]?B_@E^BH[SWey}‡yy‚mmwww‚……‘Ž……‘šš¥Œ•Ÿž¥Ÿž¨‚‹”“œ³²»š—¤Œ‹”¤£¨»»¾ÅÅÇÕÕ×ÏÏÒÆÆÈÔÔÖÈÈËßßâ»»¾ÍÍÏÇÇÉÈÈËÌÌÎËËÍÆÆÈÂÂÅÎÎÐÇÆˬ«°¶´»«ª±º¹ÂÎÎ×¾ÁÌ{€‹‚‰Ÿ¬±ãóôæúúæþúåÿúåÿúäþùãýøãýøâû÷âû÷âû÷âû÷ßù÷ßù÷ßù÷ßù÷ÞøöÞøöÞøöÞøöÜöóÜöóÜöóÜöóÜöóÜöóÜöóÛôòÚóñÚóñÚóñÚóñÙòðÙòðÙòð×ñïUVQ[\Wfhclmffha^_Xef_cd][\U^_[^_[Z[ViifccaŽŽŒttrceddfe]_^TVUehf{}bcekmlsssiiilll‘‘‘}}}vvxxxz{z‰ˆ€‡ddpVXhTUjTVoINh@E^AGZAGZ@EZ?D[?B_@E^>BWBFWkmz€€‰llvxx„‡‡”jiytt‚‚Ž‰ˆ–•œŸž¨¨¦°zy‚ˆ†“ŽŒš…‚”“—¤¤¦··¹ÄÄÆÎÎÐÎÎÐÐÐÓ××ÚÔÔÖÛÛÝÏÏÒÎÎÐÏÏÒÌÌÎËËÍÍÍÏÅÅÇÈÈËÂÁƾ½Ä´³º·¶½²±ºÒÐÚÒÒ݌š„‰~ˆŽÞïðêýýêÿþãýøåÿúäþùãýøãýøâû÷âû÷âû÷âû÷ßù÷áø÷áø÷áø÷ß÷öß÷öß÷öß÷öÝôóÝôóÝôóÝôóÝôóÝôóÝôóÜóòÛòñÛòñÛòñÛòñÚñðÚñðÚñðÙðïOOM[[Xiif…†klhefbcd]cd]NOJTTQUUSQQOppmppmXXVddbjjhbb_[[XWWUffdjjjjjjffdhheddbaa^^^\iifxxxqqqttrmmmomr__iPO_LMbGIbAD\AEVLOaNQf?AZAD\?AZ?BWBFZacr††‘}}ˆkkx‰‰–ˆˆ•ddq‹ŽŽ—‡‡˜˜˜¢ªª¶‹‹—˜˜¥˜Œžž¡°°²ÀÀÂËËÍÀÀÂÏÏÒÔÔÖÈÈËÛÛÝÅÅÇÒÒÔÆÆÈÇÇÉÆÆÈÍÍÏÆÆÆËËÍÉÈϾ½Ä­¬±¥¤©³²»ÆÄÐÒÐÚ·¶¿‡‹“‘šê÷ùéûùéþúäý÷æÿùåþøäý÷äý÷ãûöãûöãûöãú÷áøôãøôãøôãøôâ÷óâ÷óâ÷óâ÷óáöòáöòáöòáöòßôñßôñßôñÞóðÞóðÝòïÝòïÝòïÝòïÜñíÜñíÜñíSSSNNNkkissqqrm]^Zbc\ijeQQOLLIZZWMMJbb_ŽŽŒ^^\ZZWiifbb_\\Z[[Xbb_hheaa^ddbeecVVTddbaa^__]eeceecmmksrojieiii…„ˆ__kTScTVhQUfLP\UZeUXjEH\AEZ?AZ?BWBDXIHXlly††“}}‰‚‚Ž©©´¤¤°„„__kppyˆ””Ÿ••¢””¡¡¡­ªª³”žž¡´´···¹ËËÍÉÉÌÅÅÇÎÎÐÇÇÉÛÛÝÍÍÏÓÓÕÄÄÆÉÉÌÓÓÕÝÝßÚÚÚÆÆÈÆÅÌÄÂÉËÉ͜¡¯­·¿½ÉËÇÒÖÕޜ£•šßíìëþûéþùæÿùæÿùåþøäý÷äý÷ãûöãûöãûöãûöãøôãøôãøôãøôâ÷óâ÷óâ÷óâ÷óáöòáöòáöòáöòßôñßôñßôñÞóðÞóðÝòïÝòïÝòïÝòïÜñíÜñíÜñíJJJMMM]][hhefhcbc^_aZfhcTTQOOM__]TTQUUS‚‚€~~{SSPbc^]^Z[\W\]Xbc^efb^_[efbiifPPN]][eechhemmkjjhddbppmbb____Ž{}‚^^hZZeX\dU[_\biZ^jFI[@DW@DXBF[HI^TSeiivzz‡ww‚–£¤ª²³¹ÅÆ̉‰•bboZZfŽ““žŽŽ—““œ¶¶¿¶´¹œœž¸¸º¸¸ºÂÂÅÒÒÔÂÂÅÐÐÓÔÔÖÔÔÖÓÓÕÐÐÓÇÇÉÆÆÈÈÈË¥¥¨···»»¾ÄÂÇÉÈÍ¿¾ÂÇÆ˛š£²±ºÍÉÔ¾½ÆÀÁǎ”˜Üêéåøöéþùäý÷æÿùåþøäý÷äý÷ãûöãûöãûöãûöãøôãøôãøôãøôâ÷óâ÷óâ÷óâ÷óáöòáöòáöòáöòßôñßôñßôñÞóðÞóðÞóðÝòïÝòïÝòïÝòïÜñíÜñíAAALLLUUSccabc^Z[V^_Xab]WWUXXVUUSNNLXXVeec““LLI[\WWXTWXTZ[V_a\\]X\]X_a\^^\ZZW]][eeceecmmkjjhZZWffd[]\^a_vxwˆ‰Œklo]^bZ[]X]\]bcX\dDFS@BTDEZLMbTVhLJ[aam~~‰€€‰…†Œ¡¢¥¶·¹ÆÇËÆÆ҅…‘XWhbbo‹‹”„…‹——¡¤¥«ž¢¥¥¨¨¨ªÀÀ¶¶¸»»¾ËËÍÌÌÎÀÀÂÌÌÎÆÆÈÆÆÈÎÎÐÉÉ̎Žyy{‹‹‹¨¨ªÂÂÅÀ¿Ä¿¾ÂÕÔÛ«ª±“‘›¹¶ÀÏÎ×Ö×ݕ›ŸÍÛÚèúøêÿúèÿúæÿùåþøäý÷äý÷ãûöãûöãûöãûöãøôãøôãøôãøôâ÷óâ÷óâ÷óâ÷óáöòáöòáöòáöòßôñßôñßôñÞóðÞóðÞóðÝòïÝòïÝòïÝòïÜñíÜñíHHHNNNVVTffd]^ZNOJQSL_a\WWU\\ZPPNPPN^^\cca}}zVVTXZU_aZWXQ_aZab[\]VZ[T_a\bb_]][ZZW^^\eeceecddbUUSX[ZX[Z^a_ceddddiiihheTVSTVQ[]Z^_cQQ[IIVEDTML\^]m]]jaaliir‚ˆ}~‰‹¯±°¾¿ÁÉÉÓÎÎۇ†–QQ^qqz˜šŸ˜˜¢–~}ÅÅǬ¬¯¿¿ÁËËÍÀÀž¾ÀÍÍÏÆÆȸ¸º½½¿ÎÎÐÍÍÏÓÓÕÎÎÐÌÌί¯±¾¾ÀÉÈ͸·¾½»ÂÐÏ֓‘˜¹¶ÀÍÌÕÜÝãÍÓ×Õãâéûùæû÷ãûöæÿùåþøäý÷äý÷ãûöãûöãûöãûöäùöäùöäùöäùöãøôãøôãøôãøôâ÷óâ÷óâ÷óâ÷óßôñßôñßôñÞóðßôñßôñÞóðÞóðÝòïÝòïÝòïÝòïOOOGGGQQO]][VWSMNIUVOab]]][__]__][[XVVTaa^aa^]][TUPhibUVObc\bc\]^WWXQ^_[[[X]][VVT]][eecddbddbVXWSWXX]^[\^bdcbbbccalljWXTZ[Vbd_abdTU[JJTFFQJJVWWcaajijp\]c€„z{~}~¥¨¦±²´ÁÁËÐÐÝÍÍÚss€^^j››¤››¤¢¢«š˜¿¿Á³³¶ÅÅdz³¶ÈÈËÂÂÅËËÍÓÓÕÒÒÔªª¬ŸŸ¢¹¹»ËËÍÓÓÕÔÔÖããåééë··¹ÄÂÇËÉв±ºÂÁȯ­´¯«¶ÁÀÉÎÏÕÙÞãÛéèèúøæû÷äý÷æÿùåþøäý÷äý÷ãûöãûöãûöãûöäùöäùöäùöäùöãøôãøôãøôãøôâ÷óâ÷óâ÷óâ÷óßôñßôñßôñÞóðßôñßôñÞóðÞóðÝòïÝòïÝòïÝòïZZZPPPQQOOOMPQMNOJWXQ^_[[[X^^\__]\\ZVVTcca^^\ZZW_a\de^XZSde^ab[_aZbc\TUPZZW]][\\Z__]cca__]aa^VXWUZ[PUWNSUcdfiik]]]cab[ZWVWSccaddd\[_WV]TSZSQVTSW]^bvwycdfqrttwvwyx›œ˜šœ©ª°ÉÉÓ××á¾¾É[[hxx…ˆˆ•””‰ˆ½½¿ÝÝߢ¢¤°°²¾¾ÀÌÌÎÂÂÅËËÍÒÒÔ¦¦©––˜½½¿ÆÆÈÕÕ×ÚÚÜææéææéÛÛÛ¿¿ÁÍÌÕ¿¾ÇÍÌÓÈÇӟª·¶¿ÀÁÇÝãèáïíèúøèýøåþøæÿùåþøäý÷äý÷ãûöãûöãûöãûöäùöäùöäùöäùöãøôãøôãøôãøôâ÷óâ÷óâ÷óâ÷óßôñßôñßôñÞóðßôñßôñÞóðÞóðÝòïÝòïÝòïÝòïXXXaa^XXVTTQOPLOPLWXQZ[V[\Weec__]bb_[[X__]XXVUVQbc\de^STMXZSde^cd]]^WUVOWXTSSP]][XXV]][XXV[[XTVSNSTGLMEIJ_acaac]]]b_a_^\VVTcca^^^VVXTTVWWZZZ\WWWZ\[fih_ba_bafihqsrŽ‚…‹Œ‘ÅÆÌÖ×ÝÒÓÙ¥¥±vveeq‡‡“‡†Ž“âá囚ž´´·ÅÅÇÍÍÏÈÈËÈÈËÒÒÔÍÍÏææéÒÒÔÍÍÏÌÌÎßßâååèññóäääÇÇÉÆÅÌÐÏÖÈÇÎÄÂǽ¹Á­¬³ÅÆÉÎÔÖßëëé÷ôêûøêýùéþùåþøäý÷äý÷ãûöãûöãûöåúöäùöäùöäùöäùöãøôãøôãøôãøôâ÷óâ÷óâ÷óâ÷óßôñßóóßóóÞòòßôñßôñÞóðÞóðÝòïÝññÝññÝññUUS\]X_a\UVQQSNIJFOPL\]XZ[V]^Z^_[]^Z]^ZWXTZ[VXZUab[de^[\UMNG^_XWXQZ[TZ[TVWSWXT_a\XZUab]QSNXZU\\ZNPMVXWGIHXXXeee^^\aa^ccaUUSeecddbZZZSSS___[[[OOO[[[hhhXXVddbjjhyyw‡‡‡€€‚Œ‹¿¾ÅÒÒÔÈÈ˶´¹£¢¦~}„^]fvs€ˆ†“Ÿž¨“‘˜ž¢ÞÞáÒÒÔÌÌÌÇÇÉÅÅÇßßâÉÉÌÖÖÙÐÐÓÒÒÔÙÙÛÞÞÞêêêêêêÕÕÕÅÅÇÈÈË××ÚÀÀÂÀÀ··¹½¾ÀÉÎÏËÒÒÙßÝìöòìøôêýùéþùèýöåúóéþ÷åúóäùòäùôãööä÷÷ä÷ôåøöãöòåøôåøôâôñâôñâôñâôòáóñáóóáòöáòöáòöáóñáóðáóñáóñßòòßñôßñôßñôOPLUVQ]^ZZ[VHIEEFAMNIXZUTUP]^Z^_[WXT^_[XZUTUP]^Z\]V]^WXZSEF?WXQUVOZ[TZ[TXZU]^Z[\WZ[V^_[STOQSN]^ZSSP\\ZPPNVVTZZWQQOWWU\\ZPPNXXViif^^\LLIUUSWWUSSP^^^cca_a\cd_rsowxsttrqqs{z—–›¤¤¦‚‚‚ŸŸŸ¹¹»˜—œ€‰dbp}zˆ‡…‘Œ•–•œ©¨¬ÂÂÅÀÀÀÇÇÉÐÐÓÖÖÙºº½ÏÏÒÛÛÝÈÈËÉÉÌÝÝÝ×××ßßßÖÖÖÄÄÄÍÍÍÌÌÌÅÅÅÁÁÁÇÇÇ°²±ÅÇÆÅÉÈÂÇÆìñðïøôìþúêÿúéþ÷éþ÷åúöèýøåúóâ÷òæù÷åøøãöóâôòæùöãöòâöïåøôâôñâôòáóñßòòáóóáòöáòøáòöáóñáóñáóñáóóßòòßñôßñ÷ßñ÷IJFQSN^_[^_[JLGBD?GHDTUPSTO\]X]^ZVWS^_[XZUTUP]^ZZ[T[\U]^WGHAVWPVWP[\UXZSXZS^_XZ[TXZS\]VNOHOPI^_[]^ZZ[VQSNSTO^_[QSNSTOXZUQSNWXTjkfXZUMNIQSNZ[V[[X__]eec_a\de^fhadeappmlllihl~}rrp€€~¥¥¥‰ˆ£¢©ecpb_lljw´³½´³ºœ¡±±³––˜´´·ÕÕ×ÛÛÝÜÜÞÍÍÏÖÖÙÈÈËÐÐÓÓÓÓÐÐÐÜÜÜÓÓÓÍÍÍÔÔÔÆÆÆÈÈÈÆÆƾ¾¾‹Œ¹»ºÎÓÒÁÆÅ×ÜÛôþúìþøèûôåøôåøôéûùåøöä÷ôæù÷âôòãöóåøôæùöãöòåøôåøôâôñä÷ôãöóâôôâôôáóóáòöáòöáòöáóñáóñáóóáóóßòòßñôßñôßñôHIETUPbc^^_[MNI>?:EFA[\WXZU\]X]^Z\]X]^ZWXTZ[VXZU^_X^_Xbc\STM[\U[\U]^WVWPZ[T]^WZ[TUVO[\UHIBQSL_aZUVQWXTWXTQSN\]XOPLSTO]^ZPQM\]XijeXZUVWSUVQbc^^_[ccaab]fhacd]bc\de^efb___aacqqsmmmhheqqo{{~ÂÁÆ{z„\[d[Zcdcjjimˆ‡Œ¨¨ª••—««­ËËÍÇÇÉßßâÔÔÖÕÕ×ÕÕ×ÓÓÕÙÙÙÙÙÙßßßáááÔÔÔÍÍÍÕÕÕÌÌ̹¹¹ÉÉ̜Ÿ²³¶³¸¹ÐÕÖÌÐÒéððíûùëûúìýûéùúéùýéøÿè÷ÿåôûå÷úä÷÷ä÷÷ãöóä÷ôâôòâôñä÷ôä÷ôãööãööâôôáóóáóóáóóáóóáóóáóóáóóáóóßòòßòòßòòßñôIJFIJFZ[V\]XLMH9:6BD?]^ZVWSUVQXZU\]X[\WZ[V^_[WXTab[\]V]^WXZSNOHTUNXZSXZSZ[Q]^UbcZ\]T\]TTULWXOZ[TSTMXZS]^WLMFZ[T[\U_aZbc\OPI]^Wde^bc\^_XWXQab[]^Z[\W^_[hibde\abX_aZ]^Zbb_VVXjjljjh_a\jkfccahhj¦¥ªÌËώ”•”›zy~~‚ªª¬¥¥¨bbd­­°¾¾À¤¤¦¾¾ÀÀÀÂÍÍÏÓÓÕÒÒÔÜÜÜåååßßßâââÜÜÜÔÔÔÈÈÈÁÁÁÆÆÈÅÄȝž¢°±´­²´ÂÇÉÛßââèêêôøéöúÕâé·Â̗£±—¢´š£¹¡«¾·ÅÓ°À˺ÌÓÍÞäâó÷áóóÝðíä÷ôÝððáóóáóóßòòáóóáóóáóóáóóáóóáóóáóóáóóáóóáóóáóóáóóLMHLMH\]X^_[QSN?@;EFA[\WOPLQSNZ[VVWSZ[VZ[VZ[VUVQ\]V[\U]^W\]VJLEPQJUVOUVOWXO\]TabXZ[QQSITULXZP[\SOPIWXQbc\UVOTUNWXQ[\U[\UNOHde^ab[[\U\]VUVO]^WXZS\]Xcd]Z[Q]^Ucd[]^UZ[T__]ZZZfffbb_Z[V_a\bb____‹‹žž¡¶´¹²±¶‰ˆ~~€œœž¸¸ºkkm‚‚…‘œœž¨¨ª´´·²²´½½¿ÍÍÏÐÐÐÚÚÚßßßéééæææÛÛÛÌÌ̸¸ºÎÍÒ¿¾Â¡¢¥©ª­«°²´¹»ÎÓÕáæëÏÖÝÇÎ×­´¿Ÿ¥¶¬±Æ¢»„‡¤„‹¦„¥€Ÿ‹›¨—¨²¨¹ÀÆ×ÝÜíñÞðóäöùâó÷ßñôÞññáóóáóóáóñáóñáòöáòöáòöáóóáóóáóóáóñáóñVWSQSN[\WZ[V[\WGHDHIEXZUNOJSTOZ[VXZUZ[VVWSXZUOPLWXQXZSWXQXZSGHAMNGPQJQSLSTJ[\S_aWUVMMNEQSIUVMZ[Q\]VQSLXZSXZSVWPZ[TXZSUVOOPIde^^_XWXQ\]VTUN[\UUVOZ[VZ[T]^U_bU^aTbcZUVOaa^\\\bbb\\ZXZUbc^iif^^^iiikkk´´·¡¡£‡‡‰œœž¥¥¨jjl‡‡‰ÄÄƔ”–††ˆ••—¹¹»¹¹»„‘‘‘ÒÒÒðððÔÔÔÙÙÙåååÙÙÙÆÆÈÂÁÆÎÍԚ›¡¬­³°³¹¥©¯»¿ÅÞâèëñóÖÜáÇÌÖ¸½Ëßãø±³ÎŽ­„‹¨~‹¢z‰›}ŒŽŸŽž­¡­¢²¿¾ÎÙÙêñãôúáòöÝððáóóáóñáóñáóñáòöáòøáòöáóóáóóáóñáóñáóðWXTTUP]^Z\]Xab]MNIJLGWXTIJFPQMNOJ[\W[\WPQMZ[VIJFUVOVWPOPIPQMDE@IJFNOJNOJPQJZ[Q^_VSTJOPGOPGNOFXZP[\UNOHUVOVWPIJDSTMWXQUVOPQJ\]V_aZZ[T^_XUVO[\UTUP\]XZ[V_aWcd[WXO\]Tab]^^\]][aa^\]X^_[dealmieecbbbTTQddb———¢¢¢}}}«««›››SSUyy{»»¾……‡ppr€€‚¾¾À˜˜›†††ppp¶¶¶äääíííæææâââÝÝÝÏÏÏÄÂÇÄÂÇÁÂƎ“±²¶—˜œ¨©¬ÒÖÙèìïÓ×Ú»¿Ç½Á͸»Ïœž·€‚Ÿ…¢}†ž{ˆ{ˆy†›x…š}‰žˆ•ª•£´¡¯½ËÜãáòøáòöáóóáóñáóðáóðáóóáòöáòöáóóáóóáóñáóñáóñSTOVWSWXTUVQUVQVWSPQMXZUGHDJLGOPLab]LMH[\WPQMIJFQSNMNISTOTTQIIGNNLLLLMMJSTOSTMUVOUVOQSLWXQSTMVWPVWPPQJOPISTMGHAQSLPQJSTMUVMUVM\]V^_XXZUUVQUVQMMJTTQZ[V]^W_aZab[[\UXZU^^\[[X\\Zdeade^ab[lmiffd__]UVQZ[Tjkf}~yool‚‚€iifXXXlll¡¡¡ŒŒŒ•••žžž~~~hhhddb““¸¸¶ÎÎÌååãÕÕÓÖÖÔ¯¯¬ÇÇǺº½ÐÐÓ±±³©©«ŸŸ¢Ÿ¥¦©ÍÎÒæèí¯¯¸©«¸›¯Ž¥†‡Ÿ„†¡ˆ¤”˜­Ÿ¤»¤©À“—¯‰Ž¨„t{•…¢¤²ÂÕåðáòøÞòòßôðßôíãøñâôñãóòãóòãóôâòóâòöâòöâòöJLGPQMVWSVWS[\WXZUNOJSTOGHDIJFUVQ_a\LMHWXTNOJEFAQSNLMHNNLUUSLLLLLLGGIJJJOPLPQJTUNTUNSTMMNGZ[T[\UVWPPQJOPISTMNOHLMFSTMOPIUVMUVMXZS]^WVWSTUPOOMHHFPPNXXV]^Zab[cd]^_[XXV]][^^\__]^_[^_XUVO^_[XXVTUPQSLVWNcd]mohbc^xytffd\\Z___qqq‡‡‡†††vvvmmm\\\^^\qrm¦¨£‰‹†×ÙÔ«¬¨ÄÅÀ‹ÌÌÌ···ÎÎÎÐÐÐÛÛÛ½½½ºººªª¬ÝÜãÒÐÚ¾¾É¬«»”–¨‡ˆˆ‰¢‰‹£…‡Ÿ†ˆ¡“«›¶¢¤½–˜±Ž©„ˆŸy‚˜‡•¦£±¿ÕæíÞðóÛðìáöïßôíâôñãóòãóòãóôâòóâòöâñøâñøFGBNOJTUPVWSVWSTUPJLGOPLPQMLMHZ[VTUPJLGVWSVWSNOJOPLMNIMMJSSPPPPGGG@@BGGGIJFQSLMNGUVONOHWXQ]^W^_XVWPPQJOPISTMUVOHIBOPINOHUVMUVMVWN[\UQSLUVQQSNPQMOOMVVTWXTZ[Tef_deaZZW^^\[[Xaa^XZU^_X[\U]^ZPPNJLGUVO\]Tab[bc\TUPefb[[XQQO\\Z__]eeeqqqxxxiiiqqsVVV\\Zffdlljyyw¶¶³{{y——•rrp´´´³³³ÆÆÆÁÁÁÈÈȬ¬¬½½½»»¾âáèÇÅÒÍÌܖ•¨‘“©€˜{{—„„¢€‚Ÿwy–~€›‘ª£¦»²¶ÉŸ£´–©€ˆœ{†˜‚¡¬»ÆÚëñæùùãøôáöñâôòãóòãóôãóôâòóâòöâòöâòö@@>FFDJJHJJHVVTPPNBB@DE@QSNJLG[\WTUPJLGTUPUVQIJFIJFPQMNNLOOMSSSEEE::=EEEHIEHIEQSNQSNNOJQSNTUPZ[VVWSPQMOPLSTOXZUIJFJLGNOHJLBTULXZP]^WNOHSTOMNIJLGPPNPPNOPLPQJ]^Wbc^XXVVVT\\ZccaPQMVWPWXQWXTJJHGHDVWP]^U^_XXZSPQMcd_bb_]][TTQ\\Z___hhh[[[TTV^^aLLNNNNbb_ddbiif‚‚€eecmmk]][ŸŸŸ³³³ººº¾¾¾………{{{žžžÂÂÅÙ×ÜÆÅÎÆÆÒ°¯¿Ö×ì¿Àٕ•±††¤wx—xy˜}œ€‚›‹Ž£”—©¢°šŸ­•©t‘w“Œ›¥ÏÞæÜíóßñôä÷÷ãóôãóôãóôãóôâòóâòóâòóâòö>>>AAAMMMFFFHHHJJJAAA@@>PQMPQMXZUMNI@A=HIENOJEFAGHDFGBFFDGGENNNAAA99;>>>DDA@@>LLIMMJLLIJJHNNLOOMUUSMMJIIGPPN]][SSPJJHMNISTJWXO^_X^_XPQMEFAHHFIIGLLIPPNVWSSTM^_X]^ZSSPTTQUUS]][UVQUVOZ[TUVQHHFMNIWXQ_aWab[]^WUVQ\]XZZWPPNUVQXXVQQO[[[UUUMMOONSGGIPPPZZZZZZ\\\TTT[[[\\\iii‹‹‹Â–––°°°sss€€€ˆˆˆžžž½½ºÉÉÇÀÁŕ•žÅÇÖÐÒ昚²‡‡¥{}œxy˜xz•}—‚†š˜œ­šžª¨­»¢ª½‹“¦r}ŽsŽ¨·ÆÐÜíôÛìòãó÷äôöãóôäôöãóôâòóâòóâòó666;;;GGG>>>DDDAAA>>>AAAMMJJLGPQMNOJBD?HIELMHDE@DE@EFAEEBDDASSSFFF88:::=;;;@@>??=IIGGGELLIGGEFFDOOMLLIFFDQQOXXVSSPNNLHIELMFSTJ\]V^_XWXTGHDFFDDDAGGEJJHQSNMNG[\U]^ZSSPOOMVVTXXVUVQPQJVWPLMHIIGSTO^_Xfh^fhabc\VWSQSNSSPXXVNOJTUPWWUPPPQQTGGIHGLA@ENNP[[[UUU___WWWSSS]]]ZZZbbbœœœ~~~jjjlllooo‚‚‚‹‹‹±²­ÇÈĪ¬«xy¦ª´}Ž‚—xy‘rty{–{~–wz…˜¢šž¬£¨¶¢¨º˜¬rzlwˆr}ŽŒšª²ÀÎãóþâñøãó÷äôøâòóãóôãóôâòñâòñ>>@::=FFHFFHJJMHHJBBE999MMJIJFGHDDE@BD?FGBLMHGHDBD?FGBFFDBB@TTTJJJ88:99;888777>>>BBBLLLEEEGGGGGGJJJFFFGGGQQQQQQUUUOOODDAMNGPQHVWPVWPWXTIJFLLILLINNLLLIMNIHIB^_Xfhc\\ZTTQWWU[[XZ[VUVOWXQOPLHHFTUPbc\ijahibab[XZUFGBJJHXXVOPLXZUUUSNNNSSUPPSQPUDBGJJMUUWPPS^^aJJMHHJPPSbbd^^acceXX[VVX[[]ddf~~€‚‚…¯¯¯–––£¤¦mot˜z~ˆoq~qs‚ps‡{”{”rv‹tx„‡œ‚†›Ž‘¦—œ±‘¦iq…fo‚s~ˆ“¥—¥·ÄÒâÜëóâñøâòöâòóãóôãóòãóòâóð???>>>IIIGGI??A;;>AADAAAEEBHIEEFA?@;?@;BD?LMHMNIDE@IJFIIGDDAQQQNNN:::;;;777666===AAAJJJNNNMMMNNNGGG>>>JJJPPPMMMVVVLLLFFDGHDMNGUVOVWP]^ZOPLQQOQQOJJHEEBHIEFGBUVQbc^VVTJJHOOMZ[VVWSXZUQSNXZULLI]][^_Xde\cd]\]Vab]NOJNNLSSP\]XXZUXXVJJHQQQPPSUTXLJOFEIIHMNMQZZ\PPSMMOOOQ[[][[]]]___bWWZTTViikŽŽooq‚‚…yy{ˆ‰fhkz~„kotfjrilwoq~vx‡rt„fizwx„…›~•„†žˆ‹£…‰¡vz‰‘¤qypyˆ‘ª˜¥ºÅÔÞÛêñßðóßðñâòóãóòãóòãóò480794BEALNM>?A236687FHE@@>FFDFFD>>;GGEAA?FFDHHFDDALLIJJHJJHVVT]][TTQFFD442997;;9@@>DDALLIJJHBB@BEAEGFILJEFHFGINPO@BAGIFDDAIIGUVQ[\WOPLLMH997???GGEBB@FFDEEB]][[[XQQONNLLMHSTONOJWWUSSPMMMIIIUUSef_de\ab[ef_cd_TUPMMJNNLWWU\\Z[[XTUPMMJNNLUUUSSUDEJEFLFGMVW[MNQIJNIJMUVX[[[QQQUUUVVVOOO]]]eeeeeeefi^_bijlabdcdhopshilefjdekqqzssjjwrq‹‰œ˜—¬‡ˆž„„ŸoqŒcfzw}{”s{‘t{˜~‡¢›¬ÌÙßßìïâðïäôöáñòãööáóóAE:AE=EGBDFE9:=013687ILHEEBLLIGGE886663@@>GGEEEB??=MMJPPNQQOaa^bb_XXVTTQ??=@@>;;9DDAFFDLLIJJHAA?JMIEGFGHJFGIDEGIJMJMLFHEFFFGGGVVTXZUQSNTTQ???;;;FFD@@>FFDNNLiif__]SSPSSPMNIOPLOOMJJHQQQFFFDDFBBBVWPab[ab[TUP\]XVVTMMJMMJSSSWWWPPNXZUIJFNNLNNNOOQFGMDDMGHNTU[NOSEFIEFHVWZSSSMMMTTTXXXQQQVVV[[[WWW^_bUVX^_b]^a]^abcebce]^abcfdei{}‚ffpvv‚Œœ«ª½Œ‰¡‰‰¥ij‚advsx†£¦º{€—tx–t{—€ˆ›¤ÛèêäòñãóôäôöÛííä÷÷BF;=@8>@;>@?78:-/1243BEAMMJOOMFFD220220??=EEBGGE@@>IIGJJHLLIUUSbb_ccaaa^OOMEEB997BB@DDALLIJJHBB@DFB>@??@B?@B:;>@ADNPOILHLLLNNNZZW\]XUVQOOMPPP@@@LLIMMJIIGJJHbb___]TTQLLIUVQMNIOOMNNLNNN===::=???PQMXZS[\WGHD\\ZLLIFFFIIIQQQWWWLLIXZUJLGOOMOOOJJMGHL@AGEFLNOSOPTFGIFGIWZXMMMHHHNNNTTTVVVUUUSSSWWWTUWUVX\]_VWZbce]^a]^abcebcfdeibci^^hhhsllyzy‰‹‰žˆ‰¢qrˆikzšœ©¬°Á–š¯qsrwr{‹v€ˆÅÒÔâðïáñôãó÷åö÷ßðñTWMNQI?A=@BA@AD347243?A>NNLIIG??=774>>;;;9==:JJHGGEHHFHHFQQOJJHQQOSSPXXV]][HHF774AA???=IIGMMJGGE:=9;>=@AD=>@;=?;=?EGFDFBGGGOOOOOMXZUWXTJJHJJJ???LLIHHFAA?GGEZZWTTQJJHJJHHIEHIEHHFGGE@@@;;;88:AAA\]XXZUXZUUUSLLI???IIIGGGJJJQQQQQO_a\QSNQQOSSSFFH@AE;=BBDGGHLPQTMNPMONX[ZJJJEEEIIINNNIIITTTTTTVVVOPSNOQTUWVWZXZ\_acXZ\[\^^_cefjfhmQSXeeoddpxx…†…•‘“¨z}Ž_bomq{ž—˜­~—txqw…w‰Ÿª­Üéëäñøâñøãó÷áñò[^TOSJ8:68:9@AD124364>@=LLIJJHAA?::8HHF;;9??=QQOBB@??=QQOSSPNNLOOMOOMNNLZZWEEB::8EEBEEBFFDLLIPPN9;80219:=46801389;>@?@B?FFFQQQSSPNOJXZUSSP>>>BBBOOMLLIGGEFFDOOMHHFBB@BB@EFA?@;AA?DDADDD???::=EEEPPNPQM[[XAA?>>>:::FFHPPSFFFMMMNNLXZUSTOMMJSSSIIL?@D:;?GHLSTVIJMMONNPOUWTOOONNNFFFMMMHHHQQQOOOQQQLMONOQOPSMNPXZ\]^aVWZPQTVWZVW[^_c]^b[\b_afffpmmyz}‰acp[^feiqmp}‰ˆ˜{zoq‚lq{ipw‡“ÅÏÕáëóÞëòßìñéöúTWMLOGDFA;>=@AD679132@B?LLIQQOGGE774MMJEEBAA?NNLEEBAA?PPNLLIWWULLIFFDIIGVVTAA?442??=BB@IIGJJHTTQ>@=79889;/02124>?A?A@GIFGGGEEEHHFQSN[\W^^\LLLLLLLLIIIGJJHEEBIIGIIGAA?GGE@A=DE@HHF@@>BBBHHHEEGBBBJJHMMJZZWBBB@@@NNPGGIMMODDDAAA>>;NOJXZUMMJPPPIIIBDFBDGNOQZ[]LNMLNMJMIQTPVVVQQQEEELLLHHHNNNSSSVVVPQTOPSPQTFGITUW[\^UVXTUWQSUTUW\]_^_cOPTVW[cdhklrdhpSV^W\^efjss}€€vt…oo{eiqekpv~¶½ÄÜãì¸ÁÌâìòâïóMPFMPHNPL=?>=>@78:8:9LNJGGENNLHHF>>;PPNNNL>>;JJHBB@@@>MMJFFDUUSTTQNNLHHFWWU@@>331;;9DDAQQOIIGUUSFHE;>=89;34789;@AD:=;>@=HHH>>>HHF]^Zab]\\ZZZZLLLLLIMMJHHF@@>DDAEEB774IIG@A=;=8FFDBB@DDDJJJNNPGGGGGELLI[[[EEENNPQQT@?DBBE;;;>>>??=QSN^_[LLIJJJIIIGHJHILMNPUWVNPOHJGEGDMOLSSSVVVLLLOOOMMMJJJWWWWWWVWZMNPIJMIJMPQTPQTZ[]WX[LMONOQ[\^_acUVXUVX^_bbceX]_TX[dehdehdcjvt~wtjirbfibhjjpt£©°¿ÆЍ”žÌÖÜÙãæIMBSVNPSN=?>=>@1249;:HJGMMJFFD??=@@>AA?UUS>>;TTQFFDGGEUUSMMJLLIOOMOOMLLI\\ZEEB663>>;FFD[[XFFDNNLBEA8:92364682369;:HJGILHDDDFFDIJFJLG[\WPQMJJH??=GGETTQJJH886>>;MMJ;;9HHFFGB?@;NNLIIGFFFSSSUUU???GGGMMM^^^JJMIILEDHDBG;:?88:>>>==:HIE^_[GGEFFFHHJBDFFGIGHJSUTOQPGIF@B?ILHGGEXXXWWWVVVSSSHHHXXXUUUQTSQSUGHJMNPMNPNOQXZ\\]_QSUPQTVXWX[Z[]\UWV^a_abd^cdPUVacbbdcbaehfmmlsbah^cdejkilrv{‚‚‡“}„Ž»ÂÉÙãæGH?PQJWXTMMM@@B//1BBBBB@JMH:=8362798>@?TVSFHDTVQDE@MNIVWSOPLNOJJLGMNIHIEQQOOOM9:6FGBVWSab]GHDLMHJJJ>>@224224DDDIII??=DDALLIHHFMNINOJVWP^_XSTMHIEJJHMMJDDA442::8886886EEBNNL@@>EEBTTQAA?LLIXXVIIIOOOEEETTVPPSVUZFEIBAH:9>88:???@@>JJH]][AA????JJMABFFGJHIMFGIXZ\MONILJMOLIIGTTQ^^\MMJTTQPPNNNLTTQSUTQTSNPOLNMILJILJQTS\^][\^UVXUVXVWZ^_b[\^[\^]^a\]__ac\]_^_babd^_c^_c_ac^_bijlhioeiq_dp^co€‡Ž•œ£@A8MNGOPLIIIDDF113:::;;9PSNAD?3644768:9NPO@B>OSJNOJLMHOPLLMHPQMFGBLMHAB>UUS[[XGHD?@;HIBZ[THIBJLGFFH327224BBELLLPPPAA???=??=??=HHFNOJPQM\]VUVONOHJLGPPNNNL;;9442663774GGEPPNBB@AA?QQOIIGEEBPPNSSPWWWIIIGGI??AFEI@?D=;B98=779??ADDDZZWaa^LLL@@BJJM9:>?@DDEHFGJOPSGHJGIHJMLJJHOPLZ[VUVQXZUVWSQSNVVTPSQX[ZWZXLNMHJIFHGJMLUWV\]_TUWQSUTUWZ[]WX[STVXZ\[\^XZ\[\^Z[]^_bQSUVWZ\]_bbbcce_afadlX\fX]hsy€}„‹GH?LMFTUPFFFQQT113111;;9MOJAD@/10/02468JML=?;JMHNOJIJFOPLMNIGHD?@;IJFEFAFFDUUSWXTPQMNOHWXQJLEQSNLLL99;,,/;;;UUUUUSNNL@@>@@>>>>EEBGGELMHVWSNOHGHABD?MMJPPNHHF886220997@@>SSPDDA==:NNLJJHFFDPPNTTQWWUJJJNNNHHJ@@B;:??>BIHM@@B??AMMMZZWZZWQQQ;;>GGI:;?;=@?@DBDGMNPHILGIHHJIMMJNNLUUSZZWVVTUUSPPNTTQQTSWZXVXWOQPHJIGIHGIHOQPXZ\QSUNOQVWZXZ\Z[]PQTWX[XZ\VWZXZ\WX[]^aPQTQSU]^a_acWX[TU[[^fSV^LOW_ej_ejLMDQSLNOJ@@@SSU>>@777;;9LNJFHE236347679LMO>@=MOLPPNIIGOOMEEBBB@AA?FFDDDAMMJVVTVWSFGBFG@VWPQSLVWSFFF447;;;777LLITTQHIEEFADDDAAAGGGIIGFFDTUPQSNMNIMMJUUSMMJPPN==:++)997??=TTQDDA>>;LLILLIIIGNNLPPN\\ZIIGUUULLLWWZMMOFEI87;668==?HHHQQO\\ZJJJMMOHHJ89=9:>@AEHIMIJMEFHADB?A@OOOQQQSSSVVVNNNQQQNNNPPPSUTLNMMONSUTNPOILJHJIMONWX[TUWJLNMNPTUWTUWIJMTUWTUWWX[TUWVWZUVXSTVJLNXZ\WX[OTVIMSPTZQU]OSXZ^aW\^VWNUVOMNIDDDOOQTTVHHH::8>@==?>*+-&(+67:@AD@BAEGFFFF>>>FFFFFFMMMPPPPPPMMJAA?QQOWXTHIEIJDNOHTUNPQMJJHGGGTTTFFDIIGVWSSTO>?:888668>>>AAA>>;HHFPPNSSPSSPMMJNNLQQOIIG774442??=VVTOOMEEBFFDUUSMMJIIGMMJXZUXXVWWULLLTTTLLN--0336113>>@HHHUUS\\ZOOOLLN??A67::;?=>AGHLDEGFGI=?>EGFLLLTTVLLNOOQLLNNNPGGIQQQSUTOQPQTSOQPWZXPSQOQPZ\[VWZUVXGHJLMOPQTXZ\FGIHILPQTQSUTUWWX[STVZ[]MNPPQTMQSPUWJNTBFLLOUVZ_TXZTUWWXOLMFHIE???DDFGGI>>>11/79878:)*-*+/237469468:=;DDD======AAADDD@@@@@@DDDEEBUUSVWS?@;@A:FG@VWP[\WQQO[[[SSPEEBNOJQSNOPI894AAA>>@>>@======JJHPPNJJHIIGMMJHHFNNLOOM@@>AA?BB@UUSVVTNNLHHFSSPIIGMMJJJHUVQSTOLLIQQONNNOOO;;>IIL88:;;>QQQOOM[[X\\\BBEDDF?@D?@D;=@>?BDEGDEG9;:;>=EEGPPSJJMGGIGGIJJMFFHLLNJMLMONQTSPSQSUTSUTUWVTVUOPSNOQEFHIJMIJMPQTBDFFGILMOPQTSTVTUWMNPVWZTUWOTUFMMPVXPTZEHNIMSTUXQTSQTSUVMPQJEFABBB??AEEGBBB886243236(),/0634:67:78:=?>FFH::=::=??AAAD::=::=AAATTQVVTWXTMNIJLEFG@NOHOPLTTQ^^\]][GHDMNIQSLEF?9:6IILGFJ??A::=999HHHMMJGGEGGEJJHGGENNLSSPGGE??=;;9OOMWWUTTQGGEIIGJJHNNLFFDQSNLMHMNILLINNLNNNFFF@@@GGIMMOUUUGGEXXVNNNAAD88:78;9:>?@D@AE:;>>?A?A@@BAEEGNNPLLNFFHEEGGGIEEGEEGFHGMONOQPSUTTVUQTSVXWOQPTUWNOQEFHEFHBDFMNPHILNOQHILJLNPQTQSUHILPQTWX[MQSAGIMSUQW\IMSMQTTUWTTQVVTJLEIJDHIE??=BBBQQQIII444-/1236+,0/03-/29:>;=??A@JJM447@@B@@B>>@::=::=>>@@@@UUS[\WGHD?@;BD?OPLWXTPPN\\Z\\ZLMHGHDLMHDE@==:LLNMMOAAD==?777AAAJJHLLIHHFBB@>>;@@>@@>BB@>>>999HHHPPPMMJFFDJJHFFDFFDLLIMNINOJJJHIIGMMJ[[[XXXUUULLNPPSQQQDDAUUSPPP99;44778;=>AIJNIJM9:=?@BDEGBDFBBEEEGFFHBBBBBBEEGFFHAADEGFNPONPOSUTPSQILJVXWVXWUVXOPSJLNJLNEFHJLNFGIFGIFGIHILLMOPQTHILMNPWX[GLM=BEJPSOUWJOQLPQPSQNPMTTQNOJOPLPQMGHDDE@GGEBBE87;:;A01778;013124468:;>>?AEEG779779AADIIL99;336::=HHHZZWUUSEEB>>;FFDLLI[[XPPNZZZ\\\PPNBB@MMJBB@BBBDFEILJ;>=FHG=?>8:9MONFHGLNM@B?9;8=?;?A>;>=78:348=>AGHJHJIILHLNIJMIHJGFHGHJINPOFHGFHGFHGLMO?@B:;>ABEIJMEGF?A>TVSOQP@AD89;/039:>FGIUWV=?>;=?:;>?@D=>AEFHSUTOQNEGD=?>@AD?@BLNMNPOPSQQTSQTSJMLNPOX[ZTUWPQTIJMNOQJLNGHJGHJIJMDFEGIHGIHMONFHGLNMVXWMNPINOHMNMQSLPQHMNINOMQPQTSPPNMMJLMHDE>;=8AA?>>@32911:/06;=@6796879;:9:=:;>>>@224779??A==?224//1668===IIGVVTNNL>>;EEBPPNTTQZZZXXX\\\IIIAAAGGGFFF@@@?A@BEDDFEBED:=;8:9JMLEGFILJ>@?362362:=;89;46923967=@AEDFEEGDGIEFHEEGFDFEJLNNOQDEGABEFGIQSUDEG9:=BDFABEDFE@B?OQNTVUIJMABE2379:>EFHVXWEGF;=?78;:;?>?BIJMTVSTVQJMHBEADEG?@DGHJHJILNMQTSMONHJILNMTVUWX[OPSIJMMNPPQTJLNEFHABEGIHHJINPOLNMJMLSUTTVUMONFJLEIJINOHMNEIJGLMINOLPQIIGQQOTUPFG@9:6??===?329,,6-/4?@D?@B243=?>;=?>?A?@D12667:78;-/2+,0126;=?>>>HHFUUSQQOAA?AA?JJHUUSUUUZZZWWWPPP@@@JJJEEEEEEADB@BAHJIADB9;:9;:HJIEGFMONDFE9;8796=?>9:=46912889??@D=?>;>:?A=BEAFHGILJGHJJLN?@B?@B?@BQSUNOQLMO>?AEFHILJ?A>NPMSUTHIL@AD:;?ABFGHJVXWSUT>?A78;89=469GHJQTPWZUOQMILHEFH>?BJLNHILFGIGHJEFHBDFFGILMONOQIJMGHJEFHJLNJLNHILEFHEGFFHGJMLEGFBEDMONQTSNPOHMNGLMLPQJOPBGHDHIGLMINOQQOUUSUVQGHA=>9BB@AAD98?//8+,2>?BEFH687ADB:;>:;>>?B126:;?:;?237014348:;>???MMJLLIDDA>>;FFDHHFQQOWWWUUUZZZPPPEEEIIINNNGGG@BA8:9=?>:=;7989;:FHGDFEHJIDFE=?;9;8DFE>?A469/0667=>?B>@?=?;FHDFHEFHGFHG>?AFGI?@BBDF;=?EFH;=?9:=78:?@B=?>=?>PSQLNM=>@67:67:ABFDEGJML\^]GHJ>?B89=:;?MNPPSOTVQOQMILHGHJABFHILGHJEFHEFH?@B?@BBDFFGIHILMNPNOQ?@BIJMJLNIJMEFHFHGNPOJMLFHGILJMONVXWLNMEIJDHIHMNGLM?DEAFGDHIFJLSSPWWUWXTPQJ=>9??=DDFA@GEEN12878;347798@BA@AD4687;>&*037=14:-17*-3*-3-/2:::NNLQQOIIG??=AA?GGELLINNNUUUWWWNNNAAAHHHQQQGGGEGF?A@9;:ADB8:99;:DFE@BAEGFBED@B?=?;GIH9:=126-/478>;=@ADB>@=BE@?A>:=;798>?A;=?67978:67989;78:3479:>:;?9:=>?AGHJPQTFGJ46989=?@DEFHEGF\^]LMO=>A12667:FGIQTPX[V^a\JMIHIL;=@?@D>?B?@D?@D;=@:;?@AEHIMGHJ?@BJLN9:=ABELMODEG=>@HJIOQPJMLFHGFHGEGFNPOHJIAFG@EFDHIAFG>BD;@ABGHGLMOOMUUSUVQOPI894886==?>=D88A/0689=6799;:ADB>?A12637=$(-,06(+1*-3(+1*-3014;;;LLINNLHHF@@>==:??=HHFJJJUUUTTTQQQ===GGGMMMIII@BA=?>9;:HJI?A@>@?DFEBEDADBBEDEGDGIFILJ;=?46912834:78;>@?>@=GIEEGDBEDADB>?A9:=12434767989;89;67:78>;=B89=89;DEGNOSDEJ12878;=>AABEDFE[]\PQTBDG9:>FGJMNPSUQWZU]_[PSOLMO?@D;=@:;??@D>?B78;9:>ABFIJNMNPGHJPQT@ADGHJIJMGHJGHJPSQOQPNPOEGFGIHEGFMONMONFJLDHIFJLDHI;@A;@ABGHEIJSSPUUSPQMGHA340997;;>218++4,-378;679364>@?>?A469-17&*2/2:,08%)1%)1*-6239PPPQQOBB@442==:??=;;9==:IIIOOOUUUSSS;;;AAAIIIDDD@BA=?>>@?JMLEGF?A@?A@?A@9;:?A@GIFLNJILJ;=?469128/060149;::=9FHDBEA=?>8:99:=67901334746878:78:46934:89?46967:HIMIJN?@F67=34889=?@BDFESUTPQTDEH>?BGHLIJMOQNSUPZ\WUWTOPS?@D9:@9:@>?E:;A78>89?=>D@AEABEABENOQEFHEFHFGIJLNLMOGIHGIHMONJMLGIHADBFHGHJIBGH?DE@EF=AB9>?;@ABGHBGHNNLQSNOPIFG@:;7::8779,+2%&,,-3469124687>?A;=?014),4#&/&*2"%-"%-#&,*-3348DFELLIFFD??=886DDALLIGGEBBBNNNPPNTTQ;;9IIG@@@>>>9;:@BAGIHPSQOQPEGFDFE?A@=?>BEDLNJQTPGIH9:=126-/467=237:=;=?;@B?=?;9;:78:89=67:01434812634834812622;33=12878;IJNNOSDEJ78>12889==>@ABESTVVWZJLNFGJNOSHILILHJMHQTOTVSMON;=?9:>67=:;A9:@12878>>?EABH>?B?@BHILABE89;?@BJLNIJMFHGJMLLNMQTSNPOADBFHGIJMDHI?DE?DE;@A7;=;@ABGH@EFPQMMNGOPISTMMNIBBB=;@43:)*0()/78;3474689:=78;/06**6%%1$$-%%/()/126236013>@?BED@BA9;:;>=ADBFHGLNMPPPHHFQSNQSNHIELMHHHFBBB78:@ADFHEMOLLNJJMIIJM@AD>?ADFEOQNNPMADB78:34:,,612812689;9;::=;89;78;78>14:14=03;/2=-1;69A26>03;009009/0678;GHJJLO@AG34:1289:@?@D=>AFGJVWZEFHEFH@@BLLNFFFIIGUUSUUSNNLGGGEFH89=34:67=11:88A66?88A?@DEFHHILBDF=>@=>@GHJEFHEFHBDFIJMNOQGHJGHJEFHHIL:;?=>A>?B:;?9:>9:>=>A:;?PQJIJDZ[TMNIDDA===76:218*+1,-3:;?236-/1348128((1))4%%2%%1&&0&(--/2468687?A@:=;798798?A@?A@BEDADB???GGGJLGOPI892>?:???;;>89=ABEFHENPLQTOGIFFGIFGJBDFABEGIHNPMJML=>@239,,634:67=9:>236?@B?@D;=B46;37?,0:+-:02?13@79F14?,08--922;017237:;>QSVFGM67=34:78>EFL=>DBDGNOSGHJ>?A??ALLNHHHFFFSSPWWUPQMEEBNOQ?@D34:239//866?11=>>G?@DDEGGHJ@AD:;>=>@DEGDEG>?A=>@HILNOQMNPFGIABEGHJDEH@AE;=@78;=>A=>A>?B=>AQSLGHAWXQOPLBB@III649107+,2128>?B013+,/126/06%%/&&2$$1%%1))2%&,*+/9:=>@?>@?9;:364243:=;BED>@?>@?GGIGGGNNLLMHAB>IIG;;;==?9:>>?A>@=DFASUP@B?;=?@AE@AD9:=:=;GIFNPO?@B017//834:89?=>A89;>?A89=34:34:26>+/9*,9/1>13@79F14?,0822>//8,-3348?@BOPT@AG23923967=DEJ;=BABFMNQHIL;=?JJMIILFFFHHHVVTTTQMNIHHFX[ZWX\89=67=34:66?11:88A=>A@AEABF:;?78;;=@?@DABF?@B>?AHILNOQMNPFGIABEGHJ?@D9:>67:469BDGEFIBDGDEHLMFQSLTUNNOJMMJ@@@327/-4()/128=>A)*,*+-/03,-3$$-%%1$$1&&2**3#$*&(+;=?BEDEGF679/02679679:;>ABE@ADDDFDDFNNLPPNOOMMMJJJMAAD>?BDEG>@=>@;NPL:=9124014@AD>?A:=;BEALNM=>@-/400967=23934812478:78;67=34:/2:-1;,/;+-:-0=68E26@03;11=11:,-3014FGISTWIJP67=-/467==>D9:@ABFQSVIJM=>@>>@@@BEEEFFFTTQVVTOPLFFDZ\[abdABF237/0612899B67=BDIEFIEFI=>A469:;?;=@@AE?@B=>@DEGHILGHJGHJEFHHILGHL=>A469126>?BBDG@AE?@DSTMIJDQSLVWSEEB@@@98=/-4()/128;=@+,/*+-)*-#$*$$-%%1##0$$0&&0&(-)*-BDFILJ236/03,-1348;=@67:78;34898===?GGGLLLAAA???336-,1+,0236AD@HJFTVQGIF124/03=>@>?AADBEGDBED89;,-3++412846;78;01378:89=89?78>,08(+6-0=02?+-:+-:+/9+/7--9,,6,-3469LMOLMPNOU:;A/0646;89?9:@@AEIJN@AD9:=AAD==?:::AAATTQ[[XLMHLLIZ\[TUWMNP=>A126/06*+19:@;=B;=B>?E46;67==>D?@F>?B;=?;=?BDFEFHIJMIJMHILFGIDEGEFH679124?@BHILHILDEG?@9@A:PQJUVQTTQFFF438(&-()/78>BDG/02)*,)*-#$*%%/##/""/$$0**3()/014GHJTUW89=/03+,0-/2/03014:;?237,+0;:?BBEHHHEEE@@B76:0/34699:=:=9?A=OQMLNJ:;>78;89;;=?>@?=?;>@?78:-/4,,646;46;469,-089;67:128-/4+/7),7,/;,/;*,9*,9*-8*-6--9,,6&(-9:>JLNJLOQSXLMS0172399:@46;BDGDEH>?A;=?HHJ99;888>>>MMJOOMIJFMNIPSOTVUUVXNOQ:;?014/0646;89?67=78>34:01789?;=B:;A9:>468=>@EFHGHJGHJEFHBDFDEGABE9:=89;=>@DEGEFH>?A:;4>?8PQJWXTMMJBBB327%$+*+1?@FJLO347,-0,-1)*0++4((3$$1**666?$%+126BDFSTV:;?-/4,-3+,2239-/4017)*00/698=HHJHHHEEE??A98=/-4:;?;=?8:79;7NPLJMI9:=67:89;@AD@BA9;88:9679/06--746;128126,-078:4699:@ABH+/7),7,/;(*7)+8)+8),7),4))4009+,278;BDFFGJFGM:;A9:@01723934:>?B@AE;=?:;>>>@::=>>>===IIGOOMSTOQSNLNJQTSNPOIJM469-/2126;=B:;A66?33=22;11:77@;;E;=B@AE9:=;=?@AD?@BDEGGHJIJMHJI=?>687798=?>ADBFHGADB>?8:;4IJDTUPIIGHHH:9>&%,+,2DEJQSV78:124237/0622;%%1""/**699B()/78;?@BQSU9:>+,2/06,-3,-39:@89?67=43::9>AADHHHBBB::=76:+*189=78:;>::=8QTOEGD-/1+,0236>?A>@?241364468017--7017,-3014/0223678;78>34:*-6*-8+-:$&3(*7(*7(+6(+3++7))2,-39:>GHJIJNOPVQSX@AG017*+146;;=@?@D89;89;??A==?777;;;FFDOOMTUPPQMLNJSUQLNMGHJ/03-/2+,0;=B??H66?22;33=33=22;33=67=78;4689:=9:=@ADEFHHILIJMDFE4763648:9@BA@BAFHGHJI
\ No newline at end of file
diff --git a/examples/unicode.h b/examples/unicode.h
new file mode 100644
index 0000000..0831e23
--- /dev/null
+++ b/examples/unicode.h
@@ -0,0 +1,116 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Unicode support for Windows. The main idea is to maintain an array of Unicode
+// arguments (wargv) and use it only for file paths. The regular argv is used
+// for everything else.
+//
+// Author: Yannis Guyon (yguyon@google.com)
+
+#ifndef WEBP_EXAMPLES_UNICODE_H_
+#define WEBP_EXAMPLES_UNICODE_H_
+
+#include <stdio.h>
+
+#if defined(_WIN32) && defined(_UNICODE)
+
+// wchar_t is used instead of TCHAR because we only perform additional work when
+// Unicode is enabled and because the output of CommandLineToArgvW() is wchar_t.
+
+#include <fcntl.h>
+#include <io.h>
+#include <wchar.h>
+#include <windows.h>
+#include <shellapi.h>
+
+// Create a wchar_t array containing Unicode parameters.
+#define INIT_WARGV(ARGC, ARGV)                                                \
+  int wargc;                                                                  \
+  const W_CHAR** const wargv =                                                \
+      (const W_CHAR**)CommandLineToArgvW(GetCommandLineW(), &wargc);          \
+  do {                                                                        \
+    if (wargv == NULL || wargc != (ARGC)) {                                   \
+      fprintf(stderr, "Error: Unable to get Unicode arguments.\n");           \
+      FREE_WARGV_AND_RETURN(-1);                                              \
+    }                                                                         \
+  } while (0)
+
+// Use this to get a Unicode argument (e.g. file path).
+#define GET_WARGV(UNUSED, C) wargv[C]
+// For cases where argv is shifted by one compared to wargv.
+#define GET_WARGV_SHIFTED(UNUSED, C) wargv[(C) + 1]
+#define GET_WARGV_OR_NULL() wargv
+
+// Release resources. LocalFree() is needed after CommandLineToArgvW().
+#define FREE_WARGV() LOCAL_FREE((W_CHAR** const)wargv)
+#define LOCAL_FREE(WARGV)                  \
+  do {                                     \
+    if ((WARGV) != NULL) LocalFree(WARGV); \
+  } while (0)
+
+#define W_CHAR wchar_t  // WCHAR without underscore might already be defined.
+#define TO_W_CHAR(STR) (L##STR)
+
+#define WFOPEN(ARG, OPT) _wfopen((const W_CHAR*)ARG, TO_W_CHAR(OPT))
+
+#define WFPRINTF(STREAM, STR, ...)                    \
+  do {                                                \
+    int prev_mode;                                    \
+    fflush(STREAM);                                   \
+    prev_mode = _setmode(_fileno(STREAM), _O_U8TEXT); \
+    fwprintf(STREAM, TO_W_CHAR(STR), __VA_ARGS__);    \
+    fflush(STREAM);                                   \
+    (void)_setmode(_fileno(STREAM), prev_mode);       \
+  } while (0)
+#define WPRINTF(STR, ...) WFPRINTF(stdout, STR, __VA_ARGS__)
+
+#define WSTRLEN(FILENAME) wcslen((const W_CHAR*)FILENAME)
+#define WSTRCMP(FILENAME, STR) wcscmp((const W_CHAR*)FILENAME, TO_W_CHAR(STR))
+#define WSTRRCHR(FILENAME, STR) wcsrchr((const W_CHAR*)FILENAME, TO_W_CHAR(STR))
+#define WSNPRINTF(A, B, STR, ...) _snwprintf(A, B, TO_W_CHAR(STR), __VA_ARGS__)
+
+#else
+
+#include <string.h>
+
+// Unicode file paths work as is on Unix platforms, and no extra work is done on
+// Windows either if Unicode is disabled.
+
+#define INIT_WARGV(ARGC, ARGV)
+
+#define GET_WARGV(ARGV, C) (ARGV)[C]
+#define GET_WARGV_SHIFTED(ARGV, C) (ARGV)[C]
+#define GET_WARGV_OR_NULL() NULL
+
+#define FREE_WARGV()
+#define LOCAL_FREE(WARGV)
+
+#define W_CHAR char
+#define TO_W_CHAR(STR) (STR)
+
+#define WFOPEN(ARG, OPT) fopen(ARG, OPT)
+
+#define WPRINTF(STR, ...) printf(STR, __VA_ARGS__)
+#define WFPRINTF(STREAM, STR, ...) fprintf(STREAM, STR, __VA_ARGS__)
+
+#define WSTRLEN(FILENAME) strlen(FILENAME)
+#define WSTRCMP(FILENAME, STR) strcmp(FILENAME, STR)
+#define WSTRRCHR(FILENAME, STR) strrchr(FILENAME, STR)
+#define WSNPRINTF(A, B, STR, ...) snprintf(A, B, STR, __VA_ARGS__)
+
+#endif  // defined(_WIN32) && defined(_UNICODE)
+
+// Don't forget to free wargv before returning (e.g. from main).
+#define FREE_WARGV_AND_RETURN(VALUE) \
+  do {                               \
+    FREE_WARGV();                    \
+    return (VALUE);                  \
+  } while (0)
+
+#endif  // WEBP_EXAMPLES_UNICODE_H_
diff --git a/examples/unicode_gif.h b/examples/unicode_gif.h
new file mode 100644
index 0000000..626c6e7
--- /dev/null
+++ b/examples/unicode_gif.h
@@ -0,0 +1,76 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// giflib doesn't have a Unicode DGifOpenFileName(). Let's make one.
+//
+// Author: Yannis Guyon (yguyon@google.com)
+
+#ifndef WEBP_EXAMPLES_UNICODE_GIF_H_
+#define WEBP_EXAMPLES_UNICODE_GIF_H_
+
+#include "./unicode.h"
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"  // For WEBP_HAVE_GIF
+#endif
+
+#if defined(WEBP_HAVE_GIF)
+
+#ifdef _WIN32
+#include <fcntl.h>  // Not standard, needed for _topen and flags.
+#include <io.h>
+#endif
+
+#include <gif_lib.h>
+#include <string.h>
+#include "./gifdec.h"
+
+#if !defined(STDIN_FILENO)
+#define STDIN_FILENO 0
+#endif
+
+static GifFileType* DGifOpenFileUnicode(const W_CHAR* file_name, int* error) {
+  if (!WSTRCMP(file_name, "-")) {
+#if LOCAL_GIF_PREREQ(5, 0)
+    return DGifOpenFileHandle(STDIN_FILENO, error);
+#else
+    (void)error;
+    return DGifOpenFileHandle(STDIN_FILENO);
+#endif
+  }
+
+#if defined(_WIN32) && defined(_UNICODE)
+  {
+    int file_handle = _wopen(file_name, _O_RDONLY | _O_BINARY);
+    if (file_handle == -1) {
+      if (error != NULL) *error = D_GIF_ERR_OPEN_FAILED;
+      return NULL;
+    }
+
+#if LOCAL_GIF_PREREQ(5, 0)
+    return DGifOpenFileHandle(file_handle, error);
+#else
+    return DGifOpenFileHandle(file_handle);
+#endif
+  }
+
+#else
+
+#if LOCAL_GIF_PREREQ(5, 0)
+  return DGifOpenFileName(file_name, error);
+#else
+  return DGifOpenFileName(file_name);
+#endif
+
+#endif  // defined(_WIN32) && defined(_UNICODE)
+  // DGifCloseFile() is called later.
+}
+
+#endif  // defined(WEBP_HAVE_GIF)
+
+#endif  // WEBP_EXAMPLES_UNICODE_GIF_H_
diff --git a/examples/vwebp.c b/examples/vwebp.c
new file mode 100644
index 0000000..35f1b18
--- /dev/null
+++ b/examples/vwebp.c
@@ -0,0 +1,654 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  Simple OpenGL-based WebP file viewer.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#if defined(__unix__) || defined(__CYGWIN__)
+#define _POSIX_C_SOURCE 200112L  // for setenv
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(WEBP_HAVE_GL)
+
+#if defined(HAVE_GLUT_GLUT_H)
+#include <GLUT/glut.h>
+#else
+#include <GL/glut.h>
+#ifdef FREEGLUT
+#include <GL/freeglut.h>
+#endif
+#endif
+
+#ifdef WEBP_HAVE_QCMS
+#include <qcms.h>
+#endif
+
+#include "webp/decode.h"
+#include "webp/demux.h"
+
+#include "../examples/example_util.h"
+#include "../imageio/imageio_util.h"
+#include "./unicode.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf _snprintf
+#endif
+
+// Unfortunate global variables. Gathered into a struct for comfort.
+static struct {
+  int has_animation;
+  int has_color_profile;
+  int done;
+  int decoding_error;
+  int print_info;
+  int only_deltas;
+  int use_color_profile;
+  int draw_anim_background_color;
+
+  int canvas_width, canvas_height;
+  int loop_count;
+  uint32_t bg_color;
+
+  const char* file_name;
+  WebPData data;
+  WebPDecoderConfig config;
+  const WebPDecBuffer* pic;
+  WebPDemuxer* dmux;
+  WebPIterator curr_frame;
+  WebPIterator prev_frame;
+  WebPChunkIterator iccp;
+  int viewport_width, viewport_height;
+} kParams;
+
+static void ClearPreviousPic(void) {
+  WebPFreeDecBuffer((WebPDecBuffer*)kParams.pic);
+  kParams.pic = NULL;
+}
+
+static void ClearParams(void) {
+  ClearPreviousPic();
+  WebPDataClear(&kParams.data);
+  WebPDemuxReleaseIterator(&kParams.curr_frame);
+  WebPDemuxReleaseIterator(&kParams.prev_frame);
+  WebPDemuxReleaseChunkIterator(&kParams.iccp);
+  WebPDemuxDelete(kParams.dmux);
+  kParams.dmux = NULL;
+}
+
+// Sets the previous frame to the dimensions of the canvas and has it dispose
+// to background to cause the canvas to be cleared.
+static void ClearPreviousFrame(void) {
+  WebPIterator* const prev = &kParams.prev_frame;
+  prev->width = kParams.canvas_width;
+  prev->height = kParams.canvas_height;
+  prev->x_offset = prev->y_offset = 0;
+  prev->dispose_method = WEBP_MUX_DISPOSE_BACKGROUND;
+}
+
+// -----------------------------------------------------------------------------
+// Color profile handling
+static int ApplyColorProfile(const WebPData* const profile,
+                             WebPDecBuffer* const rgba) {
+#ifdef WEBP_HAVE_QCMS
+  int i, ok = 0;
+  uint8_t* line;
+  uint8_t major_revision;
+  qcms_profile* input_profile = NULL;
+  qcms_profile* output_profile = NULL;
+  qcms_transform* transform = NULL;
+  const qcms_data_type input_type = QCMS_DATA_RGBA_8;
+  const qcms_data_type output_type = QCMS_DATA_RGBA_8;
+  const qcms_intent intent = QCMS_INTENT_DEFAULT;
+
+  if (profile == NULL || rgba == NULL) return 0;
+  if (profile->bytes == NULL || profile->size < 10) return 1;
+  major_revision = profile->bytes[8];
+
+  qcms_enable_iccv4();
+  input_profile = qcms_profile_from_memory(profile->bytes, profile->size);
+  // qcms_profile_is_bogus() is broken with ICCv4.
+  if (input_profile == NULL ||
+      (major_revision < 4 && qcms_profile_is_bogus(input_profile))) {
+    fprintf(stderr, "Color profile is bogus!\n");
+    goto Error;
+  }
+
+  output_profile = qcms_profile_sRGB();
+  if (output_profile == NULL) {
+    fprintf(stderr, "Error creating output color profile!\n");
+    goto Error;
+  }
+
+  qcms_profile_precache_output_transform(output_profile);
+  transform = qcms_transform_create(input_profile, input_type,
+                                    output_profile, output_type,
+                                    intent);
+  if (transform == NULL) {
+    fprintf(stderr, "Error creating color transform!\n");
+    goto Error;
+  }
+
+  line = rgba->u.RGBA.rgba;
+  for (i = 0; i < rgba->height; ++i, line += rgba->u.RGBA.stride) {
+    qcms_transform_data(transform, line, line, rgba->width);
+  }
+  ok = 1;
+
+ Error:
+  if (input_profile != NULL) qcms_profile_release(input_profile);
+  if (output_profile != NULL) qcms_profile_release(output_profile);
+  if (transform != NULL) qcms_transform_release(transform);
+  return ok;
+#else
+  (void)profile;
+  (void)rgba;
+  return 1;
+#endif  // WEBP_HAVE_QCMS
+}
+
+//------------------------------------------------------------------------------
+// File decoding
+
+static int Decode(void) {   // Fills kParams.curr_frame
+  const WebPIterator* const curr = &kParams.curr_frame;
+  WebPDecoderConfig* const config = &kParams.config;
+  WebPDecBuffer* const output_buffer = &config->output;
+  int ok = 0;
+
+  ClearPreviousPic();
+  output_buffer->colorspace = MODE_RGBA;
+  ok = (WebPDecode(curr->fragment.bytes, curr->fragment.size,
+                   config) == VP8_STATUS_OK);
+  if (!ok) {
+    fprintf(stderr, "Decoding of frame #%d failed!\n", curr->frame_num);
+  } else {
+    kParams.pic = output_buffer;
+    if (kParams.use_color_profile) {
+      ok = ApplyColorProfile(&kParams.iccp.chunk, output_buffer);
+      if (!ok) {
+        fprintf(stderr, "Applying color profile to frame #%d failed!\n",
+                curr->frame_num);
+      }
+    }
+  }
+  return ok;
+}
+
+static void decode_callback(int what) {
+  if (what == 0 && !kParams.done) {
+    int duration = 0;
+    if (kParams.dmux != NULL) {
+      WebPIterator* const curr = &kParams.curr_frame;
+      if (!WebPDemuxNextFrame(curr)) {
+        WebPDemuxReleaseIterator(curr);
+        if (WebPDemuxGetFrame(kParams.dmux, 1, curr)) {
+          --kParams.loop_count;
+          kParams.done = (kParams.loop_count == 0);
+          if (kParams.done) return;
+          ClearPreviousFrame();
+        } else {
+          kParams.decoding_error = 1;
+          kParams.done = 1;
+          return;
+        }
+      }
+      duration = curr->duration;
+      // Behavior copied from Chrome, cf:
+      // https://cs.chromium.org/chromium/src/third_party/WebKit/Source/
+      // platform/graphics/DeferredImageDecoder.cpp?
+      // rcl=b4c33049f096cd283f32be9a58b9a9e768227c26&l=246
+      if (duration <= 10) duration = 100;
+    }
+    if (!Decode()) {
+      kParams.decoding_error = 1;
+      kParams.done = 1;
+    } else {
+      glutPostRedisplay();
+      glutTimerFunc(duration, decode_callback, what);
+    }
+  }
+}
+
+//------------------------------------------------------------------------------
+// Callbacks
+
+static void HandleKey(unsigned char key, int pos_x, int pos_y) {
+  // Note: rescaling the window or toggling some features during an animation
+  // generates visual artifacts. This is not fixed because refreshing the frame
+  // may require rendering the whole animation from start till current frame.
+  (void)pos_x;
+  (void)pos_y;
+  if (key == 'q' || key == 'Q' || key == 27 /* Esc */) {
+#ifdef FREEGLUT
+    glutLeaveMainLoop();
+#else
+    ClearParams();
+    exit(0);
+#endif
+  } else if (key == 'c') {
+    if (kParams.has_color_profile && !kParams.decoding_error) {
+      kParams.use_color_profile = 1 - kParams.use_color_profile;
+
+      if (kParams.has_animation) {
+        // Restart the completed animation to pickup the color profile change.
+        if (kParams.done && kParams.loop_count == 0) {
+          kParams.loop_count =
+              (int)WebPDemuxGetI(kParams.dmux, WEBP_FF_LOOP_COUNT) + 1;
+          kParams.done = 0;
+          // Start the decode loop immediately.
+          glutTimerFunc(0, decode_callback, 0);
+        }
+      } else {
+        Decode();
+        glutPostRedisplay();
+      }
+    }
+  } else if (key == 'b') {
+    kParams.draw_anim_background_color = 1 - kParams.draw_anim_background_color;
+    if (!kParams.has_animation) ClearPreviousFrame();
+    glutPostRedisplay();
+  } else if (key == 'i') {
+    kParams.print_info = 1 - kParams.print_info;
+    if (!kParams.has_animation) ClearPreviousFrame();
+    glutPostRedisplay();
+  } else if (key == 'd') {
+    kParams.only_deltas = 1 - kParams.only_deltas;
+    glutPostRedisplay();
+  }
+}
+
+static void HandleReshape(int width, int height) {
+  // Note: reshape doesn't preserve aspect ratio, and might
+  // be handling larger-than-screen pictures incorrectly.
+  glViewport(0, 0, width, height);
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+  kParams.viewport_width = width;
+  kParams.viewport_height = height;
+  if (!kParams.has_animation) ClearPreviousFrame();
+}
+
+static void PrintString(const char* const text) {
+  void* const font = GLUT_BITMAP_9_BY_15;
+  int i;
+  for (i = 0; text[i]; ++i) {
+    glutBitmapCharacter(font, text[i]);
+  }
+}
+
+static void PrintStringW(const char* const text) {
+#if defined(_WIN32) && defined(_UNICODE)
+  void* const font = GLUT_BITMAP_9_BY_15;
+  const W_CHAR* const wtext = (const W_CHAR*)text;
+  int i;
+  for (i = 0; wtext[i]; ++i) {
+    glutBitmapCharacter(font, wtext[i]);
+  }
+#else
+  PrintString(text);
+#endif
+}
+
+static float GetColorf(uint32_t color, int shift) {
+  return ((color >> shift) & 0xff) / 255.f;
+}
+
+static void DrawCheckerBoard(void) {
+  const int square_size = 8;  // must be a power of 2
+  int x, y;
+  GLint viewport[4];  // x, y, width, height
+
+  glPushMatrix();
+
+  glGetIntegerv(GL_VIEWPORT, viewport);
+  // shift to integer coordinates with (0,0) being top-left.
+  glOrtho(0, viewport[2], viewport[3], 0, -1, 1);
+  for (y = 0; y < viewport[3]; y += square_size) {
+    for (x = 0; x < viewport[2]; x += square_size) {
+      const GLubyte color = 128 + 64 * (!((x + y) & square_size));
+      glColor3ub(color, color, color);
+      glRecti(x, y, x + square_size, y + square_size);
+    }
+  }
+  glPopMatrix();
+}
+
+static void DrawBackground(void) {
+  // Whole window cleared with clear color, checkerboard rendered on top of it.
+  glClear(GL_COLOR_BUFFER_BIT);
+  DrawCheckerBoard();
+
+  // ANIM background color rendered (blend) on top. Default is white for still
+  // images (without ANIM chunk). glClear() can't be used for that (no blend).
+  if (kParams.draw_anim_background_color) {
+    glPushMatrix();
+    glLoadIdentity();
+    glColor4f(GetColorf(kParams.bg_color, 16),  // BGRA from spec
+              GetColorf(kParams.bg_color, 8),
+              GetColorf(kParams.bg_color, 0),
+              GetColorf(kParams.bg_color, 24));
+    glRecti(-1, -1, +1, +1);
+    glPopMatrix();
+  }
+}
+
+// Draw background in a scissored rectangle.
+static void DrawBackgroundScissored(int window_x, int window_y, int frame_w,
+                                    int frame_h) {
+  // Only update the requested area, not the whole canvas.
+  window_x = window_x * kParams.viewport_width / kParams.canvas_width;
+  window_y = window_y * kParams.viewport_height / kParams.canvas_height;
+  frame_w = frame_w * kParams.viewport_width / kParams.canvas_width;
+  frame_h = frame_h * kParams.viewport_height / kParams.canvas_height;
+
+  // glScissor() takes window coordinates (0,0 at bottom left).
+  window_y = kParams.viewport_height - window_y - frame_h;
+
+  glEnable(GL_SCISSOR_TEST);
+  glScissor(window_x, window_y, frame_w, frame_h);
+  DrawBackground();
+  glDisable(GL_SCISSOR_TEST);
+}
+
+static void HandleDisplay(void) {
+  const WebPDecBuffer* const pic = kParams.pic;
+  const WebPIterator* const curr = &kParams.curr_frame;
+  WebPIterator* const prev = &kParams.prev_frame;
+  GLfloat xoff, yoff;
+  if (pic == NULL) return;
+  glPushMatrix();
+  glPixelZoom((GLfloat)(+1. / kParams.canvas_width * kParams.viewport_width),
+              (GLfloat)(-1. / kParams.canvas_height * kParams.viewport_height));
+  xoff = (GLfloat)(2. * curr->x_offset / kParams.canvas_width);
+  yoff = (GLfloat)(2. * curr->y_offset / kParams.canvas_height);
+  glRasterPos2f(-1.f + xoff, 1.f - yoff);
+  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+  glPixelStorei(GL_UNPACK_ROW_LENGTH, pic->u.RGBA.stride / 4);
+
+  if (kParams.only_deltas) {
+    DrawBackground();
+  } else {
+    // The rectangle of the previous frame might be different than the current
+    // frame, so we may need to DrawBackgroundScissored for both.
+    if (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
+      // Clear the previous frame rectangle.
+      DrawBackgroundScissored(prev->x_offset, prev->y_offset, prev->width,
+                              prev->height);
+    }
+    if (curr->blend_method == WEBP_MUX_NO_BLEND) {
+      // We simulate no-blending behavior by first clearing the current frame
+      // rectangle and then alpha-blending against it.
+      DrawBackgroundScissored(curr->x_offset, curr->y_offset, curr->width,
+                              curr->height);
+    }
+  }
+
+  *prev = *curr;
+
+  glDrawPixels(pic->width, pic->height,
+               GL_RGBA, GL_UNSIGNED_BYTE,
+               (GLvoid*)pic->u.RGBA.rgba);
+  if (kParams.print_info) {
+    char tmp[32];
+
+    glColor4f(0.90f, 0.0f, 0.90f, 1.0f);
+    glRasterPos2f(-0.95f, 0.90f);
+    PrintStringW(kParams.file_name);
+
+    snprintf(tmp, sizeof(tmp), "Dimension:%d x %d", pic->width, pic->height);
+    glColor4f(0.90f, 0.0f, 0.90f, 1.0f);
+    glRasterPos2f(-0.95f, 0.80f);
+    PrintString(tmp);
+    if (curr->x_offset != 0 || curr->y_offset != 0) {
+      snprintf(tmp, sizeof(tmp), " (offset:%d,%d)",
+               curr->x_offset, curr->y_offset);
+      glRasterPos2f(-0.95f, 0.70f);
+      PrintString(tmp);
+    }
+  }
+  glPopMatrix();
+#if defined(__APPLE__) || defined(_WIN32)
+  glFlush();
+#else
+  glutSwapBuffers();
+#endif
+}
+
+static void StartDisplay(void) {
+  int width = kParams.canvas_width;
+  int height = kParams.canvas_height;
+  int screen_width, screen_height;
+  // TODO(webp:365) GLUT_DOUBLE results in flickering / old frames to be
+  // partially displayed with animated webp + alpha.
+#if defined(__APPLE__) || defined(_WIN32)
+  glutInitDisplayMode(GLUT_RGBA);
+#else
+  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
+#endif
+  screen_width = glutGet(GLUT_SCREEN_WIDTH);
+  screen_height = glutGet(GLUT_SCREEN_HEIGHT);
+  if (width > screen_width || height > screen_height) {
+    if (width > screen_width) {
+      height = (height * screen_width + width - 1) / width;
+      width = screen_width;
+    }
+    if (height > screen_height) {
+      width = (width * screen_height + height - 1) / height;
+      height = screen_height;
+    }
+  }
+  glutInitWindowSize(width, height);
+  glutCreateWindow("WebP viewer");
+  glutDisplayFunc(HandleDisplay);
+  glutReshapeFunc(HandleReshape);
+  glutIdleFunc(NULL);
+  glutKeyboardFunc(HandleKey);
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  glEnable(GL_BLEND);
+  glClearColor(0, 0, 0, 0);  // window will be cleared to black (no blend)
+  DrawBackground();
+}
+
+//------------------------------------------------------------------------------
+// Main
+
+static void Help(void) {
+  printf(
+      "Usage: vwebp in_file [options]\n\n"
+      "Decodes the WebP image file and visualize it using OpenGL\n"
+      "Options are:\n"
+      "  -version ..... print version number and exit\n"
+      "  -noicc ....... don't use the icc profile if present\n"
+      "  -nofancy ..... don't use the fancy YUV420 upscaler\n"
+      "  -nofilter .... disable in-loop filtering\n"
+      "  -dither <int>  dithering strength (0..100), default=50\n"
+      "  -noalphadither disable alpha plane dithering\n"
+      "  -usebgcolor .. display background color\n"
+      "  -mt .......... use multi-threading\n"
+      "  -info ........ print info\n"
+      "  -h ........... this help message\n"
+      "\n"
+      "Keyboard shortcuts:\n"
+      "  'c' ................ toggle use of color profile\n"
+      "  'b' ................ toggle background color display\n"
+      "  'i' ................ overlay file information\n"
+      "  'd' ................ disable blending & disposal (debug)\n"
+      "  'q' / 'Q' / ESC .... quit\n");
+}
+
+int main(int argc, char* argv[]) {
+  int c;
+  WebPDecoderConfig* const config = &kParams.config;
+  WebPIterator* const curr = &kParams.curr_frame;
+
+  INIT_WARGV(argc, argv);
+
+  if (!WebPInitDecoderConfig(config)) {
+    fprintf(stderr, "Library version mismatch!\n");
+    FREE_WARGV_AND_RETURN(-1);
+  }
+  config->options.dithering_strength = 50;
+  config->options.alpha_dithering_strength = 100;
+  kParams.use_color_profile = 1;
+  // Background color hidden by default to see transparent areas.
+  kParams.draw_anim_background_color = 0;
+
+  for (c = 1; c < argc; ++c) {
+    int parse_error = 0;
+    if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
+      Help();
+      FREE_WARGV_AND_RETURN(0);
+    } else if (!strcmp(argv[c], "-noicc")) {
+      kParams.use_color_profile = 0;
+    } else if (!strcmp(argv[c], "-nofancy")) {
+      config->options.no_fancy_upsampling = 1;
+    } else if (!strcmp(argv[c], "-nofilter")) {
+      config->options.bypass_filtering = 1;
+    } else if (!strcmp(argv[c], "-noalphadither")) {
+      config->options.alpha_dithering_strength = 0;
+    } else if (!strcmp(argv[c], "-usebgcolor")) {
+      kParams.draw_anim_background_color = 1;
+    } else if (!strcmp(argv[c], "-dither") && c + 1 < argc) {
+      config->options.dithering_strength =
+          ExUtilGetInt(argv[++c], 0, &parse_error);
+    } else if (!strcmp(argv[c], "-info")) {
+      kParams.print_info = 1;
+    } else if (!strcmp(argv[c], "-version")) {
+      const int dec_version = WebPGetDecoderVersion();
+      const int dmux_version = WebPGetDemuxVersion();
+      printf("WebP Decoder version: %d.%d.%d\nWebP Demux version: %d.%d.%d\n",
+             (dec_version >> 16) & 0xff, (dec_version >> 8) & 0xff,
+             dec_version & 0xff, (dmux_version >> 16) & 0xff,
+             (dmux_version >> 8) & 0xff, dmux_version & 0xff);
+      FREE_WARGV_AND_RETURN(0);
+    } else if (!strcmp(argv[c], "-mt")) {
+      config->options.use_threads = 1;
+    } else if (!strcmp(argv[c], "--")) {
+      if (c < argc - 1) kParams.file_name = (const char*)GET_WARGV(argv, ++c);
+      break;
+    } else if (argv[c][0] == '-') {
+      printf("Unknown option '%s'\n", argv[c]);
+      Help();
+      FREE_WARGV_AND_RETURN(-1);
+    } else {
+      kParams.file_name = (const char*)GET_WARGV(argv, c);
+    }
+
+    if (parse_error) {
+      Help();
+      FREE_WARGV_AND_RETURN(-1);
+    }
+  }
+
+  if (kParams.file_name == NULL) {
+    printf("missing input file!!\n");
+    Help();
+    FREE_WARGV_AND_RETURN(0);
+  }
+
+  if (!ImgIoUtilReadFile(kParams.file_name,
+                         &kParams.data.bytes, &kParams.data.size)) {
+    goto Error;
+  }
+
+  if (!WebPGetInfo(kParams.data.bytes, kParams.data.size, NULL, NULL)) {
+    fprintf(stderr, "Input file doesn't appear to be WebP format.\n");
+    goto Error;
+  }
+
+  kParams.dmux = WebPDemux(&kParams.data);
+  if (kParams.dmux == NULL) {
+    fprintf(stderr, "Could not create demuxing object!\n");
+    goto Error;
+  }
+
+  kParams.canvas_width = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_WIDTH);
+  kParams.canvas_height = WebPDemuxGetI(kParams.dmux, WEBP_FF_CANVAS_HEIGHT);
+  if (kParams.print_info) {
+    printf("Canvas: %d x %d\n", kParams.canvas_width, kParams.canvas_height);
+  }
+
+  ClearPreviousFrame();
+
+  memset(&kParams.iccp, 0, sizeof(kParams.iccp));
+  kParams.has_color_profile =
+      !!(WebPDemuxGetI(kParams.dmux, WEBP_FF_FORMAT_FLAGS) & ICCP_FLAG);
+  if (kParams.has_color_profile) {
+#ifdef WEBP_HAVE_QCMS
+    if (!WebPDemuxGetChunk(kParams.dmux, "ICCP", 1, &kParams.iccp)) goto Error;
+    printf("VP8X: Found color profile\n");
+#else
+    fprintf(stderr, "Warning: color profile present, but qcms is unavailable!\n"
+            "Build libqcms from Mozilla or Chromium and define WEBP_HAVE_QCMS "
+            "before building.\n");
+#endif
+  }
+
+  if (!WebPDemuxGetFrame(kParams.dmux, 1, curr)) goto Error;
+
+  kParams.has_animation = (curr->num_frames > 1);
+  kParams.loop_count = (int)WebPDemuxGetI(kParams.dmux, WEBP_FF_LOOP_COUNT);
+  kParams.bg_color = WebPDemuxGetI(kParams.dmux, WEBP_FF_BACKGROUND_COLOR);
+  printf("VP8X: Found %d images in file (loop count = %d)\n",
+         curr->num_frames, kParams.loop_count);
+
+  // Decode first frame
+  if (!Decode()) goto Error;
+
+  // Position iterator to last frame. Next call to HandleDisplay will wrap over.
+  // We take this into account by bumping up loop_count.
+  WebPDemuxGetFrame(kParams.dmux, 0, curr);
+  if (kParams.loop_count) ++kParams.loop_count;
+
+#if defined(__unix__) || defined(__CYGWIN__)
+  // Work around GLUT compositor bug.
+  // https://bugs.launchpad.net/ubuntu/+source/freeglut/+bug/369891
+  setenv("XLIB_SKIP_ARGB_VISUALS", "1", 1);
+#endif
+
+  // Start display (and timer)
+  glutInit(&argc, argv);
+#ifdef FREEGLUT
+  glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION);
+#endif
+  StartDisplay();
+
+  if (kParams.has_animation) glutTimerFunc(0, decode_callback, 0);
+  glutMainLoop();
+
+  // Should only be reached when using FREEGLUT:
+  ClearParams();
+  FREE_WARGV_AND_RETURN(0);
+
+ Error:
+  ClearParams();
+  FREE_WARGV_AND_RETURN(-1);
+}
+
+#else   // !WEBP_HAVE_GL
+
+int main(int argc, const char* argv[]) {
+  fprintf(stderr, "OpenGL support not enabled in %s.\n", argv[0]);
+  (void)argc;
+  return 0;
+}
+
+#endif
+
+//------------------------------------------------------------------------------
diff --git a/examples/webpinfo.c b/examples/webpinfo.c
new file mode 100644
index 0000000..356abae
--- /dev/null
+++ b/examples/webpinfo.c
@@ -0,0 +1,1186 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  Command-line tool to print out the chunk level structure of WebP files
+//  along with basic integrity checks.
+//
+//  Author: Hui Su (huisu@google.com)
+
+#include <assert.h>
+#include <stdio.h>
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include "../imageio/imageio_util.h"
+#include "./unicode.h"
+#include "webp/decode.h"
+#include "webp/format_constants.h"
+#include "webp/mux_types.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf _snprintf
+#endif
+
+#define LOG_ERROR(MESSAGE)                     \
+  do {                                         \
+    if (webp_info->show_diagnosis_) {          \
+      fprintf(stderr, "Error: %s\n", MESSAGE); \
+    }                                          \
+  } while (0)
+
+#define LOG_WARN(MESSAGE)                        \
+  do {                                           \
+    if (webp_info->show_diagnosis_) {            \
+      fprintf(stderr, "Warning: %s\n", MESSAGE); \
+    }                                            \
+    ++webp_info->num_warnings_;                  \
+  } while (0)
+
+static const char* const kFormats[3] = {
+  "Unknown",
+  "Lossy",
+  "Lossless"
+};
+
+static const char* const kLosslessTransforms[4] = {
+  "Predictor",
+  "Cross Color",
+  "Subtract Green",
+  "Color Indexing"
+};
+
+static const char* const kAlphaFilterMethods[4] = {
+  "None",
+  "Horizontal",
+  "Vertical",
+  "Gradient"
+};
+
+typedef enum {
+  WEBP_INFO_OK = 0,
+  WEBP_INFO_TRUNCATED_DATA,
+  WEBP_INFO_PARSE_ERROR,
+  WEBP_INFO_INVALID_PARAM,
+  WEBP_INFO_BITSTREAM_ERROR,
+  WEBP_INFO_MISSING_DATA,
+  WEBP_INFO_INVALID_COMMAND
+} WebPInfoStatus;
+
+typedef enum ChunkID {
+  CHUNK_VP8,
+  CHUNK_VP8L,
+  CHUNK_VP8X,
+  CHUNK_ALPHA,
+  CHUNK_ANIM,
+  CHUNK_ANMF,
+  CHUNK_ICCP,
+  CHUNK_EXIF,
+  CHUNK_XMP,
+  CHUNK_UNKNOWN,
+  CHUNK_TYPES = CHUNK_UNKNOWN
+} ChunkID;
+
+typedef struct {
+  size_t start_;
+  size_t end_;
+  const uint8_t* buf_;
+} MemBuffer;
+
+typedef struct {
+  size_t offset_;
+  size_t size_;
+  const uint8_t* payload_;
+  ChunkID id_;
+} ChunkData;
+
+typedef struct WebPInfo {
+  int canvas_width_;
+  int canvas_height_;
+  int loop_count_;
+  int num_frames_;
+  int chunk_counts_[CHUNK_TYPES];
+  int anmf_subchunk_counts_[3];  // 0 VP8; 1 VP8L; 2 ALPH.
+  uint32_t bgcolor_;
+  int feature_flags_;
+  int has_alpha_;
+  // Used for parsing ANMF chunks.
+  int frame_width_, frame_height_;
+  size_t anim_frame_data_size_;
+  int is_processing_anim_frame_, seen_alpha_subchunk_, seen_image_subchunk_;
+  // Print output control.
+  int quiet_, show_diagnosis_, show_summary_;
+  int num_warnings_;
+  int parse_bitstream_;
+} WebPInfo;
+
+static void WebPInfoInit(WebPInfo* const webp_info) {
+  memset(webp_info, 0, sizeof(*webp_info));
+}
+
+static const uint32_t kWebPChunkTags[CHUNK_TYPES] = {
+  MKFOURCC('V', 'P', '8', ' '),
+  MKFOURCC('V', 'P', '8', 'L'),
+  MKFOURCC('V', 'P', '8', 'X'),
+  MKFOURCC('A', 'L', 'P', 'H'),
+  MKFOURCC('A', 'N', 'I', 'M'),
+  MKFOURCC('A', 'N', 'M', 'F'),
+  MKFOURCC('I', 'C', 'C', 'P'),
+  MKFOURCC('E', 'X', 'I', 'F'),
+  MKFOURCC('X', 'M', 'P', ' '),
+};
+
+// -----------------------------------------------------------------------------
+// Data reading.
+
+static int GetLE16(const uint8_t* const data) {
+  return (data[0] << 0) | (data[1] << 8);
+}
+
+static int GetLE24(const uint8_t* const data) {
+  return GetLE16(data) | (data[2] << 16);
+}
+
+static uint32_t GetLE32(const uint8_t* const data) {
+  return GetLE16(data) | ((uint32_t)GetLE16(data + 2) << 16);
+}
+
+static int ReadLE16(const uint8_t** data) {
+  const int val = GetLE16(*data);
+  *data += 2;
+  return val;
+}
+
+static int ReadLE24(const uint8_t** data) {
+  const int val = GetLE24(*data);
+  *data += 3;
+  return val;
+}
+
+static uint32_t ReadLE32(const uint8_t** data) {
+  const uint32_t val = GetLE32(*data);
+  *data += 4;
+  return val;
+}
+
+static int ReadFileToWebPData(const char* const filename,
+                              WebPData* const webp_data) {
+  const uint8_t* data;
+  size_t size;
+  if (!ImgIoUtilReadFile(filename, &data, &size)) return 0;
+  webp_data->bytes = data;
+  webp_data->size = size;
+  return 1;
+}
+
+// -----------------------------------------------------------------------------
+// MemBuffer object.
+
+static void InitMemBuffer(MemBuffer* const mem, const WebPData* webp_data) {
+  mem->buf_ = webp_data->bytes;
+  mem->start_ = 0;
+  mem->end_ = webp_data->size;
+}
+
+static size_t MemDataSize(const MemBuffer* const mem) {
+  return (mem->end_ - mem->start_);
+}
+
+static const uint8_t* GetBuffer(MemBuffer* const mem) {
+  return mem->buf_ + mem->start_;
+}
+
+static void Skip(MemBuffer* const mem, size_t size) {
+  mem->start_ += size;
+}
+
+static uint32_t ReadMemBufLE32(MemBuffer* const mem) {
+  const uint8_t* const data = mem->buf_ + mem->start_;
+  const uint32_t val = GetLE32(data);
+  assert(MemDataSize(mem) >= 4);
+  Skip(mem, 4);
+  return val;
+}
+
+// -----------------------------------------------------------------------------
+// Lossy bitstream analysis.
+
+static int GetBits(const uint8_t* const data, size_t data_size, size_t nb,
+                   int* val, uint64_t* const bit_pos) {
+  *val = 0;
+  while (nb-- > 0) {
+    const uint64_t p = (*bit_pos)++;
+    if ((p >> 3) >= data_size) {
+      return 0;
+    } else {
+      const int bit = !!(data[p >> 3] & (128 >> ((p & 7))));
+      *val = (*val << 1) | bit;
+    }
+  }
+  return 1;
+}
+
+static int GetSignedBits(const uint8_t* const data, size_t data_size, size_t nb,
+                         int* val, uint64_t* const bit_pos) {
+  int sign;
+  if (!GetBits(data, data_size, nb, val, bit_pos)) return 0;
+  if (!GetBits(data, data_size, 1, &sign, bit_pos)) return 0;
+  if (sign) *val = -(*val);
+  return 1;
+}
+
+#define GET_BITS(v, n)                                 \
+  do {                                                 \
+    if (!GetBits(data, data_size, n, &(v), bit_pos)) { \
+      LOG_ERROR("Truncated lossy bitstream.");         \
+      return WEBP_INFO_TRUNCATED_DATA;                 \
+    }                                                  \
+  } while (0)
+
+#define GET_SIGNED_BITS(v, n)                                \
+  do {                                                       \
+    if (!GetSignedBits(data, data_size, n, &(v), bit_pos)) { \
+      LOG_ERROR("Truncated lossy bitstream.");               \
+      return WEBP_INFO_TRUNCATED_DATA;                       \
+    }                                                        \
+  } while (0)
+
+static WebPInfoStatus ParseLossySegmentHeader(const WebPInfo* const webp_info,
+                                              const uint8_t* const data,
+                                              size_t data_size,
+                                              uint64_t* const bit_pos) {
+  int use_segment;
+  GET_BITS(use_segment, 1);
+  printf("  Use segment:      %d\n", use_segment);
+  if (use_segment) {
+    int update_map, update_data;
+    GET_BITS(update_map, 1);
+    GET_BITS(update_data, 1);
+    printf("  Update map:       %d\n"
+           "  Update data:      %d\n",
+           update_map, update_data);
+    if (update_data) {
+      int i, a_delta;
+      int quantizer[4] = {0, 0, 0, 0};
+      int filter_strength[4] = {0, 0, 0, 0};
+      GET_BITS(a_delta, 1);
+      printf("  Absolute delta:   %d\n", a_delta);
+      for (i = 0; i < 4; ++i) {
+        int bit;
+        GET_BITS(bit, 1);
+        if (bit) GET_SIGNED_BITS(quantizer[i], 7);
+      }
+      for (i = 0; i < 4; ++i) {
+        int bit;
+        GET_BITS(bit, 1);
+        if (bit) GET_SIGNED_BITS(filter_strength[i], 6);
+      }
+      printf("  Quantizer:        %d %d %d %d\n", quantizer[0], quantizer[1],
+             quantizer[2], quantizer[3]);
+      printf("  Filter strength:  %d %d %d %d\n", filter_strength[0],
+             filter_strength[1], filter_strength[2], filter_strength[3]);
+    }
+    if (update_map) {
+      int i;
+      int prob_segment[3] = {255, 255, 255};
+      for (i = 0; i < 3; ++i) {
+        int bit;
+        GET_BITS(bit, 1);
+        if (bit) GET_BITS(prob_segment[i], 8);
+      }
+      printf("  Prob segment:     %d %d %d\n",
+             prob_segment[0], prob_segment[1], prob_segment[2]);
+    }
+  }
+  return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ParseLossyFilterHeader(const WebPInfo* const webp_info,
+                                             const uint8_t* const data,
+                                             size_t data_size,
+                                             uint64_t* const bit_pos) {
+  int simple_filter, level, sharpness, use_lf_delta;
+  GET_BITS(simple_filter, 1);
+  GET_BITS(level, 6);
+  GET_BITS(sharpness, 3);
+  GET_BITS(use_lf_delta, 1);
+  printf("  Simple filter:    %d\n", simple_filter);
+  printf("  Level:            %d\n", level);
+  printf("  Sharpness:        %d\n", sharpness);
+  printf("  Use lf delta:     %d\n", use_lf_delta);
+  if (use_lf_delta) {
+    int update;
+    GET_BITS(update, 1);
+    printf("  Update lf delta:  %d\n", update);
+    if (update) {
+      int i;
+      for (i = 0; i < 4 + 4; ++i) {
+        int temp;
+        GET_BITS(temp, 1);
+        if (temp) GET_BITS(temp, 7);
+      }
+    }
+  }
+  return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ParseLossyHeader(const ChunkData* const chunk_data,
+                                       const WebPInfo* const webp_info) {
+  const uint8_t* data = chunk_data->payload_;
+  size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE;
+  const uint32_t bits = (uint32_t)data[0] | (data[1] << 8) | (data[2] << 16);
+  const int key_frame = !(bits & 1);
+  const int profile = (bits >> 1) & 7;
+  const int display = (bits >> 4) & 1;
+  const uint32_t partition0_length = (bits >> 5);
+  WebPInfoStatus status = WEBP_INFO_OK;
+  uint64_t bit_position = 0;
+  uint64_t* const bit_pos = &bit_position;
+  int colorspace, clamp_type;
+  printf("  Parsing lossy bitstream...\n");
+  // Calling WebPGetFeatures() in ProcessImageChunk() should ensure this.
+  assert(chunk_data->size_ >= CHUNK_HEADER_SIZE + 10);
+  if (profile > 3) {
+    LOG_ERROR("Unknown profile.");
+    return WEBP_INFO_BITSTREAM_ERROR;
+  }
+  if (!display) {
+    LOG_ERROR("Frame is not displayable.");
+    return WEBP_INFO_BITSTREAM_ERROR;
+  }
+  data += 3;
+  data_size -= 3;
+  printf("  Key frame:        %s\n"
+         "  Profile:          %d\n"
+         "  Display:          %s\n"
+         "  Part. 0 length:   %d\n",
+         key_frame ? "Yes" : "No", profile,
+         display ? "Yes" : "No", partition0_length);
+  if (key_frame) {
+    if (!(data[0] == 0x9d && data[1] == 0x01 && data[2] == 0x2a)) {
+      LOG_ERROR("Invalid lossy bitstream signature.");
+      return WEBP_INFO_BITSTREAM_ERROR;
+    }
+    printf("  Width:            %d\n"
+           "  X scale:          %d\n"
+           "  Height:           %d\n"
+           "  Y scale:          %d\n",
+           ((data[4] << 8) | data[3]) & 0x3fff, data[4] >> 6,
+           ((data[6] << 8) | data[5]) & 0x3fff, data[6] >> 6);
+    data += 7;
+    data_size -= 7;
+  } else {
+    LOG_ERROR("Non-keyframe detected in lossy bitstream.");
+    return WEBP_INFO_BITSTREAM_ERROR;
+  }
+  if (partition0_length >= data_size) {
+    LOG_ERROR("Bad partition length.");
+    return WEBP_INFO_BITSTREAM_ERROR;
+  }
+  GET_BITS(colorspace, 1);
+  GET_BITS(clamp_type, 1);
+  printf("  Color space:      %d\n", colorspace);
+  printf("  Clamp type:       %d\n", clamp_type);
+  status = ParseLossySegmentHeader(webp_info, data, data_size, bit_pos);
+  if (status != WEBP_INFO_OK) return status;
+  status = ParseLossyFilterHeader(webp_info, data, data_size, bit_pos);
+  if (status != WEBP_INFO_OK) return status;
+  {  // Partition number and size.
+    const uint8_t* part_size = data + partition0_length;
+    int num_parts, i;
+    size_t part_data_size;
+    GET_BITS(num_parts, 2);
+    num_parts = 1 << num_parts;
+    if ((int)(data_size - partition0_length) < (num_parts - 1) * 3) {
+      LOG_ERROR("Truncated lossy bitstream.");
+      return WEBP_INFO_TRUNCATED_DATA;
+    }
+    part_data_size = data_size - partition0_length - (num_parts - 1) * 3;
+    printf("  Total partitions: %d\n", num_parts);
+    for (i = 1; i < num_parts; ++i) {
+      const size_t psize =
+          part_size[0] | (part_size[1] << 8) | (part_size[2] << 16);
+      if (psize > part_data_size) {
+        LOG_ERROR("Truncated partition.");
+        return WEBP_INFO_TRUNCATED_DATA;
+      }
+      printf("  Part. %d length:   %d\n", i, (int)psize);
+      part_data_size -= psize;
+      part_size += 3;
+    }
+  }
+  // Quantizer.
+  {
+    int base_q, bit;
+    int dq_y1_dc = 0, dq_y2_dc = 0, dq_y2_ac = 0, dq_uv_dc = 0, dq_uv_ac = 0;
+    GET_BITS(base_q, 7);
+    GET_BITS(bit, 1);
+    if (bit) GET_SIGNED_BITS(dq_y1_dc, 4);
+    GET_BITS(bit, 1);
+    if (bit) GET_SIGNED_BITS(dq_y2_dc, 4);
+    GET_BITS(bit, 1);
+    if (bit) GET_SIGNED_BITS(dq_y2_ac, 4);
+    GET_BITS(bit, 1);
+    if (bit) GET_SIGNED_BITS(dq_uv_dc, 4);
+    GET_BITS(bit, 1);
+    if (bit) GET_SIGNED_BITS(dq_uv_ac, 4);
+    printf("  Base Q:           %d\n", base_q);
+    printf("  DQ Y1 DC:         %d\n", dq_y1_dc);
+    printf("  DQ Y2 DC:         %d\n", dq_y2_dc);
+    printf("  DQ Y2 AC:         %d\n", dq_y2_ac);
+    printf("  DQ UV DC:         %d\n", dq_uv_dc);
+    printf("  DQ UV AC:         %d\n", dq_uv_ac);
+  }
+  if ((*bit_pos >> 3) >= partition0_length) {
+    LOG_ERROR("Truncated lossy bitstream.");
+    return WEBP_INFO_TRUNCATED_DATA;
+  }
+  return WEBP_INFO_OK;
+}
+
+// -----------------------------------------------------------------------------
+// Lossless bitstream analysis.
+
+static int LLGetBits(const uint8_t* const data, size_t data_size, size_t nb,
+                     int* val, uint64_t* const bit_pos) {
+  uint32_t i = 0;
+  *val = 0;
+  while (i < nb) {
+    const uint64_t p = (*bit_pos)++;
+    if ((p >> 3) >= data_size) {
+      return 0;
+    } else {
+      const int bit = !!(data[p >> 3] & (1 << ((p & 7))));
+      *val = *val | (bit << i);
+      ++i;
+    }
+  }
+  return 1;
+}
+
+#define LL_GET_BITS(v, n)                                \
+  do {                                                   \
+    if (!LLGetBits(data, data_size, n, &(v), bit_pos)) { \
+      LOG_ERROR("Truncated lossless bitstream.");        \
+      return WEBP_INFO_TRUNCATED_DATA;                   \
+    }                                                    \
+  } while (0)
+
+static WebPInfoStatus ParseLosslessTransform(WebPInfo* const webp_info,
+                                             const uint8_t* const data,
+                                             size_t data_size,
+                                             uint64_t* const  bit_pos) {
+  int use_transform, block_size, n_colors;
+  LL_GET_BITS(use_transform, 1);
+  printf("  Use transform:    %s\n", use_transform ? "Yes" : "No");
+  if (use_transform) {
+    int type;
+    LL_GET_BITS(type, 2);
+    printf("  1st transform:    %s (%d)\n", kLosslessTransforms[type], type);
+    switch (type) {
+      case PREDICTOR_TRANSFORM:
+      case CROSS_COLOR_TRANSFORM:
+        LL_GET_BITS(block_size, 3);
+        block_size = 1 << (block_size + 2);
+        printf("  Tran. block size: %d\n", block_size);
+        break;
+      case COLOR_INDEXING_TRANSFORM:
+        LL_GET_BITS(n_colors, 8);
+        n_colors += 1;
+        printf("  No. of colors:    %d\n", n_colors);
+        break;
+      default: break;
+    }
+  }
+  return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ParseLosslessHeader(const ChunkData* const chunk_data,
+                                          WebPInfo* const webp_info) {
+  const uint8_t* data = chunk_data->payload_;
+  size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE;
+  uint64_t bit_position = 0;
+  uint64_t* const bit_pos = &bit_position;
+  WebPInfoStatus status;
+  printf("  Parsing lossless bitstream...\n");
+  if (data_size < VP8L_FRAME_HEADER_SIZE) {
+    LOG_ERROR("Truncated lossless bitstream.");
+    return WEBP_INFO_TRUNCATED_DATA;
+  }
+  if (data[0] != VP8L_MAGIC_BYTE) {
+    LOG_ERROR("Invalid lossless bitstream signature.");
+    return WEBP_INFO_BITSTREAM_ERROR;
+  }
+  data += 1;
+  data_size -= 1;
+  {
+    int width, height, has_alpha, version;
+    LL_GET_BITS(width, 14);
+    LL_GET_BITS(height, 14);
+    LL_GET_BITS(has_alpha, 1);
+    LL_GET_BITS(version, 3);
+    width += 1;
+    height += 1;
+    printf("  Width:            %d\n", width);
+    printf("  Height:           %d\n", height);
+    printf("  Alpha:            %d\n", has_alpha);
+    printf("  Version:          %d\n", version);
+  }
+  status = ParseLosslessTransform(webp_info, data, data_size, bit_pos);
+  if (status != WEBP_INFO_OK) return status;
+  return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ParseAlphaHeader(const ChunkData* const chunk_data,
+                                       WebPInfo* const webp_info) {
+  const uint8_t* data = chunk_data->payload_;
+  size_t data_size = chunk_data->size_ - CHUNK_HEADER_SIZE;
+  if (data_size <= ALPHA_HEADER_LEN) {
+    LOG_ERROR("Truncated ALPH chunk.");
+    return WEBP_INFO_TRUNCATED_DATA;
+  }
+  printf("  Parsing ALPH chunk...\n");
+  {
+    const int compression_method = (data[0] >> 0) & 0x03;
+    const int filter = (data[0] >> 2) & 0x03;
+    const int pre_processing = (data[0] >> 4) & 0x03;
+    const int reserved_bits = (data[0] >> 6) & 0x03;
+    printf("  Compression:      %d\n", compression_method);
+    printf("  Filter:           %s (%d)\n",
+           kAlphaFilterMethods[filter], filter);
+    printf("  Pre-processing:   %d\n", pre_processing);
+    if (compression_method > ALPHA_LOSSLESS_COMPRESSION) {
+      LOG_ERROR("Invalid Alpha compression method.");
+      return WEBP_INFO_BITSTREAM_ERROR;
+    }
+    if (pre_processing > ALPHA_PREPROCESSED_LEVELS) {
+      LOG_ERROR("Invalid Alpha pre-processing method.");
+      return WEBP_INFO_BITSTREAM_ERROR;
+    }
+    if (reserved_bits != 0) {
+      LOG_WARN("Reserved bits in ALPH chunk header are not all 0.");
+    }
+    data += ALPHA_HEADER_LEN;
+    data_size -= ALPHA_HEADER_LEN;
+    if (compression_method == ALPHA_LOSSLESS_COMPRESSION) {
+      uint64_t bit_pos = 0;
+      WebPInfoStatus status =
+          ParseLosslessTransform(webp_info, data, data_size, &bit_pos);
+      if (status != WEBP_INFO_OK) return status;
+    }
+  }
+  return WEBP_INFO_OK;
+}
+
+// -----------------------------------------------------------------------------
+// Chunk parsing.
+
+static WebPInfoStatus ParseRIFFHeader(WebPInfo* const webp_info,
+                                      MemBuffer* const mem) {
+  const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE;
+  size_t riff_size;
+
+  if (MemDataSize(mem) < min_size) {
+    LOG_ERROR("Truncated data detected when parsing RIFF header.");
+    return WEBP_INFO_TRUNCATED_DATA;
+  }
+  if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) ||
+      memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) {
+    LOG_ERROR("Corrupted RIFF header.");
+    return WEBP_INFO_PARSE_ERROR;
+  }
+  riff_size = GetLE32(GetBuffer(mem) + TAG_SIZE);
+  if (riff_size < CHUNK_HEADER_SIZE) {
+    LOG_ERROR("RIFF size is too small.");
+    return WEBP_INFO_PARSE_ERROR;
+  }
+  if (riff_size > MAX_CHUNK_PAYLOAD) {
+    LOG_ERROR("RIFF size is over limit.");
+    return WEBP_INFO_PARSE_ERROR;
+  }
+  riff_size += CHUNK_HEADER_SIZE;
+  if (!webp_info->quiet_) {
+    printf("RIFF HEADER:\n");
+    printf("  File size: %6d\n", (int)riff_size);
+  }
+  if (riff_size < mem->end_) {
+    LOG_WARN("RIFF size is smaller than the file size.");
+    mem->end_ = riff_size;
+  } else if (riff_size > mem->end_) {
+    LOG_ERROR("Truncated data detected when parsing RIFF payload.");
+    return WEBP_INFO_TRUNCATED_DATA;
+  }
+  Skip(mem, RIFF_HEADER_SIZE);
+  return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ParseChunk(const WebPInfo* const webp_info,
+                                 MemBuffer* const mem,
+                                 ChunkData* const chunk_data) {
+  memset(chunk_data, 0, sizeof(*chunk_data));
+  if (MemDataSize(mem) < CHUNK_HEADER_SIZE) {
+    LOG_ERROR("Truncated data detected when parsing chunk header.");
+    return WEBP_INFO_TRUNCATED_DATA;
+  } else {
+    const size_t chunk_start_offset = mem->start_;
+    const uint32_t fourcc = ReadMemBufLE32(mem);
+    const uint32_t payload_size = ReadMemBufLE32(mem);
+    const uint32_t payload_size_padded = payload_size + (payload_size & 1);
+    const size_t chunk_size = CHUNK_HEADER_SIZE + payload_size_padded;
+    int i;
+    if (payload_size > MAX_CHUNK_PAYLOAD) {
+      LOG_ERROR("Size of chunk payload is over limit.");
+      return WEBP_INFO_INVALID_PARAM;
+    }
+    if (payload_size_padded > MemDataSize(mem)){
+      LOG_ERROR("Truncated data detected when parsing chunk payload.");
+      return WEBP_INFO_TRUNCATED_DATA;
+    }
+    for (i = 0; i < CHUNK_TYPES; ++i) {
+      if (kWebPChunkTags[i] == fourcc) break;
+    }
+    chunk_data->offset_ = chunk_start_offset;
+    chunk_data->size_ = chunk_size;
+    chunk_data->id_ = (ChunkID)i;
+    chunk_data->payload_ = GetBuffer(mem);
+    if (chunk_data->id_ == CHUNK_ANMF) {
+      if (payload_size != payload_size_padded) {
+        LOG_ERROR("ANMF chunk size should always be even.");
+        return WEBP_INFO_PARSE_ERROR;
+      }
+      // There are sub-chunks to be parsed in an ANMF chunk.
+      Skip(mem, ANMF_CHUNK_SIZE);
+    } else {
+      Skip(mem, payload_size_padded);
+    }
+    return WEBP_INFO_OK;
+  }
+}
+
+// -----------------------------------------------------------------------------
+// Chunk analysis.
+
+static WebPInfoStatus ProcessVP8XChunk(const ChunkData* const chunk_data,
+                                       WebPInfo* const webp_info) {
+  const uint8_t* data = chunk_data->payload_;
+  if (webp_info->chunk_counts_[CHUNK_VP8] ||
+      webp_info->chunk_counts_[CHUNK_VP8L] ||
+      webp_info->chunk_counts_[CHUNK_VP8X]) {
+    LOG_ERROR("Already seen a VP8/VP8L/VP8X chunk when parsing VP8X chunk.");
+    return WEBP_INFO_PARSE_ERROR;
+  }
+  if (chunk_data->size_ != VP8X_CHUNK_SIZE + CHUNK_HEADER_SIZE) {
+    LOG_ERROR("Corrupted VP8X chunk.");
+    return WEBP_INFO_PARSE_ERROR;
+  }
+  ++webp_info->chunk_counts_[CHUNK_VP8X];
+  webp_info->feature_flags_ = *data;
+  data += 4;
+  webp_info->canvas_width_ = 1 + ReadLE24(&data);
+  webp_info->canvas_height_ = 1 + ReadLE24(&data);
+  if (!webp_info->quiet_) {
+    printf("  ICCP: %d\n  Alpha: %d\n  EXIF: %d\n  XMP: %d\n  Animation: %d\n",
+           (webp_info->feature_flags_ & ICCP_FLAG) != 0,
+           (webp_info->feature_flags_ & ALPHA_FLAG) != 0,
+           (webp_info->feature_flags_ & EXIF_FLAG) != 0,
+           (webp_info->feature_flags_ & XMP_FLAG) != 0,
+           (webp_info->feature_flags_ & ANIMATION_FLAG) != 0);
+    printf("  Canvas size %d x %d\n",
+           webp_info->canvas_width_, webp_info->canvas_height_);
+  }
+  if (webp_info->canvas_width_ > MAX_CANVAS_SIZE) {
+    LOG_WARN("Canvas width is out of range in VP8X chunk.");
+  }
+  if (webp_info->canvas_height_ > MAX_CANVAS_SIZE) {
+    LOG_WARN("Canvas height is out of range in VP8X chunk.");
+  }
+  if ((uint64_t)webp_info->canvas_width_ * webp_info->canvas_height_ >
+      MAX_IMAGE_AREA) {
+    LOG_WARN("Canvas area is out of range in VP8X chunk.");
+  }
+  return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ProcessANIMChunk(const ChunkData* const chunk_data,
+                                       WebPInfo* const webp_info) {
+  const uint8_t* data = chunk_data->payload_;
+  if (!webp_info->chunk_counts_[CHUNK_VP8X]) {
+    LOG_ERROR("ANIM chunk detected before VP8X chunk.");
+    return WEBP_INFO_PARSE_ERROR;
+  }
+  if (chunk_data->size_ != ANIM_CHUNK_SIZE + CHUNK_HEADER_SIZE) {
+    LOG_ERROR("Corrupted ANIM chunk.");
+    return WEBP_INFO_PARSE_ERROR;
+  }
+  webp_info->bgcolor_ = ReadLE32(&data);
+  webp_info->loop_count_ = ReadLE16(&data);
+  ++webp_info->chunk_counts_[CHUNK_ANIM];
+  if (!webp_info->quiet_) {
+    printf("  Background color:(ARGB) %02x %02x %02x %02x\n",
+           (webp_info->bgcolor_ >> 24) & 0xff,
+           (webp_info->bgcolor_ >> 16) & 0xff,
+           (webp_info->bgcolor_ >> 8) & 0xff,
+           webp_info->bgcolor_ & 0xff);
+    printf("  Loop count      : %d\n", webp_info->loop_count_);
+  }
+  if (webp_info->loop_count_ > MAX_LOOP_COUNT) {
+    LOG_WARN("Loop count is out of range in ANIM chunk.");
+  }
+  return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ProcessANMFChunk(const ChunkData* const chunk_data,
+                                       WebPInfo* const webp_info) {
+  const uint8_t* data = chunk_data->payload_;
+  int offset_x, offset_y, width, height, duration, blend, dispose, temp;
+  if (webp_info->is_processing_anim_frame_) {
+    LOG_ERROR("ANMF chunk detected within another ANMF chunk.");
+    return WEBP_INFO_PARSE_ERROR;
+  }
+  if (!webp_info->chunk_counts_[CHUNK_ANIM]) {
+    LOG_ERROR("ANMF chunk detected before ANIM chunk.");
+    return WEBP_INFO_PARSE_ERROR;
+  }
+  if (chunk_data->size_ <= CHUNK_HEADER_SIZE + ANMF_CHUNK_SIZE) {
+    LOG_ERROR("Truncated data detected when parsing ANMF chunk.");
+    return WEBP_INFO_TRUNCATED_DATA;
+  }
+  offset_x = 2 * ReadLE24(&data);
+  offset_y = 2 * ReadLE24(&data);
+  width = 1 + ReadLE24(&data);
+  height = 1 + ReadLE24(&data);
+  duration = ReadLE24(&data);
+  temp = *data;
+  dispose = temp & 1;
+  blend = (temp >> 1) & 1;
+  ++webp_info->chunk_counts_[CHUNK_ANMF];
+  if (!webp_info->quiet_) {
+    printf("  Offset_X: %d\n  Offset_Y: %d\n  Width: %d\n  Height: %d\n"
+           "  Duration: %d\n  Dispose: %d\n  Blend: %d\n",
+           offset_x, offset_y, width, height, duration, dispose, blend);
+  }
+  if (duration > MAX_DURATION) {
+    LOG_ERROR("Invalid duration parameter in ANMF chunk.");
+    return WEBP_INFO_INVALID_PARAM;
+  }
+  if (offset_x > MAX_POSITION_OFFSET || offset_y > MAX_POSITION_OFFSET) {
+    LOG_ERROR("Invalid offset parameters in ANMF chunk.");
+    return WEBP_INFO_INVALID_PARAM;
+  }
+  if ((uint64_t)offset_x + width > (uint64_t)webp_info->canvas_width_ ||
+      (uint64_t)offset_y + height > (uint64_t)webp_info->canvas_height_) {
+    LOG_ERROR("Frame exceeds canvas in ANMF chunk.");
+    return WEBP_INFO_INVALID_PARAM;
+  }
+  webp_info->is_processing_anim_frame_ = 1;
+  webp_info->seen_alpha_subchunk_ = 0;
+  webp_info->seen_image_subchunk_ = 0;
+  webp_info->frame_width_ = width;
+  webp_info->frame_height_ = height;
+  webp_info->anim_frame_data_size_ =
+      chunk_data->size_ - CHUNK_HEADER_SIZE - ANMF_CHUNK_SIZE;
+  return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ProcessImageChunk(const ChunkData* const chunk_data,
+                                        WebPInfo* const webp_info) {
+  const uint8_t* data = chunk_data->payload_ - CHUNK_HEADER_SIZE;
+  WebPBitstreamFeatures features;
+  const VP8StatusCode vp8_status =
+      WebPGetFeatures(data, chunk_data->size_, &features);
+  if (vp8_status != VP8_STATUS_OK) {
+    LOG_ERROR("VP8/VP8L bitstream error.");
+    return WEBP_INFO_BITSTREAM_ERROR;
+  }
+  if (!webp_info->quiet_) {
+    assert(features.format >= 0 && features.format <= 2);
+    printf("  Width: %d\n  Height: %d\n  Alpha: %d\n  Animation: %d\n"
+           "  Format: %s (%d)\n",
+           features.width, features.height, features.has_alpha,
+           features.has_animation, kFormats[features.format], features.format);
+  }
+  if (webp_info->is_processing_anim_frame_) {
+    ++webp_info->anmf_subchunk_counts_[chunk_data->id_ == CHUNK_VP8 ? 0 : 1];
+    if (chunk_data->id_ == CHUNK_VP8L && webp_info->seen_alpha_subchunk_) {
+      LOG_ERROR("Both VP8L and ALPH sub-chunks are present in an ANMF chunk.");
+      return WEBP_INFO_PARSE_ERROR;
+    }
+    if (webp_info->frame_width_ != features.width ||
+        webp_info->frame_height_ != features.height) {
+      LOG_ERROR("Frame size in VP8/VP8L sub-chunk differs from ANMF header.");
+      return WEBP_INFO_PARSE_ERROR;
+    }
+    if (webp_info->seen_image_subchunk_) {
+      LOG_ERROR("Consecutive VP8/VP8L sub-chunks in an ANMF chunk.");
+      return WEBP_INFO_PARSE_ERROR;
+    }
+    webp_info->seen_image_subchunk_ = 1;
+  } else {
+    if (webp_info->chunk_counts_[CHUNK_VP8] ||
+        webp_info->chunk_counts_[CHUNK_VP8L]) {
+      LOG_ERROR("Multiple VP8/VP8L chunks detected.");
+      return WEBP_INFO_PARSE_ERROR;
+    }
+    if (chunk_data->id_ == CHUNK_VP8L &&
+        webp_info->chunk_counts_[CHUNK_ALPHA]) {
+      LOG_WARN("Both VP8L and ALPH chunks are detected.");
+    }
+    if (webp_info->chunk_counts_[CHUNK_ANIM] ||
+        webp_info->chunk_counts_[CHUNK_ANMF]) {
+      LOG_ERROR("VP8/VP8L chunk and ANIM/ANMF chunk are both detected.");
+      return WEBP_INFO_PARSE_ERROR;
+    }
+    if (webp_info->chunk_counts_[CHUNK_VP8X]) {
+      if (webp_info->canvas_width_ != features.width ||
+          webp_info->canvas_height_ != features.height) {
+        LOG_ERROR("Image size in VP8/VP8L chunk differs from VP8X chunk.");
+        return WEBP_INFO_PARSE_ERROR;
+      }
+    } else {
+      webp_info->canvas_width_ = features.width;
+      webp_info->canvas_height_ = features.height;
+      if (webp_info->canvas_width_ < 1 || webp_info->canvas_height_ < 1 ||
+          webp_info->canvas_width_ > MAX_CANVAS_SIZE ||
+          webp_info->canvas_height_ > MAX_CANVAS_SIZE ||
+          (uint64_t)webp_info->canvas_width_ * webp_info->canvas_height_ >
+              MAX_IMAGE_AREA) {
+        LOG_WARN("Invalid parameters in VP8/VP8L chunk.");
+      }
+    }
+    ++webp_info->chunk_counts_[chunk_data->id_];
+  }
+  ++webp_info->num_frames_;
+  webp_info->has_alpha_ |= features.has_alpha;
+  if (webp_info->parse_bitstream_) {
+    const int is_lossy = (chunk_data->id_ == CHUNK_VP8);
+    const WebPInfoStatus status =
+        is_lossy ? ParseLossyHeader(chunk_data, webp_info)
+                 : ParseLosslessHeader(chunk_data, webp_info);
+    if (status != WEBP_INFO_OK) return status;
+  }
+  return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ProcessALPHChunk(const ChunkData* const chunk_data,
+                                       WebPInfo* const webp_info) {
+  if (webp_info->is_processing_anim_frame_) {
+    ++webp_info->anmf_subchunk_counts_[2];
+    if (webp_info->seen_alpha_subchunk_) {
+      LOG_ERROR("Consecutive ALPH sub-chunks in an ANMF chunk.");
+      return WEBP_INFO_PARSE_ERROR;
+    }
+    webp_info->seen_alpha_subchunk_ = 1;
+
+    if (webp_info->seen_image_subchunk_) {
+      LOG_ERROR("ALPHA sub-chunk detected after VP8 sub-chunk "
+                "in an ANMF chunk.");
+      return WEBP_INFO_PARSE_ERROR;
+    }
+  } else {
+    if (webp_info->chunk_counts_[CHUNK_ANIM] ||
+        webp_info->chunk_counts_[CHUNK_ANMF]) {
+      LOG_ERROR("ALPHA chunk and ANIM/ANMF chunk are both detected.");
+      return WEBP_INFO_PARSE_ERROR;
+    }
+    if (!webp_info->chunk_counts_[CHUNK_VP8X]) {
+      LOG_ERROR("ALPHA chunk detected before VP8X chunk.");
+      return WEBP_INFO_PARSE_ERROR;
+    }
+    if (webp_info->chunk_counts_[CHUNK_VP8]) {
+      LOG_ERROR("ALPHA chunk detected after VP8 chunk.");
+      return WEBP_INFO_PARSE_ERROR;
+    }
+    if (webp_info->chunk_counts_[CHUNK_ALPHA]) {
+      LOG_ERROR("Multiple ALPHA chunks detected.");
+      return WEBP_INFO_PARSE_ERROR;
+    }
+    ++webp_info->chunk_counts_[CHUNK_ALPHA];
+  }
+  webp_info->has_alpha_ = 1;
+  if (webp_info->parse_bitstream_) {
+    const WebPInfoStatus status = ParseAlphaHeader(chunk_data, webp_info);
+    if (status != WEBP_INFO_OK) return status;
+  }
+  return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ProcessICCPChunk(const ChunkData* const chunk_data,
+                                       WebPInfo* const webp_info) {
+  (void)chunk_data;
+  if (!webp_info->chunk_counts_[CHUNK_VP8X]) {
+    LOG_ERROR("ICCP chunk detected before VP8X chunk.");
+    return WEBP_INFO_PARSE_ERROR;
+  }
+  if (webp_info->chunk_counts_[CHUNK_VP8] ||
+      webp_info->chunk_counts_[CHUNK_VP8L] ||
+      webp_info->chunk_counts_[CHUNK_ANIM]) {
+    LOG_ERROR("ICCP chunk detected after image data.");
+    return WEBP_INFO_PARSE_ERROR;
+  }
+  ++webp_info->chunk_counts_[CHUNK_ICCP];
+  return WEBP_INFO_OK;
+}
+
+static WebPInfoStatus ProcessChunk(const ChunkData* const chunk_data,
+                                   WebPInfo* const webp_info) {
+  WebPInfoStatus status = WEBP_INFO_OK;
+  ChunkID id = chunk_data->id_;
+  if (chunk_data->id_ == CHUNK_UNKNOWN) {
+    char error_message[50];
+    snprintf(error_message, 50, "Unknown chunk at offset %6d, length %6d",
+            (int)chunk_data->offset_, (int)chunk_data->size_);
+    LOG_WARN(error_message);
+  } else {
+    if (!webp_info->quiet_) {
+      char tag[4];
+      uint32_t fourcc = kWebPChunkTags[chunk_data->id_];
+#ifdef WORDS_BIGENDIAN
+      fourcc = (fourcc >> 24) | ((fourcc >> 8) & 0xff00) |
+               ((fourcc << 8) & 0xff0000) | (fourcc << 24);
+#endif
+      memcpy(tag, &fourcc, sizeof(tag));
+      printf("Chunk %c%c%c%c at offset %6d, length %6d\n",
+             tag[0], tag[1], tag[2], tag[3], (int)chunk_data->offset_,
+             (int)chunk_data->size_);
+    }
+  }
+  switch (id) {
+    case CHUNK_VP8:
+    case CHUNK_VP8L:
+      status = ProcessImageChunk(chunk_data, webp_info);
+      break;
+    case CHUNK_VP8X:
+      status = ProcessVP8XChunk(chunk_data, webp_info);
+      break;
+    case CHUNK_ALPHA:
+      status = ProcessALPHChunk(chunk_data, webp_info);
+      break;
+    case CHUNK_ANIM:
+      status = ProcessANIMChunk(chunk_data, webp_info);
+      break;
+    case CHUNK_ANMF:
+      status = ProcessANMFChunk(chunk_data, webp_info);
+      break;
+    case CHUNK_ICCP:
+      status = ProcessICCPChunk(chunk_data, webp_info);
+      break;
+    case CHUNK_EXIF:
+    case CHUNK_XMP:
+      ++webp_info->chunk_counts_[id];
+      break;
+    case CHUNK_UNKNOWN:
+    default:
+      break;
+  }
+  if (webp_info->is_processing_anim_frame_ && id != CHUNK_ANMF) {
+    if (webp_info->anim_frame_data_size_ == chunk_data->size_) {
+      if (!webp_info->seen_image_subchunk_) {
+        LOG_ERROR("No VP8/VP8L chunk detected in an ANMF chunk.");
+        return WEBP_INFO_PARSE_ERROR;
+      }
+      webp_info->is_processing_anim_frame_ = 0;
+    } else if (webp_info->anim_frame_data_size_ > chunk_data->size_) {
+      webp_info->anim_frame_data_size_ -= chunk_data->size_;
+    } else {
+      LOG_ERROR("Truncated data detected when parsing ANMF chunk.");
+      return WEBP_INFO_TRUNCATED_DATA;
+    }
+  }
+  return status;
+}
+
+static WebPInfoStatus Validate(WebPInfo* const webp_info) {
+  if (webp_info->num_frames_ < 1) {
+    LOG_ERROR("No image/frame detected.");
+    return WEBP_INFO_MISSING_DATA;
+  }
+  if (webp_info->chunk_counts_[CHUNK_VP8X]) {
+    const int iccp = !!(webp_info->feature_flags_ & ICCP_FLAG);
+    const int exif = !!(webp_info->feature_flags_ & EXIF_FLAG);
+    const int xmp = !!(webp_info->feature_flags_ & XMP_FLAG);
+    const int animation = !!(webp_info->feature_flags_ & ANIMATION_FLAG);
+    const int alpha = !!(webp_info->feature_flags_ & ALPHA_FLAG);
+    if (!alpha && webp_info->has_alpha_) {
+      LOG_ERROR("Unexpected alpha data detected.");
+      return WEBP_INFO_PARSE_ERROR;
+    }
+    if (alpha && !webp_info->has_alpha_) {
+      LOG_WARN("Alpha flag is set with no alpha data present.");
+    }
+    if (iccp && !webp_info->chunk_counts_[CHUNK_ICCP]) {
+      LOG_ERROR("Missing ICCP chunk.");
+      return WEBP_INFO_MISSING_DATA;
+    }
+    if (exif && !webp_info->chunk_counts_[CHUNK_EXIF]) {
+      LOG_ERROR("Missing EXIF chunk.");
+      return WEBP_INFO_MISSING_DATA;
+    }
+    if (xmp && !webp_info->chunk_counts_[CHUNK_XMP]) {
+      LOG_ERROR("Missing XMP chunk.");
+      return WEBP_INFO_MISSING_DATA;
+    }
+    if (!iccp && webp_info->chunk_counts_[CHUNK_ICCP]) {
+      LOG_ERROR("Unexpected ICCP chunk detected.");
+      return WEBP_INFO_PARSE_ERROR;
+    }
+    if (!exif && webp_info->chunk_counts_[CHUNK_EXIF]) {
+      LOG_ERROR("Unexpected EXIF chunk detected.");
+      return WEBP_INFO_PARSE_ERROR;
+    }
+    if (!xmp && webp_info->chunk_counts_[CHUNK_XMP]) {
+      LOG_ERROR("Unexpected XMP chunk detected.");
+      return WEBP_INFO_PARSE_ERROR;
+    }
+    // Incomplete animation frame.
+    if (webp_info->is_processing_anim_frame_) return WEBP_INFO_MISSING_DATA;
+    if (!animation && webp_info->num_frames_ > 1) {
+      LOG_ERROR("More than 1 frame detected in non-animation file.");
+      return WEBP_INFO_PARSE_ERROR;
+    }
+    if (animation && (!webp_info->chunk_counts_[CHUNK_ANIM] ||
+        !webp_info->chunk_counts_[CHUNK_ANMF])) {
+      LOG_ERROR("No ANIM/ANMF chunk detected in animation file.");
+      return WEBP_INFO_PARSE_ERROR;
+    }
+  }
+  return WEBP_INFO_OK;
+}
+
+static void ShowSummary(const WebPInfo* const webp_info) {
+  int i;
+  printf("Summary:\n");
+  printf("Number of frames: %d\n", webp_info->num_frames_);
+  printf("Chunk type  :  VP8 VP8L VP8X ALPH ANIM ANMF(VP8 /VP8L/ALPH) ICCP "
+      "EXIF  XMP\n");
+  printf("Chunk counts: ");
+  for (i = 0; i < CHUNK_TYPES; ++i) {
+    printf("%4d ", webp_info->chunk_counts_[i]);
+    if (i == CHUNK_ANMF) {
+      printf("%4d %4d %4d  ",
+             webp_info->anmf_subchunk_counts_[0],
+             webp_info->anmf_subchunk_counts_[1],
+             webp_info->anmf_subchunk_counts_[2]);
+    }
+  }
+  printf("\n");
+}
+
+static WebPInfoStatus AnalyzeWebP(WebPInfo* const webp_info,
+                                  const WebPData* webp_data) {
+  ChunkData chunk_data;
+  MemBuffer mem_buffer;
+  WebPInfoStatus webp_info_status = WEBP_INFO_OK;
+
+  InitMemBuffer(&mem_buffer, webp_data);
+  webp_info_status = ParseRIFFHeader(webp_info, &mem_buffer);
+  if (webp_info_status != WEBP_INFO_OK) goto Error;
+
+  //  Loop through all the chunks. Terminate immediately in case of error.
+  while (webp_info_status == WEBP_INFO_OK && MemDataSize(&mem_buffer) > 0) {
+    webp_info_status = ParseChunk(webp_info, &mem_buffer, &chunk_data);
+    if (webp_info_status != WEBP_INFO_OK) goto Error;
+    webp_info_status = ProcessChunk(&chunk_data, webp_info);
+  }
+  if (webp_info_status != WEBP_INFO_OK) goto Error;
+  if (webp_info->show_summary_) ShowSummary(webp_info);
+
+  //  Final check.
+  webp_info_status = Validate(webp_info);
+
+ Error:
+  if (!webp_info->quiet_) {
+    if (webp_info_status == WEBP_INFO_OK) {
+      printf("No error detected.\n");
+    } else {
+      printf("Errors detected.\n");
+    }
+    if (webp_info->num_warnings_ > 0) {
+      printf("There were %d warning(s).\n", webp_info->num_warnings_);
+    }
+  }
+  return webp_info_status;
+}
+
+static void Help(void) {
+  printf("Usage: webpinfo [options] in_files\n"
+         "Note: there could be multiple input files;\n"
+         "      options must come before input files.\n"
+         "Options:\n"
+         "  -version ........... Print version number and exit.\n"
+         "  -quiet ............. Do not show chunk parsing information.\n"
+         "  -diag .............. Show parsing error diagnosis.\n"
+         "  -summary ........... Show chunk stats summary.\n"
+         "  -bitstream_info .... Parse bitstream header.\n");
+}
+
+int main(int argc, const char* argv[]) {
+  int c, quiet = 0, show_diag = 0, show_summary = 0;
+  int parse_bitstream = 0;
+  WebPInfoStatus webp_info_status = WEBP_INFO_OK;
+  WebPInfo webp_info;
+
+  INIT_WARGV(argc, argv);
+
+  if (argc == 1) {
+    Help();
+    FREE_WARGV_AND_RETURN(WEBP_INFO_OK);
+  }
+
+  // Parse command-line input.
+  for (c = 1; c < argc; ++c) {
+    if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help") ||
+        !strcmp(argv[c], "-H") || !strcmp(argv[c], "-longhelp")) {
+      Help();
+      FREE_WARGV_AND_RETURN(WEBP_INFO_OK);
+    } else if (!strcmp(argv[c], "-quiet")) {
+      quiet = 1;
+    } else if (!strcmp(argv[c], "-diag")) {
+      show_diag = 1;
+    } else if (!strcmp(argv[c], "-summary")) {
+      show_summary = 1;
+    } else if (!strcmp(argv[c], "-bitstream_info")) {
+      parse_bitstream = 1;
+    } else if (!strcmp(argv[c], "-version")) {
+      const int version = WebPGetDecoderVersion();
+      printf("WebP Decoder version: %d.%d.%d\n",
+             (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
+      FREE_WARGV_AND_RETURN(0);
+    } else {  // Assume the remaining are all input files.
+      break;
+    }
+  }
+
+  if (c == argc) {
+    Help();
+    FREE_WARGV_AND_RETURN(WEBP_INFO_INVALID_COMMAND);
+  }
+
+  // Process input files one by one.
+  for (; c < argc; ++c) {
+    WebPData webp_data;
+    const W_CHAR* in_file = NULL;
+    WebPInfoInit(&webp_info);
+    webp_info.quiet_ = quiet;
+    webp_info.show_diagnosis_ = show_diag;
+    webp_info.show_summary_ = show_summary;
+    webp_info.parse_bitstream_ = parse_bitstream;
+    in_file = GET_WARGV(argv, c);
+    if (in_file == NULL ||
+        !ReadFileToWebPData((const char*)in_file, &webp_data)) {
+      webp_info_status = WEBP_INFO_INVALID_COMMAND;
+      WFPRINTF(stderr, "Failed to open input file %s.\n", in_file);
+      continue;
+    }
+    if (!webp_info.quiet_) WPRINTF("File: %s\n", in_file);
+    webp_info_status = AnalyzeWebP(&webp_info, &webp_data);
+    WebPDataClear(&webp_data);
+  }
+  FREE_WARGV_AND_RETURN(webp_info_status);
+}
diff --git a/examples/webpmux.c b/examples/webpmux.c
new file mode 100644
index 0000000..b61eed6
--- /dev/null
+++ b/examples/webpmux.c
@@ -0,0 +1,1234 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  Simple command-line to create a WebP container file and to extract or strip
+//  relevant data from the container file.
+//
+// Authors: Vikas (vikaas.arora@gmail.com),
+//          Urvang (urvang@google.com)
+
+/*  Usage examples:
+
+  Create container WebP file:
+    webpmux -frame anim_1.webp +100+10+10   \
+            -frame anim_2.webp +100+25+25+1 \
+            -frame anim_3.webp +100+50+50+1 \
+            -frame anim_4.webp +100         \
+            -loop 10 -bgcolor 128,255,255,255 \
+            -o out_animation_container.webp
+
+    webpmux -set icc image_profile.icc in.webp -o out_icc_container.webp
+    webpmux -set exif image_metadata.exif in.webp -o out_exif_container.webp
+    webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp
+    webpmux -set loop 1 in.webp -o out_looped.webp
+
+  Extract relevant data from WebP container file:
+    webpmux -get frame n in.webp -o out_frame.webp
+    webpmux -get icc in.webp -o image_profile.icc
+    webpmux -get exif in.webp -o image_metadata.exif
+    webpmux -get xmp in.webp -o image_metadata.xmp
+
+  Strip data from WebP Container file:
+    webpmux -strip icc in.webp -o out.webp
+    webpmux -strip exif in.webp -o out.webp
+    webpmux -strip xmp in.webp -o out.webp
+
+  Change duration of frame intervals:
+    webpmux -duration 150 in.webp -o out.webp
+    webpmux -duration 33,2 in.webp -o out.webp
+    webpmux -duration 200,10,0 -duration 150,6,50 in.webp -o out.webp
+
+  Misc:
+    webpmux -info in.webp
+    webpmux [ -h | -help ]
+    webpmux -version
+    webpmux argument_file_name
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "webp/decode.h"
+#include "webp/mux.h"
+#include "../examples/example_util.h"
+#include "../imageio/imageio_util.h"
+#include "./unicode.h"
+
+//------------------------------------------------------------------------------
+// Config object to parse command-line arguments.
+
+typedef enum {
+  NIL_ACTION = 0,
+  ACTION_GET,
+  ACTION_SET,
+  ACTION_STRIP,
+  ACTION_INFO,
+  ACTION_HELP,
+  ACTION_DURATION
+} ActionType;
+
+typedef enum {
+  NIL_SUBTYPE = 0,
+  SUBTYPE_ANMF,
+  SUBTYPE_LOOP,
+  SUBTYPE_BGCOLOR
+} FeatureSubType;
+
+typedef struct {
+  FeatureSubType subtype_;
+  const char* filename_;
+  const char* params_;
+} FeatureArg;
+
+typedef enum {
+  NIL_FEATURE = 0,
+  FEATURE_EXIF,
+  FEATURE_XMP,
+  FEATURE_ICCP,
+  FEATURE_ANMF,
+  FEATURE_DURATION,
+  FEATURE_LOOP,
+  FEATURE_BGCOLOR,
+  LAST_FEATURE
+} FeatureType;
+
+static const char* const kFourccList[LAST_FEATURE] = {
+  NULL, "EXIF", "XMP ", "ICCP", "ANMF"
+};
+
+static const char* const kDescriptions[LAST_FEATURE] = {
+  NULL, "EXIF metadata", "XMP metadata", "ICC profile",
+  "Animation frame"
+};
+
+typedef struct {
+  CommandLineArguments cmd_args_;
+
+  ActionType action_type_;
+  const char* input_;
+  const char* output_;
+  FeatureType type_;
+  FeatureArg* args_;
+  int arg_count_;
+} Config;
+
+//------------------------------------------------------------------------------
+// Helper functions.
+
+static int CountOccurrences(const CommandLineArguments* const args,
+                            const char* const arg) {
+  int i;
+  int num_occurences = 0;
+
+  for (i = 0; i < args->argc_; ++i) {
+    if (!strcmp(args->argv_[i], arg)) {
+      ++num_occurences;
+    }
+  }
+  return num_occurences;
+}
+
+static const char* const kErrorMessages[-WEBP_MUX_NOT_ENOUGH_DATA + 1] = {
+  "WEBP_MUX_NOT_FOUND", "WEBP_MUX_INVALID_ARGUMENT", "WEBP_MUX_BAD_DATA",
+  "WEBP_MUX_MEMORY_ERROR", "WEBP_MUX_NOT_ENOUGH_DATA"
+};
+
+static const char* ErrorString(WebPMuxError err) {
+  assert(err <= WEBP_MUX_NOT_FOUND && err >= WEBP_MUX_NOT_ENOUGH_DATA);
+  return kErrorMessages[-err];
+}
+
+#define RETURN_IF_ERROR(ERR_MSG)                                     \
+  if (err != WEBP_MUX_OK) {                                          \
+    fprintf(stderr, ERR_MSG);                                        \
+    return err;                                                      \
+  }
+
+#define RETURN_IF_ERROR3(ERR_MSG, FORMAT_STR1, FORMAT_STR2)          \
+  if (err != WEBP_MUX_OK) {                                          \
+    fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2);              \
+    return err;                                                      \
+  }
+
+#define ERROR_GOTO1(ERR_MSG, LABEL)                                  \
+  do {                                                               \
+    fprintf(stderr, ERR_MSG);                                        \
+    ok = 0;                                                          \
+    goto LABEL;                                                      \
+  } while (0)
+
+#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL)                      \
+  do {                                                               \
+    fprintf(stderr, ERR_MSG, FORMAT_STR);                            \
+    ok = 0;                                                          \
+    goto LABEL;                                                      \
+  } while (0)
+
+#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL)        \
+  do {                                                               \
+    fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2);              \
+    ok = 0;                                                          \
+    goto LABEL;                                                      \
+  } while (0)
+
+static WebPMuxError DisplayInfo(const WebPMux* mux) {
+  int width, height;
+  uint32_t flag;
+
+  WebPMuxError err = WebPMuxGetCanvasSize(mux, &width, &height);
+  assert(err == WEBP_MUX_OK);  // As WebPMuxCreate() was successful earlier.
+  printf("Canvas size: %d x %d\n", width, height);
+
+  err = WebPMuxGetFeatures(mux, &flag);
+  RETURN_IF_ERROR("Failed to retrieve features\n");
+
+  if (flag == 0) {
+    printf("No features present.\n");
+    return err;
+  }
+
+  // Print the features present.
+  printf("Features present:");
+  if (flag & ANIMATION_FLAG) printf(" animation");
+  if (flag & ICCP_FLAG)      printf(" ICC profile");
+  if (flag & EXIF_FLAG)      printf(" EXIF metadata");
+  if (flag & XMP_FLAG)       printf(" XMP metadata");
+  if (flag & ALPHA_FLAG)     printf(" transparency");
+  printf("\n");
+
+  if (flag & ANIMATION_FLAG) {
+    const WebPChunkId id = WEBP_CHUNK_ANMF;
+    const char* const type_str = "frame";
+    int nFrames;
+
+    WebPMuxAnimParams params;
+    err = WebPMuxGetAnimationParams(mux, &params);
+    assert(err == WEBP_MUX_OK);
+    printf("Background color : 0x%.8X  Loop Count : %d\n",
+           params.bgcolor, params.loop_count);
+
+    err = WebPMuxNumChunks(mux, id, &nFrames);
+    assert(err == WEBP_MUX_OK);
+
+    printf("Number of %ss: %d\n", type_str, nFrames);
+    if (nFrames > 0) {
+      int i;
+      printf("No.: width height alpha x_offset y_offset ");
+      printf("duration   dispose blend ");
+      printf("image_size  compression\n");
+      for (i = 1; i <= nFrames; i++) {
+        WebPMuxFrameInfo frame;
+        err = WebPMuxGetFrame(mux, i, &frame);
+        if (err == WEBP_MUX_OK) {
+          WebPBitstreamFeatures features;
+          const VP8StatusCode status = WebPGetFeatures(
+              frame.bitstream.bytes, frame.bitstream.size, &features);
+          assert(status == VP8_STATUS_OK);  // Checked by WebPMuxCreate().
+          (void)status;
+          printf("%3d: %5d %5d %5s %8d %8d ", i, features.width,
+                 features.height, features.has_alpha ? "yes" : "no",
+                 frame.x_offset, frame.y_offset);
+          {
+            const char* const dispose =
+                (frame.dispose_method == WEBP_MUX_DISPOSE_NONE) ? "none"
+                                                                : "background";
+            const char* const blend =
+                (frame.blend_method == WEBP_MUX_BLEND) ? "yes" : "no";
+            printf("%8d %10s %5s ", frame.duration, dispose, blend);
+          }
+          printf("%10d %11s\n", (int)frame.bitstream.size,
+                 (features.format == 1) ? "lossy" :
+                 (features.format == 2) ? "lossless" :
+                                          "undefined");
+        }
+        WebPDataClear(&frame.bitstream);
+        RETURN_IF_ERROR3("Failed to retrieve %s#%d\n", type_str, i);
+      }
+    }
+  }
+
+  if (flag & ICCP_FLAG) {
+    WebPData icc_profile;
+    err = WebPMuxGetChunk(mux, "ICCP", &icc_profile);
+    assert(err == WEBP_MUX_OK);
+    printf("Size of the ICC profile data: %d\n", (int)icc_profile.size);
+  }
+
+  if (flag & EXIF_FLAG) {
+    WebPData exif;
+    err = WebPMuxGetChunk(mux, "EXIF", &exif);
+    assert(err == WEBP_MUX_OK);
+    printf("Size of the EXIF metadata: %d\n", (int)exif.size);
+  }
+
+  if (flag & XMP_FLAG) {
+    WebPData xmp;
+    err = WebPMuxGetChunk(mux, "XMP ", &xmp);
+    assert(err == WEBP_MUX_OK);
+    printf("Size of the XMP metadata: %d\n", (int)xmp.size);
+  }
+
+  if ((flag & ALPHA_FLAG) && !(flag & ANIMATION_FLAG)) {
+    WebPMuxFrameInfo image;
+    err = WebPMuxGetFrame(mux, 1, &image);
+    if (err == WEBP_MUX_OK) {
+      printf("Size of the image (with alpha): %d\n", (int)image.bitstream.size);
+    }
+    WebPDataClear(&image.bitstream);
+    RETURN_IF_ERROR("Failed to retrieve the image\n");
+  }
+
+  return WEBP_MUX_OK;
+}
+
+static void PrintHelp(void) {
+  printf("Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT\n");
+  printf("       webpmux -set SET_OPTIONS INPUT -o OUTPUT\n");
+  printf("       webpmux -duration DURATION_OPTIONS [-duration ...]\n");
+  printf("               INPUT -o OUTPUT\n");
+  printf("       webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT\n");
+  printf("       webpmux -frame FRAME_OPTIONS [-frame...] [-loop LOOP_COUNT]"
+         "\n");
+  printf("               [-bgcolor BACKGROUND_COLOR] -o OUTPUT\n");
+  printf("       webpmux -info INPUT\n");
+  printf("       webpmux [-h|-help]\n");
+  printf("       webpmux -version\n");
+  printf("       webpmux argument_file_name\n");
+
+  printf("\n");
+  printf("GET_OPTIONS:\n");
+  printf(" Extract relevant data:\n");
+  printf("   icc       get ICC profile\n");
+  printf("   exif      get EXIF metadata\n");
+  printf("   xmp       get XMP metadata\n");
+  printf("   frame n   get nth frame\n");
+
+  printf("\n");
+  printf("SET_OPTIONS:\n");
+  printf(" Set color profile/metadata/parameters:\n");
+  printf("   loop LOOP_COUNT            set the loop count\n");
+  printf("   bgcolor BACKGROUND_COLOR   set the animation background color\n");
+  printf("   icc  file.icc              set ICC profile\n");
+  printf("   exif file.exif             set EXIF metadata\n");
+  printf("   xmp  file.xmp              set XMP metadata\n");
+  printf("   where:    'file.icc' contains the ICC profile to be set,\n");
+  printf("             'file.exif' contains the EXIF metadata to be set\n");
+  printf("             'file.xmp' contains the XMP metadata to be set\n");
+
+  printf("\n");
+  printf("DURATION_OPTIONS:\n");
+  printf(" Set duration of selected frames:\n");
+  printf("   duration            set duration for all frames\n");
+  printf("   duration,frame      set duration of a particular frame\n");
+  printf("   duration,start,end  set duration of frames in the\n");
+  printf("                        interval [start,end])\n");
+  printf("   where: 'duration' is the duration in milliseconds\n");
+  printf("          'start' is the start frame index\n");
+  printf("          'end' is the inclusive end frame index\n");
+  printf("           The special 'end' value '0' means: last frame.\n");
+
+  printf("\n");
+  printf("STRIP_OPTIONS:\n");
+  printf(" Strip color profile/metadata:\n");
+  printf("   icc       strip ICC profile\n");
+  printf("   exif      strip EXIF metadata\n");
+  printf("   xmp       strip XMP metadata\n");
+
+  printf("\n");
+  printf("FRAME_OPTIONS(i):\n");
+  printf(" Create animation:\n");
+  printf("   file_i +di[+xi+yi[+mi[bi]]]\n");
+  printf("   where:    'file_i' is the i'th animation frame (WebP format),\n");
+  printf("             'di' is the pause duration before next frame,\n");
+  printf("             'xi','yi' specify the image offset for this frame,\n");
+  printf("             'mi' is the dispose method for this frame (0 or 1),\n");
+  printf("             'bi' is the blending method for this frame (+b or -b)"
+         "\n");
+
+  printf("\n");
+  printf("LOOP_COUNT:\n");
+  printf(" Number of times to repeat the animation.\n");
+  printf(" Valid range is 0 to 65535 [Default: 0 (infinite)].\n");
+
+  printf("\n");
+  printf("BACKGROUND_COLOR:\n");
+  printf(" Background color of the canvas.\n");
+  printf("  A,R,G,B\n");
+  printf("  where:    'A', 'R', 'G' and 'B' are integers in the range 0 to 255 "
+         "specifying\n");
+  printf("            the Alpha, Red, Green and Blue component values "
+         "respectively\n");
+  printf("            [Default: 255,255,255,255]\n");
+
+  printf("\nINPUT & OUTPUT are in WebP format.\n");
+
+  printf("\nNote: The nature of EXIF, XMP and ICC data is not checked");
+  printf(" and is assumed to be\nvalid.\n");
+  printf("\nNote: if a single file name is passed as the argument, the "
+         "arguments will be\n");
+  printf("tokenized from this file. The file name must not start with "
+         "the character '-'.\n");
+}
+
+static void WarnAboutOddOffset(const WebPMuxFrameInfo* const info) {
+  if ((info->x_offset | info->y_offset) & 1) {
+    fprintf(stderr, "Warning: odd offsets will be snapped to even values"
+            " (%d, %d) -> (%d, %d)\n", info->x_offset, info->y_offset,
+            info->x_offset & ~1, info->y_offset & ~1);
+  }
+}
+
+static int CreateMux(const char* const filename, WebPMux** mux) {
+  WebPData bitstream;
+  assert(mux != NULL);
+  if (!ExUtilReadFileToWebPData(filename, &bitstream)) return 0;
+  *mux = WebPMuxCreate(&bitstream, 1);
+  WebPDataClear(&bitstream);
+  if (*mux != NULL) return 1;
+  WFPRINTF(stderr, "Failed to create mux object from file %s.\n",
+           (const W_CHAR*)filename);
+  return 0;
+}
+
+static int WriteData(const char* filename, const WebPData* const webpdata) {
+  int ok = 0;
+  FILE* fout = WSTRCMP(filename, "-") ? WFOPEN(filename, "wb")
+                                      : ImgIoUtilSetBinaryMode(stdout);
+  if (fout == NULL) {
+    WFPRINTF(stderr, "Error opening output WebP file %s!\n",
+             (const W_CHAR*)filename);
+    return 0;
+  }
+  if (fwrite(webpdata->bytes, webpdata->size, 1, fout) != 1) {
+    WFPRINTF(stderr, "Error writing file %s!\n", (const W_CHAR*)filename);
+  } else {
+    WFPRINTF(stderr, "Saved file %s (%d bytes)\n",
+             (const W_CHAR*)filename, (int)webpdata->size);
+    ok = 1;
+  }
+  if (fout != stdout) fclose(fout);
+  return ok;
+}
+
+static int WriteWebP(WebPMux* const mux, const char* filename) {
+  int ok;
+  WebPData webp_data;
+  const WebPMuxError err = WebPMuxAssemble(mux, &webp_data);
+  if (err != WEBP_MUX_OK) {
+    fprintf(stderr, "Error (%s) assembling the WebP file.\n", ErrorString(err));
+    return 0;
+  }
+  ok = WriteData(filename, &webp_data);
+  WebPDataClear(&webp_data);
+  return ok;
+}
+
+static WebPMux* DuplicateMuxHeader(const WebPMux* const mux) {
+  WebPMux* new_mux = WebPMuxNew();
+  WebPMuxAnimParams p;
+  WebPMuxError err;
+  int i;
+  int ok = 1;
+
+  if (new_mux == NULL) return NULL;
+
+  err = WebPMuxGetAnimationParams(mux, &p);
+  if (err == WEBP_MUX_OK) {
+    err = WebPMuxSetAnimationParams(new_mux, &p);
+    if (err != WEBP_MUX_OK) {
+      ERROR_GOTO2("Error (%s) handling animation params.\n",
+                  ErrorString(err), End);
+    }
+  } else {
+    /* it might not be an animation. Just keep moving. */
+  }
+
+  for (i = 1; i <= 3; ++i) {
+    WebPData metadata;
+    err = WebPMuxGetChunk(mux, kFourccList[i], &metadata);
+    if (err == WEBP_MUX_OK && metadata.size > 0) {
+      err = WebPMuxSetChunk(new_mux, kFourccList[i], &metadata, 1);
+      if (err != WEBP_MUX_OK) {
+        ERROR_GOTO1("Error transferring metadata in DuplicateMuxHeader().",
+                    End);
+      }
+    }
+  }
+
+ End:
+  if (!ok) {
+    WebPMuxDelete(new_mux);
+    new_mux = NULL;
+  }
+  return new_mux;
+}
+
+static int ParseFrameArgs(const char* args, WebPMuxFrameInfo* const info) {
+  int dispose_method, unused;
+  char plus_minus, blend_method;
+  const int num_args = sscanf(args, "+%d+%d+%d+%d%c%c+%d", &info->duration,
+                              &info->x_offset, &info->y_offset, &dispose_method,
+                              &plus_minus, &blend_method, &unused);
+  switch (num_args) {
+    case 1:
+      info->x_offset = info->y_offset = 0;  // fall through
+    case 3:
+      dispose_method = 0;  // fall through
+    case 4:
+      plus_minus = '+';
+      blend_method = 'b';  // fall through
+    case 6:
+      break;
+    case 2:
+    case 5:
+    default:
+      return 0;
+  }
+
+  WarnAboutOddOffset(info);
+
+  // Note: The validity of the following conversion is checked by
+  // WebPMuxPushFrame().
+  info->dispose_method = (WebPMuxAnimDispose)dispose_method;
+
+  if (blend_method != 'b') return 0;
+  if (plus_minus != '-' && plus_minus != '+') return 0;
+  info->blend_method =
+      (plus_minus == '+') ? WEBP_MUX_BLEND : WEBP_MUX_NO_BLEND;
+  return 1;
+}
+
+static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) {
+  uint32_t a, r, g, b;
+  if (sscanf(args, "%u,%u,%u,%u", &a, &r, &g, &b) != 4) return 0;
+  if (a >= 256 || r >= 256 || g >= 256 || b >= 256) return 0;
+  *bgcolor = (a << 24) | (r << 16) | (g << 8) | (b << 0);
+  return 1;
+}
+
+//------------------------------------------------------------------------------
+// Clean-up.
+
+static void DeleteConfig(Config* const config) {
+  if (config != NULL) {
+    free(config->args_);
+    ExUtilDeleteCommandLineArguments(&config->cmd_args_);
+    memset(config, 0, sizeof(*config));
+  }
+}
+
+//------------------------------------------------------------------------------
+// Parsing.
+
+// Basic syntactic checks on the command-line arguments.
+// Returns 1 on valid, 0 otherwise.
+// Also fills up num_feature_args to be number of feature arguments given.
+// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
+static int ValidateCommandLine(const CommandLineArguments* const cmd_args,
+                               int* num_feature_args) {
+  int num_frame_args;
+  int num_loop_args;
+  int num_bgcolor_args;
+  int num_durations_args;
+  int ok = 1;
+
+  assert(num_feature_args != NULL);
+  *num_feature_args = 0;
+
+  // Simple checks.
+  if (CountOccurrences(cmd_args, "-get") > 1) {
+    ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
+  }
+  if (CountOccurrences(cmd_args, "-set") > 1) {
+    ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
+  }
+  if (CountOccurrences(cmd_args, "-strip") > 1) {
+    ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
+  }
+  if (CountOccurrences(cmd_args, "-info") > 1) {
+    ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
+  }
+  if (CountOccurrences(cmd_args, "-o") > 1) {
+    ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
+  }
+
+  // Compound checks.
+  num_frame_args = CountOccurrences(cmd_args, "-frame");
+  num_loop_args = CountOccurrences(cmd_args, "-loop");
+  num_bgcolor_args = CountOccurrences(cmd_args, "-bgcolor");
+  num_durations_args = CountOccurrences(cmd_args, "-duration");
+
+  if (num_loop_args > 1) {
+    ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
+  }
+  if (num_bgcolor_args > 1) {
+    ERROR_GOTO1("ERROR: Multiple background colors specified.\n", ErrValidate);
+  }
+
+  if ((num_frame_args == 0) && (num_loop_args + num_bgcolor_args > 0)) {
+    ERROR_GOTO1("ERROR: Loop count and background color are relevant only in "
+                "case of animation.\n", ErrValidate);
+  }
+  if (num_durations_args > 0 && num_frame_args != 0) {
+    ERROR_GOTO1("ERROR: Can not combine -duration and -frame commands.\n",
+                ErrValidate);
+  }
+
+  assert(ok == 1);
+  if (num_durations_args > 0) {
+    *num_feature_args = num_durations_args;
+  } else if (num_frame_args == 0) {
+    // Single argument ('set' action for ICCP/EXIF/XMP, OR a 'get' action).
+    *num_feature_args = 1;
+  } else {
+    // Multiple arguments ('set' action for animation)
+    *num_feature_args = num_frame_args + num_loop_args + num_bgcolor_args;
+  }
+
+ ErrValidate:
+  return ok;
+}
+
+#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
+
+#define FEATURETYPE_IS_NIL (config->type_ == NIL_FEATURE)
+
+#define CHECK_NUM_ARGS_AT_LEAST(NUM, LABEL)                              \
+  if (argc < i + (NUM)) {                                                \
+    fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]);    \
+    goto LABEL;                                                          \
+  }
+
+#define CHECK_NUM_ARGS_AT_MOST(NUM, LABEL)                               \
+  if (argc > i + (NUM)) {                                                \
+    fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]);   \
+    goto LABEL;                                                          \
+  }
+
+#define CHECK_NUM_ARGS_EXACTLY(NUM, LABEL)                               \
+  CHECK_NUM_ARGS_AT_LEAST(NUM, LABEL);                                   \
+  CHECK_NUM_ARGS_AT_MOST(NUM, LABEL);
+
+// Parses command-line arguments to fill up config object. Also performs some
+// semantic checks. unicode_argv contains wchar_t arguments or is null.
+static int ParseCommandLine(Config* config, const W_CHAR** const unicode_argv) {
+  int i = 0;
+  int feature_arg_index = 0;
+  int ok = 1;
+  int argc = config->cmd_args_.argc_;
+  const char* const* argv = config->cmd_args_.argv_;
+  // Unicode file paths will be used if available.
+  const char* const* wargv =
+      (unicode_argv != NULL) ? (const char**)(unicode_argv + 1) : argv;
+
+  while (i < argc) {
+    FeatureArg* const arg = &config->args_[feature_arg_index];
+    if (argv[i][0] == '-') {  // One of the action types or output.
+      if (!strcmp(argv[i], "-set")) {
+        if (ACTION_IS_NIL) {
+          config->action_type_ = ACTION_SET;
+        } else {
+          ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
+        }
+        ++i;
+      } else if (!strcmp(argv[i], "-duration")) {
+        CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
+        if (ACTION_IS_NIL || config->action_type_ == ACTION_DURATION) {
+          config->action_type_ = ACTION_DURATION;
+        } else {
+          ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
+        }
+        if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_DURATION) {
+          config->type_ = FEATURE_DURATION;
+        } else {
+          ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
+        }
+        arg->params_ = argv[i + 1];
+        ++feature_arg_index;
+        i += 2;
+      } else if (!strcmp(argv[i], "-get")) {
+        if (ACTION_IS_NIL) {
+          config->action_type_ = ACTION_GET;
+        } else {
+          ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
+        }
+        ++i;
+      } else if (!strcmp(argv[i], "-strip")) {
+        if (ACTION_IS_NIL) {
+          config->action_type_ = ACTION_STRIP;
+          config->arg_count_ = 0;
+        } else {
+          ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
+        }
+        ++i;
+      } else if (!strcmp(argv[i], "-frame")) {
+        CHECK_NUM_ARGS_AT_LEAST(3, ErrParse);
+        if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
+          config->action_type_ = ACTION_SET;
+        } else {
+          ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
+        }
+        if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) {
+          config->type_ = FEATURE_ANMF;
+        } else {
+          ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
+        }
+        arg->subtype_ = SUBTYPE_ANMF;
+        arg->filename_ = wargv[i + 1];
+        arg->params_ = argv[i + 2];
+        ++feature_arg_index;
+        i += 3;
+      } else if (!strcmp(argv[i], "-loop") || !strcmp(argv[i], "-bgcolor")) {
+        CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
+        if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
+          config->action_type_ = ACTION_SET;
+        } else {
+          ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
+        }
+        if (FEATURETYPE_IS_NIL || config->type_ == FEATURE_ANMF) {
+          config->type_ = FEATURE_ANMF;
+        } else {
+          ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
+        }
+        arg->subtype_ =
+            !strcmp(argv[i], "-loop") ? SUBTYPE_LOOP : SUBTYPE_BGCOLOR;
+        arg->params_ = argv[i + 1];
+        ++feature_arg_index;
+        i += 2;
+      } else if (!strcmp(argv[i], "-o")) {
+        CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
+        config->output_ = wargv[i + 1];
+        i += 2;
+      } else if (!strcmp(argv[i], "-info")) {
+        CHECK_NUM_ARGS_EXACTLY(2, ErrParse);
+        if (config->action_type_ != NIL_ACTION) {
+          ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
+        } else {
+          config->action_type_ = ACTION_INFO;
+          config->arg_count_ = 0;
+          config->input_ = wargv[i + 1];
+        }
+        i += 2;
+      } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
+        PrintHelp();
+        DeleteConfig(config);
+        LOCAL_FREE((W_CHAR** const)unicode_argv);
+        exit(0);
+      } else if (!strcmp(argv[i], "-version")) {
+        const int version = WebPGetMuxVersion();
+        printf("%d.%d.%d\n",
+               (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff);
+        DeleteConfig(config);
+        LOCAL_FREE((W_CHAR** const)unicode_argv);
+        exit(0);
+      } else if (!strcmp(argv[i], "--")) {
+        if (i < argc - 1) {
+          ++i;
+          if (config->input_ == NULL) {
+            config->input_ = wargv[i];
+          } else {
+            ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
+                        argv[i], ErrParse);
+          }
+        }
+        break;
+      } else {
+        ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
+      }
+    } else {  // One of the feature types or input.
+      if (ACTION_IS_NIL) {
+        ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
+                    ErrParse);
+      }
+      if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "exif") ||
+          !strcmp(argv[i], "xmp")) {
+        if (FEATURETYPE_IS_NIL) {
+          config->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
+              (!strcmp(argv[i], "exif")) ? FEATURE_EXIF : FEATURE_XMP;
+        } else {
+          ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
+        }
+        if (config->action_type_ == ACTION_SET) {
+          CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
+          arg->filename_ = wargv[i + 1];
+          ++feature_arg_index;
+          i += 2;
+        } else {
+          ++i;
+        }
+      } else if (!strcmp(argv[i], "frame") &&
+                 (config->action_type_ == ACTION_GET)) {
+        CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
+        config->type_ = FEATURE_ANMF;
+        arg->params_ = argv[i + 1];
+        ++feature_arg_index;
+        i += 2;
+      } else if (!strcmp(argv[i], "loop") &&
+                 (config->action_type_ == ACTION_SET)) {
+        CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
+        config->type_ = FEATURE_LOOP;
+        arg->params_ = argv[i + 1];
+        ++feature_arg_index;
+        i += 2;
+      } else if (!strcmp(argv[i], "bgcolor") &&
+                 (config->action_type_ == ACTION_SET)) {
+        CHECK_NUM_ARGS_AT_LEAST(2, ErrParse);
+        config->type_ = FEATURE_BGCOLOR;
+        arg->params_ = argv[i + 1];
+        ++feature_arg_index;
+        i += 2;
+      } else {  // Assume input file.
+        if (config->input_ == NULL) {
+          config->input_ = wargv[i];
+        } else {
+          ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
+                      argv[i], ErrParse);
+        }
+        ++i;
+      }
+    }
+  }
+ ErrParse:
+  return ok;
+}
+
+// Additional checks after config is filled.
+static int ValidateConfig(Config* const config) {
+  int ok = 1;
+
+  // Action.
+  if (ACTION_IS_NIL) {
+    ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
+  }
+
+  // Feature type.
+  if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
+    ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
+  }
+
+  // Input file.
+  if (config->input_ == NULL) {
+    if (config->action_type_ != ACTION_SET) {
+      ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
+    } else if (config->type_ != FEATURE_ANMF) {
+      ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
+    }
+  }
+
+  // Output file.
+  if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
+    ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
+  }
+
+ ErrValidate2:
+  return ok;
+}
+
+// Create config object from command-line arguments.
+static int InitializeConfig(int argc, const char* argv[], Config* const config,
+                            const W_CHAR** const unicode_argv) {
+  int num_feature_args = 0;
+  int ok;
+
+  memset(config, 0, sizeof(*config));
+
+  ok = ExUtilInitCommandLineArguments(argc, argv, &config->cmd_args_);
+  if (!ok) return 0;
+
+  // Validate command-line arguments.
+  if (!ValidateCommandLine(&config->cmd_args_, &num_feature_args)) {
+    ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
+  }
+
+  config->arg_count_ = num_feature_args;
+  config->args_ = (FeatureArg*)calloc(num_feature_args, sizeof(*config->args_));
+  if (config->args_ == NULL) {
+    ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
+  }
+
+  // Parse command-line.
+  if (!ParseCommandLine(config, unicode_argv) || !ValidateConfig(config)) {
+    ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
+  }
+
+ Err1:
+  return ok;
+}
+
+#undef ACTION_IS_NIL
+#undef FEATURETYPE_IS_NIL
+#undef CHECK_NUM_ARGS_AT_LEAST
+#undef CHECK_NUM_ARGS_AT_MOST
+#undef CHECK_NUM_ARGS_EXACTLY
+
+//------------------------------------------------------------------------------
+// Processing.
+
+static int GetFrame(const WebPMux* mux, const Config* config) {
+  WebPMuxError err = WEBP_MUX_OK;
+  WebPMux* mux_single = NULL;
+  int num = 0;
+  int ok = 1;
+  int parse_error = 0;
+  const WebPChunkId id = WEBP_CHUNK_ANMF;
+  WebPMuxFrameInfo info;
+  WebPDataInit(&info.bitstream);
+
+  num = ExUtilGetInt(config->args_[0].params_, 10, &parse_error);
+  if (num < 0) {
+    ERROR_GOTO1("ERROR: Frame/Fragment index must be non-negative.\n", ErrGet);
+  }
+  if (parse_error) goto ErrGet;
+
+  err = WebPMuxGetFrame(mux, num, &info);
+  if (err == WEBP_MUX_OK && info.id != id) err = WEBP_MUX_NOT_FOUND;
+  if (err != WEBP_MUX_OK) {
+    ERROR_GOTO3("ERROR (%s): Could not get frame %d.\n",
+                ErrorString(err), num, ErrGet);
+  }
+
+  mux_single = WebPMuxNew();
+  if (mux_single == NULL) {
+    err = WEBP_MUX_MEMORY_ERROR;
+    ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
+                ErrorString(err), ErrGet);
+  }
+  err = WebPMuxSetImage(mux_single, &info.bitstream, 1);
+  if (err != WEBP_MUX_OK) {
+    ERROR_GOTO2("ERROR (%s): Could not create single image mux object.\n",
+                ErrorString(err), ErrGet);
+  }
+
+  ok = WriteWebP(mux_single, config->output_);
+
+ ErrGet:
+  WebPDataClear(&info.bitstream);
+  WebPMuxDelete(mux_single);
+  return ok && !parse_error;
+}
+
+// Read and process config.
+static int Process(const Config* config) {
+  WebPMux* mux = NULL;
+  WebPData chunk;
+  WebPMuxError err = WEBP_MUX_OK;
+  int ok = 1;
+
+  switch (config->action_type_) {
+    case ACTION_GET: {
+      ok = CreateMux(config->input_, &mux);
+      if (!ok) goto Err2;
+      switch (config->type_) {
+        case FEATURE_ANMF:
+          ok = GetFrame(mux, config);
+          break;
+
+        case FEATURE_ICCP:
+        case FEATURE_EXIF:
+        case FEATURE_XMP:
+          err = WebPMuxGetChunk(mux, kFourccList[config->type_], &chunk);
+          if (err != WEBP_MUX_OK) {
+            ERROR_GOTO3("ERROR (%s): Could not get the %s.\n",
+                        ErrorString(err), kDescriptions[config->type_], Err2);
+          }
+          ok = WriteData(config->output_, &chunk);
+          break;
+
+        default:
+          ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
+          break;
+      }
+      break;
+    }
+    case ACTION_SET: {
+      switch (config->type_) {
+        case FEATURE_ANMF: {
+          int i;
+          WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
+          mux = WebPMuxNew();
+          if (mux == NULL) {
+            ERROR_GOTO2("ERROR (%s): Could not allocate a mux object.\n",
+                        ErrorString(WEBP_MUX_MEMORY_ERROR), Err2);
+          }
+          for (i = 0; i < config->arg_count_; ++i) {
+            switch (config->args_[i].subtype_) {
+              case SUBTYPE_BGCOLOR: {
+                uint32_t bgcolor;
+                ok = ParseBgcolorArgs(config->args_[i].params_, &bgcolor);
+                if (!ok) {
+                  ERROR_GOTO1("ERROR: Could not parse the background color \n",
+                              Err2);
+                }
+                params.bgcolor = bgcolor;
+                break;
+              }
+              case SUBTYPE_LOOP: {
+                int parse_error = 0;
+                const int loop_count =
+                    ExUtilGetInt(config->args_[i].params_, 10, &parse_error);
+                if (loop_count < 0 || loop_count > 65535) {
+                  // Note: This is only a 'necessary' condition for loop_count
+                  // to be valid. The 'sufficient' conditioned in checked in
+                  // WebPMuxSetAnimationParams() method called later.
+                  ERROR_GOTO1("ERROR: Loop count must be in the range 0 to "
+                              "65535.\n", Err2);
+                }
+                ok = !parse_error;
+                if (!ok) goto Err2;
+                params.loop_count = loop_count;
+                break;
+              }
+              case SUBTYPE_ANMF: {
+                WebPMuxFrameInfo frame;
+                frame.id = WEBP_CHUNK_ANMF;
+                ok = ExUtilReadFileToWebPData(config->args_[i].filename_,
+                                              &frame.bitstream);
+                if (!ok) goto Err2;
+                ok = ParseFrameArgs(config->args_[i].params_, &frame);
+                if (!ok) {
+                  WebPDataClear(&frame.bitstream);
+                  ERROR_GOTO1("ERROR: Could not parse frame properties.\n",
+                              Err2);
+                }
+                err = WebPMuxPushFrame(mux, &frame, 1);
+                WebPDataClear(&frame.bitstream);
+                if (err != WEBP_MUX_OK) {
+                  ERROR_GOTO3("ERROR (%s): Could not add a frame at index %d."
+                              "\n", ErrorString(err), i, Err2);
+                }
+                break;
+              }
+              default: {
+                ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
+                break;
+              }
+            }
+          }
+          err = WebPMuxSetAnimationParams(mux, &params);
+          if (err != WEBP_MUX_OK) {
+            ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n",
+                        ErrorString(err), Err2);
+          }
+          break;
+        }
+
+        case FEATURE_ICCP:
+        case FEATURE_EXIF:
+        case FEATURE_XMP: {
+          ok = CreateMux(config->input_, &mux);
+          if (!ok) goto Err2;
+          ok = ExUtilReadFileToWebPData(config->args_[0].filename_, &chunk);
+          if (!ok) goto Err2;
+          err = WebPMuxSetChunk(mux, kFourccList[config->type_], &chunk, 1);
+          WebPDataClear(&chunk);
+          if (err != WEBP_MUX_OK) {
+            ERROR_GOTO3("ERROR (%s): Could not set the %s.\n",
+                        ErrorString(err), kDescriptions[config->type_], Err2);
+          }
+          break;
+        }
+        case FEATURE_LOOP: {
+          WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
+          int parse_error = 0;
+          const int loop_count =
+              ExUtilGetInt(config->args_[0].params_, 10, &parse_error);
+          if (loop_count < 0 || loop_count > 65535 || parse_error) {
+            ERROR_GOTO1("ERROR: Loop count must be in the range 0 to 65535.\n",
+                        Err2);
+          }
+          ok = CreateMux(config->input_, &mux);
+          if (!ok) goto Err2;
+          ok = (WebPMuxGetAnimationParams(mux, &params) == WEBP_MUX_OK);
+          if (!ok) {
+            ERROR_GOTO1("ERROR: input file does not seem to be an animation.\n",
+                        Err2);
+          }
+          params.loop_count = loop_count;
+          err = WebPMuxSetAnimationParams(mux, &params);
+          ok = (err == WEBP_MUX_OK);
+          if (!ok) {
+            ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n",
+                        ErrorString(err), Err2);
+          }
+          break;
+        }
+        case FEATURE_BGCOLOR: {
+          WebPMuxAnimParams params = { 0xFFFFFFFF, 0 };
+          uint32_t bgcolor;
+          ok = ParseBgcolorArgs(config->args_[0].params_, &bgcolor);
+          if (!ok) {
+            ERROR_GOTO1("ERROR: Could not parse the background color.\n",
+                        Err2);
+          }
+          ok = CreateMux(config->input_, &mux);
+          if (!ok) goto Err2;
+          ok = (WebPMuxGetAnimationParams(mux, &params) == WEBP_MUX_OK);
+          if (!ok) {
+            ERROR_GOTO1("ERROR: input file does not seem to be an animation.\n",
+                        Err2);
+          }
+          params.bgcolor = bgcolor;
+          err = WebPMuxSetAnimationParams(mux, &params);
+          ok = (err == WEBP_MUX_OK);
+          if (!ok) {
+            ERROR_GOTO2("ERROR (%s): Could not set animation parameters.\n",
+                        ErrorString(err), Err2);
+          }
+          break;
+        }
+        default: {
+          ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
+          break;
+        }
+      }
+      ok = WriteWebP(mux, config->output_);
+      break;
+    }
+    case ACTION_DURATION: {
+      int num_frames;
+      ok = CreateMux(config->input_, &mux);
+      if (!ok) goto Err2;
+      err = WebPMuxNumChunks(mux, WEBP_CHUNK_ANMF, &num_frames);
+      ok = (err == WEBP_MUX_OK);
+      if (!ok) {
+        ERROR_GOTO1("ERROR: can not parse the number of frames.\n", Err2);
+      }
+      if (num_frames == 0) {
+        fprintf(stderr, "Doesn't look like the source is animated. "
+                        "Skipping duration setting.\n");
+        ok = WriteWebP(mux, config->output_);
+        if (!ok) goto Err2;
+      } else {
+        int i;
+        int* durations = NULL;
+        WebPMux* new_mux = DuplicateMuxHeader(mux);
+        if (new_mux == NULL) goto Err2;
+        durations = (int*)WebPMalloc((size_t)num_frames * sizeof(*durations));
+        if (durations == NULL) goto Err2;
+        for (i = 0; i < num_frames; ++i) durations[i] = -1;
+
+        // Parse intervals to process.
+        for (i = 0; i < config->arg_count_; ++i) {
+          int k;
+          int args[3];
+          int duration, start, end;
+          const int nb_args = ExUtilGetInts(config->args_[i].params_,
+                                            10, 3, args);
+          ok = (nb_args >= 1);
+          if (!ok) goto Err3;
+          duration = args[0];
+          if (duration < 0) {
+            ERROR_GOTO1("ERROR: duration must be strictly positive.\n", Err3);
+          }
+
+          if (nb_args == 1) {   // only duration is present -> use full interval
+            start = 1;
+            end = num_frames;
+          } else {
+            start = args[1];
+            if (start <= 0) {
+              start = 1;
+            } else if (start > num_frames) {
+              start = num_frames;
+            }
+            end = (nb_args >= 3) ? args[2] : start;
+            if (end == 0 || end > num_frames) end = num_frames;
+          }
+
+          for (k = start; k <= end; ++k) {
+            assert(k >= 1 && k <= num_frames);
+            durations[k - 1] = duration;
+          }
+        }
+
+        // Apply non-negative durations to their destination frames.
+        for (i = 1; i <= num_frames; ++i) {
+          WebPMuxFrameInfo frame;
+          err = WebPMuxGetFrame(mux, i, &frame);
+          if (err != WEBP_MUX_OK || frame.id != WEBP_CHUNK_ANMF) {
+            ERROR_GOTO2("ERROR: can not retrieve frame #%d.\n", i, Err3);
+          }
+          if (durations[i - 1] >= 0) frame.duration = durations[i - 1];
+          err = WebPMuxPushFrame(new_mux, &frame, 1);
+          if (err != WEBP_MUX_OK) {
+            ERROR_GOTO2("ERROR: error push frame data #%d\n", i, Err3);
+          }
+          WebPDataClear(&frame.bitstream);
+        }
+        WebPMuxDelete(mux);
+        ok = WriteWebP(new_mux, config->output_);
+        mux = new_mux;  // transfer for the WebPMuxDelete() call
+        new_mux = NULL;
+
+ Err3:
+        WebPFree(durations);
+        WebPMuxDelete(new_mux);
+        if (!ok) goto Err2;
+      }
+      break;
+    }
+    case ACTION_STRIP: {
+      ok = CreateMux(config->input_, &mux);
+      if (!ok) goto Err2;
+      if (config->type_ == FEATURE_ICCP || config->type_ == FEATURE_EXIF ||
+          config->type_ == FEATURE_XMP) {
+        err = WebPMuxDeleteChunk(mux, kFourccList[config->type_]);
+        if (err != WEBP_MUX_OK) {
+          ERROR_GOTO3("ERROR (%s): Could not strip the %s.\n",
+                      ErrorString(err), kDescriptions[config->type_], Err2);
+        }
+      } else {
+        ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
+        break;
+      }
+      ok = WriteWebP(mux, config->output_);
+      break;
+    }
+    case ACTION_INFO: {
+      ok = CreateMux(config->input_, &mux);
+      if (!ok) goto Err2;
+      ok = (DisplayInfo(mux) == WEBP_MUX_OK);
+      break;
+    }
+    default: {
+      assert(0);  // Invalid action.
+      break;
+    }
+  }
+
+ Err2:
+  WebPMuxDelete(mux);
+  return ok;
+}
+
+//------------------------------------------------------------------------------
+// Main.
+
+int main(int argc, const char* argv[]) {
+  Config config;
+  int ok;
+
+  INIT_WARGV(argc, argv);
+
+  ok = InitializeConfig(argc - 1, argv + 1, &config, GET_WARGV_OR_NULL());
+  if (ok) {
+    ok = Process(&config);
+  } else {
+    PrintHelp();
+  }
+  DeleteConfig(&config);
+  FREE_WARGV_AND_RETURN(!ok);
+}
+
+//------------------------------------------------------------------------------
diff --git a/extras/Makefile.am b/extras/Makefile.am
new file mode 100644
index 0000000..7e29888
--- /dev/null
+++ b/extras/Makefile.am
@@ -0,0 +1,44 @@
+AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir)
+AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src
+noinst_LTLIBRARIES = libwebpextras.la
+
+noinst_HEADERS =
+noinst_HEADERS += ../src/webp/types.h
+
+libwebpextras_la_SOURCES =
+libwebpextras_la_SOURCES += extras.c extras.h quality_estimate.c
+
+libwebpextras_la_CPPFLAGS = $(AM_CPPFLAGS)
+libwebpextras_la_LDFLAGS = -lm
+libwebpextras_la_LIBADD = ../src/libwebp.la
+
+noinst_PROGRAMS =
+noinst_PROGRAMS += webp_quality
+if BUILD_DEMUX
+  noinst_PROGRAMS += get_disto
+endif
+if BUILD_VWEBP_SDL
+  noinst_PROGRAMS += vwebp_sdl
+endif
+
+get_disto_SOURCES  = get_disto.c
+get_disto_CPPFLAGS = $(AM_CPPFLAGS)
+get_disto_LDADD =
+get_disto_LDADD += ../imageio/libimageio_util.la
+get_disto_LDADD += ../imageio/libimagedec.la
+get_disto_LDADD += ../src/libwebp.la
+get_disto_LDADD += $(PNG_LIBS) $(JPEG_LIBS) $(TIFF_LIBS)
+
+webp_quality_SOURCES  = webp_quality.c
+webp_quality_CPPFLAGS = $(AM_CPPFLAGS)
+webp_quality_LDADD =
+webp_quality_LDADD += ../imageio/libimageio_util.la
+webp_quality_LDADD += libwebpextras.la
+webp_quality_LDADD += ../src/libwebp.la
+
+vwebp_sdl_SOURCES  = vwebp_sdl.c webp_to_sdl.c webp_to_sdl.h
+vwebp_sdl_CPPFLAGS = $(AM_CPPFLAGS) $(SDL_INCLUDES)
+vwebp_sdl_LDADD =
+vwebp_sdl_LDADD += ../imageio/libimageio_util.la
+vwebp_sdl_LDADD += ../src/libwebp.la
+vwebp_sdl_LDADD += $(SDL_LIBS)
diff --git a/extras/extras.c b/extras/extras.c
new file mode 100644
index 0000000..b170ee2
--- /dev/null
+++ b/extras/extras.c
@@ -0,0 +1,162 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  Additional WebP utilities.
+//
+
+#include "extras/extras.h"
+#include "webp/format_constants.h"
+#include "src/dsp/dsp.h"
+
+#include <assert.h>
+#include <string.h>
+
+#define XTRA_MAJ_VERSION 1
+#define XTRA_MIN_VERSION 3
+#define XTRA_REV_VERSION 0
+
+//------------------------------------------------------------------------------
+
+int WebPGetExtrasVersion(void) {
+  return (XTRA_MAJ_VERSION << 16) | (XTRA_MIN_VERSION << 8) | XTRA_REV_VERSION;
+}
+
+//------------------------------------------------------------------------------
+
+int WebPImportGray(const uint8_t* gray_data, WebPPicture* pic) {
+  int y, width, uv_width;
+  if (pic == NULL || gray_data == NULL) return 0;
+  pic->colorspace = WEBP_YUV420;
+  if (!WebPPictureAlloc(pic)) return 0;
+  width = pic->width;
+  uv_width = (width + 1) >> 1;
+  for (y = 0; y < pic->height; ++y) {
+    memcpy(pic->y + y * pic->y_stride, gray_data, width);
+    gray_data += width;    // <- we could use some 'data_stride' here if needed
+    if ((y & 1) == 0) {
+      memset(pic->u + (y >> 1) * pic->uv_stride, 128, uv_width);
+      memset(pic->v + (y >> 1) * pic->uv_stride, 128, uv_width);
+    }
+  }
+  return 1;
+}
+
+int WebPImportRGB565(const uint8_t* rgb565, WebPPicture* pic) {
+  int x, y;
+  uint32_t* dst;
+  if (pic == NULL || rgb565 == NULL) return 0;
+  pic->colorspace = WEBP_YUV420;
+  pic->use_argb = 1;
+  if (!WebPPictureAlloc(pic)) return 0;
+  dst = pic->argb;
+  for (y = 0; y < pic->height; ++y) {
+    const int width = pic->width;
+    for (x = 0; x < width; ++x) {
+#if defined(WEBP_SWAP_16BIT_CSP) && (WEBP_SWAP_16BIT_CSP == 1)
+      const uint32_t rg = rgb565[2 * x + 1];
+      const uint32_t gb = rgb565[2 * x + 0];
+#else
+      const uint32_t rg = rgb565[2 * x + 0];
+      const uint32_t gb = rgb565[2 * x + 1];
+#endif
+      uint32_t r = rg & 0xf8;
+      uint32_t g = ((rg << 5) | (gb >> 3)) & 0xfc;
+      uint32_t b = (gb << 5);
+      // dithering
+      r = r | (r >> 5);
+      g = g | (g >> 6);
+      b = b | (b >> 5);
+      dst[x] = (0xffu << 24) | (r << 16) | (g << 8) | b;
+    }
+    rgb565 += 2 * width;
+    dst += pic->argb_stride;
+  }
+  return 1;
+}
+
+int WebPImportRGB4444(const uint8_t* rgb4444, WebPPicture* pic) {
+  int x, y;
+  uint32_t* dst;
+  if (pic == NULL || rgb4444 == NULL) return 0;
+  pic->colorspace = WEBP_YUV420;
+  pic->use_argb = 1;
+  if (!WebPPictureAlloc(pic)) return 0;
+  dst = pic->argb;
+  for (y = 0; y < pic->height; ++y) {
+    const int width = pic->width;
+    for (x = 0; x < width; ++x) {
+#if defined(WEBP_SWAP_16BIT_CSP) && (WEBP_SWAP_16BIT_CSP == 1)
+      const uint32_t rg = rgb4444[2 * x + 1];
+      const uint32_t ba = rgb4444[2 * x + 0];
+#else
+      const uint32_t rg = rgb4444[2 * x + 0];
+      const uint32_t ba = rgb4444[2 * x + 1];
+#endif
+      uint32_t r = rg & 0xf0;
+      uint32_t g = (rg << 4);
+      uint32_t b = (ba & 0xf0);
+      uint32_t a = (ba << 4);
+      // dithering
+      r = r | (r >> 4);
+      g = g | (g >> 4);
+      b = b | (b >> 4);
+      a = a | (a >> 4);
+      dst[x] = (a << 24) | (r << 16) | (g << 8) | b;
+    }
+    rgb4444 += 2 * width;
+    dst += pic->argb_stride;
+  }
+  return 1;
+}
+
+int WebPImportColorMappedARGB(const uint8_t* indexed, int indexed_stride,
+                              const uint32_t palette[], int palette_size,
+                              WebPPicture* pic) {
+  int x, y;
+  uint32_t* dst;
+  // 256 as the input buffer is uint8_t.
+  assert(MAX_PALETTE_SIZE <= 256);
+  if (pic == NULL || indexed == NULL || indexed_stride < pic->width ||
+      palette == NULL || palette_size > MAX_PALETTE_SIZE || palette_size <= 0) {
+    return 0;
+  }
+  pic->use_argb = 1;
+  if (!WebPPictureAlloc(pic)) return 0;
+  dst = pic->argb;
+  for (y = 0; y < pic->height; ++y) {
+    for (x = 0; x < pic->width; ++x) {
+      // Make sure we are within the palette.
+      if (indexed[x] >= palette_size) {
+        WebPPictureFree(pic);
+        return 0;
+      }
+      dst[x] = palette[indexed[x]];
+    }
+    indexed += indexed_stride;
+    dst += pic->argb_stride;
+  }
+  return 1;
+}
+
+//------------------------------------------------------------------------------
+
+int WebPUnmultiplyARGB(WebPPicture* pic) {
+  int y;
+  uint32_t* dst;
+  if (pic == NULL || pic->use_argb != 1 || pic->argb == NULL) return 0;
+  WebPInitAlphaProcessing();
+  dst = pic->argb;
+  for (y = 0; y < pic->height; ++y) {
+    WebPMultARGBRow(dst, pic->width, /*inverse=*/1);
+    dst += pic->argb_stride;
+  }
+  return 1;
+}
+
+//------------------------------------------------------------------------------
diff --git a/extras/extras.h b/extras/extras.h
new file mode 100644
index 0000000..c084682
--- /dev/null
+++ b/extras/extras.h
@@ -0,0 +1,77 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+
+#ifndef WEBP_EXTRAS_EXTRAS_H_
+#define WEBP_EXTRAS_EXTRAS_H_
+
+#include "webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "webp/encode.h"
+
+#define WEBP_EXTRAS_ABI_VERSION 0x0002    // MAJOR(8b) + MINOR(8b)
+
+//------------------------------------------------------------------------------
+
+// Returns the version number of the extras library, packed in hexadecimal using
+// 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507.
+WEBP_EXTERN int WebPGetExtrasVersion(void);
+
+//------------------------------------------------------------------------------
+// Ad-hoc colorspace importers.
+
+// Import luma sample (gray scale image) into 'picture'. The 'picture'
+// width and height must be set prior to calling this function.
+WEBP_EXTERN int WebPImportGray(const uint8_t* gray, WebPPicture* picture);
+
+// Import rgb sample in RGB565 packed format into 'picture'. The 'picture'
+// width and height must be set prior to calling this function.
+WEBP_EXTERN int WebPImportRGB565(const uint8_t* rgb565, WebPPicture* pic);
+
+// Import rgb sample in RGB4444 packed format into 'picture'. The 'picture'
+// width and height must be set prior to calling this function.
+WEBP_EXTERN int WebPImportRGB4444(const uint8_t* rgb4444, WebPPicture* pic);
+
+// Import a color mapped image. The number of colors is less or equal to
+// MAX_PALETTE_SIZE. 'pic' must have been initialized. Its content, if any,
+// will be discarded. Returns 'false' in case of error, or if indexed[] contains
+// invalid indices.
+WEBP_EXTERN int
+WebPImportColorMappedARGB(const uint8_t* indexed, int indexed_stride,
+                          const uint32_t palette[], int palette_size,
+                          WebPPicture* pic);
+
+// Convert the ARGB content of 'pic' from associated to unassociated.
+// 'pic' can be for instance the result of calling of some WebPPictureImportXXX
+// functions, with pic->use_argb set to 'true'. It is assumed (and not checked)
+// that the pre-multiplied r/g/b values as less or equal than the alpha value.
+// Return false in case of error (invalid parameter, ...).
+WEBP_EXTERN int WebPUnmultiplyARGB(WebPPicture* pic);
+
+//------------------------------------------------------------------------------
+
+// Parse a bitstream, search for VP8 (lossy) header and report a
+// rough estimation of the quality factor used for compressing the bitstream.
+// If the bitstream is in lossless format, the special value '101' is returned.
+// Otherwise (lossy bitstream), the returned value is in the range [0..100].
+// Any error (invalid bitstream, animated WebP, incomplete header, etc.)
+// will return a value of -1.
+WEBP_EXTERN int VP8EstimateQuality(const uint8_t* const data, size_t size);
+
+//------------------------------------------------------------------------------
+
+#ifdef __cplusplus
+}    // extern "C"
+#endif
+
+#endif  // WEBP_EXTRAS_EXTRAS_H_
diff --git a/extras/get_disto.c b/extras/get_disto.c
new file mode 100644
index 0000000..3aa345b
--- /dev/null
+++ b/extras/get_disto.c
@@ -0,0 +1,356 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Simple tool to load two webp/png/jpg/tiff files and compute PSNR/SSIM.
+// This is mostly a wrapper around WebPPictureDistortion().
+//
+/*
+ gcc -o get_disto get_disto.c -O3 -I../ -L../examples -L../imageio \
+    -lexample_util -limageio_util -limagedec -lwebp -L/opt/local/lib \
+    -lpng -lz -ljpeg -ltiff -lm -lpthread
+*/
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "webp/encode.h"
+#include "imageio/image_dec.h"
+#include "imageio/imageio_util.h"
+#include "../examples/unicode.h"
+
+static size_t ReadPicture(const char* const filename, WebPPicture* const pic,
+                          int keep_alpha) {
+  const uint8_t* data = NULL;
+  size_t data_size = 0;
+  WebPImageReader reader = NULL;
+  int ok = ImgIoUtilReadFile(filename, &data, &data_size);
+  if (!ok) goto End;
+
+  pic->use_argb = 1;  // force ARGB
+
+#ifdef HAVE_WINCODEC_H
+  // Try to decode the file using WIC falling back to the other readers for
+  // e.g., WebP.
+  ok = ReadPictureWithWIC(filename, pic, keep_alpha, NULL);
+  if (ok) goto End;
+#endif
+  reader = WebPGuessImageReader(data, data_size);
+  ok = reader(data, data_size, pic, keep_alpha, NULL);
+
+ End:
+  if (!ok) {
+    WFPRINTF(stderr, "Error! Could not process file %s\n",
+             (const W_CHAR*)filename);
+  }
+  free((void*)data);
+  return ok ? data_size : 0;
+}
+
+static void RescalePlane(uint8_t* plane, int width, int height,
+                         int x_stride, int y_stride, int max) {
+  const uint32_t factor = (max > 0) ? (255u << 16) / max : 0;
+  int x, y;
+  for (y = 0; y < height; ++y) {
+    uint8_t* const ptr = plane + y * y_stride;
+    for (x = 0; x < width * x_stride; x += x_stride) {
+      const uint32_t diff = (ptr[x] * factor + (1 << 15)) >> 16;
+      ptr[x] = diff;
+    }
+  }
+}
+
+// Return the max absolute difference.
+static int DiffScaleChannel(uint8_t* src1, int stride1,
+                            const uint8_t* src2, int stride2,
+                            int x_stride, int w, int h, int do_scaling) {
+  int x, y;
+  int max = 0;
+  for (y = 0; y < h; ++y) {
+    uint8_t* const ptr1 = src1 + y * stride1;
+    const uint8_t* const ptr2 = src2 + y * stride2;
+    for (x = 0; x < w * x_stride; x += x_stride) {
+      const int diff = abs(ptr1[x] - ptr2[x]);
+      if (diff > max) max = diff;
+      ptr1[x] = diff;
+    }
+  }
+
+  if (do_scaling) RescalePlane(src1, w, h, x_stride, stride1, max);
+  return max;
+}
+
+//------------------------------------------------------------------------------
+// SSIM calculation. We re-implement these functions here, out of dsp/, to avoid
+// breaking the library's hidden visibility. This code duplication avoids the
+// bigger annoyance of having to open up internal details of libdsp...
+
+#define SSIM_KERNEL 3   // total size of the kernel: 2 * SSIM_KERNEL + 1
+
+// struct for accumulating statistical moments
+typedef struct {
+  uint32_t w;              // sum(w_i) : sum of weights
+  uint32_t xm, ym;         // sum(w_i * x_i), sum(w_i * y_i)
+  uint32_t xxm, xym, yym;  // sum(w_i * x_i * x_i), etc.
+} DistoStats;
+
+// hat-shaped filter. Sum of coefficients is equal to 16.
+static const uint32_t kWeight[2 * SSIM_KERNEL + 1] = { 1, 2, 3, 4, 3, 2, 1 };
+
+static WEBP_INLINE double SSIMCalculation(const DistoStats* const stats) {
+  const uint32_t N = stats->w;
+  const uint32_t w2 =  N * N;
+  const uint32_t C1 = 20 * w2;
+  const uint32_t C2 = 60 * w2;
+  const uint32_t C3 = 8 * 8 * w2;   // 'dark' limit ~= 6
+  const uint64_t xmxm = (uint64_t)stats->xm * stats->xm;
+  const uint64_t ymym = (uint64_t)stats->ym * stats->ym;
+  if (xmxm + ymym >= C3) {
+    const int64_t xmym = (int64_t)stats->xm * stats->ym;
+    const int64_t sxy = (int64_t)stats->xym * N - xmym;    // can be negative
+    const uint64_t sxx = (uint64_t)stats->xxm * N - xmxm;
+    const uint64_t syy = (uint64_t)stats->yym * N - ymym;
+    // we descale by 8 to prevent overflow during the fnum/fden multiply.
+    const uint64_t num_S = (2 * (uint64_t)(sxy < 0 ? 0 : sxy) + C2) >> 8;
+    const uint64_t den_S = (sxx + syy + C2) >> 8;
+    const uint64_t fnum = (2 * xmym + C1) * num_S;
+    const uint64_t fden = (xmxm + ymym + C1) * den_S;
+    const double r = (double)fnum / fden;
+    assert(r >= 0. && r <= 1.0);
+    return r;
+  }
+  return 1.;   // area is too dark to contribute meaningfully
+}
+
+static double SSIMGetClipped(const uint8_t* src1, int stride1,
+                             const uint8_t* src2, int stride2,
+                             int xo, int yo, int W, int H) {
+  DistoStats stats = { 0, 0, 0, 0, 0, 0 };
+  const int ymin = (yo - SSIM_KERNEL < 0) ? 0 : yo - SSIM_KERNEL;
+  const int ymax = (yo + SSIM_KERNEL > H - 1) ? H - 1 : yo + SSIM_KERNEL;
+  const int xmin = (xo - SSIM_KERNEL < 0) ? 0 : xo - SSIM_KERNEL;
+  const int xmax = (xo + SSIM_KERNEL > W - 1) ? W - 1 : xo + SSIM_KERNEL;
+  int x, y;
+  src1 += ymin * stride1;
+  src2 += ymin * stride2;
+  for (y = ymin; y <= ymax; ++y, src1 += stride1, src2 += stride2) {
+    for (x = xmin; x <= xmax; ++x) {
+      const uint32_t w = kWeight[SSIM_KERNEL + x - xo]
+                       * kWeight[SSIM_KERNEL + y - yo];
+      const uint32_t s1 = src1[x];
+      const uint32_t s2 = src2[x];
+      stats.w   += w;
+      stats.xm  += w * s1;
+      stats.ym  += w * s2;
+      stats.xxm += w * s1 * s1;
+      stats.xym += w * s1 * s2;
+      stats.yym += w * s2 * s2;
+    }
+  }
+  return SSIMCalculation(&stats);
+}
+
+// Compute SSIM-score map. Return -1 in case of error, max diff otherwise.
+static int SSIMScaleChannel(uint8_t* src1, int stride1,
+                            const uint8_t* src2, int stride2,
+                            int x_stride, int w, int h, int do_scaling) {
+  int x, y;
+  int max = 0;
+  uint8_t* const plane1 = (uint8_t*)malloc(2 * w * h * sizeof(*plane1));
+  uint8_t* const plane2 = plane1 + w * h;
+  if (plane1 == NULL) return -1;
+
+  // extract plane
+  for (y = 0; y < h; ++y) {
+    for (x = 0; x < w; ++x) {
+      plane1[x + y * w] = src1[x * x_stride + y * stride1];
+      plane2[x + y * w] = src2[x * x_stride + y * stride2];
+    }
+  }
+  for (y = 0; y < h; ++y) {
+    for (x = 0; x < w; ++x) {
+      const double ssim = SSIMGetClipped(plane1, w, plane2, w, x, y, w, h);
+      int diff = (int)(255 * (1. - ssim));
+      if (diff < 0) {
+        diff = 0;
+      } else if (diff > max) {
+        max = diff;
+      }
+      src1[x * x_stride + y * stride1] = (diff > 255) ? 255u : (uint8_t)diff;
+    }
+  }
+  free(plane1);
+
+  if (do_scaling) RescalePlane(src1, w, h, x_stride, stride1, max);
+  return max;
+}
+
+// Convert an argb picture to luminance.
+static void ConvertToGray(WebPPicture* const pic) {
+  int x, y;
+  assert(pic != NULL);
+  assert(pic->use_argb);
+  for (y = 0; y < pic->height; ++y) {
+    uint32_t* const row = &pic->argb[y * pic->argb_stride];
+    for (x = 0; x < pic->width; ++x) {
+      const uint32_t argb = row[x];
+      const uint32_t r = (argb >> 16) & 0xff;
+      const uint32_t g = (argb >>  8) & 0xff;
+      const uint32_t b = (argb >>  0) & 0xff;
+      // We use BT.709 for converting to luminance.
+      const uint32_t Y = (uint32_t)(0.2126 * r + 0.7152 * g + 0.0722 * b + .5);
+      row[x] = (argb & 0xff000000u) | (Y * 0x010101u);
+    }
+  }
+}
+
+static void Help(void) {
+  fprintf(stderr,
+          "Usage: get_disto [-ssim][-psnr][-alpha] compressed.webp orig.webp\n"
+          "  -ssim ..... print SSIM distortion\n"
+          "  -psnr ..... print PSNR distortion (default)\n"
+          "  -alpha .... preserve alpha plane\n"
+          "  -h ........ this message\n"
+          "  -o <file> . save the diff map as a WebP lossless file\n"
+          "  -scale .... scale the difference map to fit [0..255] range\n"
+          "  -gray ..... use grayscale for difference map (-scale)\n"
+          "\nSupported input formats:\n  %s\n",
+          WebPGetEnabledInputFileFormats());
+}
+
+int main(int argc, const char* argv[]) {
+  WebPPicture pic1, pic2;
+  size_t size1 = 0, size2 = 0;
+  int ret = 1;
+  float disto[5];
+  int type = 0;
+  int c;
+  int help = 0;
+  int keep_alpha = 0;
+  int scale = 0;
+  int use_gray = 0;
+  const char* name1 = NULL;
+  const char* name2 = NULL;
+  const char* output = NULL;
+
+  INIT_WARGV(argc, argv);
+
+  if (!WebPPictureInit(&pic1) || !WebPPictureInit(&pic2)) {
+    fprintf(stderr, "Can't init pictures\n");
+    FREE_WARGV_AND_RETURN(1);
+  }
+
+  for (c = 1; c < argc; ++c) {
+    if (!strcmp(argv[c], "-ssim")) {
+      type = 1;
+    } else if (!strcmp(argv[c], "-psnr")) {
+      type = 0;
+    } else if (!strcmp(argv[c], "-alpha")) {
+      keep_alpha = 1;
+    } else if (!strcmp(argv[c], "-scale")) {
+      scale = 1;
+    } else if (!strcmp(argv[c], "-gray")) {
+      use_gray = 1;
+    } else if (!strcmp(argv[c], "-h")) {
+      help = 1;
+      ret = 0;
+    } else if (!strcmp(argv[c], "-o")) {
+      if (++c == argc) {
+        fprintf(stderr, "missing file name after %s option.\n", argv[c - 1]);
+        goto End;
+      }
+      output = (const char*)GET_WARGV(argv, c);
+    } else if (name1 == NULL) {
+      name1 = (const char*)GET_WARGV(argv, c);
+    } else {
+      name2 = (const char*)GET_WARGV(argv, c);
+    }
+  }
+  if (help || name1 == NULL || name2 == NULL) {
+    if (!help) {
+      fprintf(stderr, "Error: missing arguments.\n");
+    }
+    Help();
+    goto End;
+  }
+  size1 = ReadPicture(name1, &pic1, 1);
+  size2 = ReadPicture(name2, &pic2, 1);
+  if (size1 == 0 || size2 == 0) goto End;
+
+  if (!keep_alpha) {
+    WebPBlendAlpha(&pic1, 0x00000000);
+    WebPBlendAlpha(&pic2, 0x00000000);
+  }
+
+  if (!WebPPictureDistortion(&pic1, &pic2, type, disto)) {
+    fprintf(stderr, "Error while computing the distortion.\n");
+    goto End;
+  }
+  printf("%u %.2f    %.2f %.2f %.2f %.2f [ %.2f bpp ]\n",
+         (unsigned int)size1,
+         disto[4], disto[0], disto[1], disto[2], disto[3],
+         8.f * size1 / pic1.width / pic1.height);
+
+  if (output != NULL) {
+    uint8_t* data = NULL;
+    size_t data_size = 0;
+    if (pic1.use_argb != pic2.use_argb) {
+      fprintf(stderr, "Pictures are not in the same argb format. "
+                      "Can't save the difference map.\n");
+      goto End;
+    }
+    if (pic1.use_argb) {
+      int n;
+      fprintf(stderr, "max differences per channel: ");
+      for (n = 0; n < 3; ++n) {    // skip the alpha channel
+        const int range = (type == 1) ?
+          SSIMScaleChannel((uint8_t*)pic1.argb + n, pic1.argb_stride * 4,
+                           (const uint8_t*)pic2.argb + n, pic2.argb_stride * 4,
+                           4, pic1.width, pic1.height, scale) :
+          DiffScaleChannel((uint8_t*)pic1.argb + n, pic1.argb_stride * 4,
+                           (const uint8_t*)pic2.argb + n, pic2.argb_stride * 4,
+                           4, pic1.width, pic1.height, scale);
+        if (range < 0) fprintf(stderr, "\nError computing diff map\n");
+        fprintf(stderr, "[%d]", range);
+      }
+      fprintf(stderr, "\n");
+      if (use_gray) ConvertToGray(&pic1);
+    } else {
+      fprintf(stderr, "Can only compute the difference map in ARGB format.\n");
+      goto End;
+    }
+#if !defined(WEBP_REDUCE_CSP)
+    data_size = WebPEncodeLosslessBGRA((const uint8_t*)pic1.argb,
+                                       pic1.width, pic1.height,
+                                       pic1.argb_stride * 4,
+                                       &data);
+    if (data_size == 0) {
+      fprintf(stderr, "Error during lossless encoding.\n");
+      goto End;
+    }
+    ret = ImgIoUtilWriteFile(output, data, data_size) ? 0 : 1;
+    WebPFree(data);
+    if (ret) goto End;
+#else
+    (void)data;
+    (void)data_size;
+    fprintf(stderr, "Cannot save the difference map. Please recompile "
+                    "without the WEBP_REDUCE_CSP flag.\n");
+#endif  // WEBP_REDUCE_CSP
+  }
+  ret = 0;
+
+ End:
+  WebPPictureFree(&pic1);
+  WebPPictureFree(&pic2);
+  FREE_WARGV_AND_RETURN(ret);
+}
diff --git a/extras/quality_estimate.c b/extras/quality_estimate.c
new file mode 100644
index 0000000..17e98d9
--- /dev/null
+++ b/extras/quality_estimate.c
@@ -0,0 +1,129 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  VP8EstimateQuality(): rough encoding quality estimate
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "extras/extras.h"
+#include "webp/decode.h"
+
+#include <math.h>
+
+//------------------------------------------------------------------------------
+
+#define INVALID_BIT_POS (1ull << 63)
+
+// In most cases, we don't need to use a full arithmetic decoder, since
+// all the header's bits are written using a uniform probability of 128.
+// We can just parse the header as if it was bits (works in 99.999% cases).
+static WEBP_INLINE uint32_t GetBit(const uint8_t* const data, size_t nb,
+                                   uint64_t max_size, uint64_t* const bit_pos) {
+  uint32_t val = 0;
+  if (*bit_pos + nb <= 8 * max_size) {
+    while (nb-- > 0) {
+      const uint64_t p = (*bit_pos)++;
+      const int bit = !!(data[p >> 3] & (128 >> ((p & 7))));
+      val = (val << 1) | bit;
+    }
+  } else {
+    *bit_pos = INVALID_BIT_POS;
+  }
+  return val;
+}
+
+#define GET_BIT(n) GetBit(data, (n), size, &bit_pos)
+#define CONDITIONAL_SKIP(n) (GET_BIT(1) ? GET_BIT((n)) : 0)
+
+int VP8EstimateQuality(const uint8_t* const data, size_t size) {
+  size_t pos = 0;
+  uint64_t bit_pos;
+  uint64_t sig = 0x00;
+  int ok = 0;
+  int Q = -1;
+  WebPBitstreamFeatures features;
+
+  if (data == NULL) return -1;
+
+  if (WebPGetFeatures(data, size, &features) != VP8_STATUS_OK) {
+    return -1;   // invalid file
+  }
+  if (features.format == 2) return 101;  // lossless
+  if (features.format == 0 || features.has_animation) return -1;   // mixed
+
+  while (pos < size) {
+    sig = (sig >> 8) | ((uint64_t)data[pos++] << 40);
+    if ((sig >> 24) == 0x2a019dull) {
+      ok = 1;
+      break;
+    }
+  }
+  if (!ok) return -1;
+  if (pos + 4 > size) return -1;
+
+  // Skip main Header
+  // width  = (data[pos + 0] | (data[pos + 1] << 8)) & 0x3fff;
+  // height = (data[pos + 2] | (data[pos + 3] << 8)) & 0x3fff;
+  pos += 4;
+  bit_pos = pos * 8;
+
+  GET_BIT(2);  // colorspace + clamp type
+
+  // Segment header
+  if (GET_BIT(1)) {       // use_segment_
+    int s;
+    const int update_map = GET_BIT(1);
+    if (GET_BIT(1)) {     // update data
+      const int absolute_delta = GET_BIT(1);
+      int q[4]  = { 0, 0, 0, 0 };
+      for (s = 0; s < 4; ++s) {
+        if (GET_BIT(1)) {
+          q[s] = GET_BIT(7);
+          if (GET_BIT(1)) q[s] = -q[s];   // sign
+        }
+      }
+      if (absolute_delta) Q = q[0];  // just use the first segment's quantizer
+      for (s = 0; s < 4; ++s) CONDITIONAL_SKIP(7);   //  filter strength
+    }
+    if (update_map) {
+      for (s = 0; s < 3; ++s) CONDITIONAL_SKIP(8);
+    }
+  }
+  // Filter header
+  GET_BIT(1 + 6 + 3);     // simple + level + sharpness
+  if (GET_BIT(1)) {       // use_lf_delta
+    if (GET_BIT(1)) {     // update lf_delta?
+      int n;
+      for (n = 0; n < 4 + 4; ++n) CONDITIONAL_SKIP(6);
+    }
+  }
+  // num partitions
+  GET_BIT(2);
+
+  // ParseQuant
+  {
+    const int base_q = GET_BIT(7);
+    /* dqy1_dc = */ CONDITIONAL_SKIP(5);
+    /* dqy2_dc = */ CONDITIONAL_SKIP(5);
+    /* dqy2_ac = */ CONDITIONAL_SKIP(5);
+    /* dquv_dc = */ CONDITIONAL_SKIP(5);
+    /* dquv_ac = */ CONDITIONAL_SKIP(5);
+
+    if (Q < 0) Q = base_q;
+  }
+  if (bit_pos == INVALID_BIT_POS) return -1;
+
+  // base mapping
+  Q = (127 - Q) * 100 / 127;
+  // correction for power-law behavior in low range
+  if (Q < 80) {
+    Q = (int)(pow(Q / 80., 1. / 0.38) * 80);
+  }
+  return Q;
+}
diff --git a/extras/vwebp_sdl.c b/extras/vwebp_sdl.c
new file mode 100644
index 0000000..e9554eb
--- /dev/null
+++ b/extras/vwebp_sdl.c
@@ -0,0 +1,101 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Simple SDL-based WebP file viewer.
+// Does not support animation, just static images.
+//
+// Press 'q' to exit.
+//
+// Author: James Zern (jzern@google.com)
+
+#include <stdio.h>
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#if defined(WEBP_HAVE_SDL)
+
+#include "webp_to_sdl.h"
+#include "webp/decode.h"
+#include "imageio/imageio_util.h"
+#include "../examples/unicode.h"
+
+#if defined(WEBP_HAVE_JUST_SDL_H)
+#include <SDL.h>
+#else
+#include <SDL/SDL.h>
+#endif
+
+static void ProcessEvents(void) {
+  int done = 0;
+  SDL_Event event;
+  while (!done && SDL_WaitEvent(&event)) {
+    switch (event.type) {
+      case SDL_KEYUP:
+        switch (event.key.keysym.sym) {
+          case SDLK_q: done = 1; break;
+          default: break;
+        }
+        break;
+      default: break;
+    }
+  }
+}
+
+int main(int argc, char* argv[]) {
+  int c;
+  int ok = 0;
+
+  INIT_WARGV(argc, argv);
+
+  for (c = 1; c < argc; ++c) {
+    const char* file = NULL;
+    const uint8_t* webp = NULL;
+    size_t webp_size = 0;
+    if (!strcmp(argv[c], "-h")) {
+      printf("Usage: %s [-h] image.webp [more_files.webp...]\n", argv[0]);
+      FREE_WARGV_AND_RETURN(0);
+    } else {
+      file = (const char*)GET_WARGV(argv, c);
+    }
+    if (file == NULL) continue;
+    if (!ImgIoUtilReadFile(file, &webp, &webp_size)) {
+      WFPRINTF(stderr, "Error opening file: %s\n", (const W_CHAR*)file);
+      goto Error;
+    }
+    if (webp_size != (size_t)(int)webp_size) {
+      free((void*)webp);
+      fprintf(stderr, "File too large.\n");
+      goto Error;
+    }
+    ok = WebPToSDL((const char*)webp, (int)webp_size);
+    free((void*)webp);
+    if (!ok) {
+      WFPRINTF(stderr, "Error decoding file %s\n", (const W_CHAR*)file);
+      goto Error;
+    }
+    ProcessEvents();
+  }
+  ok = 1;
+
+ Error:
+  SDL_Quit();
+  FREE_WARGV_AND_RETURN(ok ? 0 : 1);
+}
+
+#else  // !WEBP_HAVE_SDL
+
+int main(int argc, const char* argv[]) {
+  fprintf(stderr, "SDL support not enabled in %s.\n", argv[0]);
+  (void)argc;
+  return 0;
+}
+
+#endif
diff --git a/extras/webp_quality.c b/extras/webp_quality.c
new file mode 100644
index 0000000..0a3b25f
--- /dev/null
+++ b/extras/webp_quality.c
@@ -0,0 +1,54 @@
+// Simple tool to roughly evaluate the quality encoding of a webp bitstream
+//
+// Result is a *rough* estimation of the quality. You should just consider
+// the bucket it's in (q > 80? > 50? > 20?) and not take it for face value.
+/*
+ gcc -o webp_quality webp_quality.c -O3 -I../ -L. -L../imageio \
+    -limageio_util -lwebpextras -lwebp -lm -lpthread
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "extras/extras.h"
+#include "imageio/imageio_util.h"
+#include "../examples/unicode.h"
+
+int main(int argc, const char* argv[]) {
+  int c;
+  int quiet = 0;
+  int ok = 1;
+
+  INIT_WARGV(argc, argv);
+
+  for (c = 1; ok && c < argc; ++c) {
+    if (!strcmp(argv[c], "-quiet")) {
+      quiet = 1;
+    } else if (!strcmp(argv[c], "-help") || !strcmp(argv[c], "-h")) {
+      printf("webp_quality [-h][-quiet] webp_files...\n");
+      FREE_WARGV_AND_RETURN(0);
+    } else {
+      const char* const filename = (const char*)GET_WARGV(argv, c);
+      const uint8_t* data = NULL;
+      size_t data_size = 0;
+      int q;
+      ok = ImgIoUtilReadFile(filename, &data, &data_size);
+      if (!ok) break;
+      q = VP8EstimateQuality(data, data_size);
+      if (!quiet) WPRINTF("[%s] ", (const W_CHAR*)filename);
+      if (q < 0) {
+        fprintf(stderr, "Not a WebP file, or not a lossy WebP file.\n");
+        ok = 0;
+      } else {
+        if (!quiet) {
+          printf("Estimated quality factor: %d\n", q);
+        } else {
+          printf("%d\n", q);   // just print the number
+        }
+      }
+      free((void*)data);
+    }
+  }
+  FREE_WARGV_AND_RETURN(ok ? 0 : 1);
+}
diff --git a/extras/webp_to_sdl.c b/extras/webp_to_sdl.c
new file mode 100644
index 0000000..1e52681
--- /dev/null
+++ b/extras/webp_to_sdl.c
@@ -0,0 +1,110 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  Simple WebP-to-SDL wrapper. Useful for emscripten.
+//
+// Author: James Zern (jzern@google.com)
+
+#ifdef HAVE_CONFIG_H
+#include "src/webp/config.h"
+#endif
+
+#if defined(WEBP_HAVE_SDL)
+
+#include "webp_to_sdl.h"
+
+#include <stdio.h>
+#include "src/webp/decode.h"
+
+#if defined(WEBP_HAVE_JUST_SDL_H)
+#include <SDL.h>
+#else
+#include <SDL/SDL.h>
+#endif
+
+static int init_ok = 0;
+int WebPToSDL(const char* data, unsigned int data_size) {
+  int ok = 0;
+  VP8StatusCode status;
+  WebPDecoderConfig config;
+  WebPBitstreamFeatures* const input = &config.input;
+  WebPDecBuffer* const output = &config.output;
+  SDL_Surface* screen = NULL;
+  SDL_Surface* surface = NULL;
+
+  if (!WebPInitDecoderConfig(&config)) {
+    fprintf(stderr, "Library version mismatch!\n");
+    return 0;
+  }
+
+  if (!init_ok) {
+    SDL_Init(SDL_INIT_VIDEO);
+    init_ok = 1;
+  }
+
+  status = WebPGetFeatures((uint8_t*)data, (size_t)data_size, &config.input);
+  if (status != VP8_STATUS_OK) goto Error;
+
+  screen = SDL_SetVideoMode(input->width, input->height, 32, SDL_SWSURFACE);
+  if (screen == NULL) {
+    fprintf(stderr, "Unable to set video mode (32bpp %dx%d)!\n",
+            input->width, input->height);
+    goto Error;
+  }
+
+  surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
+                                 input->width, input->height, 32,
+                                 0x000000ffu,   // R mask
+                                 0x0000ff00u,   // G mask
+                                 0x00ff0000u,   // B mask
+                                 0xff000000u);  // A mask
+
+  if (surface == NULL) {
+    fprintf(stderr, "Unable to create %dx%d RGBA surface!\n",
+            input->width, input->height);
+    goto Error;
+  }
+  if (SDL_MUSTLOCK(surface)) SDL_LockSurface(surface);
+
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+  output->colorspace = MODE_BGRA;
+#else
+  output->colorspace = MODE_RGBA;
+#endif
+  output->width  = surface->w;
+  output->height = surface->h;
+  output->u.RGBA.rgba   = surface->pixels;
+  output->u.RGBA.stride = surface->pitch;
+  output->u.RGBA.size   = surface->pitch * surface->h;
+  output->is_external_memory = 1;
+
+  status = WebPDecode((const uint8_t*)data, (size_t)data_size, &config);
+  if (status != VP8_STATUS_OK) {
+    fprintf(stderr, "Error decoding image (%d)\n", status);
+    goto Error;
+  }
+
+  if (SDL_MUSTLOCK(surface)) SDL_UnlockSurface(surface);
+  if (SDL_BlitSurface(surface, NULL, screen, NULL) ||
+      SDL_Flip(screen)) {
+    goto Error;
+  }
+
+  ok = 1;
+
+ Error:
+  SDL_FreeSurface(surface);
+  SDL_FreeSurface(screen);
+  WebPFreeDecBuffer(output);
+  return ok;
+}
+
+//------------------------------------------------------------------------------
+
+#endif  // WEBP_HAVE_SDL
diff --git a/extras/webp_to_sdl.h b/extras/webp_to_sdl.h
new file mode 100644
index 0000000..1534f2b
--- /dev/null
+++ b/extras/webp_to_sdl.h
@@ -0,0 +1,22 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  Simple WebP-to-SDL wrapper. Useful for emscripten.
+//
+// Author: James Zern (jzern@google.com)
+
+#ifndef WEBP_EXTRAS_WEBP_TO_SDL_H_
+#define WEBP_EXTRAS_WEBP_TO_SDL_H_
+
+// Exports the method WebPToSDL(const char* data, int data_size) which decodes
+// a WebP bitstream into an RGBA SDL surface.
+// Return false on failure.
+extern int WebPToSDL(const char* data, unsigned int data_size);
+
+#endif  // WEBP_EXTRAS_WEBP_TO_SDL_H_
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..28d39f5
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,14 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Versions for gradle
+BUILD_TOOLS_VERSION=23.0.3
+COMPILE_SDK_VERSION=23
+ANDROID_GRADLE_PLUGIN_VERSION=1.5.0
+GRADLE_DOWNLOAD_TASK_VERSION=2.1.0
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f3d88b1
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..1b16c34
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..2fe81a7
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,183 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=`expr $i + 1`
+    done
+    case $i in
+        0) set -- ;;
+        1) set -- "$args0" ;;
+        2) set -- "$args0" "$args1" ;;
+        3) set -- "$args0" "$args1" "$args2" ;;
+        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..9618d8d
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,100 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem      https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/imageio/Android.mk b/imageio/Android.mk
new file mode 100644
index 0000000..aa877ce
--- /dev/null
+++ b/imageio/Android.mk
@@ -0,0 +1,66 @@
+# Ignore this file during non-NDK builds.
+ifdef NDK_ROOT
+LOCAL_PATH := $(call my-dir)
+
+################################################################################
+# libimageio_util
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    imageio_util.c \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
+
+LOCAL_MODULE := imageio_util
+
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-BSD
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../COPYING $(LOCAL_PATH)/../NOTICE $(LOCAL_PATH)/../PATENTS
+include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+# libimagedec
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    image_dec.c \
+    jpegdec.c \
+    metadata.c \
+    pngdec.c \
+    pnmdec.c \
+    tiffdec.c \
+    webpdec.c \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
+LOCAL_STATIC_LIBRARIES := imageio_util
+
+LOCAL_MODULE := imagedec
+
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-BSD
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../COPYING $(LOCAL_PATH)/../NOTICE $(LOCAL_PATH)/../PATENTS
+include $(BUILD_STATIC_LIBRARY)
+
+################################################################################
+# libimageenc
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    image_enc.c \
+
+LOCAL_CFLAGS := $(WEBP_CFLAGS)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src
+LOCAL_STATIC_LIBRARIES := imageio_util
+
+LOCAL_MODULE := imageenc
+
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-BSD
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../COPYING $(LOCAL_PATH)/../NOTICE $(LOCAL_PATH)/../PATENTS
+include $(BUILD_STATIC_LIBRARY)
+endif  # NDK_ROOT
diff --git a/imageio/Makefile.am b/imageio/Makefile.am
new file mode 100644
index 0000000..500ec7e
--- /dev/null
+++ b/imageio/Makefile.am
@@ -0,0 +1,32 @@
+AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src
+noinst_LTLIBRARIES =
+noinst_LTLIBRARIES += libimageio_util.la
+if BUILD_DEMUX
+  noinst_LTLIBRARIES += libimagedec.la
+endif
+noinst_LTLIBRARIES += libimageenc.la
+
+noinst_HEADERS =
+noinst_HEADERS += ../src/webp/decode.h
+noinst_HEADERS += ../src/webp/types.h
+
+libimageio_util_la_SOURCES =
+libimageio_util_la_SOURCES += imageio_util.c imageio_util.h
+
+libimagedec_la_SOURCES  =
+libimagedec_la_SOURCES += image_dec.c image_dec.h
+libimagedec_la_SOURCES += jpegdec.c jpegdec.h
+libimagedec_la_SOURCES += metadata.c metadata.h
+libimagedec_la_SOURCES += pngdec.c pngdec.h
+libimagedec_la_SOURCES += pnmdec.c pnmdec.h
+libimagedec_la_SOURCES += tiffdec.c tiffdec.h
+libimagedec_la_SOURCES += webpdec.c webpdec.h
+libimagedec_la_SOURCES += wicdec.c wicdec.h
+libimagedec_la_CPPFLAGS = $(JPEG_INCLUDES) $(PNG_INCLUDES) $(TIFF_INCLUDES)
+libimagedec_la_CPPFLAGS += $(AM_CPPFLAGS)
+libimagedec_la_LIBADD = ../src/demux/libwebpdemux.la
+
+libimageenc_la_SOURCES  =
+libimageenc_la_SOURCES += image_enc.c image_enc.h
+libimageenc_la_CPPFLAGS = $(JPEG_INCLUDES) $(PNG_INCLUDES) $(TIFF_INCLUDES)
+libimageenc_la_CPPFLAGS += $(AM_CPPFLAGS)
diff --git a/imageio/image_dec.c b/imageio/image_dec.c
new file mode 100644
index 0000000..5e003fa
--- /dev/null
+++ b/imageio/image_dec.c
@@ -0,0 +1,84 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Generic image-type guessing.
+
+#include "./image_dec.h"
+
+const char* WebPGetEnabledInputFileFormats(void) {
+  return "WebP"
+#ifdef WEBP_HAVE_JPEG
+         ", JPEG"
+#endif
+#ifdef WEBP_HAVE_PNG
+         ", PNG"
+#endif
+         ", PNM (PGM, PPM, PAM)"
+#ifdef WEBP_HAVE_TIFF
+         ", TIFF"
+#endif
+#ifdef HAVE_WINCODEC_H
+         ", Windows Imaging Component (WIC)"
+#endif
+         "";
+}
+
+static WEBP_INLINE uint32_t GetBE32(const uint8_t buf[]) {
+  return ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+}
+
+WebPInputFileFormat WebPGuessImageType(const uint8_t* const data,
+                                       size_t data_size) {
+  WebPInputFileFormat format = WEBP_UNSUPPORTED_FORMAT;
+  if (data != NULL && data_size >= 12) {
+    const uint32_t magic1 = GetBE32(data + 0);
+    const uint32_t magic2 = GetBE32(data + 8);
+    if (magic1 == 0x89504E47U) {
+      format = WEBP_PNG_FORMAT;
+    } else if (magic1 >= 0xFFD8FF00U && magic1 <= 0xFFD8FFFFU) {
+      format = WEBP_JPEG_FORMAT;
+    } else if (magic1 == 0x49492A00 || magic1 == 0x4D4D002A) {
+      format = WEBP_TIFF_FORMAT;
+    } else if (magic1 == 0x52494646 && magic2 == 0x57454250) {
+      format = WEBP_WEBP_FORMAT;
+    } else if (((magic1 >> 24) & 0xff) == 'P') {
+      const int type = (magic1 >> 16) & 0xff;
+      // we only support 'P5 -> P7' for now.
+      if (type >= '5' && type <= '7') format = WEBP_PNM_FORMAT;
+    }
+  }
+  return format;
+}
+
+static int FailReader(const uint8_t* const data, size_t data_size,
+                      struct WebPPicture* const pic,
+                      int keep_alpha, struct Metadata* const metadata) {
+  (void)data;
+  (void)data_size;
+  (void)pic;
+  (void)keep_alpha;
+  (void)metadata;
+  return 0;
+}
+
+WebPImageReader WebPGetImageReader(WebPInputFileFormat format) {
+  switch (format) {
+    case WEBP_PNG_FORMAT: return ReadPNG;
+    case WEBP_JPEG_FORMAT: return ReadJPEG;
+    case WEBP_TIFF_FORMAT: return ReadTIFF;
+    case WEBP_WEBP_FORMAT: return ReadWebP;
+    case WEBP_PNM_FORMAT: return ReadPNM;
+    default: return FailReader;
+  }
+}
+
+WebPImageReader WebPGuessImageReader(const uint8_t* const data,
+                                     size_t data_size) {
+  return WebPGetImageReader(WebPGuessImageType(data, data_size));
+}
diff --git a/imageio/image_dec.h b/imageio/image_dec.h
new file mode 100644
index 0000000..f09f564
--- /dev/null
+++ b/imageio/image_dec.h
@@ -0,0 +1,70 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  All-in-one library to decode PNG/JPEG/WebP/TIFF/WIC input images.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_IMAGEIO_IMAGE_DEC_H_
+#define WEBP_IMAGEIO_IMAGE_DEC_H_
+
+#include "webp/types.h"
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include "./metadata.h"
+#include "./jpegdec.h"
+#include "./pngdec.h"
+#include "./pnmdec.h"
+#include "./tiffdec.h"
+#include "./webpdec.h"
+#include "./wicdec.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+  WEBP_PNG_FORMAT = 0,
+  WEBP_JPEG_FORMAT,
+  WEBP_TIFF_FORMAT,
+  WEBP_WEBP_FORMAT,
+  WEBP_PNM_FORMAT,
+  WEBP_UNSUPPORTED_FORMAT
+} WebPInputFileFormat;
+
+// Returns a comma separated list of enabled input formats.
+const char* WebPGetEnabledInputFileFormats(void);
+
+// Try to infer the image format. 'data_size' should be larger than 12.
+// Returns WEBP_UNSUPPORTED_FORMAT if format can't be guess safely.
+WebPInputFileFormat WebPGuessImageType(const uint8_t* const data,
+                                       size_t data_size);
+
+// Signature for common image-reading functions (ReadPNG, ReadJPEG, ...)
+typedef int (*WebPImageReader)(const uint8_t* const data, size_t data_size,
+                               struct WebPPicture* const pic,
+                               int keep_alpha, struct Metadata* const metadata);
+
+// Return the reader associated to a given file format.
+WebPImageReader WebPGetImageReader(WebPInputFileFormat format);
+
+// This function is similar to WebPGuessImageType(), but returns a
+// suitable reader function. The returned reader is never NULL, but
+// unknown formats will return an always-failing valid reader.
+WebPImageReader WebPGuessImageReader(const uint8_t* const data,
+                                     size_t data_size);
+
+#ifdef __cplusplus
+}    // extern "C"
+#endif
+
+#endif  // WEBP_IMAGEIO_IMAGE_DEC_H_
diff --git a/imageio/image_enc.c b/imageio/image_enc.c
new file mode 100644
index 0000000..e06bcaf
--- /dev/null
+++ b/imageio/image_enc.c
@@ -0,0 +1,615 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Save image
+
+#include "./image_enc.h"
+
+#include <assert.h>
+#include <string.h>
+
+#ifdef WEBP_HAVE_PNG
+#include <png.h>
+#include <setjmp.h>   // note: this must be included *after* png.h
+#endif
+
+#ifdef HAVE_WINCODEC_H
+#ifdef __MINGW32__
+#define INITGUID  // Without this GUIDs are declared extern and fail to link
+#endif
+#define CINTERFACE
+#define COBJMACROS
+#define _WIN32_IE 0x500  // Workaround bug in shlwapi.h when compiling C++
+                         // code with COBJMACROS.
+#include <ole2.h>  // CreateStreamOnHGlobal()
+#include <shlwapi.h>
+#include <tchar.h>
+#include <windows.h>
+#include <wincodec.h>
+#endif
+
+#include "./imageio_util.h"
+#include "../examples/unicode.h"
+
+//------------------------------------------------------------------------------
+// PNG
+
+#ifdef HAVE_WINCODEC_H
+
+#define IFS(fn)                                                     \
+  do {                                                              \
+    if (SUCCEEDED(hr)) {                                            \
+      hr = (fn);                                                    \
+      if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr);   \
+    }                                                               \
+  } while (0)
+
+#ifdef __cplusplus
+#define MAKE_REFGUID(x) (x)
+#else
+#define MAKE_REFGUID(x) &(x)
+#endif
+
+static HRESULT CreateOutputStream(const char* out_file_name,
+                                  int write_to_mem, IStream** stream) {
+  HRESULT hr = S_OK;
+  if (write_to_mem) {
+    // Output to a memory buffer. This is freed when 'stream' is released.
+    IFS(CreateStreamOnHGlobal(NULL, TRUE, stream));
+  } else {
+    IFS(SHCreateStreamOnFile((const LPTSTR)out_file_name,
+                             STGM_WRITE | STGM_CREATE, stream));
+  }
+  if (FAILED(hr)) {
+    _ftprintf(stderr, _T("Error opening output file %s (%08lx)\n"),
+              (const LPTSTR)out_file_name, hr);
+  }
+  return hr;
+}
+
+static HRESULT WriteUsingWIC(const char* out_file_name, int use_stdout,
+                             REFGUID container_guid,
+                             uint8_t* rgb, int stride,
+                             uint32_t width, uint32_t height, int has_alpha) {
+  HRESULT hr = S_OK;
+  IWICImagingFactory* factory = NULL;
+  IWICBitmapFrameEncode* frame = NULL;
+  IWICBitmapEncoder* encoder = NULL;
+  IStream* stream = NULL;
+  WICPixelFormatGUID pixel_format = has_alpha ? GUID_WICPixelFormat32bppBGRA
+                                              : GUID_WICPixelFormat24bppBGR;
+
+  if (out_file_name == NULL || rgb == NULL) return E_INVALIDARG;
+
+  IFS(CoInitialize(NULL));
+  IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
+                       CLSCTX_INPROC_SERVER,
+                       MAKE_REFGUID(IID_IWICImagingFactory),
+                       (LPVOID*)&factory));
+  if (hr == REGDB_E_CLASSNOTREG) {
+    fprintf(stderr,
+            "Couldn't access Windows Imaging Component (are you running "
+            "Windows XP SP3 or newer?). PNG support not available. "
+            "Use -ppm or -pgm for available PPM and PGM formats.\n");
+  }
+  IFS(CreateOutputStream(out_file_name, use_stdout, &stream));
+  IFS(IWICImagingFactory_CreateEncoder(factory, container_guid, NULL,
+                                       &encoder));
+  IFS(IWICBitmapEncoder_Initialize(encoder, stream,
+                                   WICBitmapEncoderNoCache));
+  IFS(IWICBitmapEncoder_CreateNewFrame(encoder, &frame, NULL));
+  IFS(IWICBitmapFrameEncode_Initialize(frame, NULL));
+  IFS(IWICBitmapFrameEncode_SetSize(frame, width, height));
+  IFS(IWICBitmapFrameEncode_SetPixelFormat(frame, &pixel_format));
+  IFS(IWICBitmapFrameEncode_WritePixels(frame, height, stride,
+                                        height * stride, rgb));
+  IFS(IWICBitmapFrameEncode_Commit(frame));
+  IFS(IWICBitmapEncoder_Commit(encoder));
+
+  if (SUCCEEDED(hr) && use_stdout) {
+    HGLOBAL image;
+    IFS(GetHGlobalFromStream(stream, &image));
+    if (SUCCEEDED(hr)) {
+      HANDLE std_output = GetStdHandle(STD_OUTPUT_HANDLE);
+      DWORD mode;
+      const BOOL update_mode = GetConsoleMode(std_output, &mode);
+      const void* const image_mem = GlobalLock(image);
+      DWORD bytes_written = 0;
+
+      // Clear output processing if necessary, then output the image.
+      if (update_mode) SetConsoleMode(std_output, 0);
+      if (!WriteFile(std_output, image_mem, (DWORD)GlobalSize(image),
+                     &bytes_written, NULL) ||
+          bytes_written != GlobalSize(image)) {
+        hr = E_FAIL;
+      }
+      if (update_mode) SetConsoleMode(std_output, mode);
+      GlobalUnlock(image);
+    }
+  }
+
+  if (frame != NULL) IUnknown_Release(frame);
+  if (encoder != NULL) IUnknown_Release(encoder);
+  if (factory != NULL) IUnknown_Release(factory);
+  if (stream != NULL) IUnknown_Release(stream);
+  return hr;
+}
+
+int WebPWritePNG(const char* out_file_name, int use_stdout,
+                 const WebPDecBuffer* const buffer) {
+  const uint32_t width = buffer->width;
+  const uint32_t height = buffer->height;
+  uint8_t* const rgb = buffer->u.RGBA.rgba;
+  const int stride = buffer->u.RGBA.stride;
+  const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
+
+  return SUCCEEDED(WriteUsingWIC(out_file_name, use_stdout,
+                                 MAKE_REFGUID(GUID_ContainerFormatPng),
+                                 rgb, stride, width, height, has_alpha));
+}
+
+#elif defined(WEBP_HAVE_PNG)    // !HAVE_WINCODEC_H
+static void PNGAPI PNGErrorFunction(png_structp png, png_const_charp unused) {
+  (void)unused;  // remove variable-unused warning
+  longjmp(png_jmpbuf(png), 1);
+}
+
+int WebPWritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
+  volatile png_structp png;
+  volatile png_infop info;
+
+  if (out_file == NULL || buffer == NULL) return 0;
+
+  png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+                                NULL, PNGErrorFunction, NULL);
+  if (png == NULL) {
+    return 0;
+  }
+  info = png_create_info_struct(png);
+  if (info == NULL) {
+    png_destroy_write_struct((png_structpp)&png, NULL);
+    return 0;
+  }
+  if (setjmp(png_jmpbuf(png))) {
+    png_destroy_write_struct((png_structpp)&png, (png_infopp)&info);
+    return 0;
+  }
+  png_init_io(png, out_file);
+  {
+    const uint32_t width = buffer->width;
+    const uint32_t height = buffer->height;
+    png_bytep row = buffer->u.RGBA.rgba;
+    const int stride = buffer->u.RGBA.stride;
+    const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
+    uint32_t y;
+
+    png_set_IHDR(png, info, width, height, 8,
+                 has_alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB,
+                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
+                 PNG_FILTER_TYPE_DEFAULT);
+    png_write_info(png, info);
+    for (y = 0; y < height; ++y) {
+      png_write_rows(png, &row, 1);
+      row += stride;
+    }
+  }
+  png_write_end(png, info);
+  png_destroy_write_struct((png_structpp)&png, (png_infopp)&info);
+  return 1;
+}
+#else    // !HAVE_WINCODEC_H && !WEBP_HAVE_PNG
+int WebPWritePNG(FILE* fout, const WebPDecBuffer* const buffer) {
+  if (fout == NULL || buffer == NULL) return 0;
+
+  fprintf(stderr, "PNG support not compiled. Please install the libpng "
+          "development package before building.\n");
+  fprintf(stderr, "You can run with -ppm flag to decode in PPM format.\n");
+  return 0;
+}
+#endif
+
+//------------------------------------------------------------------------------
+// PPM / PAM
+
+static int WritePPMPAM(FILE* fout, const WebPDecBuffer* const buffer,
+                       int alpha) {
+  if (fout == NULL || buffer == NULL) {
+    return 0;
+  } else {
+    const uint32_t width = buffer->width;
+    const uint32_t height = buffer->height;
+    const uint8_t* row = buffer->u.RGBA.rgba;
+    const int stride = buffer->u.RGBA.stride;
+    const size_t bytes_per_px = alpha ? 4 : 3;
+    uint32_t y;
+
+    if (row == NULL) return 0;
+
+    if (alpha) {
+      fprintf(fout, "P7\nWIDTH %u\nHEIGHT %u\nDEPTH 4\nMAXVAL 255\n"
+                    "TUPLTYPE RGB_ALPHA\nENDHDR\n", width, height);
+    } else {
+      fprintf(fout, "P6\n%u %u\n255\n", width, height);
+    }
+    for (y = 0; y < height; ++y) {
+      if (fwrite(row, width, bytes_per_px, fout) != bytes_per_px) {
+        return 0;
+      }
+      row += stride;
+    }
+  }
+  return 1;
+}
+
+int WebPWritePPM(FILE* fout, const WebPDecBuffer* const buffer) {
+  return WritePPMPAM(fout, buffer, 0);
+}
+
+int WebPWritePAM(FILE* fout, const WebPDecBuffer* const buffer) {
+  return WritePPMPAM(fout, buffer, 1);
+}
+
+//------------------------------------------------------------------------------
+// Raw PGM
+
+// Save 16b mode (RGBA4444, RGB565, ...) for debugging purpose.
+int WebPWrite16bAsPGM(FILE* fout, const WebPDecBuffer* const buffer) {
+  const uint32_t width = buffer->width;
+  const uint32_t height = buffer->height;
+  const uint8_t* rgba = buffer->u.RGBA.rgba;
+  const int stride = buffer->u.RGBA.stride;
+  const uint32_t bytes_per_px = 2;
+  uint32_t y;
+
+  if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
+
+  fprintf(fout, "P5\n%u %u\n255\n", width * bytes_per_px, height);
+  for (y = 0; y < height; ++y) {
+    if (fwrite(rgba, width, bytes_per_px, fout) != bytes_per_px) {
+      return 0;
+    }
+    rgba += stride;
+  }
+  return 1;
+}
+
+//------------------------------------------------------------------------------
+// BMP (see https://en.wikipedia.org/wiki/BMP_file_format#Pixel_storage)
+
+static void PutLE16(uint8_t* const dst, uint32_t value) {
+  dst[0] = (value >> 0) & 0xff;
+  dst[1] = (value >> 8) & 0xff;
+}
+
+static void PutLE32(uint8_t* const dst, uint32_t value) {
+  PutLE16(dst + 0, (value >>  0) & 0xffff);
+  PutLE16(dst + 2, (value >> 16) & 0xffff);
+}
+
+#define BMP_HEADER_SIZE 54
+#define BMP_HEADER_ALPHA_EXTRA_SIZE 16  // for alpha info
+int WebPWriteBMP(FILE* fout, const WebPDecBuffer* const buffer) {
+  const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
+  const int header_size =
+      BMP_HEADER_SIZE + (has_alpha ? BMP_HEADER_ALPHA_EXTRA_SIZE : 0);
+  const uint32_t width = buffer->width;
+  const uint32_t height = buffer->height;
+  const uint8_t* rgba = buffer->u.RGBA.rgba;
+  const int stride = buffer->u.RGBA.stride;
+  const uint32_t bytes_per_px = has_alpha ? 4 : 3;
+  uint32_t y;
+  const uint32_t line_size = bytes_per_px * width;
+  const uint32_t bmp_stride = (line_size + 3) & ~3;   // pad to 4
+  const uint32_t image_size = bmp_stride * height;
+  const uint32_t total_size =  image_size + header_size;
+  uint8_t bmp_header[BMP_HEADER_SIZE + BMP_HEADER_ALPHA_EXTRA_SIZE] = { 0 };
+
+  if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
+
+  // bitmap file header
+  PutLE16(bmp_header + 0, 0x4d42);                // signature 'BM'
+  PutLE32(bmp_header + 2, total_size);            // size including header
+  PutLE32(bmp_header + 6, 0);                     // reserved
+  PutLE32(bmp_header + 10, header_size);          // offset to pixel array
+  // bitmap info header
+  PutLE32(bmp_header + 14, header_size - 14);     // DIB header size
+  PutLE32(bmp_header + 18, width);                // dimensions
+  PutLE32(bmp_header + 22, height);               // no vertical flip
+  PutLE16(bmp_header + 26, 1);                    // number of planes
+  PutLE16(bmp_header + 28, bytes_per_px * 8);     // bits per pixel
+  PutLE32(bmp_header + 30, has_alpha ? 3 : 0);    // BI_BITFIELDS or BI_RGB
+  PutLE32(bmp_header + 34, image_size);
+  PutLE32(bmp_header + 38, 2400);                 // x pixels/meter
+  PutLE32(bmp_header + 42, 2400);                 // y pixels/meter
+  PutLE32(bmp_header + 46, 0);                    // number of palette colors
+  PutLE32(bmp_header + 50, 0);                    // important color count
+  if (has_alpha) {  // BITMAPV3INFOHEADER complement
+    PutLE32(bmp_header + 54, 0x00ff0000);         // red mask
+    PutLE32(bmp_header + 58, 0x0000ff00);         // green mask
+    PutLE32(bmp_header + 62, 0x000000ff);         // blue mask
+    PutLE32(bmp_header + 66, 0xff000000);         // alpha mask
+  }
+
+  // TODO(skal): color profile
+
+  // write header
+  if (fwrite(bmp_header, header_size, 1, fout) != 1) {
+    return 0;
+  }
+
+  // write pixel array, bottom to top
+  for (y = 0; y < height; ++y) {
+    const uint8_t* const src = &rgba[(uint64_t)(height - 1 - y) * stride];
+    if (fwrite(src, line_size, 1, fout) != 1) {
+      return 0;
+    }
+    // write padding zeroes
+    if (bmp_stride != line_size) {
+      const uint8_t zeroes[3] = { 0 };
+      if (fwrite(zeroes, bmp_stride - line_size, 1, fout) != 1) {
+        return 0;
+      }
+    }
+  }
+  return 1;
+}
+#undef BMP_HEADER_SIZE
+#undef BMP_HEADER_ALPHA_EXTRA_SIZE
+
+//------------------------------------------------------------------------------
+// TIFF
+
+#define NUM_IFD_ENTRIES 15
+#define EXTRA_DATA_SIZE 16
+// 10b for signature/header + n * 12b entries + 4b for IFD terminator:
+#define EXTRA_DATA_OFFSET (10 + 12 * NUM_IFD_ENTRIES + 4)
+#define TIFF_HEADER_SIZE (EXTRA_DATA_OFFSET + EXTRA_DATA_SIZE)
+
+int WebPWriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) {
+  const int has_alpha = WebPIsAlphaMode(buffer->colorspace);
+  const uint32_t width = buffer->width;
+  const uint32_t height = buffer->height;
+  const uint8_t* rgba = buffer->u.RGBA.rgba;
+  const int stride = buffer->u.RGBA.stride;
+  const uint8_t bytes_per_px = has_alpha ? 4 : 3;
+  const uint8_t assoc_alpha =
+      WebPIsPremultipliedMode(buffer->colorspace) ? 1 : 2;
+  // For non-alpha case, we omit tag 0x152 (ExtraSamples).
+  const uint8_t num_ifd_entries = has_alpha ? NUM_IFD_ENTRIES
+                                            : NUM_IFD_ENTRIES - 1;
+  uint8_t tiff_header[TIFF_HEADER_SIZE] = {
+    0x49, 0x49, 0x2a, 0x00,   // little endian signature
+    8, 0, 0, 0,               // offset to the unique IFD that follows
+    // IFD (offset = 8). Entries must be written in increasing tag order.
+    num_ifd_entries, 0,       // Number of entries in the IFD (12 bytes each).
+    0x00, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0,    //  10: Width  (TBD)
+    0x01, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0,    //  22: Height (TBD)
+    0x02, 0x01, 3, 0, bytes_per_px, 0, 0, 0,     //  34: BitsPerSample: 8888
+        EXTRA_DATA_OFFSET + 0, 0, 0, 0,
+    0x03, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0,    //  46: Compression: none
+    0x06, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0,    //  58: Photometric: RGB
+    0x11, 0x01, 4, 0, 1, 0, 0, 0,                //  70: Strips offset:
+        TIFF_HEADER_SIZE, 0, 0, 0,               //      data follows header
+    0x12, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0,    //  82: Orientation: topleft
+    0x15, 0x01, 3, 0, 1, 0, 0, 0,                //  94: SamplesPerPixels
+        bytes_per_px, 0, 0, 0,
+    0x16, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0,    // 106: Rows per strip (TBD)
+    0x17, 0x01, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0,    // 118: StripByteCount (TBD)
+    0x1a, 0x01, 5, 0, 1, 0, 0, 0,                // 130: X-resolution
+        EXTRA_DATA_OFFSET + 8, 0, 0, 0,
+    0x1b, 0x01, 5, 0, 1, 0, 0, 0,                // 142: Y-resolution
+        EXTRA_DATA_OFFSET + 8, 0, 0, 0,
+    0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0,    // 154: PlanarConfiguration
+    0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0,    // 166: ResolutionUnit (inch)
+    0x52, 0x01, 3, 0, 1, 0, 0, 0,
+        assoc_alpha, 0, 0, 0,                    // 178: ExtraSamples: rgbA/RGBA
+    0, 0, 0, 0,                                  // 190: IFD terminator
+    // EXTRA_DATA_OFFSET:
+    8, 0, 8, 0, 8, 0, 8, 0,      // BitsPerSample
+    72, 0, 0, 0, 1, 0, 0, 0      // 72 pixels/inch, for X/Y-resolution
+  };
+  uint32_t y;
+
+  if (fout == NULL || buffer == NULL || rgba == NULL) return 0;
+
+  // Fill placeholders in IFD:
+  PutLE32(tiff_header + 10 + 8, width);
+  PutLE32(tiff_header + 22 + 8, height);
+  PutLE32(tiff_header + 106 + 8, height);
+  PutLE32(tiff_header + 118 + 8, width * bytes_per_px * height);
+  if (!has_alpha) PutLE32(tiff_header + 178, 0);  // IFD terminator
+
+  // write header
+  if (fwrite(tiff_header, sizeof(tiff_header), 1, fout) != 1) {
+    return 0;
+  }
+  // write pixel values
+  for (y = 0; y < height; ++y) {
+    if (fwrite(rgba, bytes_per_px, width, fout) != width) {
+      return 0;
+    }
+    rgba += stride;
+  }
+
+  return 1;
+}
+
+#undef TIFF_HEADER_SIZE
+#undef EXTRA_DATA_OFFSET
+#undef EXTRA_DATA_SIZE
+#undef NUM_IFD_ENTRIES
+
+//------------------------------------------------------------------------------
+// Raw Alpha
+
+int WebPWriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) {
+  if (fout == NULL || buffer == NULL) {
+    return 0;
+  } else {
+    const uint32_t width = buffer->width;
+    const uint32_t height = buffer->height;
+    const uint8_t* a = buffer->u.YUVA.a;
+    const int a_stride = buffer->u.YUVA.a_stride;
+    uint32_t y;
+
+    if (a == NULL) return 0;
+
+    fprintf(fout, "P5\n%u %u\n255\n", width, height);
+    for (y = 0; y < height; ++y) {
+      if (fwrite(a, width, 1, fout) != 1) return 0;
+      a += a_stride;
+    }
+    return 1;
+  }
+}
+
+//------------------------------------------------------------------------------
+// PGM with IMC4 layout
+
+int WebPWritePGM(FILE* fout, const WebPDecBuffer* const buffer) {
+  if (fout == NULL || buffer == NULL) {
+    return 0;
+  } else {
+    const int width = buffer->width;
+    const int height = buffer->height;
+    const WebPYUVABuffer* const yuv = &buffer->u.YUVA;
+    const uint8_t* src_y = yuv->y;
+    const uint8_t* src_u = yuv->u;
+    const uint8_t* src_v = yuv->v;
+    const uint8_t* src_a = yuv->a;
+    const int uv_width = (width + 1) / 2;
+    const int uv_height = (height + 1) / 2;
+    const int a_height = (src_a != NULL) ? height : 0;
+    int ok = 1;
+    int y;
+
+    if (src_y == NULL || src_u == NULL || src_v == NULL) return 0;
+
+    fprintf(fout, "P5\n%d %d\n255\n",
+            (width + 1) & ~1, height + uv_height + a_height);
+    for (y = 0; ok && y < height; ++y) {
+      ok &= (fwrite(src_y, width, 1, fout) == 1);
+      if (width & 1) fputc(0, fout);    // padding byte
+      src_y += yuv->y_stride;
+    }
+    for (y = 0; ok && y < uv_height; ++y) {
+      ok &= (fwrite(src_u, uv_width, 1, fout) == 1);
+      ok &= (fwrite(src_v, uv_width, 1, fout) == 1);
+      src_u += yuv->u_stride;
+      src_v += yuv->v_stride;
+    }
+    for (y = 0; ok && y < a_height; ++y) {
+      ok &= (fwrite(src_a, width, 1, fout) == 1);
+      if (width & 1) fputc(0, fout);    // padding byte
+      src_a += yuv->a_stride;
+    }
+    return ok;
+  }
+}
+
+//------------------------------------------------------------------------------
+// Raw YUV(A) planes
+
+int WebPWriteYUV(FILE* fout, const WebPDecBuffer* const buffer) {
+  if (fout == NULL || buffer == NULL) {
+    return 0;
+  } else {
+    const int width = buffer->width;
+    const int height = buffer->height;
+    const WebPYUVABuffer* const yuv = &buffer->u.YUVA;
+    const uint8_t* src_y = yuv->y;
+    const uint8_t* src_u = yuv->u;
+    const uint8_t* src_v = yuv->v;
+    const uint8_t* src_a = yuv->a;
+    const int uv_width = (width + 1) / 2;
+    const int uv_height = (height + 1) / 2;
+    const int a_height = (src_a != NULL) ? height : 0;
+    int ok = 1;
+    int y;
+
+    if (src_y == NULL || src_u == NULL || src_v == NULL) return 0;
+
+    for (y = 0; ok && y < height; ++y) {
+      ok &= (fwrite(src_y, width, 1, fout) == 1);
+      src_y += yuv->y_stride;
+    }
+    for (y = 0; ok && y < uv_height; ++y) {
+      ok &= (fwrite(src_u, uv_width, 1, fout) == 1);
+      src_u += yuv->u_stride;
+    }
+    for (y = 0; ok && y < uv_height; ++y) {
+      ok &= (fwrite(src_v, uv_width, 1, fout) == 1);
+      src_v += yuv->v_stride;
+    }
+    for (y = 0; ok && y < a_height; ++y) {
+      ok &= (fwrite(src_a, width, 1, fout) == 1);
+      src_a += yuv->a_stride;
+    }
+    return ok;
+  }
+}
+
+//------------------------------------------------------------------------------
+// Generic top-level call
+
+int WebPSaveImage(const WebPDecBuffer* const buffer,
+                  WebPOutputFileFormat format,
+                  const char* const out_file_name) {
+  FILE* fout = NULL;
+  int needs_open_file = 1;
+  const int use_stdout =
+      (out_file_name != NULL) && !WSTRCMP(out_file_name, "-");
+  int ok = 1;
+
+  if (buffer == NULL || out_file_name == NULL) return 0;
+
+#ifdef HAVE_WINCODEC_H
+  needs_open_file = (format != PNG);
+#endif
+
+  if (needs_open_file) {
+    fout = use_stdout ? ImgIoUtilSetBinaryMode(stdout)
+                      : WFOPEN(out_file_name, "wb");
+    if (fout == NULL) {
+      WFPRINTF(stderr, "Error opening output file %s\n",
+               (const W_CHAR*)out_file_name);
+      return 0;
+    }
+  }
+
+  if (format == PNG ||
+      format == RGBA || format == BGRA || format == ARGB ||
+      format == rgbA || format == bgrA || format == Argb) {
+#ifdef HAVE_WINCODEC_H
+    ok &= WebPWritePNG(out_file_name, use_stdout, buffer);
+#else
+    ok &= WebPWritePNG(fout, buffer);
+#endif
+  } else if (format == PAM) {
+    ok &= WebPWritePAM(fout, buffer);
+  } else if (format == PPM || format == RGB || format == BGR) {
+    ok &= WebPWritePPM(fout, buffer);
+  } else if (format == RGBA_4444 || format == RGB_565 || format == rgbA_4444) {
+    ok &= WebPWrite16bAsPGM(fout, buffer);
+  } else if (format == BMP) {
+    ok &= WebPWriteBMP(fout, buffer);
+  } else if (format == TIFF) {
+    ok &= WebPWriteTIFF(fout, buffer);
+  } else if (format == RAW_YUV) {
+    ok &= WebPWriteYUV(fout, buffer);
+  } else if (format == PGM || format == YUV || format == YUVA) {
+    ok &= WebPWritePGM(fout, buffer);
+  } else if (format == ALPHA_PLANE_ONLY) {
+    ok &= WebPWriteAlphaPlane(fout, buffer);
+  }
+  if (fout != NULL && fout != stdout) {
+    fclose(fout);
+  }
+  return ok;
+}
diff --git a/imageio/image_enc.h b/imageio/image_enc.h
new file mode 100644
index 0000000..d31e4bd
--- /dev/null
+++ b/imageio/image_enc.h
@@ -0,0 +1,96 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  All-in-one library to save PNG/JPEG/WebP/TIFF/WIC images.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_IMAGEIO_IMAGE_ENC_H_
+#define WEBP_IMAGEIO_IMAGE_ENC_H_
+
+#include <stdio.h>
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include "webp/types.h"
+#include "webp/decode.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Output types
+typedef enum {
+  PNG = 0,
+  PAM,
+  PPM,
+  PGM,
+  BMP,
+  TIFF,
+  RAW_YUV,
+  ALPHA_PLANE_ONLY,  // this is for experimenting only
+  // forced colorspace output (for testing, mostly)
+  RGB, RGBA, BGR, BGRA, ARGB,
+  RGBA_4444, RGB_565,
+  rgbA, bgrA, Argb, rgbA_4444,
+  YUV, YUVA
+} WebPOutputFileFormat;
+
+// General all-purpose call.
+// Most formats expect a 'buffer' containing RGBA-like samples, except
+// RAW_YUV, YUV and YUVA formats.
+// If 'out_file_name' is "-", data is saved to stdout.
+// Returns false if an error occurred, true otherwise.
+int WebPSaveImage(const WebPDecBuffer* const buffer,
+                  WebPOutputFileFormat format, const char* const out_file_name);
+
+// Save to PNG.
+#ifdef HAVE_WINCODEC_H
+int WebPWritePNG(const char* out_file_name, int use_stdout,
+                 const struct WebPDecBuffer* const buffer);
+#else
+int WebPWritePNG(FILE* out_file, const WebPDecBuffer* const buffer);
+#endif
+
+// Save to PPM format (RGB, no alpha)
+int WebPWritePPM(FILE* fout, const struct WebPDecBuffer* const buffer);
+
+// Save to PAM format (= PPM + alpha)
+int WebPWritePAM(FILE* fout, const struct WebPDecBuffer* const buffer);
+
+// Save 16b mode (RGBA4444, RGB565, ...) for debugging purposes.
+int WebPWrite16bAsPGM(FILE* fout, const struct WebPDecBuffer* const buffer);
+
+// Save as BMP
+int WebPWriteBMP(FILE* fout, const struct WebPDecBuffer* const buffer);
+
+// Save as TIFF
+int WebPWriteTIFF(FILE* fout, const struct WebPDecBuffer* const buffer);
+
+// Save the ALPHA plane (only) as a PGM
+int WebPWriteAlphaPlane(FILE* fout, const struct WebPDecBuffer* const buffer);
+
+// Save as YUV samples as PGM format (using IMC4 layout).
+// See: https://www.fourcc.org/yuv.php#IMC4.
+// (very convenient format for viewing the samples, esp. for odd dimensions).
+int WebPWritePGM(FILE* fout, const struct WebPDecBuffer* const buffer);
+
+// Save YUV(A) planes sequentially (raw dump)
+int WebPWriteYUV(FILE* fout, const struct WebPDecBuffer* const buffer);
+
+// Save 16b mode (RGBA4444, RGB565, ...) as PGM format, for debugging purposes.
+int WebPWrite16bAsPGM(FILE* fout, const struct WebPDecBuffer* const buffer);
+
+#ifdef __cplusplus
+}    // extern "C"
+#endif
+
+#endif  // WEBP_IMAGEIO_IMAGE_ENC_H_
diff --git a/imageio/imageio_util.c b/imageio/imageio_util.c
new file mode 100644
index 0000000..df37137
--- /dev/null
+++ b/imageio/imageio_util.c
@@ -0,0 +1,162 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  Utility functions used by the image decoders.
+//
+
+#include "./imageio_util.h"
+
+#if defined(_WIN32)
+#include <fcntl.h>   // for _O_BINARY
+#include <io.h>      // for _setmode()
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include "../examples/unicode.h"
+
+// -----------------------------------------------------------------------------
+// File I/O
+
+FILE* ImgIoUtilSetBinaryMode(FILE* file) {
+#if defined(_WIN32)
+  if (_setmode(_fileno(file), _O_BINARY) == -1) {
+    fprintf(stderr, "Failed to reopen file in O_BINARY mode.\n");
+    return NULL;
+  }
+#endif
+  return file;
+}
+
+int ImgIoUtilReadFromStdin(const uint8_t** data, size_t* data_size) {
+  static const size_t kBlockSize = 16384;  // default initial size
+  size_t max_size = 0;
+  size_t size = 0;
+  uint8_t* input = NULL;
+
+  if (data == NULL || data_size == NULL) return 0;
+  *data = NULL;
+  *data_size = 0;
+
+  if (!ImgIoUtilSetBinaryMode(stdin)) return 0;
+
+  while (!feof(stdin)) {
+    // We double the buffer size each time and read as much as possible.
+    const size_t extra_size = (max_size == 0) ? kBlockSize : max_size;
+    // we allocate one extra byte for the \0 terminator
+    void* const new_data = realloc(input, max_size + extra_size + 1);
+    if (new_data == NULL) goto Error;
+    input = (uint8_t*)new_data;
+    max_size += extra_size;
+    size += fread(input + size, 1, extra_size, stdin);
+    if (size < max_size) break;
+  }
+  if (ferror(stdin)) goto Error;
+  if (input != NULL) input[size] = '\0';  // convenient 0-terminator
+  *data = input;
+  *data_size = size;
+  return 1;
+
+ Error:
+  free(input);
+  fprintf(stderr, "Could not read from stdin\n");
+  return 0;
+}
+
+int ImgIoUtilReadFile(const char* const file_name,
+                      const uint8_t** data, size_t* data_size) {
+  int ok;
+  uint8_t* file_data;
+  size_t file_size;
+  FILE* in;
+  const int from_stdin = (file_name == NULL) || !WSTRCMP(file_name, "-");
+
+  if (from_stdin) return ImgIoUtilReadFromStdin(data, data_size);
+
+  if (data == NULL || data_size == NULL) return 0;
+  *data = NULL;
+  *data_size = 0;
+
+  in = WFOPEN(file_name, "rb");
+  if (in == NULL) {
+    WFPRINTF(stderr, "cannot open input file '%s'\n", (const W_CHAR*)file_name);
+    return 0;
+  }
+  fseek(in, 0, SEEK_END);
+  file_size = ftell(in);
+  fseek(in, 0, SEEK_SET);
+  // we allocate one extra byte for the \0 terminator
+  file_data = (uint8_t*)WebPMalloc(file_size + 1);
+  if (file_data == NULL) {
+    fclose(in);
+    WFPRINTF(stderr, "memory allocation failure when reading file %s\n",
+             (const W_CHAR*)file_name);
+    return 0;
+  }
+  ok = (fread(file_data, file_size, 1, in) == 1);
+  fclose(in);
+
+  if (!ok) {
+    WFPRINTF(stderr, "Could not read %d bytes of data from file %s\n",
+             (int)file_size, (const W_CHAR*)file_name);
+    WebPFree(file_data);
+    return 0;
+  }
+  file_data[file_size] = '\0';  // convenient 0-terminator
+  *data = file_data;
+  *data_size = file_size;
+  return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+int ImgIoUtilWriteFile(const char* const file_name,
+                       const uint8_t* data, size_t data_size) {
+  int ok;
+  FILE* out;
+  const int to_stdout = (file_name == NULL) || !WSTRCMP(file_name, "-");
+
+  if (data == NULL) {
+    return 0;
+  }
+  out = to_stdout ? ImgIoUtilSetBinaryMode(stdout) : WFOPEN(file_name, "wb");
+  if (out == NULL) {
+    WFPRINTF(stderr, "Error! Cannot open output file '%s'\n",
+             (const W_CHAR*)file_name);
+    return 0;
+  }
+  ok = (fwrite(data, data_size, 1, out) == 1);
+  if (out != stdout) fclose(out);
+  return ok;
+}
+
+// -----------------------------------------------------------------------------
+
+void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride,
+                        uint8_t* dst, int dst_stride, int width, int height) {
+  while (height-- > 0) {
+    memcpy(dst, src, width * sizeof(*dst));
+    src += src_stride;
+    dst += dst_stride;
+  }
+}
+
+// -----------------------------------------------------------------------------
+
+int ImgIoUtilCheckSizeArgumentsOverflow(uint64_t stride, size_t height) {
+  const uint64_t total_size = stride * height;
+  int ok = (total_size == (size_t)total_size);
+  // check that 'stride' is representable as int:
+  ok = ok && ((uint64_t)(int)stride == stride);
+#if defined(WEBP_MAX_IMAGE_SIZE)
+  ok = ok && (total_size <= (uint64_t)WEBP_MAX_IMAGE_SIZE);
+#endif
+  return ok;
+}
+
+// -----------------------------------------------------------------------------
diff --git a/imageio/imageio_util.h b/imageio/imageio_util.h
new file mode 100644
index 0000000..f135f56
--- /dev/null
+++ b/imageio/imageio_util.h
@@ -0,0 +1,64 @@
+// Copyright 2016 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  Utility functions used by the image decoders.
+//
+
+#ifndef WEBP_IMAGEIO_IMAGEIO_UTIL_H_
+#define WEBP_IMAGEIO_IMAGEIO_UTIL_H_
+
+#include <stdio.h>
+#include "webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//------------------------------------------------------------------------------
+// File I/O
+
+// Reopen file in binary (O_BINARY) mode.
+// Returns 'file' on success, NULL otherwise.
+FILE* ImgIoUtilSetBinaryMode(FILE* file);
+
+// Allocates storage for entire file 'file_name' and returns contents and size
+// in 'data' and 'data_size'. Returns 1 on success, 0 otherwise. '*data' should
+// be deleted using WebPFree().
+// Note: for convenience, the data will be null-terminated with an extra byte
+// (not accounted for in *data_size), in case the file is text and intended
+// to be used as a C-string.
+// If 'file_name' is NULL or equal to "-", input is read from stdin by calling
+// the function ImgIoUtilReadFromStdin().
+int ImgIoUtilReadFile(const char* const file_name,
+                      const uint8_t** data, size_t* data_size);
+
+// Same as ImgIoUtilReadFile(), but reads until EOF from stdin instead.
+int ImgIoUtilReadFromStdin(const uint8_t** data, size_t* data_size);
+
+// Write a data segment into a file named 'file_name'. Returns true if ok.
+// If 'file_name' is NULL or equal to "-", output is written to stdout.
+int ImgIoUtilWriteFile(const char* const file_name,
+                       const uint8_t* data, size_t data_size);
+
+//------------------------------------------------------------------------------
+
+// Copy width x height pixels from 'src' to 'dst' honoring the strides.
+void ImgIoUtilCopyPlane(const uint8_t* src, int src_stride,
+                        uint8_t* dst, int dst_stride, int width, int height);
+
+//------------------------------------------------------------------------------
+
+// Returns 0 in case of overflow, memory over-allocation or excessive dimension.
+int ImgIoUtilCheckSizeArgumentsOverflow(uint64_t stride, size_t height);
+
+#ifdef __cplusplus
+}    // extern "C"
+#endif
+
+#endif  // WEBP_IMAGEIO_IMAGEIO_UTIL_H_
diff --git a/imageio/jpegdec.c b/imageio/jpegdec.c
new file mode 100644
index 0000000..74a4c09
--- /dev/null
+++ b/imageio/jpegdec.c
@@ -0,0 +1,364 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// JPEG decode.
+
+#include "./jpegdec.h"
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include <stdio.h>
+
+#ifdef WEBP_HAVE_JPEG
+#include <jpeglib.h>
+#include <jerror.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "webp/encode.h"
+#include "./imageio_util.h"
+#include "./metadata.h"
+
+// -----------------------------------------------------------------------------
+// Metadata processing
+
+#ifndef JPEG_APP1
+# define JPEG_APP1 (JPEG_APP0 + 1)
+#endif
+#ifndef JPEG_APP2
+# define JPEG_APP2 (JPEG_APP0 + 2)
+#endif
+
+typedef struct {
+  const uint8_t* data;
+  size_t data_length;
+  int seq;  // this segment's sequence number [1, 255] for use in reassembly.
+} ICCPSegment;
+
+static void SaveMetadataMarkers(j_decompress_ptr dinfo) {
+  const unsigned int max_marker_length = 0xffff;
+  jpeg_save_markers(dinfo, JPEG_APP1, max_marker_length);  // Exif/XMP
+  jpeg_save_markers(dinfo, JPEG_APP2, max_marker_length);  // ICC profile
+}
+
+static int CompareICCPSegments(const void* a, const void* b) {
+  const ICCPSegment* s1 = (const ICCPSegment*)a;
+  const ICCPSegment* s2 = (const ICCPSegment*)b;
+  return s1->seq - s2->seq;
+}
+
+// Extract ICC profile segments from the marker list in 'dinfo', reassembling
+// and storing them in 'iccp'.
+// Returns true on success and false for memory errors and corrupt profiles.
+static int StoreICCP(j_decompress_ptr dinfo, MetadataPayload* const iccp) {
+  // ICC.1:2010-12 (4.3.0.0) Annex B.4 Embedding ICC Profiles in JPEG files
+  static const char kICCPSignature[] = "ICC_PROFILE";
+  static const size_t kICCPSignatureLength = 12;  // signature includes '\0'
+  static const size_t kICCPSkipLength = 14;  // signature + seq & count
+  int expected_count = 0;
+  int actual_count = 0;
+  int seq_max = 0;
+  size_t total_size = 0;
+  ICCPSegment iccp_segments[255];
+  jpeg_saved_marker_ptr marker;
+
+  memset(iccp_segments, 0, sizeof(iccp_segments));
+  for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) {
+    if (marker->marker == JPEG_APP2 &&
+        marker->data_length > kICCPSkipLength &&
+        !memcmp(marker->data, kICCPSignature, kICCPSignatureLength)) {
+      // ICC_PROFILE\0<seq><count>; 'seq' starts at 1.
+      const int seq = marker->data[kICCPSignatureLength];
+      const int count = marker->data[kICCPSignatureLength + 1];
+      const size_t segment_size = marker->data_length - kICCPSkipLength;
+      ICCPSegment* segment;
+
+      if (segment_size == 0 || count == 0 || seq == 0) {
+        fprintf(stderr, "[ICCP] size (%d) / count (%d) / sequence number (%d)"
+                        " cannot be 0!\n",
+                (int)segment_size, seq, count);
+        return 0;
+      }
+
+      if (expected_count == 0) {
+        expected_count = count;
+      } else if (expected_count != count) {
+        fprintf(stderr, "[ICCP] Inconsistent segment count (%d / %d)!\n",
+                expected_count, count);
+        return 0;
+      }
+
+      segment = iccp_segments + seq - 1;
+      if (segment->data_length != 0) {
+        fprintf(stderr, "[ICCP] Duplicate segment number (%d)!\n" , seq);
+        return 0;
+      }
+
+      segment->data = marker->data + kICCPSkipLength;
+      segment->data_length = segment_size;
+      segment->seq = seq;
+      total_size += segment_size;
+      if (seq > seq_max) seq_max = seq;
+      ++actual_count;
+    }
+  }
+
+  if (actual_count == 0) return 1;
+  if (seq_max != actual_count) {
+    fprintf(stderr, "[ICCP] Discontinuous segments, expected: %d actual: %d!\n",
+            actual_count, seq_max);
+    return 0;
+  }
+  if (expected_count != actual_count) {
+    fprintf(stderr, "[ICCP] Segment count: %d does not match expected: %d!\n",
+            actual_count, expected_count);
+    return 0;
+  }
+
+  // The segments may appear out of order in the file, sort them based on
+  // sequence number before assembling the payload.
+  qsort(iccp_segments, actual_count, sizeof(*iccp_segments),
+        CompareICCPSegments);
+
+  iccp->bytes = (uint8_t*)malloc(total_size);
+  if (iccp->bytes == NULL) return 0;
+  iccp->size = total_size;
+
+  {
+    int i;
+    size_t offset = 0;
+    for (i = 0; i < seq_max; ++i) {
+      memcpy(iccp->bytes + offset,
+             iccp_segments[i].data, iccp_segments[i].data_length);
+      offset += iccp_segments[i].data_length;
+    }
+  }
+  return 1;
+}
+
+// Returns true on success and false for memory errors and corrupt profiles.
+// The caller must use MetadataFree() on 'metadata' in all cases.
+static int ExtractMetadataFromJPEG(j_decompress_ptr dinfo,
+                                   Metadata* const metadata) {
+  static const struct {
+    int marker;
+    const char* signature;
+    size_t signature_length;
+    size_t storage_offset;
+  } kJPEGMetadataMap[] = {
+    // Exif 2.2 Section 4.7.2 Interoperability Structure of APP1 ...
+    { JPEG_APP1, "Exif\0",                        6, METADATA_OFFSET(exif) },
+    // XMP Specification Part 3 Section 3 Embedding XMP Metadata ... #JPEG
+    // TODO(jzern) Add support for 'ExtendedXMP'
+    { JPEG_APP1, "http://ns.adobe.com/xap/1.0/", 29, METADATA_OFFSET(xmp) },
+    { 0, NULL, 0, 0 },
+  };
+  jpeg_saved_marker_ptr marker;
+  // Treat ICC profiles separately as they may be segmented and out of order.
+  if (!StoreICCP(dinfo, &metadata->iccp)) return 0;
+
+  for (marker = dinfo->marker_list; marker != NULL; marker = marker->next) {
+    int i;
+    for (i = 0; kJPEGMetadataMap[i].marker != 0; ++i) {
+      if (marker->marker == kJPEGMetadataMap[i].marker &&
+          marker->data_length > kJPEGMetadataMap[i].signature_length &&
+          !memcmp(marker->data, kJPEGMetadataMap[i].signature,
+                  kJPEGMetadataMap[i].signature_length)) {
+        MetadataPayload* const payload =
+            (MetadataPayload*)((uint8_t*)metadata +
+                               kJPEGMetadataMap[i].storage_offset);
+
+        if (payload->bytes == NULL) {
+          const char* marker_data = (const char*)marker->data +
+                                    kJPEGMetadataMap[i].signature_length;
+          const size_t marker_data_length =
+              marker->data_length - kJPEGMetadataMap[i].signature_length;
+          if (!MetadataCopy(marker_data, marker_data_length, payload)) return 0;
+        } else {
+          fprintf(stderr, "Ignoring additional '%s' marker\n",
+                  kJPEGMetadataMap[i].signature);
+        }
+      }
+    }
+  }
+  return 1;
+}
+
+#undef JPEG_APP1
+#undef JPEG_APP2
+
+// -----------------------------------------------------------------------------
+// JPEG decoding
+
+struct my_error_mgr {
+  struct jpeg_error_mgr pub;
+  jmp_buf setjmp_buffer;
+};
+
+static void my_error_exit(j_common_ptr dinfo) {
+  struct my_error_mgr* myerr = (struct my_error_mgr*)dinfo->err;
+  fprintf(stderr, "libjpeg error: ");
+  dinfo->err->output_message(dinfo);
+  longjmp(myerr->setjmp_buffer, 1);
+}
+
+typedef struct {
+  struct jpeg_source_mgr pub;
+  const uint8_t* data;
+  size_t data_size;
+} JPEGReadContext;
+
+static void ContextInit(j_decompress_ptr cinfo) {
+  JPEGReadContext* const ctx = (JPEGReadContext*)cinfo->src;
+  ctx->pub.next_input_byte = ctx->data;
+  ctx->pub.bytes_in_buffer = ctx->data_size;
+}
+
+static boolean ContextFill(j_decompress_ptr cinfo) {
+  // we shouldn't get here.
+  ERREXIT(cinfo, JERR_FILE_READ);
+  return FALSE;
+}
+
+static void ContextSkip(j_decompress_ptr cinfo, long jump_size) {
+  JPEGReadContext* const ctx = (JPEGReadContext*)cinfo->src;
+  size_t jump = (size_t)jump_size;
+  if (jump > ctx->pub.bytes_in_buffer) {  // Don't overflow the buffer.
+    jump = ctx->pub.bytes_in_buffer;
+  }
+  ctx->pub.bytes_in_buffer -= jump;
+  ctx->pub.next_input_byte += jump;
+}
+
+static void ContextTerm(j_decompress_ptr cinfo) {
+  (void)cinfo;
+}
+
+static void ContextSetup(volatile struct jpeg_decompress_struct* const cinfo,
+                         JPEGReadContext* const ctx) {
+  cinfo->src = (struct jpeg_source_mgr*)ctx;
+  ctx->pub.init_source = ContextInit;
+  ctx->pub.fill_input_buffer = ContextFill;
+  ctx->pub.skip_input_data = ContextSkip;
+  ctx->pub.resync_to_restart = jpeg_resync_to_restart;
+  ctx->pub.term_source = ContextTerm;
+  ctx->pub.bytes_in_buffer = 0;
+  ctx->pub.next_input_byte = NULL;
+}
+
+int ReadJPEG(const uint8_t* const data, size_t data_size,
+             WebPPicture* const pic, int keep_alpha,
+             Metadata* const metadata) {
+  volatile int ok = 0;
+  int width, height;
+  int64_t stride;
+  volatile struct jpeg_decompress_struct dinfo;
+  struct my_error_mgr jerr;
+  uint8_t* volatile rgb = NULL;
+  JSAMPROW buffer[1];
+  JPEGReadContext ctx;
+
+  if (data == NULL || data_size == 0 || pic == NULL) return 0;
+
+  (void)keep_alpha;
+  memset(&ctx, 0, sizeof(ctx));
+  ctx.data = data;
+  ctx.data_size = data_size;
+
+  memset((j_decompress_ptr)&dinfo, 0, sizeof(dinfo));   // for setjmp safety
+  dinfo.err = jpeg_std_error(&jerr.pub);
+  jerr.pub.error_exit = my_error_exit;
+
+  if (setjmp(jerr.setjmp_buffer)) {
+ Error:
+    MetadataFree(metadata);
+    jpeg_destroy_decompress((j_decompress_ptr)&dinfo);
+    goto End;
+  }
+
+  jpeg_create_decompress((j_decompress_ptr)&dinfo);
+  ContextSetup(&dinfo, &ctx);
+  if (metadata != NULL) SaveMetadataMarkers((j_decompress_ptr)&dinfo);
+  jpeg_read_header((j_decompress_ptr)&dinfo, TRUE);
+
+  dinfo.out_color_space = JCS_RGB;
+  dinfo.do_fancy_upsampling = TRUE;
+
+  jpeg_start_decompress((j_decompress_ptr)&dinfo);
+
+  if (dinfo.output_components != 3) {
+    goto Error;
+  }
+
+  width = dinfo.output_width;
+  height = dinfo.output_height;
+  stride = (int64_t)dinfo.output_width * dinfo.output_components * sizeof(*rgb);
+
+  if (stride != (int)stride ||
+      !ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
+    goto Error;
+  }
+
+  rgb = (uint8_t*)malloc((size_t)stride * height);
+  if (rgb == NULL) {
+    goto Error;
+  }
+  buffer[0] = (JSAMPLE*)rgb;
+
+  while (dinfo.output_scanline < dinfo.output_height) {
+    if (jpeg_read_scanlines((j_decompress_ptr)&dinfo, buffer, 1) != 1) {
+      goto Error;
+    }
+    buffer[0] += stride;
+  }
+
+  if (metadata != NULL) {
+    ok = ExtractMetadataFromJPEG((j_decompress_ptr)&dinfo, metadata);
+    if (!ok) {
+      fprintf(stderr, "Error extracting JPEG metadata!\n");
+      goto Error;
+    }
+  }
+
+  jpeg_finish_decompress((j_decompress_ptr)&dinfo);
+  jpeg_destroy_decompress((j_decompress_ptr)&dinfo);
+
+  // WebP conversion.
+  pic->width = width;
+  pic->height = height;
+  ok = WebPPictureImportRGB(pic, rgb, (int)stride);
+  if (!ok) {
+    pic->width = 0;   // WebPPictureImportRGB() barely touches 'pic' on failure.
+    pic->height = 0;  // Just reset dimensions but keep any 'custom_ptr' etc.
+    MetadataFree(metadata);  // In case the caller forgets to free it on error.
+  }
+
+ End:
+  free(rgb);
+  return ok;
+}
+#else  // !WEBP_HAVE_JPEG
+int ReadJPEG(const uint8_t* const data, size_t data_size,
+             struct WebPPicture* const pic, int keep_alpha,
+             struct Metadata* const metadata) {
+  (void)data;
+  (void)data_size;
+  (void)pic;
+  (void)keep_alpha;
+  (void)metadata;
+  fprintf(stderr, "JPEG support not compiled. Please install the libjpeg "
+          "development package before building.\n");
+  return 0;
+}
+#endif  // WEBP_HAVE_JPEG
+
+// -----------------------------------------------------------------------------
diff --git a/imageio/jpegdec.h b/imageio/jpegdec.h
new file mode 100644
index 0000000..effc14f
--- /dev/null
+++ b/imageio/jpegdec.h
@@ -0,0 +1,37 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// JPEG decode.
+
+#ifndef WEBP_IMAGEIO_JPEGDEC_H_
+#define WEBP_IMAGEIO_JPEGDEC_H_
+
+#include "webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Metadata;
+struct WebPPicture;
+
+// Reads a JPEG from 'data', returning the decoded output in 'pic'.
+// The output is RGB or YUV depending on pic->use_argb value.
+// Returns true on success.
+// 'keep_alpha' has no effect, but is kept for coherence with other signatures
+// for image readers.
+int ReadJPEG(const uint8_t* const data, size_t data_size,
+             struct WebPPicture* const pic, int keep_alpha,
+             struct Metadata* const metadata);
+
+#ifdef __cplusplus
+}    // extern "C"
+#endif
+
+#endif  // WEBP_IMAGEIO_JPEGDEC_H_
diff --git a/imageio/metadata.c b/imageio/metadata.c
new file mode 100644
index 0000000..936f2f4
--- /dev/null
+++ b/imageio/metadata.c
@@ -0,0 +1,49 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  Metadata types and functions.
+//
+
+#include "./metadata.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "webp/types.h"
+
+void MetadataInit(Metadata* const metadata) {
+  if (metadata == NULL) return;
+  memset(metadata, 0, sizeof(*metadata));
+}
+
+void MetadataPayloadDelete(MetadataPayload* const payload) {
+  if (payload == NULL) return;
+  free(payload->bytes);
+  payload->bytes = NULL;
+  payload->size = 0;
+}
+
+void MetadataFree(Metadata* const metadata) {
+  if (metadata == NULL) return;
+  MetadataPayloadDelete(&metadata->exif);
+  MetadataPayloadDelete(&metadata->iccp);
+  MetadataPayloadDelete(&metadata->xmp);
+}
+
+int MetadataCopy(const char* metadata, size_t metadata_len,
+                 MetadataPayload* const payload) {
+  if (metadata == NULL || metadata_len == 0 || payload == NULL) return 0;
+  payload->bytes = (uint8_t*)malloc(metadata_len);
+  if (payload->bytes == NULL) return 0;
+  payload->size = metadata_len;
+  memcpy(payload->bytes, metadata, metadata_len);
+  return 1;
+}
+
+// -----------------------------------------------------------------------------
diff --git a/imageio/metadata.h b/imageio/metadata.h
new file mode 100644
index 0000000..1d5be91
--- /dev/null
+++ b/imageio/metadata.h
@@ -0,0 +1,47 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//  Metadata types and functions.
+//
+
+#ifndef WEBP_IMAGEIO_METADATA_H_
+#define WEBP_IMAGEIO_METADATA_H_
+
+#include "webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct MetadataPayload {
+  uint8_t* bytes;
+  size_t size;
+} MetadataPayload;
+
+typedef struct Metadata {
+  MetadataPayload exif;
+  MetadataPayload iccp;
+  MetadataPayload xmp;
+} Metadata;
+
+#define METADATA_OFFSET(x) offsetof(Metadata, x)
+
+void MetadataInit(Metadata* const metadata);
+void MetadataPayloadDelete(MetadataPayload* const payload);
+void MetadataFree(Metadata* const metadata);
+
+// Stores 'metadata' to 'payload->bytes', returns false on allocation error.
+int MetadataCopy(const char* metadata, size_t metadata_len,
+                 MetadataPayload* const payload);
+
+#ifdef __cplusplus
+}    // extern "C"
+#endif
+
+#endif  // WEBP_IMAGEIO_METADATA_H_
diff --git a/imageio/pngdec.c b/imageio/pngdec.c
new file mode 100644
index 0000000..6a1543b
--- /dev/null
+++ b/imageio/pngdec.c
@@ -0,0 +1,374 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// PNG decode.
+
+#include "./pngdec.h"
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include <stdio.h>
+
+#ifdef WEBP_HAVE_PNG
+#ifndef PNG_USER_MEM_SUPPORTED
+#define PNG_USER_MEM_SUPPORTED  // for png_create_read_struct_2
+#endif
+#include <png.h>
+#include <setjmp.h>   // note: this must be included *after* png.h
+#include <stdlib.h>
+#include <string.h>
+
+#include "webp/encode.h"
+#include "./imageio_util.h"
+#include "./metadata.h"
+
+#define LOCAL_PNG_VERSION ((PNG_LIBPNG_VER_MAJOR << 8) | PNG_LIBPNG_VER_MINOR)
+#define LOCAL_PNG_PREREQ(maj, min) \
+   (LOCAL_PNG_VERSION >= (((maj) << 8) | (min)))
+
+static void PNGAPI error_function(png_structp png, png_const_charp error) {
+  if (error != NULL) fprintf(stderr, "libpng error: %s\n", error);
+  longjmp(png_jmpbuf(png), 1);
+}
+
+#if LOCAL_PNG_PREREQ(1,4)
+typedef png_alloc_size_t LocalPngAllocSize;
+#else
+typedef png_size_t LocalPngAllocSize;
+#endif
+
+static png_voidp MallocFunc(png_structp png_ptr, LocalPngAllocSize size) {
+  (void)png_ptr;
+  if (size != (size_t)size) return NULL;
+  if (!ImgIoUtilCheckSizeArgumentsOverflow(size, 1)) return NULL;
+  return (png_voidp)malloc((size_t)size);
+}
+
+static void FreeFunc(png_structp png_ptr, png_voidp ptr) {
+  (void)png_ptr;
+  free(ptr);
+}
+
+// Converts the NULL terminated 'hexstring' which contains 2-byte character
+// representations of hex values to raw data.
+// 'hexstring' may contain values consisting of [A-F][a-f][0-9] in pairs,
+// e.g., 7af2..., separated by any number of newlines.
+// 'expected_length' is the anticipated processed size.
+// On success the raw buffer is returned with its length equivalent to
+// 'expected_length'. NULL is returned if the processed length is less than
+// 'expected_length' or any character aside from those above is encountered.
+// The returned buffer must be freed by the caller.
+static uint8_t* HexStringToBytes(const char* hexstring,
+                                 size_t expected_length) {
+  const char* src = hexstring;
+  size_t actual_length = 0;
+  uint8_t* const raw_data = (uint8_t*)malloc(expected_length);
+  uint8_t* dst;
+
+  if (raw_data == NULL) return NULL;
+
+  for (dst = raw_data; actual_length < expected_length && *src != '\0'; ++src) {
+    char* end;
+    char val[3];
+    if (*src == '\n') continue;
+    val[0] = *src++;
+    val[1] = *src;
+    val[2] = '\0';
+    *dst++ = (uint8_t)strtol(val, &end, 16);
+    if (end != val + 2) break;
+    ++actual_length;
+  }
+
+  if (actual_length != expected_length) {
+    free(raw_data);
+    return NULL;
+  }
+  return raw_data;
+}
+
+static int ProcessRawProfile(const char* profile, size_t profile_len,
+                             MetadataPayload* const payload) {
+  const char* src = profile;
+  char* end;
+  int expected_length;
+
+  if (profile == NULL || profile_len == 0) return 0;
+
+  // ImageMagick formats 'raw profiles' as
+  // '\n<name>\n<length>(%8lu)\n<hex payload>\n'.
+  if (*src != '\n') {
+    fprintf(stderr, "Malformed raw profile, expected '\\n' got '\\x%.2X'\n",
+            *src);
+    return 0;
+  }
+  ++src;
+  // skip the profile name and extract the length.
+  while (*src != '\0' && *src++ != '\n') {}
+  expected_length = (int)strtol(src, &end, 10);
+  if (*end != '\n') {
+    fprintf(stderr, "Malformed raw profile, expected '\\n' got '\\x%.2X'\n",
+            *end);
+    return 0;
+  }
+  ++end;
+
+  // 'end' now points to the profile payload.
+  payload->bytes = HexStringToBytes(end, expected_length);
+  if (payload->bytes == NULL) return 0;
+  payload->size = expected_length;
+  return 1;
+}
+
+static const struct {
+  const char* name;
+  int (*process)(const char* profile, size_t profile_len,
+                 MetadataPayload* const payload);
+  size_t storage_offset;
+} kPNGMetadataMap[] = {
+  // https://exiftool.org/TagNames/PNG.html#TextualData
+  // See also: ExifTool on CPAN.
+  { "Raw profile type exif", ProcessRawProfile, METADATA_OFFSET(exif) },
+  { "Raw profile type xmp",  ProcessRawProfile, METADATA_OFFSET(xmp) },
+  // Exiftool puts exif data in APP1 chunk, too.
+  { "Raw profile type APP1", ProcessRawProfile, METADATA_OFFSET(exif) },
+  // XMP Specification Part 3, Section 3 #PNG
+  { "XML:com.adobe.xmp",     MetadataCopy,      METADATA_OFFSET(xmp) },
+  { NULL, NULL, 0 },
+};
+
+// Looks for metadata at both the beginning and end of the PNG file, giving
+// preference to the head.
+// Returns true on success. The caller must use MetadataFree() on 'metadata' in
+// all cases.
+static int ExtractMetadataFromPNG(png_structp png,
+                                  png_infop const head_info,
+                                  png_infop const end_info,
+                                  Metadata* const metadata) {
+  int p;
+
+  for (p = 0; p < 2; ++p)  {
+    png_infop const info = (p == 0) ? head_info : end_info;
+    png_textp text = NULL;
+    const png_uint_32 num = png_get_text(png, info, &text, NULL);
+    png_uint_32 i;
+    // Look for EXIF / XMP metadata.
+    for (i = 0; i < num; ++i, ++text) {
+      int j;
+      for (j = 0; kPNGMetadataMap[j].name != NULL; ++j) {
+        if (!strcmp(text->key, kPNGMetadataMap[j].name)) {
+          MetadataPayload* const payload =
+              (MetadataPayload*)((uint8_t*)metadata +
+                                 kPNGMetadataMap[j].storage_offset);
+          png_size_t text_length;
+          switch (text->compression) {
+#ifdef PNG_iTXt_SUPPORTED
+            case PNG_ITXT_COMPRESSION_NONE:
+            case PNG_ITXT_COMPRESSION_zTXt:
+              text_length = text->itxt_length;
+              break;
+#endif
+            case PNG_TEXT_COMPRESSION_NONE:
+            case PNG_TEXT_COMPRESSION_zTXt:
+            default:
+              text_length = text->text_length;
+              break;
+          }
+          if (payload->bytes != NULL) {
+            fprintf(stderr, "Ignoring additional '%s'\n", text->key);
+          } else if (!kPNGMetadataMap[j].process(text->text, text_length,
+                                                 payload)) {
+            fprintf(stderr, "Failed to process: '%s'\n", text->key);
+            return 0;
+          }
+          break;
+        }
+      }
+    }
+    // Look for an ICC profile.
+    {
+      png_charp name;
+      int comp_type;
+#if LOCAL_PNG_PREREQ(1,5)
+      png_bytep profile;
+#else
+      png_charp profile;
+#endif
+      png_uint_32 len;
+
+      if (png_get_iCCP(png, info,
+                       &name, &comp_type, &profile, &len) == PNG_INFO_iCCP) {
+        if (!MetadataCopy((const char*)profile, len, &metadata->iccp)) return 0;
+      }
+    }
+  }
+  return 1;
+}
+
+typedef struct {
+  const uint8_t* data;
+  size_t data_size;
+  png_size_t offset;
+} PNGReadContext;
+
+static void ReadFunc(png_structp png_ptr, png_bytep data, png_size_t length) {
+  PNGReadContext* const ctx = (PNGReadContext*)png_get_io_ptr(png_ptr);
+  if (ctx->data_size - ctx->offset < length) {
+    png_error(png_ptr, "ReadFunc: invalid read length (overflow)!");
+  }
+  memcpy(data, ctx->data + ctx->offset, length);
+  ctx->offset += length;
+}
+
+int ReadPNG(const uint8_t* const data, size_t data_size,
+            struct WebPPicture* const pic,
+            int keep_alpha, struct Metadata* const metadata) {
+  volatile png_structp png = NULL;
+  volatile png_infop info = NULL;
+  volatile png_infop end_info = NULL;
+  PNGReadContext context = { NULL, 0, 0 };
+  int color_type, bit_depth, interlaced;
+  int has_alpha;
+  int num_passes;
+  int p;
+  volatile int ok = 0;
+  png_uint_32 width, height, y;
+  int64_t stride;
+  uint8_t* volatile rgb = NULL;
+
+  if (data == NULL || data_size == 0 || pic == NULL) return 0;
+
+  context.data = data;
+  context.data_size = data_size;
+
+  png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL,
+                                 NULL, MallocFunc, FreeFunc);
+  if (png == NULL) goto End;
+
+  png_set_error_fn(png, 0, error_function, NULL);
+  if (setjmp(png_jmpbuf(png))) {
+ Error:
+    MetadataFree(metadata);
+    goto End;
+  }
+
+#if LOCAL_PNG_PREREQ(1,5) || \
+    (LOCAL_PNG_PREREQ(1,4) && PNG_LIBPNG_VER_RELEASE >= 1)
+  // If it looks like the bitstream is going to need more memory than libpng's
+  // internal limit (default: 8M), try to (reasonably) raise it.
+  if (data_size > png_get_chunk_malloc_max(png) && data_size < (1u << 24)) {
+    png_set_chunk_malloc_max(png, data_size);
+  }
+#endif
+
+  info = png_create_info_struct(png);
+  if (info == NULL) goto Error;
+  end_info = png_create_info_struct(png);
+  if (end_info == NULL) goto Error;
+
+  png_set_read_fn(png, &context, ReadFunc);
+  png_read_info(png, info);
+  if (!png_get_IHDR(png, info,
+                    &width, &height, &bit_depth, &color_type, &interlaced,
+                    NULL, NULL)) goto Error;
+
+  png_set_strip_16(png);
+  png_set_packing(png);
+  if (color_type == PNG_COLOR_TYPE_PALETTE) {
+    png_set_palette_to_rgb(png);
+  }
+  if (color_type == PNG_COLOR_TYPE_GRAY ||
+      color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+    if (bit_depth < 8) {
+      png_set_expand_gray_1_2_4_to_8(png);
+    }
+    png_set_gray_to_rgb(png);
+  }
+  if (png_get_valid(png, info, PNG_INFO_tRNS)) {
+    png_set_tRNS_to_alpha(png);
+    has_alpha = 1;
+  } else {
+    has_alpha = !!(color_type & PNG_COLOR_MASK_ALPHA);
+  }
+
+  // Apply gamma correction if needed.
+  {
+    double image_gamma = 1 / 2.2, screen_gamma = 2.2;
+    int srgb_intent;
+    if (png_get_sRGB(png, info, &srgb_intent) ||
+        png_get_gAMA(png, info, &image_gamma)) {
+      png_set_gamma(png, screen_gamma, image_gamma);
+    }
+  }
+
+  if (!keep_alpha) {
+    png_set_strip_alpha(png);
+    has_alpha = 0;
+  }
+
+  num_passes = png_set_interlace_handling(png);
+  png_read_update_info(png, info);
+
+  stride = (int64_t)(has_alpha ? 4 : 3) * width * sizeof(*rgb);
+  if (stride != (int)stride ||
+      !ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
+    goto Error;
+  }
+
+  rgb = (uint8_t*)malloc((size_t)stride * height);
+  if (rgb == NULL) goto Error;
+  for (p = 0; p < num_passes; ++p) {
+    png_bytep row = rgb;
+    for (y = 0; y < height; ++y) {
+      png_read_rows(png, &row, NULL, 1);
+      row += stride;
+    }
+  }
+  png_read_end(png, end_info);
+
+  if (metadata != NULL &&
+      !ExtractMetadataFromPNG(png, info, end_info, metadata)) {
+    fprintf(stderr, "Error extracting PNG metadata!\n");
+    goto Error;
+  }
+
+  pic->width = (int)width;
+  pic->height = (int)height;
+  ok = has_alpha ? WebPPictureImportRGBA(pic, rgb, (int)stride)
+                 : WebPPictureImportRGB(pic, rgb, (int)stride);
+
+  if (!ok) {
+    goto Error;
+  }
+
+ End:
+  if (png != NULL) {
+    png_destroy_read_struct((png_structpp)&png,
+                            (png_infopp)&info, (png_infopp)&end_info);
+  }
+  free(rgb);
+  return ok;
+}
+#else  // !WEBP_HAVE_PNG
+int ReadPNG(const uint8_t* const data, size_t data_size,
+            struct WebPPicture* const pic,
+            int keep_alpha, struct Metadata* const metadata) {
+  (void)data;
+  (void)data_size;
+  (void)pic;
+  (void)keep_alpha;
+  (void)metadata;
+  fprintf(stderr, "PNG support not compiled. Please install the libpng "
+          "development package before building.\n");
+  return 0;
+}
+#endif  // WEBP_HAVE_PNG
+
+// -----------------------------------------------------------------------------
diff --git a/imageio/pngdec.h b/imageio/pngdec.h
new file mode 100644
index 0000000..e0a6122
--- /dev/null
+++ b/imageio/pngdec.h
@@ -0,0 +1,37 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// PNG decode.
+
+#ifndef WEBP_IMAGEIO_PNGDEC_H_
+#define WEBP_IMAGEIO_PNGDEC_H_
+
+#include "webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Metadata;
+struct WebPPicture;
+
+// Reads a PNG from 'data', returning the decoded output in 'pic'.
+// Output is RGBA or YUVA, depending on pic->use_argb value.
+// If 'keep_alpha' is true and the PNG has an alpha channel, the output is RGBA
+// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV.
+// Returns true on success.
+int ReadPNG(const uint8_t* const data, size_t data_size,
+            struct WebPPicture* const pic,
+            int keep_alpha, struct Metadata* const metadata);
+
+#ifdef __cplusplus
+}    // extern "C"
+#endif
+
+#endif  // WEBP_IMAGEIO_PNGDEC_H_
diff --git a/imageio/pnmdec.c b/imageio/pnmdec.c
new file mode 100644
index 0000000..0d592c3
--- /dev/null
+++ b/imageio/pnmdec.c
@@ -0,0 +1,301 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// (limited) PNM decoder
+
+#include "./pnmdec.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "webp/encode.h"
+#include "./imageio_util.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf _snprintf
+#endif
+
+typedef enum {
+  WIDTH_FLAG      = 1 << 0,
+  HEIGHT_FLAG     = 1 << 1,
+  DEPTH_FLAG      = 1 << 2,
+  MAXVAL_FLAG     = 1 << 3,
+  TUPLE_FLAG      = 1 << 4,
+  ALL_NEEDED_FLAGS = WIDTH_FLAG | HEIGHT_FLAG | DEPTH_FLAG | MAXVAL_FLAG
+} PNMFlags;
+
+typedef struct {
+  const uint8_t* data;
+  size_t data_size;
+  int width, height;
+  int bytes_per_px;
+  int depth;          // 1 (grayscale), 2 (grayscale + alpha), 3 (rgb), 4 (rgba)
+  int max_value;
+  int type;           // 5, 6 or 7
+  int seen_flags;
+} PNMInfo;
+
+// -----------------------------------------------------------------------------
+// PNM decoding
+
+#define MAX_LINE_SIZE 1024
+static const size_t kMinPNMHeaderSize = 3;
+
+static size_t ReadLine(const uint8_t* const data, size_t off, size_t data_size,
+                       char out[MAX_LINE_SIZE + 1], size_t* const out_size) {
+  size_t i = 0;
+  *out_size = 0;
+ redo:
+  for (i = 0; i < MAX_LINE_SIZE && off < data_size; ++i) {
+    out[i] = data[off++];
+    if (out[i] == '\n') break;
+  }
+  if (off < data_size) {
+    if (i == 0) goto redo;         // empty line
+    if (out[0] == '#') goto redo;  // skip comment
+  }
+  out[i] = 0;   // safety sentinel
+  *out_size = i;
+  return off;
+}
+
+static size_t FlagError(const char flag[]) {
+  fprintf(stderr, "PAM header error: flags '%s' already seen.\n", flag);
+  return 0;
+}
+
+// inspired from http://netpbm.sourceforge.net/doc/pam.html
+static size_t ReadPAMFields(PNMInfo* const info, size_t off) {
+  char out[MAX_LINE_SIZE + 1];
+  size_t out_size;
+  int tmp;
+  int expected_depth = -1;
+  assert(info != NULL);
+  while (1) {
+    off = ReadLine(info->data, off, info->data_size, out, &out_size);
+    if (off == 0) return 0;
+    if (sscanf(out, "WIDTH %d", &tmp) == 1) {
+      if (info->seen_flags & WIDTH_FLAG) return FlagError("WIDTH");
+      info->seen_flags |= WIDTH_FLAG;
+      info->width = tmp;
+    } else if (sscanf(out, "HEIGHT %d", &tmp) == 1) {
+      if (info->seen_flags & HEIGHT_FLAG) return FlagError("HEIGHT");
+      info->seen_flags |= HEIGHT_FLAG;
+      info->height = tmp;
+    } else if (sscanf(out, "DEPTH %d", &tmp) == 1) {
+      if (info->seen_flags & DEPTH_FLAG) return FlagError("DEPTH");
+      info->seen_flags |= DEPTH_FLAG;
+      info->depth = tmp;
+    } else if (sscanf(out, "MAXVAL %d", &tmp) == 1) {
+      if (info->seen_flags & MAXVAL_FLAG) return FlagError("MAXVAL");
+      info->seen_flags |= MAXVAL_FLAG;
+      info->max_value = tmp;
+    } else if (!strcmp(out, "TUPLTYPE RGB_ALPHA")) {
+      expected_depth = 4;
+      info->seen_flags |= TUPLE_FLAG;
+    } else if (!strcmp(out, "TUPLTYPE RGB")) {
+      expected_depth = 3;
+      info->seen_flags |= TUPLE_FLAG;
+    } else if (!strcmp(out, "TUPLTYPE GRAYSCALE_ALPHA")) {
+      expected_depth = 2;
+      info->seen_flags |= TUPLE_FLAG;
+    } else if (!strcmp(out, "TUPLTYPE GRAYSCALE")) {
+      expected_depth = 1;
+      info->seen_flags |= TUPLE_FLAG;
+    } else if (!strcmp(out, "ENDHDR")) {
+      break;
+    } else {
+      static const char kEllipsis[] = " ...";
+      const size_t kLen = strlen(kEllipsis) + 1;  // +1 = trailing \0
+      int i;
+      if (out_size > 20) snprintf(out + 20 - kLen, kLen, kEllipsis);
+      for (i = 0; i < (int)strlen(out); ++i) {
+        // isprint() might trigger a "char-subscripts" warning if given a char.
+        if (!isprint((int)out[i])) out[i] = ' ';
+      }
+      fprintf(stderr, "PAM header error: unrecognized entry [%s]\n", out);
+      return 0;
+    }
+  }
+  if (!(info->seen_flags & ALL_NEEDED_FLAGS)) {
+    fprintf(stderr, "PAM header error: missing tags%s%s%s%s\n",
+            (info->seen_flags & WIDTH_FLAG) ? "" : " WIDTH",
+            (info->seen_flags & HEIGHT_FLAG) ? "" : " HEIGHT",
+            (info->seen_flags & DEPTH_FLAG) ? "" : " DEPTH",
+            (info->seen_flags & MAXVAL_FLAG) ? "" : " MAXVAL");
+    return 0;
+  }
+  if (expected_depth != -1 && info->depth != expected_depth) {
+    fprintf(stderr, "PAM header error: expected DEPTH %d but got DEPTH %d\n",
+            expected_depth, info->depth);
+    return 0;
+  }
+  return off;
+}
+
+static size_t ReadHeader(PNMInfo* const info) {
+  size_t off = 0;
+  char out[MAX_LINE_SIZE + 1];
+  size_t out_size;
+  if (info == NULL) return 0;
+  if (info->data == NULL || info->data_size < kMinPNMHeaderSize) return 0;
+
+  info->width = info->height = 0;
+  info->type = -1;
+  info->seen_flags = 0;
+  info->bytes_per_px = 0;
+  info->depth = 0;
+  info->max_value = 0;
+
+  off = ReadLine(info->data, off, info->data_size, out, &out_size);
+  if (off == 0 || sscanf(out, "P%d", &info->type) != 1) return 0;
+  if (info->type == 7) {
+    off = ReadPAMFields(info, off);
+  } else {
+    off = ReadLine(info->data, off, info->data_size, out, &out_size);
+    if (off == 0 || sscanf(out, "%d %d", &info->width, &info->height) != 2) {
+      return 0;
+    }
+    off = ReadLine(info->data, off, info->data_size, out, &out_size);
+    if (off == 0 || sscanf(out, "%d", &info->max_value) != 1) return 0;
+
+    // finish initializing missing fields
+    info->depth = (info->type == 5) ? 1 : 3;
+  }
+  // perform some basic numerical validation
+  if (info->width <= 0 || info->height <= 0 ||
+      info->type <= 0 || info->type >= 9 ||
+      info->depth <= 0 || info->depth > 4 ||
+      info->max_value <= 0 || info->max_value >= 65536) {
+    return 0;
+  }
+  info->bytes_per_px = info->depth * (info->max_value > 255 ? 2 : 1);
+  return off;
+}
+
+int ReadPNM(const uint8_t* const data, size_t data_size,
+            WebPPicture* const pic, int keep_alpha,
+            struct Metadata* const metadata) {
+  int ok = 0;
+  int i, j;
+  uint64_t stride, pixel_bytes, sample_size, depth;
+  uint8_t* rgb = NULL, *tmp_rgb;
+  size_t offset;
+  PNMInfo info;
+
+  info.data = data;
+  info.data_size = data_size;
+  offset = ReadHeader(&info);
+  if (offset == 0) {
+    fprintf(stderr, "Error parsing PNM header.\n");
+    goto End;
+  }
+
+  if (info.type < 5 || info.type > 7) {
+    fprintf(stderr, "Unsupported P%d PNM format.\n", info.type);
+    goto End;
+  }
+
+  // Some basic validations.
+  if (pic == NULL) goto End;
+  if (info.width > WEBP_MAX_DIMENSION || info.height > WEBP_MAX_DIMENSION) {
+    fprintf(stderr, "Invalid %dx%d dimension for PNM\n",
+                    info.width, info.height);
+    goto End;
+  }
+
+  pixel_bytes = (uint64_t)info.width * info.height * info.bytes_per_px;
+  if (data_size < offset + pixel_bytes) {
+    fprintf(stderr, "Truncated PNM file (P%d).\n", info.type);
+    goto End;
+  }
+  sample_size = (info.max_value > 255) ? 2 : 1;
+  // final depth
+  depth = (info.depth == 1 || info.depth == 3 || !keep_alpha) ? 3 : 4;
+  stride = depth * info.width;
+  if (stride != (size_t)stride ||
+      !ImgIoUtilCheckSizeArgumentsOverflow(stride, info.height)) {
+    goto End;
+  }
+
+  rgb = (uint8_t*)malloc((size_t)stride * info.height);
+  if (rgb == NULL) goto End;
+
+  // Convert input.
+  // We only optimize for the sample_size=1, max_value=255, depth=1 case.
+  tmp_rgb = rgb;
+  for (j = 0; j < info.height; ++j) {
+    const uint8_t* in = data + offset;
+    offset += info.bytes_per_px * info.width;
+    assert(offset <= data_size);
+    if (info.max_value == 255 && info.depth >= 3) {
+      // RGB or RGBA
+      if (info.depth == 3 || keep_alpha) {
+        memcpy(tmp_rgb, in, info.depth * info.width * sizeof(*in));
+      } else {
+        assert(info.depth == 4 && !keep_alpha);
+        for (i = 0; i < info.width; ++i) {
+          tmp_rgb[3 * i + 0] = in[4 * i + 0];
+          tmp_rgb[3 * i + 1] = in[4 * i + 1];
+          tmp_rgb[3 * i + 2] = in[4 * i + 2];
+        }
+      }
+    } else {
+      // Unoptimized case, we need to handle non-trivial operations:
+      //   * convert 16b to 8b (if max_value > 255)
+      //   * rescale to [0..255] range (if max_value != 255)
+      //   * drop the alpha channel (if keep_alpha is false)
+      const uint32_t round = info.max_value / 2;
+      int k = 0;
+      for (i = 0; i < info.width * info.depth; ++i) {
+        uint32_t v = (sample_size == 2) ? 256u * in[2 * i + 0] + in[2 * i + 1]
+                   : in[i];
+        if (info.max_value != 255) v = (v * 255u + round) / info.max_value;
+        if (v > 255u) v = 255u;
+        if (info.depth > 2) {
+          if (!keep_alpha && info.depth == 4 && (i % 4) == 3) {
+            // skip alpha
+          } else {
+            tmp_rgb[k] = v;
+            k += 1;
+          }
+        } else if (info.depth == 1 || (i % 2) == 0) {
+          tmp_rgb[k + 0] = tmp_rgb[k + 1] = tmp_rgb[k + 2] = v;
+          k += 3;
+        } else if (keep_alpha && info.depth == 2) {
+          tmp_rgb[k] = v;
+          k += 1;
+        } else {
+          // skip alpha
+        }
+      }
+    }
+    tmp_rgb += stride;
+  }
+
+  // WebP conversion.
+  pic->width = info.width;
+  pic->height = info.height;
+  ok = (depth == 4) ? WebPPictureImportRGBA(pic, rgb, (int)stride)
+                    : WebPPictureImportRGB(pic, rgb, (int)stride);
+  if (!ok) goto End;
+
+  ok = 1;
+ End:
+  free((void*)rgb);
+
+  (void)metadata;
+  (void)keep_alpha;
+  return ok;
+}
+
+// -----------------------------------------------------------------------------
diff --git a/imageio/pnmdec.h b/imageio/pnmdec.h
new file mode 100644
index 0000000..c4d5823
--- /dev/null
+++ b/imageio/pnmdec.h
@@ -0,0 +1,37 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// partial PNM format decoder (ppm/pgm)
+
+#ifndef WEBP_IMAGEIO_PNMDEC_H_
+#define WEBP_IMAGEIO_PNMDEC_H_
+
+#include "webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Metadata;
+struct WebPPicture;
+
+// Reads a PNM file from 'data', returning the decoded output in 'pic'.
+// The output is RGB or YUV depending on pic->use_argb value.
+// Returns true on success.
+// 'metadata' has no effect, but is kept for coherence with other signatures
+// for image readers.
+int ReadPNM(const uint8_t* const data, size_t data_size,
+            struct WebPPicture* const pic, int keep_alpha,
+            struct Metadata* const metadata);
+
+#ifdef __cplusplus
+}    // extern "C"
+#endif
+
+#endif  // WEBP_IMAGEIO_PNMDEC_H_
diff --git a/imageio/tiffdec.c b/imageio/tiffdec.c
new file mode 100644
index 0000000..d711fa4
--- /dev/null
+++ b/imageio/tiffdec.c
@@ -0,0 +1,293 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// TIFF decode.
+
+#include "./tiffdec.h"
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef WEBP_HAVE_TIFF
+#include <tiffio.h>
+
+#include "webp/encode.h"
+#include "./imageio_util.h"
+#include "./metadata.h"
+
+static const struct {
+  ttag_t tag;
+  size_t storage_offset;
+} kTIFFMetadataMap[] = {
+  { TIFFTAG_ICCPROFILE, METADATA_OFFSET(iccp) },
+  { TIFFTAG_XMLPACKET,  METADATA_OFFSET(xmp) },
+  { 0, 0 },
+};
+
+// Returns true on success. The caller must use MetadataFree() on 'metadata' in
+// all cases.
+static int ExtractMetadataFromTIFF(TIFF* const tif, Metadata* const metadata) {
+  int i;
+  toff_t exif_ifd_offset;
+
+  for (i = 0; kTIFFMetadataMap[i].tag != 0; ++i) {
+    MetadataPayload* const payload =
+        (MetadataPayload*)((uint8_t*)metadata +
+                           kTIFFMetadataMap[i].storage_offset);
+    void* tag_data;
+    uint32_t tag_data_len;
+
+    if (TIFFGetField(tif, kTIFFMetadataMap[i].tag, &tag_data_len, &tag_data) &&
+        !MetadataCopy((const char*)tag_data, tag_data_len, payload)) {
+      return 0;
+    }
+  }
+
+  // TODO(jzern): To extract the raw EXIF directory some parsing of it would be
+  // necessary to determine the overall size. In addition, value offsets in
+  // individual directory entries may need to be updated as, depending on the
+  // type, they are file based.
+  // Exif 2.2 Section 4.6.2 Tag Structure
+  // TIFF Revision 6.0 Part 1 Section 2 TIFF Structure #Image File Directory
+  if (TIFFGetField(tif, TIFFTAG_EXIFIFD, &exif_ifd_offset)) {
+    fprintf(stderr, "Warning: EXIF extraction from TIFF is unsupported.\n");
+  }
+  return 1;
+}
+
+// Ad-hoc structure to supply read-from-memory functionalities.
+typedef struct {
+  const uint8_t* data;
+  toff_t size;
+  toff_t pos;
+} MyData;
+
+static int MyClose(thandle_t opaque) {
+  (void)opaque;
+  return 0;
+}
+
+static toff_t MySize(thandle_t opaque) {
+  const MyData* const my_data = (MyData*)opaque;
+  return my_data->size;
+}
+
+static toff_t MySeek(thandle_t opaque, toff_t offset, int whence) {
+  MyData* const my_data = (MyData*)opaque;
+  offset += (whence == SEEK_CUR) ? my_data->pos
+          : (whence == SEEK_SET) ? 0
+          : my_data->size;
+  if (offset > my_data->size) return (toff_t)-1;
+  my_data->pos = offset;
+  return offset;
+}
+
+static int MyMapFile(thandle_t opaque, void** base, toff_t* size) {
+  (void)opaque;
+  (void)base;
+  (void)size;
+  return 0;
+}
+static void MyUnmapFile(thandle_t opaque, void* base, toff_t size) {
+  (void)opaque;
+  (void)base;
+  (void)size;
+}
+
+static tsize_t MyRead(thandle_t opaque, void* dst, tsize_t size) {
+  MyData* const my_data = (MyData*)opaque;
+  if (my_data->pos + size > my_data->size) {
+    size = (tsize_t)(my_data->size - my_data->pos);
+  }
+  if (size > 0) {
+    memcpy(dst, my_data->data + my_data->pos, size);
+    my_data->pos += size;
+  }
+  return size;
+}
+
+// Unmultiply Argb data. Taken from dsp/alpha_processing
+// (we don't want to force a dependency to a libdspdec library).
+#define MFIX 24    // 24bit fixed-point arithmetic
+#define HALF ((1u << MFIX) >> 1)
+
+static uint32_t Unmult(uint8_t x, uint32_t mult) {
+  const uint32_t v = (x * mult + HALF) >> MFIX;
+  return (v > 255u) ? 255u : v;
+}
+
+static WEBP_INLINE uint32_t GetScale(uint32_t a) {
+  return (255u << MFIX) / a;
+}
+
+#undef MFIX
+#undef HALF
+
+static void MultARGBRow(uint8_t* ptr, int width) {
+  int x;
+  for (x = 0; x < width; ++x, ptr += 4) {
+    const uint32_t alpha = ptr[3];
+    if (alpha < 255) {
+      if (alpha == 0) {   // alpha == 0
+        ptr[0] = ptr[1] = ptr[2] = 0;
+      } else {
+        const uint32_t scale = GetScale(alpha);
+        ptr[0] = Unmult(ptr[0], scale);
+        ptr[1] = Unmult(ptr[1], scale);
+        ptr[2] = Unmult(ptr[2], scale);
+      }
+    }
+  }
+}
+
+int ReadTIFF(const uint8_t* const data, size_t data_size,
+             WebPPicture* const pic, int keep_alpha,
+             Metadata* const metadata) {
+  MyData my_data = { data, (toff_t)data_size, 0 };
+  TIFF* tif;
+  uint32_t image_width, image_height, tile_width, tile_height;
+  uint64_t stride;
+  uint16_t samples_per_px = 0;
+  uint16_t extra_samples = 0;
+  uint16_t* extra_samples_ptr = NULL;
+  uint32_t* raster;
+  int64_t alloc_size;
+  int ok = 0;
+  tdir_t dircount;
+
+  if (data == NULL || data_size == 0 || data_size > INT_MAX || pic == NULL) {
+    return 0;
+  }
+
+  tif = TIFFClientOpen("Memory", "r", &my_data,
+                       MyRead, MyRead, MySeek, MyClose,
+                       MySize, MyMapFile, MyUnmapFile);
+  if (tif == NULL) {
+    fprintf(stderr, "Error! Cannot parse TIFF file\n");
+    return 0;
+  }
+
+  dircount = TIFFNumberOfDirectories(tif);
+  if (dircount > 1) {
+    fprintf(stderr, "Warning: multi-directory TIFF files are not supported.\n"
+                    "Only the first will be used, %d will be ignored.\n",
+                    dircount - 1);
+  }
+  if (!TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_px)) {
+    fprintf(stderr, "Error! Cannot retrieve TIFF samples-per-pixel info.\n");
+    goto End;
+  }
+  if (!(samples_per_px == 1 || samples_per_px == 3 || samples_per_px == 4)) {
+    goto End;  // not supported
+  }
+
+  if (!(TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &image_width) &&
+        TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &image_height))) {
+    fprintf(stderr, "Error! Cannot retrieve TIFF image dimensions.\n");
+    goto End;
+  }
+  stride = (uint64_t)image_width * sizeof(*raster);
+  if (!ImgIoUtilCheckSizeArgumentsOverflow(stride, image_height)) {
+    fprintf(stderr, "Error! TIFF image dimension (%d x %d) is too large.\n",
+            image_width, image_height);
+    goto End;
+  }
+
+  // According to spec, a tile can be bigger than the image. However it should
+  // be a multiple of 16 and not way too large, so check that it's not more
+  // than twice the image size, for dimensions above some arbitrary minimum
+  // 32. We also check that they respect WebP's dimension and memory limit.
+  // Note that a tile can be 6byte/px in some cases. Here we assume
+  // 4byte/px with sizeof(*raster), to be conservative.
+  if (TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width) &&
+      TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height)) {
+    if ((tile_width > 32 && tile_width / 2 > image_width) ||
+        (tile_height > 32 && tile_height / 2 > image_height) ||
+        !ImgIoUtilCheckSizeArgumentsOverflow(
+            (uint64_t)tile_width * sizeof(*raster), tile_height)) {
+      fprintf(stderr, "Error! TIFF tile dimension (%d x %d) is too large.\n",
+              tile_width, tile_height);
+      goto End;
+    }
+  }
+
+  if (samples_per_px > 3 && !TIFFGetField(tif, TIFFTAG_EXTRASAMPLES,
+                                          &extra_samples, &extra_samples_ptr)) {
+    fprintf(stderr, "Error! Cannot retrieve TIFF ExtraSamples info.\n");
+    goto End;
+  }
+
+  // _Tiffmalloc uses a signed type for size.
+  alloc_size = (int64_t)(stride * image_height);
+  if (alloc_size < 0 || alloc_size != (tsize_t)alloc_size) goto End;
+
+  raster = (uint32_t*)_TIFFmalloc((tsize_t)alloc_size);
+  if (raster != NULL) {
+    if (TIFFReadRGBAImageOriented(tif, image_width, image_height, raster,
+                                  ORIENTATION_TOPLEFT, 1)) {
+      pic->width = image_width;
+      pic->height = image_height;
+      // TIFF data is ABGR
+#ifdef WORDS_BIGENDIAN
+      TIFFSwabArrayOfLong(raster, image_width * image_height);
+#endif
+      // if we have an alpha channel, we must un-multiply from rgbA to RGBA
+      if (extra_samples == 1 && extra_samples_ptr != NULL &&
+          extra_samples_ptr[0] == EXTRASAMPLE_ASSOCALPHA) {
+        uint32_t y;
+        uint8_t* tmp = (uint8_t*)raster;
+        for (y = 0; y < image_height; ++y) {
+          MultARGBRow(tmp, image_width);
+          tmp += stride;
+        }
+      }
+      ok = keep_alpha
+         ? WebPPictureImportRGBA(pic, (const uint8_t*)raster, (int)stride)
+         : WebPPictureImportRGBX(pic, (const uint8_t*)raster, (int)stride);
+    }
+    _TIFFfree(raster);
+  } else {
+    fprintf(stderr, "Error allocating TIFF RGBA memory!\n");
+  }
+
+  if (ok) {
+    if (metadata != NULL) {
+      ok = ExtractMetadataFromTIFF(tif, metadata);
+      if (!ok) {
+        fprintf(stderr, "Error extracting TIFF metadata!\n");
+        MetadataFree(metadata);
+        WebPPictureFree(pic);
+      }
+    }
+  }
+ End:
+  TIFFClose(tif);
+  return ok;
+}
+#else  // !WEBP_HAVE_TIFF
+int ReadTIFF(const uint8_t* const data, size_t data_size,
+             struct WebPPicture* const pic, int keep_alpha,
+             struct Metadata* const metadata) {
+  (void)data;
+  (void)data_size;
+  (void)pic;
+  (void)keep_alpha;
+  (void)metadata;
+  fprintf(stderr, "TIFF support not compiled. Please install the libtiff "
+          "development package before building.\n");
+  return 0;
+}
+#endif  // WEBP_HAVE_TIFF
+
+// -----------------------------------------------------------------------------
diff --git a/imageio/tiffdec.h b/imageio/tiffdec.h
new file mode 100644
index 0000000..0c8becc
--- /dev/null
+++ b/imageio/tiffdec.h
@@ -0,0 +1,37 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// TIFF decode.
+
+#ifndef WEBP_IMAGEIO_TIFFDEC_H_
+#define WEBP_IMAGEIO_TIFFDEC_H_
+
+#include "webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Metadata;
+struct WebPPicture;
+
+// Reads a TIFF from 'data', returning the decoded output in 'pic'.
+// Output is RGBA or YUVA, depending on pic->use_argb value.
+// If 'keep_alpha' is true and the TIFF has an alpha channel, the output is RGBA
+// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV.
+// Returns true on success.
+int ReadTIFF(const uint8_t* const data, size_t data_size,
+             struct WebPPicture* const pic, int keep_alpha,
+             struct Metadata* const metadata);
+
+#ifdef __cplusplus
+}    // extern "C"
+#endif
+
+#endif  // WEBP_IMAGEIO_TIFFDEC_H_
diff --git a/imageio/webpdec.c b/imageio/webpdec.c
new file mode 100644
index 0000000..aed60e1
--- /dev/null
+++ b/imageio/webpdec.c
@@ -0,0 +1,244 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// WebP decode.
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include "./webpdec.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "webp/decode.h"
+#include "webp/demux.h"
+#include "webp/encode.h"
+#include "../examples/unicode.h"
+#include "./imageio_util.h"
+#include "./metadata.h"
+
+//------------------------------------------------------------------------------
+// WebP decoding
+
+static const char* const kStatusMessages[VP8_STATUS_NOT_ENOUGH_DATA + 1] = {
+  "OK", "OUT_OF_MEMORY", "INVALID_PARAM", "BITSTREAM_ERROR",
+  "UNSUPPORTED_FEATURE", "SUSPENDED", "USER_ABORT", "NOT_ENOUGH_DATA"
+};
+
+static void PrintAnimationWarning(const WebPDecoderConfig* const config) {
+  if (config->input.has_animation) {
+    fprintf(stderr,
+            "Error! Decoding of an animated WebP file is not supported.\n"
+            "       Use webpmux to extract the individual frames or\n"
+            "       vwebp to view this image.\n");
+  }
+}
+
+void PrintWebPError(const char* const in_file, int status) {
+  WFPRINTF(stderr, "Decoding of %s failed.\n", (const W_CHAR*)in_file);
+  fprintf(stderr, "Status: %d", status);
+  if (status >= VP8_STATUS_OK && status <= VP8_STATUS_NOT_ENOUGH_DATA) {
+    fprintf(stderr, "(%s)", kStatusMessages[status]);
+  }
+  fprintf(stderr, "\n");
+}
+
+int LoadWebP(const char* const in_file,
+             const uint8_t** data, size_t* data_size,
+             WebPBitstreamFeatures* bitstream) {
+  VP8StatusCode status;
+  WebPBitstreamFeatures local_features;
+  if (!ImgIoUtilReadFile(in_file, data, data_size)) return 0;
+
+  if (bitstream == NULL) {
+    bitstream = &local_features;
+  }
+
+  status = WebPGetFeatures(*data, *data_size, bitstream);
+  if (status != VP8_STATUS_OK) {
+    WebPFree((void*)*data);
+    *data = NULL;
+    *data_size = 0;
+    PrintWebPError(in_file, status);
+    return 0;
+  }
+  return 1;
+}
+
+//------------------------------------------------------------------------------
+
+VP8StatusCode DecodeWebP(const uint8_t* const data, size_t data_size,
+                         WebPDecoderConfig* const config) {
+  if (config == NULL) return VP8_STATUS_INVALID_PARAM;
+  PrintAnimationWarning(config);
+  return WebPDecode(data, data_size, config);
+}
+
+VP8StatusCode DecodeWebPIncremental(
+    const uint8_t* const data, size_t data_size,
+    WebPDecoderConfig* const config) {
+  VP8StatusCode status = VP8_STATUS_OK;
+  if (config == NULL) return VP8_STATUS_INVALID_PARAM;
+
+  PrintAnimationWarning(config);
+
+  // Decoding call.
+  {
+    WebPIDecoder* const idec = WebPIDecode(data, data_size, config);
+    if (idec == NULL) {
+      fprintf(stderr, "Failed during WebPIDecode().\n");
+      return VP8_STATUS_OUT_OF_MEMORY;
+    } else {
+      status = WebPIUpdate(idec, data, data_size);
+      WebPIDelete(idec);
+    }
+  }
+  return status;
+}
+
+// -----------------------------------------------------------------------------
+// Metadata
+
+static int ExtractMetadata(const uint8_t* const data, size_t data_size,
+                           Metadata* const metadata) {
+  WebPData webp_data = { data, data_size };
+  WebPDemuxer* const demux = WebPDemux(&webp_data);
+  WebPChunkIterator chunk_iter;
+  uint32_t flags;
+
+  if (demux == NULL) return 0;
+  assert(metadata != NULL);
+
+  flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS);
+
+  if ((flags & ICCP_FLAG) && WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter)) {
+    MetadataCopy((const char*)chunk_iter.chunk.bytes, chunk_iter.chunk.size,
+                 &metadata->iccp);
+    WebPDemuxReleaseChunkIterator(&chunk_iter);
+  }
+  if ((flags & EXIF_FLAG) && WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter)) {
+    MetadataCopy((const char*)chunk_iter.chunk.bytes, chunk_iter.chunk.size,
+                 &metadata->exif);
+    WebPDemuxReleaseChunkIterator(&chunk_iter);
+  }
+  if ((flags & XMP_FLAG) && WebPDemuxGetChunk(demux, "XMP ", 1, &chunk_iter)) {
+    MetadataCopy((const char*)chunk_iter.chunk.bytes, chunk_iter.chunk.size,
+                 &metadata->xmp);
+    WebPDemuxReleaseChunkIterator(&chunk_iter);
+  }
+  WebPDemuxDelete(demux);
+  return 1;
+}
+
+// -----------------------------------------------------------------------------
+
+int ReadWebP(const uint8_t* const data, size_t data_size,
+             WebPPicture* const pic,
+             int keep_alpha, Metadata* const metadata) {
+  int ok = 0;
+  VP8StatusCode status = VP8_STATUS_OK;
+  WebPDecoderConfig config;
+  WebPDecBuffer* const output_buffer = &config.output;
+  WebPBitstreamFeatures* const bitstream = &config.input;
+
+  if (data == NULL || data_size == 0 || pic == NULL) return 0;
+
+  if (!WebPInitDecoderConfig(&config)) {
+    fprintf(stderr, "Library version mismatch!\n");
+    return 0;
+  }
+
+  status = WebPGetFeatures(data, data_size, bitstream);
+  if (status != VP8_STATUS_OK) {
+    PrintWebPError("input data", status);
+    return 0;
+  }
+
+  do {
+    const int has_alpha = keep_alpha && bitstream->has_alpha;
+    uint64_t stride;
+    pic->width = bitstream->width;
+    pic->height = bitstream->height;
+    if (pic->use_argb) {
+      stride = (uint64_t)bitstream->width * 4;
+    } else {
+      stride = (uint64_t)bitstream->width * (has_alpha ? 5 : 3) / 2;
+      pic->colorspace = has_alpha ? WEBP_YUV420A : WEBP_YUV420;
+    }
+
+    if (!ImgIoUtilCheckSizeArgumentsOverflow(stride, bitstream->height)) {
+      status = VP8_STATUS_OUT_OF_MEMORY;
+      break;
+    }
+
+    ok = WebPPictureAlloc(pic);
+    if (!ok) {
+      status = VP8_STATUS_OUT_OF_MEMORY;
+      break;
+    }
+    if (pic->use_argb) {
+#ifdef WORDS_BIGENDIAN
+      output_buffer->colorspace = MODE_ARGB;
+#else
+      output_buffer->colorspace = MODE_BGRA;
+#endif
+      output_buffer->u.RGBA.rgba = (uint8_t*)pic->argb;
+      output_buffer->u.RGBA.stride = pic->argb_stride * sizeof(uint32_t);
+      output_buffer->u.RGBA.size = output_buffer->u.RGBA.stride * pic->height;
+    } else {
+      output_buffer->colorspace = has_alpha ? MODE_YUVA : MODE_YUV;
+      output_buffer->u.YUVA.y = pic->y;
+      output_buffer->u.YUVA.u = pic->u;
+      output_buffer->u.YUVA.v = pic->v;
+      output_buffer->u.YUVA.a = has_alpha ? pic->a : NULL;
+      output_buffer->u.YUVA.y_stride = pic->y_stride;
+      output_buffer->u.YUVA.u_stride = pic->uv_stride;
+      output_buffer->u.YUVA.v_stride = pic->uv_stride;
+      output_buffer->u.YUVA.a_stride = has_alpha ? pic->a_stride : 0;
+      output_buffer->u.YUVA.y_size = pic->height * pic->y_stride;
+      output_buffer->u.YUVA.u_size = (pic->height + 1) / 2 * pic->uv_stride;
+      output_buffer->u.YUVA.v_size = (pic->height + 1) / 2 * pic->uv_stride;
+      output_buffer->u.YUVA.a_size = pic->height * pic->a_stride;
+    }
+    output_buffer->is_external_memory = 1;
+
+    status = DecodeWebP(data, data_size, &config);
+    ok = (status == VP8_STATUS_OK);
+    if (ok && !keep_alpha && pic->use_argb) {
+      // Need to wipe out the alpha value, as requested.
+      int x, y;
+      uint32_t* argb = pic->argb;
+      for (y = 0; y < pic->height; ++y) {
+        for (x = 0; x < pic->width; ++x) argb[x] |= 0xff000000u;
+        argb += pic->argb_stride;
+      }
+    }
+  } while (0);   // <- so we can 'break' out of the loop
+
+  if (status != VP8_STATUS_OK) {
+    PrintWebPError("input data", status);
+    ok = 0;
+  }
+
+  WebPFreeDecBuffer(output_buffer);
+
+  if (ok && metadata != NULL) {
+    ok = ExtractMetadata(data, data_size, metadata);
+    if (!ok) {
+      PrintWebPError("metadata", VP8_STATUS_BITSTREAM_ERROR);
+    }
+  }
+  if (!ok) WebPPictureFree(pic);
+  return ok;
+}
+
+// -----------------------------------------------------------------------------
diff --git a/imageio/webpdec.h b/imageio/webpdec.h
new file mode 100644
index 0000000..d329d41
--- /dev/null
+++ b/imageio/webpdec.h
@@ -0,0 +1,67 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// WebP decode.
+
+#ifndef WEBP_IMAGEIO_WEBPDEC_H_
+#define WEBP_IMAGEIO_WEBPDEC_H_
+
+#include "webp/decode.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Metadata;
+struct WebPPicture;
+
+//------------------------------------------------------------------------------
+// WebP decoding
+
+// Prints an informative error message regarding decode failure of 'in_file'.
+// 'status' is treated as a VP8StatusCode and if valid will be printed as a
+// text string.
+void PrintWebPError(const char* const in_file, int status);
+
+// Reads a WebP from 'in_file', returning the contents and size in 'data' and
+// 'data_size'. If not NULL, 'bitstream' is populated using WebPGetFeatures().
+// Returns true on success.
+int LoadWebP(const char* const in_file,
+             const uint8_t** data, size_t* data_size,
+             WebPBitstreamFeatures* bitstream);
+
+// Decodes the WebP contained in 'data'.
+// 'config' is a structure previously initialized by WebPInitDecoderConfig().
+// 'config->output' should have the desired colorspace selected.
+// Returns the decoder status. On success 'config->output' will contain the
+// decoded picture.
+VP8StatusCode DecodeWebP(const uint8_t* const data, size_t data_size,
+                         WebPDecoderConfig* const config);
+
+// Same as DecodeWebP(), but using the incremental decoder.
+VP8StatusCode DecodeWebPIncremental(
+    const uint8_t* const data, size_t data_size,
+    WebPDecoderConfig* const config);
+
+//------------------------------------------------------------------------------
+
+// Decodes a WebP contained in 'data', returning the decoded output in 'pic'.
+// Output is RGBA or YUVA, depending on pic->use_argb value.
+// If 'keep_alpha' is true and the WebP has an alpha channel, the output is RGBA
+// or YUVA. Otherwise, alpha channel is dropped and output is RGB or YUV.
+// Returns true on success.
+int ReadWebP(const uint8_t* const data, size_t data_size,
+             struct WebPPicture* const pic,
+             int keep_alpha, struct Metadata* const metadata);
+
+#ifdef __cplusplus
+}    // extern "C"
+#endif
+
+#endif  // WEBP_IMAGEIO_WEBPDEC_H_
diff --git a/imageio/wicdec.c b/imageio/wicdec.c
new file mode 100644
index 0000000..42001c6
--- /dev/null
+++ b/imageio/wicdec.c
@@ -0,0 +1,413 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Windows Imaging Component (WIC) decode.
+
+#include "./wicdec.h"
+
+#ifdef HAVE_CONFIG_H
+#include "webp/config.h"
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_WINCODEC_H
+#ifdef __MINGW32__
+#define INITGUID  // Without this GUIDs are declared extern and fail to link
+#endif
+#define CINTERFACE
+#define COBJMACROS
+#define _WIN32_IE 0x500  // Workaround bug in shlwapi.h when compiling C++
+                         // code with COBJMACROS.
+#include <ole2.h>  // CreateStreamOnHGlobal()
+#include <shlwapi.h>
+#include <tchar.h>
+#include <windows.h>
+#include <wincodec.h>
+
+#include "../examples/unicode.h"
+#include "./imageio_util.h"
+#include "./metadata.h"
+#include "webp/encode.h"
+
+#define IFS(fn)                                                     \
+  do {                                                              \
+    if (SUCCEEDED(hr)) {                                            \
+      hr = (fn);                                                    \
+      if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr);   \
+    }                                                               \
+  } while (0)
+
+// modified version of DEFINE_GUID from guiddef.h.
+#define WEBP_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
+  static const GUID name = \
+      { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }
+
+#ifdef __cplusplus
+#define MAKE_REFGUID(x) (x)
+#else
+#define MAKE_REFGUID(x) &(x)
+#endif
+
+typedef struct WICFormatImporter {
+  const GUID* pixel_format;
+  int bytes_per_pixel;
+  int (*import)(WebPPicture* const, const uint8_t* const, int);
+} WICFormatImporter;
+
+// From Microsoft SDK 7.0a -- wincodec.h
+// Create local copies for compatibility when building against earlier
+// versions of the SDK.
+WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppBGR_,
+                 0x6fddc324, 0x4e03, 0x4bfe,
+                 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0c);
+WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppRGB_,
+                 0x6fddc324, 0x4e03, 0x4bfe,
+                 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0d);
+WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppBGRA_,
+                 0x6fddc324, 0x4e03, 0x4bfe,
+                 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0f);
+WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppRGBA_,
+                 0xf5c7ad2d, 0x6a8d, 0x43dd,
+                 0xa7, 0xa8, 0xa2, 0x99, 0x35, 0x26, 0x1a, 0xe9);
+WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppBGRA_,
+                 0x1562ff7c, 0xd352, 0x46f9,
+                 0x97, 0x9e, 0x42, 0x97, 0x6b, 0x79, 0x22, 0x46);
+WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppRGBA_,
+                 0x6fddc324, 0x4e03, 0x4bfe,
+                 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x16);
+
+static HRESULT OpenInputStream(const char* filename, IStream** stream) {
+  HRESULT hr = S_OK;
+  if (!WSTRCMP(filename, "-")) {
+    const uint8_t* data = NULL;
+    size_t data_size = 0;
+    const int ok = ImgIoUtilReadFile(filename, &data, &data_size);
+    if (ok) {
+      HGLOBAL image = GlobalAlloc(GMEM_MOVEABLE, data_size);
+      if (image != NULL) {
+        void* const image_mem = GlobalLock(image);
+        if (image_mem != NULL) {
+          memcpy(image_mem, data, data_size);
+          GlobalUnlock(image);
+          IFS(CreateStreamOnHGlobal(image, TRUE, stream));
+        } else {
+          hr = E_FAIL;
+        }
+      } else {
+        hr = E_OUTOFMEMORY;
+      }
+      free((void*)data);
+    } else {
+      hr = E_FAIL;
+    }
+  } else {
+    IFS(SHCreateStreamOnFile((const LPTSTR)filename, STGM_READ, stream));
+  }
+
+  if (FAILED(hr)) {
+    _ftprintf(stderr, _T("Error opening input file %s (%08lx)\n"),
+              (const LPTSTR)filename, hr);
+  }
+  return hr;
+}
+
+// -----------------------------------------------------------------------------
+// Metadata processing
+
+// Stores the first non-zero sized color profile from 'frame' to 'iccp'.
+// Returns an HRESULT to indicate success or failure. The caller is responsible
+// for freeing 'iccp->bytes' in either case.
+static HRESULT ExtractICCP(IWICImagingFactory* const factory,
+                           IWICBitmapFrameDecode* const frame,
+                           MetadataPayload* const iccp) {
+  HRESULT hr = S_OK;
+  UINT i, count;
+  IWICColorContext** color_contexts;
+
+  IFS(IWICBitmapFrameDecode_GetColorContexts(frame, 0, NULL, &count));
+  if (FAILED(hr) || count == 0) {
+    // Treat unsupported operation as a non-fatal error. See crbug.com/webp/506.
+    return (hr == WINCODEC_ERR_UNSUPPORTEDOPERATION) ? S_OK : hr;
+  }
+
+  color_contexts = (IWICColorContext**)calloc(count, sizeof(*color_contexts));
+  if (color_contexts == NULL) return E_OUTOFMEMORY;
+  for (i = 0; SUCCEEDED(hr) && i < count; ++i) {
+    IFS(IWICImagingFactory_CreateColorContext(factory, &color_contexts[i]));
+  }
+
+  if (SUCCEEDED(hr)) {
+    UINT num_color_contexts;
+    IFS(IWICBitmapFrameDecode_GetColorContexts(frame,
+                                               count, color_contexts,
+                                               &num_color_contexts));
+    assert(FAILED(hr) || num_color_contexts <= count);
+    for (i = 0; SUCCEEDED(hr) && i < num_color_contexts; ++i) {
+      WICColorContextType type;
+      IFS(IWICColorContext_GetType(color_contexts[i], &type));
+      if (SUCCEEDED(hr) && type == WICColorContextProfile) {
+        UINT size;
+        IFS(IWICColorContext_GetProfileBytes(color_contexts[i],
+                                             0, NULL, &size));
+        if (SUCCEEDED(hr) && size > 0) {
+          iccp->bytes = (uint8_t*)malloc(size);
+          if (iccp->bytes == NULL) {
+            hr = E_OUTOFMEMORY;
+            break;
+          }
+          iccp->size = size;
+          IFS(IWICColorContext_GetProfileBytes(color_contexts[i],
+                                               (UINT)iccp->size, iccp->bytes,
+                                               &size));
+          if (SUCCEEDED(hr) && size != iccp->size) {
+            fprintf(stderr, "Warning! ICC profile size (%u) != expected (%u)\n",
+                    size, (uint32_t)iccp->size);
+            iccp->size = size;
+          }
+          break;
+        }
+      }
+    }
+  }
+  for (i = 0; i < count; ++i) {
+    if (color_contexts[i] != NULL) IUnknown_Release(color_contexts[i]);
+  }
+  free(color_contexts);
+  return hr;
+}
+
+static HRESULT ExtractMetadata(IWICImagingFactory* const factory,
+                               IWICBitmapFrameDecode* const frame,
+                               Metadata* const metadata) {
+  // TODO(jzern): add XMP/EXIF extraction.
+  const HRESULT hr = ExtractICCP(factory, frame, &metadata->iccp);
+  if (FAILED(hr)) MetadataFree(metadata);
+  return hr;
+}
+
+// -----------------------------------------------------------------------------
+
+static int HasPalette(GUID pixel_format) {
+  return (IsEqualGUID(MAKE_REFGUID(pixel_format),
+                      MAKE_REFGUID(GUID_WICPixelFormat1bppIndexed)) ||
+          IsEqualGUID(MAKE_REFGUID(pixel_format),
+                      MAKE_REFGUID(GUID_WICPixelFormat2bppIndexed)) ||
+          IsEqualGUID(MAKE_REFGUID(pixel_format),
+                      MAKE_REFGUID(GUID_WICPixelFormat4bppIndexed)) ||
+          IsEqualGUID(MAKE_REFGUID(pixel_format),
+                      MAKE_REFGUID(GUID_WICPixelFormat8bppIndexed)));
+}
+
+static int HasAlpha(IWICImagingFactory* const factory,
+                    IWICBitmapDecoder* const decoder,
+                    IWICBitmapFrameDecode* const frame,
+                    GUID pixel_format) {
+  int has_alpha;
+  if (HasPalette(pixel_format)) {
+    IWICPalette* frame_palette = NULL;
+    IWICPalette* global_palette = NULL;
+    BOOL frame_palette_has_alpha = FALSE;
+    BOOL global_palette_has_alpha = FALSE;
+
+    // A palette may exist at the frame or container level,
+    // check IWICPalette::HasAlpha() for both if present.
+    if (SUCCEEDED(IWICImagingFactory_CreatePalette(factory, &frame_palette)) &&
+        SUCCEEDED(IWICBitmapFrameDecode_CopyPalette(frame, frame_palette))) {
+      IWICPalette_HasAlpha(frame_palette, &frame_palette_has_alpha);
+    }
+    if (SUCCEEDED(IWICImagingFactory_CreatePalette(factory, &global_palette)) &&
+        SUCCEEDED(IWICBitmapDecoder_CopyPalette(decoder, global_palette))) {
+      IWICPalette_HasAlpha(global_palette, &global_palette_has_alpha);
+    }
+    has_alpha = frame_palette_has_alpha || global_palette_has_alpha;
+
+    if (frame_palette != NULL) IUnknown_Release(frame_palette);
+    if (global_palette != NULL) IUnknown_Release(global_palette);
+  } else {
+    has_alpha = IsEqualGUID(MAKE_REFGUID(pixel_format),
+                            MAKE_REFGUID(GUID_WICPixelFormat32bppRGBA_)) ||
+                IsEqualGUID(MAKE_REFGUID(pixel_format),
+                            MAKE_REFGUID(GUID_WICPixelFormat32bppBGRA_)) ||
+                IsEqualGUID(MAKE_REFGUID(pixel_format),
+                            MAKE_REFGUID(GUID_WICPixelFormat64bppRGBA_)) ||
+                IsEqualGUID(MAKE_REFGUID(pixel_format),
+                            MAKE_REFGUID(GUID_WICPixelFormat64bppBGRA_));
+  }
+  return has_alpha;
+}
+
+int ReadPictureWithWIC(const char* const filename,
+                       WebPPicture* const pic, int keep_alpha,
+                       Metadata* const metadata) {
+  // From Microsoft SDK 6.0a -- ks.h
+  // Define a local copy to avoid link errors under mingw.
+  WEBP_DEFINE_GUID(GUID_NULL_, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+  static const WICFormatImporter kAlphaFormatImporters[] = {
+    { &GUID_WICPixelFormat32bppBGRA_, 4, WebPPictureImportBGRA },
+    { &GUID_WICPixelFormat32bppRGBA_, 4, WebPPictureImportRGBA },
+    { NULL, 0, NULL },
+  };
+  static const WICFormatImporter kNonAlphaFormatImporters[] = {
+    { &GUID_WICPixelFormat24bppBGR_, 3, WebPPictureImportBGR },
+    { &GUID_WICPixelFormat24bppRGB_, 3, WebPPictureImportRGB },
+    { NULL, 0, NULL },
+  };
+  HRESULT hr = S_OK;
+  IWICBitmapFrameDecode* frame = NULL;
+  IWICFormatConverter* converter = NULL;
+  IWICImagingFactory* factory = NULL;
+  IWICBitmapDecoder* decoder = NULL;
+  IStream* stream = NULL;
+  UINT frame_count = 0;
+  UINT width = 0, height = 0;
+  BYTE* rgb = NULL;
+  WICPixelFormatGUID src_pixel_format = GUID_WICPixelFormatUndefined;
+  const WICFormatImporter* importer = NULL;
+  GUID src_container_format = GUID_NULL_;
+  // From Windows Kits\10\Include\10.0.19041.0\um\wincodec.h
+  WEBP_DEFINE_GUID(GUID_ContainerFormatWebp_,
+                   0xe094b0e2, 0x67f2, 0x45b3,
+                   0xb0, 0xea, 0x11, 0x53, 0x37, 0xca, 0x7c, 0xf3);
+  static const GUID* kAlphaContainers[] = {
+    &GUID_ContainerFormatBmp,
+    &GUID_ContainerFormatPng,
+    &GUID_ContainerFormatTiff,
+    &GUID_ContainerFormatWebp_,
+    NULL
+  };
+  int has_alpha = 0;
+  int64_t stride;
+
+  if (filename == NULL || pic == NULL) return 0;
+
+  IFS(CoInitialize(NULL));
+  IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
+                       CLSCTX_INPROC_SERVER,
+                       MAKE_REFGUID(IID_IWICImagingFactory),
+                       (LPVOID*)&factory));
+  if (hr == REGDB_E_CLASSNOTREG) {
+    fprintf(stderr,
+            "Couldn't access Windows Imaging Component (are you running "
+            "Windows XP SP3 or newer?). Most formats not available. "
+            "Use -s for the available YUV input.\n");
+  }
+  // Prepare for image decoding.
+  IFS(OpenInputStream(filename, &stream));
+  IFS(IWICImagingFactory_CreateDecoderFromStream(
+          factory, stream, NULL,
+          WICDecodeMetadataCacheOnDemand, &decoder));
+  IFS(IWICBitmapDecoder_GetFrameCount(decoder, &frame_count));
+  if (SUCCEEDED(hr)) {
+    if (frame_count == 0) {
+      fprintf(stderr, "No frame found in input file.\n");
+      hr = E_FAIL;
+    } else if (frame_count > 1) {
+      // WIC will be tried before native WebP decoding so avoid duplicating the
+      // error message.
+      hr = E_FAIL;
+    }
+  }
+  IFS(IWICBitmapDecoder_GetFrame(decoder, 0, &frame));
+  IFS(IWICBitmapFrameDecode_GetPixelFormat(frame, &src_pixel_format));
+  IFS(IWICBitmapDecoder_GetContainerFormat(decoder, &src_container_format));
+
+  if (SUCCEEDED(hr) && keep_alpha) {
+    const GUID** guid;
+    for (guid = kAlphaContainers; *guid != NULL; ++guid) {
+      if (IsEqualGUID(MAKE_REFGUID(src_container_format),
+                      MAKE_REFGUID(**guid))) {
+        has_alpha = HasAlpha(factory, decoder, frame, src_pixel_format);
+        break;
+      }
+    }
+  }
+
+  // Prepare for pixel format conversion (if necessary).
+  IFS(IWICImagingFactory_CreateFormatConverter(factory, &converter));
+
+  for (importer = has_alpha ? kAlphaFormatImporters : kNonAlphaFormatImporters;
+       hr == S_OK && importer->import != NULL; ++importer) {
+    BOOL can_convert;
+    const HRESULT cchr = IWICFormatConverter_CanConvert(
+        converter,
+        MAKE_REFGUID(src_pixel_format),
+        MAKE_REFGUID(*importer->pixel_format),
+        &can_convert);
+    if (SUCCEEDED(cchr) && can_convert) break;
+  }
+  if (importer->import == NULL) hr = E_FAIL;
+
+  IFS(IWICFormatConverter_Initialize(converter, (IWICBitmapSource*)frame,
+                                     importer->pixel_format,
+                                     WICBitmapDitherTypeNone,
+                                     NULL, 0.0, WICBitmapPaletteTypeCustom));
+
+  // Decode.
+  IFS(IWICFormatConverter_GetSize(converter, &width, &height));
+  stride = (int64_t)importer->bytes_per_pixel * width * sizeof(*rgb);
+  if (stride != (int)stride ||
+      !ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
+    hr = E_FAIL;
+  }
+
+  if (SUCCEEDED(hr)) {
+    rgb = (BYTE*)malloc((size_t)stride * height);
+    if (rgb == NULL)
+      hr = E_OUTOFMEMORY;
+  }
+  IFS(IWICFormatConverter_CopyPixels(converter, NULL,
+                                     (UINT)stride, (UINT)stride * height, rgb));
+
+  // WebP conversion.
+  if (SUCCEEDED(hr)) {
+    int ok;
+    pic->width = width;
+    pic->height = height;
+    pic->use_argb = 1;    // For WIC, we always force to argb
+    ok = importer->import(pic, rgb, (int)stride);
+    if (!ok) hr = E_FAIL;
+  }
+  if (SUCCEEDED(hr)) {
+    if (metadata != NULL) {
+      hr = ExtractMetadata(factory, frame, metadata);
+      if (FAILED(hr)) {
+        fprintf(stderr, "Error extracting image metadata using WIC!\n");
+      }
+    }
+  }
+
+  // Cleanup.
+  if (converter != NULL) IUnknown_Release(converter);
+  if (frame != NULL) IUnknown_Release(frame);
+  if (decoder != NULL) IUnknown_Release(decoder);
+  if (factory != NULL) IUnknown_Release(factory);
+  if (stream != NULL) IUnknown_Release(stream);
+  free(rgb);
+  return SUCCEEDED(hr);
+}
+#else  // !HAVE_WINCODEC_H
+int ReadPictureWithWIC(const char* const filename,
+                       struct WebPPicture* const pic, int keep_alpha,
+                       struct Metadata* const metadata) {
+  (void)filename;
+  (void)pic;
+  (void)keep_alpha;
+  (void)metadata;
+  fprintf(stderr, "Windows Imaging Component (WIC) support not compiled. "
+                  "Visual Studio and mingw-w64 builds support WIC. Make sure "
+                  "wincodec.h detection is working correctly if using autoconf "
+                  "and HAVE_WINCODEC_H is defined before building.\n");
+  return 0;
+}
+#endif  // HAVE_WINCODEC_H
+
+// -----------------------------------------------------------------------------
diff --git a/imageio/wicdec.h b/imageio/wicdec.h
new file mode 100644
index 0000000..d9eeca8
--- /dev/null
+++ b/imageio/wicdec.h
@@ -0,0 +1,34 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Windows Imaging Component (WIC) decode.
+
+#ifndef WEBP_IMAGEIO_WICDEC_H_
+#define WEBP_IMAGEIO_WICDEC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Metadata;
+struct WebPPicture;
+
+// Reads an image from 'filename', returning the decoded output in 'pic'.
+// If 'keep_alpha' is true and the image has an alpha channel, the output is
+// RGBA otherwise it will be RGB. pic->use_argb is always forced to true.
+// Returns true on success.
+int ReadPictureWithWIC(const char* const filename,
+                       struct WebPPicture* const pic, int keep_alpha,
+                       struct Metadata* const metadata);
+
+#ifdef __cplusplus
+}    // extern "C"
+#endif
+
+#endif  // WEBP_IMAGEIO_WICDEC_H_
diff --git a/include/webp/decode.h b/include/webp/decode.h
deleted file mode 100644
index d982475..0000000
--- a/include/webp/decode.h
+++ /dev/null
@@ -1,503 +0,0 @@
-// Copyright 2010 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-//  Main decoding functions for WebP images.
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#ifndef WEBP_WEBP_DECODE_H_
-#define WEBP_WEBP_DECODE_H_
-
-#include "./types.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define WEBP_DECODER_ABI_VERSION 0x0209    // MAJOR(8b) + MINOR(8b)
-
-// Note: forward declaring enumerations is not allowed in (strict) C and C++,
-// the types are left here for reference.
-// typedef enum VP8StatusCode VP8StatusCode;
-// typedef enum WEBP_CSP_MODE WEBP_CSP_MODE;
-typedef struct WebPRGBABuffer WebPRGBABuffer;
-typedef struct WebPYUVABuffer WebPYUVABuffer;
-typedef struct WebPDecBuffer WebPDecBuffer;
-typedef struct WebPIDecoder WebPIDecoder;
-typedef struct WebPBitstreamFeatures WebPBitstreamFeatures;
-typedef struct WebPDecoderOptions WebPDecoderOptions;
-typedef struct WebPDecoderConfig WebPDecoderConfig;
-
-// Return the decoder's version number, packed in hexadecimal using 8bits for
-// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
-WEBP_EXTERN int WebPGetDecoderVersion(void);
-
-// Retrieve basic header information: width, height.
-// This function will also validate the header, returning true on success,
-// false otherwise. '*width' and '*height' are only valid on successful return.
-// Pointers 'width' and 'height' can be passed NULL if deemed irrelevant.
-// Note: The following chunk sequences (before the raw VP8/VP8L data) are
-// considered valid by this function:
-// RIFF + VP8(L)
-// RIFF + VP8X + (optional chunks) + VP8(L)
-// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
-// VP8(L)     <-- Not a valid WebP format: only allowed for internal purpose.
-WEBP_EXTERN int WebPGetInfo(const uint8_t* data, size_t data_size,
-                            int* width, int* height);
-
-// Decodes WebP images pointed to by 'data' and returns RGBA samples, along
-// with the dimensions in *width and *height. The ordering of samples in
-// memory is R, G, B, A, R, G, B, A... in scan order (endian-independent).
-// The returned pointer should be deleted calling WebPFree().
-// Returns NULL in case of error.
-WEBP_EXTERN uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size,
-                                    int* width, int* height);
-
-// Same as WebPDecodeRGBA, but returning A, R, G, B, A, R, G, B... ordered data.
-WEBP_EXTERN uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size,
-                                    int* width, int* height);
-
-// Same as WebPDecodeRGBA, but returning B, G, R, A, B, G, R, A... ordered data.
-WEBP_EXTERN uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size,
-                                    int* width, int* height);
-
-// Same as WebPDecodeRGBA, but returning R, G, B, R, G, B... ordered data.
-// If the bitstream contains transparency, it is ignored.
-WEBP_EXTERN uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
-                                   int* width, int* height);
-
-// Same as WebPDecodeRGB, but returning B, G, R, B, G, R... ordered data.
-WEBP_EXTERN uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size,
-                                   int* width, int* height);
-
-
-// Decode WebP images pointed to by 'data' to Y'UV format(*). The pointer
-// returned is the Y samples buffer. Upon return, *u and *v will point to
-// the U and V chroma data. These U and V buffers need NOT be passed to
-// WebPFree(), unlike the returned Y luma one. The dimension of the U and V
-// planes are both (*width + 1) / 2 and (*height + 1)/ 2.
-// Upon return, the Y buffer has a stride returned as '*stride', while U and V
-// have a common stride returned as '*uv_stride'.
-// Return NULL in case of error.
-// (*) Also named Y'CbCr. See: https://en.wikipedia.org/wiki/YCbCr
-WEBP_EXTERN uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
-                                   int* width, int* height,
-                                   uint8_t** u, uint8_t** v,
-                                   int* stride, int* uv_stride);
-
-// These five functions are variants of the above ones, that decode the image
-// directly into a pre-allocated buffer 'output_buffer'. The maximum storage
-// available in this buffer is indicated by 'output_buffer_size'. If this
-// storage is not sufficient (or an error occurred), NULL is returned.
-// Otherwise, output_buffer is returned, for convenience.
-// The parameter 'output_stride' specifies the distance (in bytes)
-// between scanlines. Hence, output_buffer_size is expected to be at least
-// output_stride x picture-height.
-WEBP_EXTERN uint8_t* WebPDecodeRGBAInto(
-    const uint8_t* data, size_t data_size,
-    uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
-WEBP_EXTERN uint8_t* WebPDecodeARGBInto(
-    const uint8_t* data, size_t data_size,
-    uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
-WEBP_EXTERN uint8_t* WebPDecodeBGRAInto(
-    const uint8_t* data, size_t data_size,
-    uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
-
-// RGB and BGR variants. Here too the transparency information, if present,
-// will be dropped and ignored.
-WEBP_EXTERN uint8_t* WebPDecodeRGBInto(
-    const uint8_t* data, size_t data_size,
-    uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
-WEBP_EXTERN uint8_t* WebPDecodeBGRInto(
-    const uint8_t* data, size_t data_size,
-    uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
-
-// WebPDecodeYUVInto() is a variant of WebPDecodeYUV() that operates directly
-// into pre-allocated luma/chroma plane buffers. This function requires the
-// strides to be passed: one for the luma plane and one for each of the
-// chroma ones. The size of each plane buffer is passed as 'luma_size',
-// 'u_size' and 'v_size' respectively.
-// Pointer to the luma plane ('*luma') is returned or NULL if an error occurred
-// during decoding (or because some buffers were found to be too small).
-WEBP_EXTERN uint8_t* WebPDecodeYUVInto(
-    const uint8_t* data, size_t data_size,
-    uint8_t* luma, size_t luma_size, int luma_stride,
-    uint8_t* u, size_t u_size, int u_stride,
-    uint8_t* v, size_t v_size, int v_stride);
-
-//------------------------------------------------------------------------------
-// Output colorspaces and buffer
-
-// Colorspaces
-// Note: the naming describes the byte-ordering of packed samples in memory.
-// For instance, MODE_BGRA relates to samples ordered as B,G,R,A,B,G,R,A,...
-// Non-capital names (e.g.:MODE_Argb) relates to pre-multiplied RGB channels.
-// RGBA-4444 and RGB-565 colorspaces are represented by following byte-order:
-// RGBA-4444: [r3 r2 r1 r0 g3 g2 g1 g0], [b3 b2 b1 b0 a3 a2 a1 a0], ...
-// RGB-565: [r4 r3 r2 r1 r0 g5 g4 g3], [g2 g1 g0 b4 b3 b2 b1 b0], ...
-// In the case WEBP_SWAP_16BITS_CSP is defined, the bytes are swapped for
-// these two modes:
-// RGBA-4444: [b3 b2 b1 b0 a3 a2 a1 a0], [r3 r2 r1 r0 g3 g2 g1 g0], ...
-// RGB-565: [g2 g1 g0 b4 b3 b2 b1 b0], [r4 r3 r2 r1 r0 g5 g4 g3], ...
-
-typedef enum WEBP_CSP_MODE {
-  MODE_RGB = 0, MODE_RGBA = 1,
-  MODE_BGR = 2, MODE_BGRA = 3,
-  MODE_ARGB = 4, MODE_RGBA_4444 = 5,
-  MODE_RGB_565 = 6,
-  // RGB-premultiplied transparent modes (alpha value is preserved)
-  MODE_rgbA = 7,
-  MODE_bgrA = 8,
-  MODE_Argb = 9,
-  MODE_rgbA_4444 = 10,
-  // YUV modes must come after RGB ones.
-  MODE_YUV = 11, MODE_YUVA = 12,  // yuv 4:2:0
-  MODE_LAST = 13
-} WEBP_CSP_MODE;
-
-// Some useful macros:
-static WEBP_INLINE int WebPIsPremultipliedMode(WEBP_CSP_MODE mode) {
-  return (mode == MODE_rgbA || mode == MODE_bgrA || mode == MODE_Argb ||
-          mode == MODE_rgbA_4444);
-}
-
-static WEBP_INLINE int WebPIsAlphaMode(WEBP_CSP_MODE mode) {
-  return (mode == MODE_RGBA || mode == MODE_BGRA || mode == MODE_ARGB ||
-          mode == MODE_RGBA_4444 || mode == MODE_YUVA ||
-          WebPIsPremultipliedMode(mode));
-}
-
-static WEBP_INLINE int WebPIsRGBMode(WEBP_CSP_MODE mode) {
-  return (mode < MODE_YUV);
-}
-
-//------------------------------------------------------------------------------
-// WebPDecBuffer: Generic structure for describing the output sample buffer.
-
-struct WebPRGBABuffer {    // view as RGBA
-  uint8_t* rgba;    // pointer to RGBA samples
-  int stride;       // stride in bytes from one scanline to the next.
-  size_t size;      // total size of the *rgba buffer.
-};
-
-struct WebPYUVABuffer {              // view as YUVA
-  uint8_t* y, *u, *v, *a;     // pointer to luma, chroma U/V, alpha samples
-  int y_stride;               // luma stride
-  int u_stride, v_stride;     // chroma strides
-  int a_stride;               // alpha stride
-  size_t y_size;              // luma plane size
-  size_t u_size, v_size;      // chroma planes size
-  size_t a_size;              // alpha-plane size
-};
-
-// Output buffer
-struct WebPDecBuffer {
-  WEBP_CSP_MODE colorspace;  // Colorspace.
-  int width, height;         // Dimensions.
-  int is_external_memory;    // If non-zero, 'internal_memory' pointer is not
-                             // used. If value is '2' or more, the external
-                             // memory is considered 'slow' and multiple
-                             // read/write will be avoided.
-  union {
-    WebPRGBABuffer RGBA;
-    WebPYUVABuffer YUVA;
-  } u;                       // Nameless union of buffer parameters.
-  uint32_t       pad[4];     // padding for later use
-
-  uint8_t* private_memory;   // Internally allocated memory (only when
-                             // is_external_memory is 0). Should not be used
-                             // externally, but accessed via the buffer union.
-};
-
-// Internal, version-checked, entry point
-WEBP_EXTERN int WebPInitDecBufferInternal(WebPDecBuffer*, int);
-
-// Initialize the structure as empty. Must be called before any other use.
-// Returns false in case of version mismatch
-static WEBP_INLINE int WebPInitDecBuffer(WebPDecBuffer* buffer) {
-  return WebPInitDecBufferInternal(buffer, WEBP_DECODER_ABI_VERSION);
-}
-
-// Free any memory associated with the buffer. Must always be called last.
-// Note: doesn't free the 'buffer' structure itself.
-WEBP_EXTERN void WebPFreeDecBuffer(WebPDecBuffer* buffer);
-
-//------------------------------------------------------------------------------
-// Enumeration of the status codes
-
-typedef enum VP8StatusCode {
-  VP8_STATUS_OK = 0,
-  VP8_STATUS_OUT_OF_MEMORY,
-  VP8_STATUS_INVALID_PARAM,
-  VP8_STATUS_BITSTREAM_ERROR,
-  VP8_STATUS_UNSUPPORTED_FEATURE,
-  VP8_STATUS_SUSPENDED,
-  VP8_STATUS_USER_ABORT,
-  VP8_STATUS_NOT_ENOUGH_DATA
-} VP8StatusCode;
-
-//------------------------------------------------------------------------------
-// Incremental decoding
-//
-// This API allows streamlined decoding of partial data.
-// Picture can be incrementally decoded as data become available thanks to the
-// WebPIDecoder object. This object can be left in a SUSPENDED state if the
-// picture is only partially decoded, pending additional input.
-// Code example:
-//
-//   WebPInitDecBuffer(&output_buffer);
-//   output_buffer.colorspace = mode;
-//   ...
-//   WebPIDecoder* idec = WebPINewDecoder(&output_buffer);
-//   while (additional_data_is_available) {
-//     // ... (get additional data in some new_data[] buffer)
-//     status = WebPIAppend(idec, new_data, new_data_size);
-//     if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) {
-//       break;    // an error occurred.
-//     }
-//
-//     // The above call decodes the current available buffer.
-//     // Part of the image can now be refreshed by calling
-//     // WebPIDecGetRGB()/WebPIDecGetYUVA() etc.
-//   }
-//   WebPIDelete(idec);
-
-// Creates a new incremental decoder with the supplied buffer parameter.
-// This output_buffer can be passed NULL, in which case a default output buffer
-// is used (with MODE_RGB). Otherwise, an internal reference to 'output_buffer'
-// is kept, which means that the lifespan of 'output_buffer' must be larger than
-// that of the returned WebPIDecoder object.
-// The supplied 'output_buffer' content MUST NOT be changed between calls to
-// WebPIAppend() or WebPIUpdate() unless 'output_buffer.is_external_memory' is
-// not set to 0. In such a case, it is allowed to modify the pointers, size and
-// stride of output_buffer.u.RGBA or output_buffer.u.YUVA, provided they remain
-// within valid bounds.
-// All other fields of WebPDecBuffer MUST remain constant between calls.
-// Returns NULL if the allocation failed.
-WEBP_EXTERN WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer);
-
-// This function allocates and initializes an incremental-decoder object, which
-// will output the RGB/A samples specified by 'csp' into a preallocated
-// buffer 'output_buffer'. The size of this buffer is at least
-// 'output_buffer_size' and the stride (distance in bytes between two scanlines)
-// is specified by 'output_stride'.
-// Additionally, output_buffer can be passed NULL in which case the output
-// buffer will be allocated automatically when the decoding starts. The
-// colorspace 'csp' is taken into account for allocating this buffer. All other
-// parameters are ignored.
-// Returns NULL if the allocation failed, or if some parameters are invalid.
-WEBP_EXTERN WebPIDecoder* WebPINewRGB(
-    WEBP_CSP_MODE csp,
-    uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
-
-// This function allocates and initializes an incremental-decoder object, which
-// will output the raw luma/chroma samples into a preallocated planes if
-// supplied. The luma plane is specified by its pointer 'luma', its size
-// 'luma_size' and its stride 'luma_stride'. Similarly, the chroma-u plane
-// is specified by the 'u', 'u_size' and 'u_stride' parameters, and the chroma-v
-// plane by 'v' and 'v_size'. And same for the alpha-plane. The 'a' pointer
-// can be pass NULL in case one is not interested in the transparency plane.
-// Conversely, 'luma' can be passed NULL if no preallocated planes are supplied.
-// In this case, the output buffer will be automatically allocated (using
-// MODE_YUVA) when decoding starts. All parameters are then ignored.
-// Returns NULL if the allocation failed or if a parameter is invalid.
-WEBP_EXTERN WebPIDecoder* WebPINewYUVA(
-    uint8_t* luma, size_t luma_size, int luma_stride,
-    uint8_t* u, size_t u_size, int u_stride,
-    uint8_t* v, size_t v_size, int v_stride,
-    uint8_t* a, size_t a_size, int a_stride);
-
-// Deprecated version of the above, without the alpha plane.
-// Kept for backward compatibility.
-WEBP_EXTERN WebPIDecoder* WebPINewYUV(
-    uint8_t* luma, size_t luma_size, int luma_stride,
-    uint8_t* u, size_t u_size, int u_stride,
-    uint8_t* v, size_t v_size, int v_stride);
-
-// Deletes the WebPIDecoder object and associated memory. Must always be called
-// if WebPINewDecoder, WebPINewRGB or WebPINewYUV succeeded.
-WEBP_EXTERN void WebPIDelete(WebPIDecoder* idec);
-
-// Copies and decodes the next available data. Returns VP8_STATUS_OK when
-// the image is successfully decoded. Returns VP8_STATUS_SUSPENDED when more
-// data is expected. Returns error in other cases.
-WEBP_EXTERN VP8StatusCode WebPIAppend(
-    WebPIDecoder* idec, const uint8_t* data, size_t data_size);
-
-// A variant of the above function to be used when data buffer contains
-// partial data from the beginning. In this case data buffer is not copied
-// to the internal memory.
-// Note that the value of the 'data' pointer can change between calls to
-// WebPIUpdate, for instance when the data buffer is resized to fit larger data.
-WEBP_EXTERN VP8StatusCode WebPIUpdate(
-    WebPIDecoder* idec, const uint8_t* data, size_t data_size);
-
-// Returns the RGB/A image decoded so far. Returns NULL if output params
-// are not initialized yet. The RGB/A output type corresponds to the colorspace
-// specified during call to WebPINewDecoder() or WebPINewRGB().
-// *last_y is the index of last decoded row in raster scan order. Some pointers
-// (*last_y, *width etc.) can be NULL if corresponding information is not
-// needed. The values in these pointers are only valid on successful (non-NULL)
-// return.
-WEBP_EXTERN uint8_t* WebPIDecGetRGB(
-    const WebPIDecoder* idec, int* last_y,
-    int* width, int* height, int* stride);
-
-// Same as above function to get a YUVA image. Returns pointer to the luma
-// plane or NULL in case of error. If there is no alpha information
-// the alpha pointer '*a' will be returned NULL.
-WEBP_EXTERN uint8_t* WebPIDecGetYUVA(
-    const WebPIDecoder* idec, int* last_y,
-    uint8_t** u, uint8_t** v, uint8_t** a,
-    int* width, int* height, int* stride, int* uv_stride, int* a_stride);
-
-// Deprecated alpha-less version of WebPIDecGetYUVA(): it will ignore the
-// alpha information (if present). Kept for backward compatibility.
-static WEBP_INLINE uint8_t* WebPIDecGetYUV(
-    const WebPIDecoder* idec, int* last_y, uint8_t** u, uint8_t** v,
-    int* width, int* height, int* stride, int* uv_stride) {
-  return WebPIDecGetYUVA(idec, last_y, u, v, NULL, width, height,
-                         stride, uv_stride, NULL);
-}
-
-// Generic call to retrieve information about the displayable area.
-// If non NULL, the left/right/width/height pointers are filled with the visible
-// rectangular area so far.
-// Returns NULL in case the incremental decoder object is in an invalid state.
-// Otherwise returns the pointer to the internal representation. This structure
-// is read-only, tied to WebPIDecoder's lifespan and should not be modified.
-WEBP_EXTERN const WebPDecBuffer* WebPIDecodedArea(
-    const WebPIDecoder* idec, int* left, int* top, int* width, int* height);
-
-//------------------------------------------------------------------------------
-// Advanced decoding parametrization
-//
-//  Code sample for using the advanced decoding API
-/*
-     // A) Init a configuration object
-     WebPDecoderConfig config;
-     CHECK(WebPInitDecoderConfig(&config));
-
-     // B) optional: retrieve the bitstream's features.
-     CHECK(WebPGetFeatures(data, data_size, &config.input) == VP8_STATUS_OK);
-
-     // C) Adjust 'config', if needed
-     config.no_fancy_upsampling = 1;
-     config.output.colorspace = MODE_BGRA;
-     // etc.
-
-     // Note that you can also make config.output point to an externally
-     // supplied memory buffer, provided it's big enough to store the decoded
-     // picture. Otherwise, config.output will just be used to allocate memory
-     // and store the decoded picture.
-
-     // D) Decode!
-     CHECK(WebPDecode(data, data_size, &config) == VP8_STATUS_OK);
-
-     // E) Decoded image is now in config.output (and config.output.u.RGBA)
-
-     // F) Reclaim memory allocated in config's object. It's safe to call
-     // this function even if the memory is external and wasn't allocated
-     // by WebPDecode().
-     WebPFreeDecBuffer(&config.output);
-*/
-
-// Features gathered from the bitstream
-struct WebPBitstreamFeatures {
-  int width;          // Width in pixels, as read from the bitstream.
-  int height;         // Height in pixels, as read from the bitstream.
-  int has_alpha;      // True if the bitstream contains an alpha channel.
-  int has_animation;  // True if the bitstream is an animation.
-  int format;         // 0 = undefined (/mixed), 1 = lossy, 2 = lossless
-
-  uint32_t pad[5];    // padding for later use
-};
-
-// Internal, version-checked, entry point
-WEBP_EXTERN VP8StatusCode WebPGetFeaturesInternal(
-    const uint8_t*, size_t, WebPBitstreamFeatures*, int);
-
-// Retrieve features from the bitstream. The *features structure is filled
-// with information gathered from the bitstream.
-// Returns VP8_STATUS_OK when the features are successfully retrieved. Returns
-// VP8_STATUS_NOT_ENOUGH_DATA when more data is needed to retrieve the
-// features from headers. Returns error in other cases.
-// Note: The following chunk sequences (before the raw VP8/VP8L data) are
-// considered valid by this function:
-// RIFF + VP8(L)
-// RIFF + VP8X + (optional chunks) + VP8(L)
-// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
-// VP8(L)     <-- Not a valid WebP format: only allowed for internal purpose.
-static WEBP_INLINE VP8StatusCode WebPGetFeatures(
-    const uint8_t* data, size_t data_size,
-    WebPBitstreamFeatures* features) {
-  return WebPGetFeaturesInternal(data, data_size, features,
-                                 WEBP_DECODER_ABI_VERSION);
-}
-
-// Decoding options
-struct WebPDecoderOptions {
-  int bypass_filtering;               // if true, skip the in-loop filtering
-  int no_fancy_upsampling;            // if true, use faster pointwise upsampler
-  int use_cropping;                   // if true, cropping is applied _first_
-  int crop_left, crop_top;            // top-left position for cropping.
-                                      // Will be snapped to even values.
-  int crop_width, crop_height;        // dimension of the cropping area
-  int use_scaling;                    // if true, scaling is applied _afterward_
-  int scaled_width, scaled_height;    // final resolution
-  int use_threads;                    // if true, use multi-threaded decoding
-  int dithering_strength;             // dithering strength (0=Off, 100=full)
-  int flip;                           // if true, flip output vertically
-  int alpha_dithering_strength;       // alpha dithering strength in [0..100]
-
-  uint32_t pad[5];                    // padding for later use
-};
-
-// Main object storing the configuration for advanced decoding.
-struct WebPDecoderConfig {
-  WebPBitstreamFeatures input;  // Immutable bitstream features (optional)
-  WebPDecBuffer output;         // Output buffer (can point to external mem)
-  WebPDecoderOptions options;   // Decoding options
-};
-
-// Internal, version-checked, entry point
-WEBP_EXTERN int WebPInitDecoderConfigInternal(WebPDecoderConfig*, int);
-
-// Initialize the configuration as empty. This function must always be
-// called first, unless WebPGetFeatures() is to be called.
-// Returns false in case of mismatched version.
-static WEBP_INLINE int WebPInitDecoderConfig(WebPDecoderConfig* config) {
-  return WebPInitDecoderConfigInternal(config, WEBP_DECODER_ABI_VERSION);
-}
-
-// Instantiate a new incremental decoder object with the requested
-// configuration. The bitstream can be passed using 'data' and 'data_size'
-// parameter, in which case the features will be parsed and stored into
-// config->input. Otherwise, 'data' can be NULL and no parsing will occur.
-// Note that 'config' can be NULL too, in which case a default configuration
-// is used. If 'config' is not NULL, it must outlive the WebPIDecoder object
-// as some references to its fields will be used. No internal copy of 'config'
-// is made.
-// The return WebPIDecoder object must always be deleted calling WebPIDelete().
-// Returns NULL in case of error (and config->status will then reflect
-// the error condition, if available).
-WEBP_EXTERN WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size,
-                                      WebPDecoderConfig* config);
-
-// Non-incremental version. This version decodes the full data at once, taking
-// 'config' into account. Returns decoding status (which should be VP8_STATUS_OK
-// if the decoding was successful). Note that 'config' cannot be NULL.
-WEBP_EXTERN VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
-                                     WebPDecoderConfig* config);
-
-#ifdef __cplusplus
-}    // extern "C"
-#endif
-
-#endif  // WEBP_WEBP_DECODE_H_
diff --git a/include/webp/demux.h b/include/webp/demux.h
deleted file mode 100644
index 846eeb1..0000000
--- a/include/webp/demux.h
+++ /dev/null
@@ -1,363 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Demux API.
-// Enables extraction of image and extended format data from WebP files.
-
-// Code Example: Demuxing WebP data to extract all the frames, ICC profile
-// and EXIF/XMP metadata.
-/*
-  WebPDemuxer* demux = WebPDemux(&webp_data);
-
-  uint32_t width = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH);
-  uint32_t height = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT);
-  // ... (Get information about the features present in the WebP file).
-  uint32_t flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS);
-
-  // ... (Iterate over all frames).
-  WebPIterator iter;
-  if (WebPDemuxGetFrame(demux, 1, &iter)) {
-    do {
-      // ... (Consume 'iter'; e.g. Decode 'iter.fragment' with WebPDecode(),
-      // ... and get other frame properties like width, height, offsets etc.
-      // ... see 'struct WebPIterator' below for more info).
-    } while (WebPDemuxNextFrame(&iter));
-    WebPDemuxReleaseIterator(&iter);
-  }
-
-  // ... (Extract metadata).
-  WebPChunkIterator chunk_iter;
-  if (flags & ICCP_FLAG) WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter);
-  // ... (Consume the ICC profile in 'chunk_iter.chunk').
-  WebPDemuxReleaseChunkIterator(&chunk_iter);
-  if (flags & EXIF_FLAG) WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter);
-  // ... (Consume the EXIF metadata in 'chunk_iter.chunk').
-  WebPDemuxReleaseChunkIterator(&chunk_iter);
-  if (flags & XMP_FLAG) WebPDemuxGetChunk(demux, "XMP ", 1, &chunk_iter);
-  // ... (Consume the XMP metadata in 'chunk_iter.chunk').
-  WebPDemuxReleaseChunkIterator(&chunk_iter);
-  WebPDemuxDelete(demux);
-*/
-
-#ifndef WEBP_WEBP_DEMUX_H_
-#define WEBP_WEBP_DEMUX_H_
-
-#include "./decode.h"     // for WEBP_CSP_MODE
-#include "./mux_types.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define WEBP_DEMUX_ABI_VERSION 0x0107    // MAJOR(8b) + MINOR(8b)
-
-// Note: forward declaring enumerations is not allowed in (strict) C and C++,
-// the types are left here for reference.
-// typedef enum WebPDemuxState WebPDemuxState;
-// typedef enum WebPFormatFeature WebPFormatFeature;
-typedef struct WebPDemuxer WebPDemuxer;
-typedef struct WebPIterator WebPIterator;
-typedef struct WebPChunkIterator WebPChunkIterator;
-typedef struct WebPAnimInfo WebPAnimInfo;
-typedef struct WebPAnimDecoderOptions WebPAnimDecoderOptions;
-
-//------------------------------------------------------------------------------
-
-// Returns the version number of the demux library, packed in hexadecimal using
-// 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507.
-WEBP_EXTERN int WebPGetDemuxVersion(void);
-
-//------------------------------------------------------------------------------
-// Life of a Demux object
-
-typedef enum WebPDemuxState {
-  WEBP_DEMUX_PARSE_ERROR    = -1,  // An error occurred while parsing.
-  WEBP_DEMUX_PARSING_HEADER =  0,  // Not enough data to parse full header.
-  WEBP_DEMUX_PARSED_HEADER  =  1,  // Header parsing complete,
-                                   // data may be available.
-  WEBP_DEMUX_DONE           =  2   // Entire file has been parsed.
-} WebPDemuxState;
-
-// Internal, version-checked, entry point
-WEBP_EXTERN WebPDemuxer* WebPDemuxInternal(
-    const WebPData*, int, WebPDemuxState*, int);
-
-// Parses the full WebP file given by 'data'. For single images the WebP file
-// header alone or the file header and the chunk header may be absent.
-// Returns a WebPDemuxer object on successful parse, NULL otherwise.
-static WEBP_INLINE WebPDemuxer* WebPDemux(const WebPData* data) {
-  return WebPDemuxInternal(data, 0, NULL, WEBP_DEMUX_ABI_VERSION);
-}
-
-// Parses the possibly incomplete WebP file given by 'data'.
-// If 'state' is non-NULL it will be set to indicate the status of the demuxer.
-// Returns NULL in case of error or if there isn't enough data to start parsing;
-// and a WebPDemuxer object on successful parse.
-// Note that WebPDemuxer keeps internal pointers to 'data' memory segment.
-// If this data is volatile, the demuxer object should be deleted (by calling
-// WebPDemuxDelete()) and WebPDemuxPartial() called again on the new data.
-// This is usually an inexpensive operation.
-static WEBP_INLINE WebPDemuxer* WebPDemuxPartial(
-    const WebPData* data, WebPDemuxState* state) {
-  return WebPDemuxInternal(data, 1, state, WEBP_DEMUX_ABI_VERSION);
-}
-
-// Frees memory associated with 'dmux'.
-WEBP_EXTERN void WebPDemuxDelete(WebPDemuxer* dmux);
-
-//------------------------------------------------------------------------------
-// Data/information extraction.
-
-typedef enum WebPFormatFeature {
-  WEBP_FF_FORMAT_FLAGS,      // bit-wise combination of WebPFeatureFlags
-                             // corresponding to the 'VP8X' chunk (if present).
-  WEBP_FF_CANVAS_WIDTH,
-  WEBP_FF_CANVAS_HEIGHT,
-  WEBP_FF_LOOP_COUNT,        // only relevant for animated file
-  WEBP_FF_BACKGROUND_COLOR,  // idem.
-  WEBP_FF_FRAME_COUNT        // Number of frames present in the demux object.
-                             // In case of a partial demux, this is the number
-                             // of frames seen so far, with the last frame
-                             // possibly being partial.
-} WebPFormatFeature;
-
-// Get the 'feature' value from the 'dmux'.
-// NOTE: values are only valid if WebPDemux() was used or WebPDemuxPartial()
-// returned a state > WEBP_DEMUX_PARSING_HEADER.
-// If 'feature' is WEBP_FF_FORMAT_FLAGS, the returned value is a bit-wise
-// combination of WebPFeatureFlags values.
-// If 'feature' is WEBP_FF_LOOP_COUNT, WEBP_FF_BACKGROUND_COLOR, the returned
-// value is only meaningful if the bitstream is animated.
-WEBP_EXTERN uint32_t WebPDemuxGetI(
-    const WebPDemuxer* dmux, WebPFormatFeature feature);
-
-//------------------------------------------------------------------------------
-// Frame iteration.
-
-struct WebPIterator {
-  int frame_num;
-  int num_frames;          // equivalent to WEBP_FF_FRAME_COUNT.
-  int x_offset, y_offset;  // offset relative to the canvas.
-  int width, height;       // dimensions of this frame.
-  int duration;            // display duration in milliseconds.
-  WebPMuxAnimDispose dispose_method;  // dispose method for the frame.
-  int complete;   // true if 'fragment' contains a full frame. partial images
-                  // may still be decoded with the WebP incremental decoder.
-  WebPData fragment;  // The frame given by 'frame_num'. Note for historical
-                      // reasons this is called a fragment.
-  int has_alpha;      // True if the frame contains transparency.
-  WebPMuxAnimBlend blend_method;  // Blend operation for the frame.
-
-  uint32_t pad[2];         // padding for later use.
-  void* private_;          // for internal use only.
-};
-
-// Retrieves frame 'frame_number' from 'dmux'.
-// 'iter->fragment' points to the frame on return from this function.
-// Setting 'frame_number' equal to 0 will return the last frame of the image.
-// Returns false if 'dmux' is NULL or frame 'frame_number' is not present.
-// Call WebPDemuxReleaseIterator() when use of the iterator is complete.
-// NOTE: 'dmux' must persist for the lifetime of 'iter'.
-WEBP_EXTERN int WebPDemuxGetFrame(
-    const WebPDemuxer* dmux, int frame_number, WebPIterator* iter);
-
-// Sets 'iter->fragment' to point to the next ('iter->frame_num' + 1) or
-// previous ('iter->frame_num' - 1) frame. These functions do not loop.
-// Returns true on success, false otherwise.
-WEBP_EXTERN int WebPDemuxNextFrame(WebPIterator* iter);
-WEBP_EXTERN int WebPDemuxPrevFrame(WebPIterator* iter);
-
-// Releases any memory associated with 'iter'.
-// Must be called before any subsequent calls to WebPDemuxGetChunk() on the same
-// iter. Also, must be called before destroying the associated WebPDemuxer with
-// WebPDemuxDelete().
-WEBP_EXTERN void WebPDemuxReleaseIterator(WebPIterator* iter);
-
-//------------------------------------------------------------------------------
-// Chunk iteration.
-
-struct WebPChunkIterator {
-  // The current and total number of chunks with the fourcc given to
-  // WebPDemuxGetChunk().
-  int chunk_num;
-  int num_chunks;
-  WebPData chunk;    // The payload of the chunk.
-
-  uint32_t pad[6];   // padding for later use
-  void* private_;
-};
-
-// Retrieves the 'chunk_number' instance of the chunk with id 'fourcc' from
-// 'dmux'.
-// 'fourcc' is a character array containing the fourcc of the chunk to return,
-// e.g., "ICCP", "XMP ", "EXIF", etc.
-// Setting 'chunk_number' equal to 0 will return the last chunk in a set.
-// Returns true if the chunk is found, false otherwise. Image related chunk
-// payloads are accessed through WebPDemuxGetFrame() and related functions.
-// Call WebPDemuxReleaseChunkIterator() when use of the iterator is complete.
-// NOTE: 'dmux' must persist for the lifetime of the iterator.
-WEBP_EXTERN int WebPDemuxGetChunk(const WebPDemuxer* dmux,
-                                  const char fourcc[4], int chunk_number,
-                                  WebPChunkIterator* iter);
-
-// Sets 'iter->chunk' to point to the next ('iter->chunk_num' + 1) or previous
-// ('iter->chunk_num' - 1) chunk. These functions do not loop.
-// Returns true on success, false otherwise.
-WEBP_EXTERN int WebPDemuxNextChunk(WebPChunkIterator* iter);
-WEBP_EXTERN int WebPDemuxPrevChunk(WebPChunkIterator* iter);
-
-// Releases any memory associated with 'iter'.
-// Must be called before destroying the associated WebPDemuxer with
-// WebPDemuxDelete().
-WEBP_EXTERN void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter);
-
-//------------------------------------------------------------------------------
-// WebPAnimDecoder API
-//
-// This API allows decoding (possibly) animated WebP images.
-//
-// Code Example:
-/*
-  WebPAnimDecoderOptions dec_options;
-  WebPAnimDecoderOptionsInit(&dec_options);
-  // Tune 'dec_options' as needed.
-  WebPAnimDecoder* dec = WebPAnimDecoderNew(webp_data, &dec_options);
-  WebPAnimInfo anim_info;
-  WebPAnimDecoderGetInfo(dec, &anim_info);
-  for (uint32_t i = 0; i < anim_info.loop_count; ++i) {
-    while (WebPAnimDecoderHasMoreFrames(dec)) {
-      uint8_t* buf;
-      int timestamp;
-      WebPAnimDecoderGetNext(dec, &buf, &timestamp);
-      // ... (Render 'buf' based on 'timestamp').
-      // ... (Do NOT free 'buf', as it is owned by 'dec').
-    }
-    WebPAnimDecoderReset(dec);
-  }
-  const WebPDemuxer* demuxer = WebPAnimDecoderGetDemuxer(dec);
-  // ... (Do something using 'demuxer'; e.g. get EXIF/XMP/ICC data).
-  WebPAnimDecoderDelete(dec);
-*/
-
-typedef struct WebPAnimDecoder WebPAnimDecoder;  // Main opaque object.
-
-// Global options.
-struct WebPAnimDecoderOptions {
-  // Output colorspace. Only the following modes are supported:
-  // MODE_RGBA, MODE_BGRA, MODE_rgbA and MODE_bgrA.
-  WEBP_CSP_MODE color_mode;
-  int use_threads;           // If true, use multi-threaded decoding.
-  uint32_t padding[7];       // Padding for later use.
-};
-
-// Internal, version-checked, entry point.
-WEBP_EXTERN int WebPAnimDecoderOptionsInitInternal(
-    WebPAnimDecoderOptions*, int);
-
-// Should always be called, to initialize a fresh WebPAnimDecoderOptions
-// structure before modification. Returns false in case of version mismatch.
-// WebPAnimDecoderOptionsInit() must have succeeded before using the
-// 'dec_options' object.
-static WEBP_INLINE int WebPAnimDecoderOptionsInit(
-    WebPAnimDecoderOptions* dec_options) {
-  return WebPAnimDecoderOptionsInitInternal(dec_options,
-                                            WEBP_DEMUX_ABI_VERSION);
-}
-
-// Internal, version-checked, entry point.
-WEBP_EXTERN WebPAnimDecoder* WebPAnimDecoderNewInternal(
-    const WebPData*, const WebPAnimDecoderOptions*, int);
-
-// Creates and initializes a WebPAnimDecoder object.
-// Parameters:
-//   webp_data - (in) WebP bitstream. This should remain unchanged during the
-//                    lifetime of the output WebPAnimDecoder object.
-//   dec_options - (in) decoding options. Can be passed NULL to choose
-//                      reasonable defaults (in particular, color mode MODE_RGBA
-//                      will be picked).
-// Returns:
-//   A pointer to the newly created WebPAnimDecoder object, or NULL in case of
-//   parsing error, invalid option or memory error.
-static WEBP_INLINE WebPAnimDecoder* WebPAnimDecoderNew(
-    const WebPData* webp_data, const WebPAnimDecoderOptions* dec_options) {
-  return WebPAnimDecoderNewInternal(webp_data, dec_options,
-                                    WEBP_DEMUX_ABI_VERSION);
-}
-
-// Global information about the animation..
-struct WebPAnimInfo {
-  uint32_t canvas_width;
-  uint32_t canvas_height;
-  uint32_t loop_count;
-  uint32_t bgcolor;
-  uint32_t frame_count;
-  uint32_t pad[4];   // padding for later use
-};
-
-// Get global information about the animation.
-// Parameters:
-//   dec - (in) decoder instance to get information from.
-//   info - (out) global information fetched from the animation.
-// Returns:
-//   True on success.
-WEBP_EXTERN int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec,
-                                       WebPAnimInfo* info);
-
-// Fetch the next frame from 'dec' based on options supplied to
-// WebPAnimDecoderNew(). This will be a fully reconstructed canvas of size
-// 'canvas_width * 4 * canvas_height', and not just the frame sub-rectangle. The
-// returned buffer 'buf' is valid only until the next call to
-// WebPAnimDecoderGetNext(), WebPAnimDecoderReset() or WebPAnimDecoderDelete().
-// Parameters:
-//   dec - (in/out) decoder instance from which the next frame is to be fetched.
-//   buf - (out) decoded frame.
-//   timestamp - (out) timestamp of the frame in milliseconds.
-// Returns:
-//   False if any of the arguments are NULL, or if there is a parsing or
-//   decoding error, or if there are no more frames. Otherwise, returns true.
-WEBP_EXTERN int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
-                                       uint8_t** buf, int* timestamp);
-
-// Check if there are more frames left to decode.
-// Parameters:
-//   dec - (in) decoder instance to be checked.
-// Returns:
-//   True if 'dec' is not NULL and some frames are yet to be decoded.
-//   Otherwise, returns false.
-WEBP_EXTERN int WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec);
-
-// Resets the WebPAnimDecoder object, so that next call to
-// WebPAnimDecoderGetNext() will restart decoding from 1st frame. This would be
-// helpful when all frames need to be decoded multiple times (e.g.
-// info.loop_count times) without destroying and recreating the 'dec' object.
-// Parameters:
-//   dec - (in/out) decoder instance to be reset
-WEBP_EXTERN void WebPAnimDecoderReset(WebPAnimDecoder* dec);
-
-// Grab the internal demuxer object.
-// Getting the demuxer object can be useful if one wants to use operations only
-// available through demuxer; e.g. to get XMP/EXIF/ICC metadata. The returned
-// demuxer object is owned by 'dec' and is valid only until the next call to
-// WebPAnimDecoderDelete().
-//
-// Parameters:
-//   dec - (in) decoder instance from which the demuxer object is to be fetched.
-WEBP_EXTERN const WebPDemuxer* WebPAnimDecoderGetDemuxer(
-    const WebPAnimDecoder* dec);
-
-// Deletes the WebPAnimDecoder object.
-// Parameters:
-//   dec - (in/out) decoder instance to be deleted
-WEBP_EXTERN void WebPAnimDecoderDelete(WebPAnimDecoder* dec);
-
-#ifdef __cplusplus
-}    // extern "C"
-#endif
-
-#endif  // WEBP_WEBP_DEMUX_H_
diff --git a/include/webp/encode.h b/include/webp/encode.h
deleted file mode 100644
index b4c599d..0000000
--- a/include/webp/encode.h
+++ /dev/null
@@ -1,552 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-//   WebP encoder: main interface
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#ifndef WEBP_WEBP_ENCODE_H_
-#define WEBP_WEBP_ENCODE_H_
-
-#include "./types.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define WEBP_ENCODER_ABI_VERSION 0x020f    // MAJOR(8b) + MINOR(8b)
-
-// Note: forward declaring enumerations is not allowed in (strict) C and C++,
-// the types are left here for reference.
-// typedef enum WebPImageHint WebPImageHint;
-// typedef enum WebPEncCSP WebPEncCSP;
-// typedef enum WebPPreset WebPPreset;
-// typedef enum WebPEncodingError WebPEncodingError;
-typedef struct WebPConfig WebPConfig;
-typedef struct WebPPicture WebPPicture;   // main structure for I/O
-typedef struct WebPAuxStats WebPAuxStats;
-typedef struct WebPMemoryWriter WebPMemoryWriter;
-
-// Return the encoder's version number, packed in hexadecimal using 8bits for
-// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
-WEBP_EXTERN int WebPGetEncoderVersion(void);
-
-//------------------------------------------------------------------------------
-// One-stop-shop call! No questions asked:
-
-// Returns the size of the compressed data (pointed to by *output), or 0 if
-// an error occurred. The compressed data must be released by the caller
-// using the call 'WebPFree(*output)'.
-// These functions compress using the lossy format, and the quality_factor
-// can go from 0 (smaller output, lower quality) to 100 (best quality,
-// larger output).
-WEBP_EXTERN size_t WebPEncodeRGB(const uint8_t* rgb,
-                                 int width, int height, int stride,
-                                 float quality_factor, uint8_t** output);
-WEBP_EXTERN size_t WebPEncodeBGR(const uint8_t* bgr,
-                                 int width, int height, int stride,
-                                 float quality_factor, uint8_t** output);
-WEBP_EXTERN size_t WebPEncodeRGBA(const uint8_t* rgba,
-                                  int width, int height, int stride,
-                                  float quality_factor, uint8_t** output);
-WEBP_EXTERN size_t WebPEncodeBGRA(const uint8_t* bgra,
-                                  int width, int height, int stride,
-                                  float quality_factor, uint8_t** output);
-
-// These functions are the equivalent of the above, but compressing in a
-// lossless manner. Files are usually larger than lossy format, but will
-// not suffer any compression loss.
-// Note these functions, like the lossy versions, use the library's default
-// settings. For lossless this means 'exact' is disabled. RGB values in
-// transparent areas will be modified to improve compression. To avoid this,
-// use WebPEncode() and set WebPConfig::exact to 1.
-WEBP_EXTERN size_t WebPEncodeLosslessRGB(const uint8_t* rgb,
-                                         int width, int height, int stride,
-                                         uint8_t** output);
-WEBP_EXTERN size_t WebPEncodeLosslessBGR(const uint8_t* bgr,
-                                         int width, int height, int stride,
-                                         uint8_t** output);
-WEBP_EXTERN size_t WebPEncodeLosslessRGBA(const uint8_t* rgba,
-                                          int width, int height, int stride,
-                                          uint8_t** output);
-WEBP_EXTERN size_t WebPEncodeLosslessBGRA(const uint8_t* bgra,
-                                          int width, int height, int stride,
-                                          uint8_t** output);
-
-//------------------------------------------------------------------------------
-// Coding parameters
-
-// Image characteristics hint for the underlying encoder.
-typedef enum WebPImageHint {
-  WEBP_HINT_DEFAULT = 0,  // default preset.
-  WEBP_HINT_PICTURE,      // digital picture, like portrait, inner shot
-  WEBP_HINT_PHOTO,        // outdoor photograph, with natural lighting
-  WEBP_HINT_GRAPH,        // Discrete tone image (graph, map-tile etc).
-  WEBP_HINT_LAST
-} WebPImageHint;
-
-// Compression parameters.
-struct WebPConfig {
-  int lossless;           // Lossless encoding (0=lossy(default), 1=lossless).
-  float quality;          // between 0 and 100. For lossy, 0 gives the smallest
-                          // size and 100 the largest. For lossless, this
-                          // parameter is the amount of effort put into the
-                          // compression: 0 is the fastest but gives larger
-                          // files compared to the slowest, but best, 100.
-  int method;             // quality/speed trade-off (0=fast, 6=slower-better)
-
-  WebPImageHint image_hint;  // Hint for image type (lossless only for now).
-
-  int target_size;        // if non-zero, set the desired target size in bytes.
-                          // Takes precedence over the 'compression' parameter.
-  float target_PSNR;      // if non-zero, specifies the minimal distortion to
-                          // try to achieve. Takes precedence over target_size.
-  int segments;           // maximum number of segments to use, in [1..4]
-  int sns_strength;       // Spatial Noise Shaping. 0=off, 100=maximum.
-  int filter_strength;    // range: [0 = off .. 100 = strongest]
-  int filter_sharpness;   // range: [0 = off .. 7 = least sharp]
-  int filter_type;        // filtering type: 0 = simple, 1 = strong (only used
-                          // if filter_strength > 0 or autofilter > 0)
-  int autofilter;         // Auto adjust filter's strength [0 = off, 1 = on]
-  int alpha_compression;  // Algorithm for encoding the alpha plane (0 = none,
-                          // 1 = compressed with WebP lossless). Default is 1.
-  int alpha_filtering;    // Predictive filtering method for alpha plane.
-                          //  0: none, 1: fast, 2: best. Default if 1.
-  int alpha_quality;      // Between 0 (smallest size) and 100 (lossless).
-                          // Default is 100.
-  int pass;               // number of entropy-analysis passes (in [1..10]).
-
-  int show_compressed;    // if true, export the compressed picture back.
-                          // In-loop filtering is not applied.
-  int preprocessing;      // preprocessing filter:
-                          // 0=none, 1=segment-smooth, 2=pseudo-random dithering
-  int partitions;         // log2(number of token partitions) in [0..3]. Default
-                          // is set to 0 for easier progressive decoding.
-  int partition_limit;    // quality degradation allowed to fit the 512k limit
-                          // on prediction modes coding (0: no degradation,
-                          // 100: maximum possible degradation).
-  int emulate_jpeg_size;  // If true, compression parameters will be remapped
-                          // to better match the expected output size from
-                          // JPEG compression. Generally, the output size will
-                          // be similar but the degradation will be lower.
-  int thread_level;       // If non-zero, try and use multi-threaded encoding.
-  int low_memory;         // If set, reduce memory usage (but increase CPU use).
-
-  int near_lossless;      // Near lossless encoding [0 = max loss .. 100 = off
-                          // (default)].
-  int exact;              // if non-zero, preserve the exact RGB values under
-                          // transparent area. Otherwise, discard this invisible
-                          // RGB information for better compression. The default
-                          // value is 0.
-
-  int use_delta_palette;  // reserved for future lossless feature
-  int use_sharp_yuv;      // if needed, use sharp (and slow) RGB->YUV conversion
-
-  int qmin;               // minimum permissible quality factor
-  int qmax;               // maximum permissible quality factor
-};
-
-// Enumerate some predefined settings for WebPConfig, depending on the type
-// of source picture. These presets are used when calling WebPConfigPreset().
-typedef enum WebPPreset {
-  WEBP_PRESET_DEFAULT = 0,  // default preset.
-  WEBP_PRESET_PICTURE,      // digital picture, like portrait, inner shot
-  WEBP_PRESET_PHOTO,        // outdoor photograph, with natural lighting
-  WEBP_PRESET_DRAWING,      // hand or line drawing, with high-contrast details
-  WEBP_PRESET_ICON,         // small-sized colorful images
-  WEBP_PRESET_TEXT          // text-like
-} WebPPreset;
-
-// Internal, version-checked, entry point
-WEBP_EXTERN int WebPConfigInitInternal(WebPConfig*, WebPPreset, float, int);
-
-// Should always be called, to initialize a fresh WebPConfig structure before
-// modification. Returns false in case of version mismatch. WebPConfigInit()
-// must have succeeded before using the 'config' object.
-// Note that the default values are lossless=0 and quality=75.
-static WEBP_INLINE int WebPConfigInit(WebPConfig* config) {
-  return WebPConfigInitInternal(config, WEBP_PRESET_DEFAULT, 75.f,
-                                WEBP_ENCODER_ABI_VERSION);
-}
-
-// This function will initialize the configuration according to a predefined
-// set of parameters (referred to by 'preset') and a given quality factor.
-// This function can be called as a replacement to WebPConfigInit(). Will
-// return false in case of error.
-static WEBP_INLINE int WebPConfigPreset(WebPConfig* config,
-                                        WebPPreset preset, float quality) {
-  return WebPConfigInitInternal(config, preset, quality,
-                                WEBP_ENCODER_ABI_VERSION);
-}
-
-// Activate the lossless compression mode with the desired efficiency level
-// between 0 (fastest, lowest compression) and 9 (slower, best compression).
-// A good default level is '6', providing a fair tradeoff between compression
-// speed and final compressed size.
-// This function will overwrite several fields from config: 'method', 'quality'
-// and 'lossless'. Returns false in case of parameter error.
-WEBP_EXTERN int WebPConfigLosslessPreset(WebPConfig* config, int level);
-
-// Returns true if 'config' is non-NULL and all configuration parameters are
-// within their valid ranges.
-WEBP_EXTERN int WebPValidateConfig(const WebPConfig* config);
-
-//------------------------------------------------------------------------------
-// Input / Output
-// Structure for storing auxiliary statistics.
-
-struct WebPAuxStats {
-  int coded_size;         // final size
-
-  float PSNR[5];          // peak-signal-to-noise ratio for Y/U/V/All/Alpha
-  int block_count[3];     // number of intra4/intra16/skipped macroblocks
-  int header_bytes[2];    // approximate number of bytes spent for header
-                          // and mode-partition #0
-  int residual_bytes[3][4];  // approximate number of bytes spent for
-                             // DC/AC/uv coefficients for each (0..3) segments.
-  int segment_size[4];    // number of macroblocks in each segments
-  int segment_quant[4];   // quantizer values for each segments
-  int segment_level[4];   // filtering strength for each segments [0..63]
-
-  int alpha_data_size;    // size of the transparency data
-  int layer_data_size;    // size of the enhancement layer data
-
-  // lossless encoder statistics
-  uint32_t lossless_features;  // bit0:predictor bit1:cross-color transform
-                               // bit2:subtract-green bit3:color indexing
-  int histogram_bits;          // number of precision bits of histogram
-  int transform_bits;          // precision bits for transform
-  int cache_bits;              // number of bits for color cache lookup
-  int palette_size;            // number of color in palette, if used
-  int lossless_size;           // final lossless size
-  int lossless_hdr_size;       // lossless header (transform, huffman etc) size
-  int lossless_data_size;      // lossless image data size
-
-  uint32_t pad[2];        // padding for later use
-};
-
-// Signature for output function. Should return true if writing was successful.
-// data/data_size is the segment of data to write, and 'picture' is for
-// reference (and so one can make use of picture->custom_ptr).
-typedef int (*WebPWriterFunction)(const uint8_t* data, size_t data_size,
-                                  const WebPPicture* picture);
-
-// WebPMemoryWrite: a special WebPWriterFunction that writes to memory using
-// the following WebPMemoryWriter object (to be set as a custom_ptr).
-struct WebPMemoryWriter {
-  uint8_t* mem;       // final buffer (of size 'max_size', larger than 'size').
-  size_t   size;      // final size
-  size_t   max_size;  // total capacity
-  uint32_t pad[1];    // padding for later use
-};
-
-// The following must be called first before any use.
-WEBP_EXTERN void WebPMemoryWriterInit(WebPMemoryWriter* writer);
-
-// The following must be called to deallocate writer->mem memory. The 'writer'
-// object itself is not deallocated.
-WEBP_EXTERN void WebPMemoryWriterClear(WebPMemoryWriter* writer);
-// The custom writer to be used with WebPMemoryWriter as custom_ptr. Upon
-// completion, writer.mem and writer.size will hold the coded data.
-// writer.mem must be freed by calling WebPMemoryWriterClear.
-WEBP_EXTERN int WebPMemoryWrite(const uint8_t* data, size_t data_size,
-                                const WebPPicture* picture);
-
-// Progress hook, called from time to time to report progress. It can return
-// false to request an abort of the encoding process, or true otherwise if
-// everything is OK.
-typedef int (*WebPProgressHook)(int percent, const WebPPicture* picture);
-
-// Color spaces.
-typedef enum WebPEncCSP {
-  // chroma sampling
-  WEBP_YUV420  = 0,        // 4:2:0
-  WEBP_YUV420A = 4,        // alpha channel variant
-  WEBP_CSP_UV_MASK = 3,    // bit-mask to get the UV sampling factors
-  WEBP_CSP_ALPHA_BIT = 4   // bit that is set if alpha is present
-} WebPEncCSP;
-
-// Encoding error conditions.
-typedef enum WebPEncodingError {
-  VP8_ENC_OK = 0,
-  VP8_ENC_ERROR_OUT_OF_MEMORY,            // memory error allocating objects
-  VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY,  // memory error while flushing bits
-  VP8_ENC_ERROR_NULL_PARAMETER,           // a pointer parameter is NULL
-  VP8_ENC_ERROR_INVALID_CONFIGURATION,    // configuration is invalid
-  VP8_ENC_ERROR_BAD_DIMENSION,            // picture has invalid width/height
-  VP8_ENC_ERROR_PARTITION0_OVERFLOW,      // partition is bigger than 512k
-  VP8_ENC_ERROR_PARTITION_OVERFLOW,       // partition is bigger than 16M
-  VP8_ENC_ERROR_BAD_WRITE,                // error while flushing bytes
-  VP8_ENC_ERROR_FILE_TOO_BIG,             // file is bigger than 4G
-  VP8_ENC_ERROR_USER_ABORT,               // abort request by user
-  VP8_ENC_ERROR_LAST                      // list terminator. always last.
-} WebPEncodingError;
-
-// maximum width/height allowed (inclusive), in pixels
-#define WEBP_MAX_DIMENSION 16383
-
-// Main exchange structure (input samples, output bytes, statistics)
-//
-// Once WebPPictureInit() has been called, it's ok to make all the INPUT fields
-// (use_argb, y/u/v, argb, ...) point to user-owned data, even if
-// WebPPictureAlloc() has been called. Depending on the value use_argb,
-// it's guaranteed that either *argb or *y/*u/*v content will be kept untouched.
-struct WebPPicture {
-  //   INPUT
-  //////////////
-  // Main flag for encoder selecting between ARGB or YUV input.
-  // It is recommended to use ARGB input (*argb, argb_stride) for lossless
-  // compression, and YUV input (*y, *u, *v, etc.) for lossy compression
-  // since these are the respective native colorspace for these formats.
-  int use_argb;
-
-  // YUV input (mostly used for input to lossy compression)
-  WebPEncCSP colorspace;     // colorspace: should be YUV420 for now (=Y'CbCr).
-  int width, height;         // dimensions (less or equal to WEBP_MAX_DIMENSION)
-  uint8_t* y, *u, *v;        // pointers to luma/chroma planes.
-  int y_stride, uv_stride;   // luma/chroma strides.
-  uint8_t* a;                // pointer to the alpha plane
-  int a_stride;              // stride of the alpha plane
-  uint32_t pad1[2];          // padding for later use
-
-  // ARGB input (mostly used for input to lossless compression)
-  uint32_t* argb;            // Pointer to argb (32 bit) plane.
-  int argb_stride;           // This is stride in pixels units, not bytes.
-  uint32_t pad2[3];          // padding for later use
-
-  //   OUTPUT
-  ///////////////
-  // Byte-emission hook, to store compressed bytes as they are ready.
-  WebPWriterFunction writer;  // can be NULL
-  void* custom_ptr;           // can be used by the writer.
-
-  // map for extra information (only for lossy compression mode)
-  int extra_info_type;    // 1: intra type, 2: segment, 3: quant
-                          // 4: intra-16 prediction mode,
-                          // 5: chroma prediction mode,
-                          // 6: bit cost, 7: distortion
-  uint8_t* extra_info;    // if not NULL, points to an array of size
-                          // ((width + 15) / 16) * ((height + 15) / 16) that
-                          // will be filled with a macroblock map, depending
-                          // on extra_info_type.
-
-  //   STATS AND REPORTS
-  ///////////////////////////
-  // Pointer to side statistics (updated only if not NULL)
-  WebPAuxStats* stats;
-
-  // Error code for the latest error encountered during encoding
-  WebPEncodingError error_code;
-
-  // If not NULL, report progress during encoding.
-  WebPProgressHook progress_hook;
-
-  void* user_data;        // this field is free to be set to any value and
-                          // used during callbacks (like progress-report e.g.).
-
-  uint32_t pad3[3];       // padding for later use
-
-  // Unused for now
-  uint8_t* pad4, *pad5;
-  uint32_t pad6[8];       // padding for later use
-
-  // PRIVATE FIELDS
-  ////////////////////
-  void* memory_;          // row chunk of memory for yuva planes
-  void* memory_argb_;     // and for argb too.
-  void* pad7[2];          // padding for later use
-};
-
-// Internal, version-checked, entry point
-WEBP_EXTERN int WebPPictureInitInternal(WebPPicture*, int);
-
-// Should always be called, to initialize the structure. Returns false in case
-// of version mismatch. WebPPictureInit() must have succeeded before using the
-// 'picture' object.
-// Note that, by default, use_argb is false and colorspace is WEBP_YUV420.
-static WEBP_INLINE int WebPPictureInit(WebPPicture* picture) {
-  return WebPPictureInitInternal(picture, WEBP_ENCODER_ABI_VERSION);
-}
-
-//------------------------------------------------------------------------------
-// WebPPicture utils
-
-// Convenience allocation / deallocation based on picture->width/height:
-// Allocate y/u/v buffers as per colorspace/width/height specification.
-// Note! This function will free the previous buffer if needed.
-// Returns false in case of memory error.
-WEBP_EXTERN int WebPPictureAlloc(WebPPicture* picture);
-
-// Release the memory allocated by WebPPictureAlloc() or WebPPictureImport*().
-// Note that this function does _not_ free the memory used by the 'picture'
-// object itself.
-// Besides memory (which is reclaimed) all other fields of 'picture' are
-// preserved.
-WEBP_EXTERN void WebPPictureFree(WebPPicture* picture);
-
-// Copy the pixels of *src into *dst, using WebPPictureAlloc. Upon return, *dst
-// will fully own the copied pixels (this is not a view). The 'dst' picture need
-// not be initialized as its content is overwritten.
-// Returns false in case of memory allocation error.
-WEBP_EXTERN int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst);
-
-// Compute the single distortion for packed planes of samples.
-// 'src' will be compared to 'ref', and the raw distortion stored into
-// '*distortion'. The refined metric (log(MSE), log(1 - ssim),...' will be
-// stored in '*result'.
-// 'x_step' is the horizontal stride (in bytes) between samples.
-// 'src/ref_stride' is the byte distance between rows.
-// Returns false in case of error (bad parameter, memory allocation error, ...).
-WEBP_EXTERN int WebPPlaneDistortion(const uint8_t* src, size_t src_stride,
-                                    const uint8_t* ref, size_t ref_stride,
-                                    int width, int height,
-                                    size_t x_step,
-                                    int type,   // 0 = PSNR, 1 = SSIM, 2 = LSIM
-                                    float* distortion, float* result);
-
-// Compute PSNR, SSIM or LSIM distortion metric between two pictures. Results
-// are in dB, stored in result[] in the B/G/R/A/All order. The distortion is
-// always performed using ARGB samples. Hence if the input is YUV(A), the
-// picture will be internally converted to ARGB (just for the measurement).
-// Warning: this function is rather CPU-intensive.
-WEBP_EXTERN int WebPPictureDistortion(
-    const WebPPicture* src, const WebPPicture* ref,
-    int metric_type,           // 0 = PSNR, 1 = SSIM, 2 = LSIM
-    float result[5]);
-
-// self-crops a picture to the rectangle defined by top/left/width/height.
-// Returns false in case of memory allocation error, or if the rectangle is
-// outside of the source picture.
-// The rectangle for the view is defined by the top-left corner pixel
-// coordinates (left, top) as well as its width and height. This rectangle
-// must be fully be comprised inside the 'src' source picture. If the source
-// picture uses the YUV420 colorspace, the top and left coordinates will be
-// snapped to even values.
-WEBP_EXTERN int WebPPictureCrop(WebPPicture* picture,
-                                int left, int top, int width, int height);
-
-// Extracts a view from 'src' picture into 'dst'. The rectangle for the view
-// is defined by the top-left corner pixel coordinates (left, top) as well
-// as its width and height. This rectangle must be fully be comprised inside
-// the 'src' source picture. If the source picture uses the YUV420 colorspace,
-// the top and left coordinates will be snapped to even values.
-// Picture 'src' must out-live 'dst' picture. Self-extraction of view is allowed
-// ('src' equal to 'dst') as a mean of fast-cropping (but note that doing so,
-// the original dimension will be lost). Picture 'dst' need not be initialized
-// with WebPPictureInit() if it is different from 'src', since its content will
-// be overwritten.
-// Returns false in case of memory allocation error or invalid parameters.
-WEBP_EXTERN int WebPPictureView(const WebPPicture* src,
-                                int left, int top, int width, int height,
-                                WebPPicture* dst);
-
-// Returns true if the 'picture' is actually a view and therefore does
-// not own the memory for pixels.
-WEBP_EXTERN int WebPPictureIsView(const WebPPicture* picture);
-
-// Rescale a picture to new dimension width x height.
-// If either 'width' or 'height' (but not both) is 0 the corresponding
-// dimension will be calculated preserving the aspect ratio.
-// No gamma correction is applied.
-// Returns false in case of error (invalid parameter or insufficient memory).
-WEBP_EXTERN int WebPPictureRescale(WebPPicture* pic, int width, int height);
-
-// Colorspace conversion function to import RGB samples.
-// Previous buffer will be free'd, if any.
-// *rgb buffer should have a size of at least height * rgb_stride.
-// Returns false in case of memory error.
-WEBP_EXTERN int WebPPictureImportRGB(
-    WebPPicture* picture, const uint8_t* rgb, int rgb_stride);
-// Same, but for RGBA buffer.
-WEBP_EXTERN int WebPPictureImportRGBA(
-    WebPPicture* picture, const uint8_t* rgba, int rgba_stride);
-// Same, but for RGBA buffer. Imports the RGB direct from the 32-bit format
-// input buffer ignoring the alpha channel. Avoids needing to copy the data
-// to a temporary 24-bit RGB buffer to import the RGB only.
-WEBP_EXTERN int WebPPictureImportRGBX(
-    WebPPicture* picture, const uint8_t* rgbx, int rgbx_stride);
-
-// Variants of the above, but taking BGR(A|X) input.
-WEBP_EXTERN int WebPPictureImportBGR(
-    WebPPicture* picture, const uint8_t* bgr, int bgr_stride);
-WEBP_EXTERN int WebPPictureImportBGRA(
-    WebPPicture* picture, const uint8_t* bgra, int bgra_stride);
-WEBP_EXTERN int WebPPictureImportBGRX(
-    WebPPicture* picture, const uint8_t* bgrx, int bgrx_stride);
-
-// Converts picture->argb data to the YUV420A format. The 'colorspace'
-// parameter is deprecated and should be equal to WEBP_YUV420.
-// Upon return, picture->use_argb is set to false. The presence of real
-// non-opaque transparent values is detected, and 'colorspace' will be
-// adjusted accordingly. Note that this method is lossy.
-// Returns false in case of error.
-WEBP_EXTERN int WebPPictureARGBToYUVA(WebPPicture* picture,
-                                      WebPEncCSP /*colorspace = WEBP_YUV420*/);
-
-// Same as WebPPictureARGBToYUVA(), but the conversion is done using
-// pseudo-random dithering with a strength 'dithering' between
-// 0.0 (no dithering) and 1.0 (maximum dithering). This is useful
-// for photographic picture.
-WEBP_EXTERN int WebPPictureARGBToYUVADithered(
-    WebPPicture* picture, WebPEncCSP colorspace, float dithering);
-
-// Performs 'sharp' RGBA->YUVA420 downsampling and colorspace conversion.
-// Downsampling is handled with extra care in case of color clipping. This
-// method is roughly 2x slower than WebPPictureARGBToYUVA() but produces better
-// and sharper YUV representation.
-// Returns false in case of error.
-WEBP_EXTERN int WebPPictureSharpARGBToYUVA(WebPPicture* picture);
-// kept for backward compatibility:
-WEBP_EXTERN int WebPPictureSmartARGBToYUVA(WebPPicture* picture);
-
-// Converts picture->yuv to picture->argb and sets picture->use_argb to true.
-// The input format must be YUV_420 or YUV_420A. The conversion from YUV420 to
-// ARGB incurs a small loss too.
-// Note that the use of this colorspace is discouraged if one has access to the
-// raw ARGB samples, since using YUV420 is comparatively lossy.
-// Returns false in case of error.
-WEBP_EXTERN int WebPPictureYUVAToARGB(WebPPicture* picture);
-
-// Helper function: given a width x height plane of RGBA or YUV(A) samples
-// clean-up or smoothen the YUV or RGB samples under fully transparent area,
-// to help compressibility (no guarantee, though).
-WEBP_EXTERN void WebPCleanupTransparentArea(WebPPicture* picture);
-
-// Scan the picture 'picture' for the presence of non fully opaque alpha values.
-// Returns true in such case. Otherwise returns false (indicating that the
-// alpha plane can be ignored altogether e.g.).
-WEBP_EXTERN int WebPPictureHasTransparency(const WebPPicture* picture);
-
-// Remove the transparency information (if present) by blending the color with
-// the background color 'background_rgb' (specified as 24bit RGB triplet).
-// After this call, all alpha values are reset to 0xff.
-WEBP_EXTERN void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb);
-
-//------------------------------------------------------------------------------
-// Main call
-
-// Main encoding call, after config and picture have been initialized.
-// 'picture' must be less than 16384x16384 in dimension (cf WEBP_MAX_DIMENSION),
-// and the 'config' object must be a valid one.
-// Returns false in case of error, true otherwise.
-// In case of error, picture->error_code is updated accordingly.
-// 'picture' can hold the source samples in both YUV(A) or ARGB input, depending
-// on the value of 'picture->use_argb'. It is highly recommended to use
-// the former for lossy encoding, and the latter for lossless encoding
-// (when config.lossless is true). Automatic conversion from one format to
-// another is provided but they both incur some loss.
-WEBP_EXTERN int WebPEncode(const WebPConfig* config, WebPPicture* picture);
-
-//------------------------------------------------------------------------------
-
-#ifdef __cplusplus
-}    // extern "C"
-#endif
-
-#endif  // WEBP_WEBP_ENCODE_H_
diff --git a/include/webp/extras.h b/include/webp/extras.h
deleted file mode 100644
index 1c24be2..0000000
--- a/include/webp/extras.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2015 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-
-#ifndef WEBP_WEBP_EXTRAS_H_
-#define WEBP_WEBP_EXTRAS_H_
-
-#include "./types.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "./encode.h"
-
-#define WEBP_EXTRAS_ABI_VERSION 0x0000    // MAJOR(8b) + MINOR(8b)
-
-//------------------------------------------------------------------------------
-
-// Returns the version number of the extras library, packed in hexadecimal using
-// 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507.
-WEBP_EXTERN(int) WebPGetExtrasVersion(void);
-
-//------------------------------------------------------------------------------
-// Ad-hoc colorspace importers.
-
-// Import luma sample (gray scale image) into 'picture'. The 'picture'
-// width and height must be set prior to calling this function.
-WEBP_EXTERN(int) WebPImportGray(const uint8_t* gray, WebPPicture* picture);
-
-// Import rgb sample in RGB565 packed format into 'picture'. The 'picture'
-// width and height must be set prior to calling this function.
-WEBP_EXTERN(int) WebPImportRGB565(const uint8_t* rgb565, WebPPicture* pic);
-
-// Import rgb sample in RGB4444 packed format into 'picture'. The 'picture'
-// width and height must be set prior to calling this function.
-WEBP_EXTERN(int) WebPImportRGB4444(const uint8_t* rgb4444, WebPPicture* pic);
-
-//------------------------------------------------------------------------------
-
-#ifdef __cplusplus
-}    // extern "C"
-#endif
-
-#endif  /* WEBP_WEBP_EXTRAS_H_ */
diff --git a/include/webp/format_constants.h b/include/webp/format_constants.h
deleted file mode 100644
index eca6981..0000000
--- a/include/webp/format_constants.h
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-//  Internal header for constants related to WebP file format.
-//
-// Author: Urvang (urvang@google.com)
-
-#ifndef WEBP_WEBP_FORMAT_CONSTANTS_H_
-#define WEBP_WEBP_FORMAT_CONSTANTS_H_
-
-// Create fourcc of the chunk from the chunk tag characters.
-#define MKFOURCC(a, b, c, d) ((a) | (b) << 8 | (c) << 16 | (uint32_t)(d) << 24)
-
-// VP8 related constants.
-#define VP8_SIGNATURE 0x9d012a              // Signature in VP8 data.
-#define VP8_MAX_PARTITION0_SIZE (1 << 19)   // max size of mode partition
-#define VP8_MAX_PARTITION_SIZE  (1 << 24)   // max size for token partition
-#define VP8_FRAME_HEADER_SIZE 10  // Size of the frame header within VP8 data.
-
-// VP8L related constants.
-#define VP8L_SIGNATURE_SIZE          1      // VP8L signature size.
-#define VP8L_MAGIC_BYTE              0x2f   // VP8L signature byte.
-#define VP8L_IMAGE_SIZE_BITS         14     // Number of bits used to store
-                                            // width and height.
-#define VP8L_VERSION_BITS            3      // 3 bits reserved for version.
-#define VP8L_VERSION                 0      // version 0
-#define VP8L_FRAME_HEADER_SIZE       5      // Size of the VP8L frame header.
-
-#define MAX_PALETTE_SIZE             256
-#define MAX_CACHE_BITS               11
-#define HUFFMAN_CODES_PER_META_CODE  5
-#define ARGB_BLACK                   0xff000000
-
-#define DEFAULT_CODE_LENGTH          8
-#define MAX_ALLOWED_CODE_LENGTH      15
-
-#define NUM_LITERAL_CODES            256
-#define NUM_LENGTH_CODES             24
-#define NUM_DISTANCE_CODES           40
-#define CODE_LENGTH_CODES            19
-
-#define MIN_HUFFMAN_BITS             2  // min number of Huffman bits
-#define MAX_HUFFMAN_BITS             9  // max number of Huffman bits
-
-#define TRANSFORM_PRESENT            1  // The bit to be written when next data
-                                        // to be read is a transform.
-#define NUM_TRANSFORMS               4  // Maximum number of allowed transform
-                                        // in a bitstream.
-typedef enum {
-  PREDICTOR_TRANSFORM      = 0,
-  CROSS_COLOR_TRANSFORM    = 1,
-  SUBTRACT_GREEN           = 2,
-  COLOR_INDEXING_TRANSFORM = 3
-} VP8LImageTransformType;
-
-// Alpha related constants.
-#define ALPHA_HEADER_LEN            1
-#define ALPHA_NO_COMPRESSION        0
-#define ALPHA_LOSSLESS_COMPRESSION  1
-#define ALPHA_PREPROCESSED_LEVELS   1
-
-// Mux related constants.
-#define TAG_SIZE           4     // Size of a chunk tag (e.g. "VP8L").
-#define CHUNK_SIZE_BYTES   4     // Size needed to store chunk's size.
-#define CHUNK_HEADER_SIZE  8     // Size of a chunk header.
-#define RIFF_HEADER_SIZE   12    // Size of the RIFF header ("RIFFnnnnWEBP").
-#define ANMF_CHUNK_SIZE    16    // Size of an ANMF chunk.
-#define ANIM_CHUNK_SIZE    6     // Size of an ANIM chunk.
-#define VP8X_CHUNK_SIZE    10    // Size of a VP8X chunk.
-
-#define MAX_CANVAS_SIZE     (1 << 24)     // 24-bit max for VP8X width/height.
-#define MAX_IMAGE_AREA      (1ULL << 32)  // 32-bit max for width x height.
-#define MAX_LOOP_COUNT      (1 << 16)     // maximum value for loop-count
-#define MAX_DURATION        (1 << 24)     // maximum duration
-#define MAX_POSITION_OFFSET (1 << 24)     // maximum frame x/y offset
-
-// Maximum chunk payload is such that adding the header and padding won't
-// overflow a uint32_t.
-#define MAX_CHUNK_PAYLOAD (~0U - CHUNK_HEADER_SIZE - 1)
-
-#endif  // WEBP_WEBP_FORMAT_CONSTANTS_H_
diff --git a/include/webp/mux.h b/include/webp/mux.h
deleted file mode 100644
index 7d27489..0000000
--- a/include/webp/mux.h
+++ /dev/null
@@ -1,530 +0,0 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-//  RIFF container manipulation and encoding for WebP images.
-//
-// Authors: Urvang (urvang@google.com)
-//          Vikas (vikasa@google.com)
-
-#ifndef WEBP_WEBP_MUX_H_
-#define WEBP_WEBP_MUX_H_
-
-#include "./mux_types.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define WEBP_MUX_ABI_VERSION 0x0108        // MAJOR(8b) + MINOR(8b)
-
-//------------------------------------------------------------------------------
-// Mux API
-//
-// This API allows manipulation of WebP container images containing features
-// like color profile, metadata, animation.
-//
-// Code Example#1: Create a WebPMux object with image data, color profile and
-// XMP metadata.
-/*
-  int copy_data = 0;
-  WebPMux* mux = WebPMuxNew();
-  // ... (Prepare image data).
-  WebPMuxSetImage(mux, &image, copy_data);
-  // ... (Prepare ICCP color profile data).
-  WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data);
-  // ... (Prepare XMP metadata).
-  WebPMuxSetChunk(mux, "XMP ", &xmp, copy_data);
-  // Get data from mux in WebP RIFF format.
-  WebPMuxAssemble(mux, &output_data);
-  WebPMuxDelete(mux);
-  // ... (Consume output_data; e.g. write output_data.bytes to file).
-  WebPDataClear(&output_data);
-*/
-
-// Code Example#2: Get image and color profile data from a WebP file.
-/*
-  int copy_data = 0;
-  // ... (Read data from file).
-  WebPMux* mux = WebPMuxCreate(&data, copy_data);
-  WebPMuxGetFrame(mux, 1, &image);
-  // ... (Consume image; e.g. call WebPDecode() to decode the data).
-  WebPMuxGetChunk(mux, "ICCP", &icc_profile);
-  // ... (Consume icc_data).
-  WebPMuxDelete(mux);
-  WebPFree(data);
-*/
-
-// Note: forward declaring enumerations is not allowed in (strict) C and C++,
-// the types are left here for reference.
-// typedef enum WebPMuxError WebPMuxError;
-// typedef enum WebPChunkId WebPChunkId;
-typedef struct WebPMux WebPMux;   // main opaque object.
-typedef struct WebPMuxFrameInfo WebPMuxFrameInfo;
-typedef struct WebPMuxAnimParams WebPMuxAnimParams;
-typedef struct WebPAnimEncoderOptions WebPAnimEncoderOptions;
-
-// Error codes
-typedef enum WebPMuxError {
-  WEBP_MUX_OK                 =  1,
-  WEBP_MUX_NOT_FOUND          =  0,
-  WEBP_MUX_INVALID_ARGUMENT   = -1,
-  WEBP_MUX_BAD_DATA           = -2,
-  WEBP_MUX_MEMORY_ERROR       = -3,
-  WEBP_MUX_NOT_ENOUGH_DATA    = -4
-} WebPMuxError;
-
-// IDs for different types of chunks.
-typedef enum WebPChunkId {
-  WEBP_CHUNK_VP8X,        // VP8X
-  WEBP_CHUNK_ICCP,        // ICCP
-  WEBP_CHUNK_ANIM,        // ANIM
-  WEBP_CHUNK_ANMF,        // ANMF
-  WEBP_CHUNK_DEPRECATED,  // (deprecated from FRGM)
-  WEBP_CHUNK_ALPHA,       // ALPH
-  WEBP_CHUNK_IMAGE,       // VP8/VP8L
-  WEBP_CHUNK_EXIF,        // EXIF
-  WEBP_CHUNK_XMP,         // XMP
-  WEBP_CHUNK_UNKNOWN,     // Other chunks.
-  WEBP_CHUNK_NIL
-} WebPChunkId;
-
-//------------------------------------------------------------------------------
-
-// Returns the version number of the mux library, packed in hexadecimal using
-// 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507.
-WEBP_EXTERN int WebPGetMuxVersion(void);
-
-//------------------------------------------------------------------------------
-// Life of a Mux object
-
-// Internal, version-checked, entry point
-WEBP_EXTERN WebPMux* WebPNewInternal(int);
-
-// Creates an empty mux object.
-// Returns:
-//   A pointer to the newly created empty mux object.
-//   Or NULL in case of memory error.
-static WEBP_INLINE WebPMux* WebPMuxNew(void) {
-  return WebPNewInternal(WEBP_MUX_ABI_VERSION);
-}
-
-// Deletes the mux object.
-// Parameters:
-//   mux - (in/out) object to be deleted
-WEBP_EXTERN void WebPMuxDelete(WebPMux* mux);
-
-//------------------------------------------------------------------------------
-// Mux creation.
-
-// Internal, version-checked, entry point
-WEBP_EXTERN WebPMux* WebPMuxCreateInternal(const WebPData*, int, int);
-
-// Creates a mux object from raw data given in WebP RIFF format.
-// Parameters:
-//   bitstream - (in) the bitstream data in WebP RIFF format
-//   copy_data - (in) value 1 indicates given data WILL be copied to the mux
-//               object and value 0 indicates data will NOT be copied.
-// Returns:
-//   A pointer to the mux object created from given data - on success.
-//   NULL - In case of invalid data or memory error.
-static WEBP_INLINE WebPMux* WebPMuxCreate(const WebPData* bitstream,
-                                          int copy_data) {
-  return WebPMuxCreateInternal(bitstream, copy_data, WEBP_MUX_ABI_VERSION);
-}
-
-//------------------------------------------------------------------------------
-// Non-image chunks.
-
-// Note: Only non-image related chunks should be managed through chunk APIs.
-// (Image related chunks are: "ANMF", "VP8 ", "VP8L" and "ALPH").
-// To add, get and delete images, use WebPMuxSetImage(), WebPMuxPushFrame(),
-// WebPMuxGetFrame() and WebPMuxDeleteFrame().
-
-// Adds a chunk with id 'fourcc' and data 'chunk_data' in the mux object.
-// Any existing chunk(s) with the same id will be removed.
-// Parameters:
-//   mux - (in/out) object to which the chunk is to be added
-//   fourcc - (in) a character array containing the fourcc of the given chunk;
-//                 e.g., "ICCP", "XMP ", "EXIF" etc.
-//   chunk_data - (in) the chunk data to be added
-//   copy_data - (in) value 1 indicates given data WILL be copied to the mux
-//               object and value 0 indicates data will NOT be copied.
-// Returns:
-//   WEBP_MUX_INVALID_ARGUMENT - if mux, fourcc or chunk_data is NULL
-//                               or if fourcc corresponds to an image chunk.
-//   WEBP_MUX_MEMORY_ERROR - on memory allocation error.
-//   WEBP_MUX_OK - on success.
-WEBP_EXTERN WebPMuxError WebPMuxSetChunk(
-    WebPMux* mux, const char fourcc[4], const WebPData* chunk_data,
-    int copy_data);
-
-// Gets a reference to the data of the chunk with id 'fourcc' in the mux object.
-// The caller should NOT free the returned data.
-// Parameters:
-//   mux - (in) object from which the chunk data is to be fetched
-//   fourcc - (in) a character array containing the fourcc of the chunk;
-//                 e.g., "ICCP", "XMP ", "EXIF" etc.
-//   chunk_data - (out) returned chunk data
-// Returns:
-//   WEBP_MUX_INVALID_ARGUMENT - if mux, fourcc or chunk_data is NULL
-//                               or if fourcc corresponds to an image chunk.
-//   WEBP_MUX_NOT_FOUND - If mux does not contain a chunk with the given id.
-//   WEBP_MUX_OK - on success.
-WEBP_EXTERN WebPMuxError WebPMuxGetChunk(
-    const WebPMux* mux, const char fourcc[4], WebPData* chunk_data);
-
-// Deletes the chunk with the given 'fourcc' from the mux object.
-// Parameters:
-//   mux - (in/out) object from which the chunk is to be deleted
-//   fourcc - (in) a character array containing the fourcc of the chunk;
-//                 e.g., "ICCP", "XMP ", "EXIF" etc.
-// Returns:
-//   WEBP_MUX_INVALID_ARGUMENT - if mux or fourcc is NULL
-//                               or if fourcc corresponds to an image chunk.
-//   WEBP_MUX_NOT_FOUND - If mux does not contain a chunk with the given fourcc.
-//   WEBP_MUX_OK - on success.
-WEBP_EXTERN WebPMuxError WebPMuxDeleteChunk(
-    WebPMux* mux, const char fourcc[4]);
-
-//------------------------------------------------------------------------------
-// Images.
-
-// Encapsulates data about a single frame.
-struct WebPMuxFrameInfo {
-  WebPData    bitstream;  // image data: can be a raw VP8/VP8L bitstream
-                          // or a single-image WebP file.
-  int         x_offset;   // x-offset of the frame.
-  int         y_offset;   // y-offset of the frame.
-  int         duration;   // duration of the frame (in milliseconds).
-
-  WebPChunkId id;         // frame type: should be one of WEBP_CHUNK_ANMF
-                          // or WEBP_CHUNK_IMAGE
-  WebPMuxAnimDispose dispose_method;  // Disposal method for the frame.
-  WebPMuxAnimBlend   blend_method;    // Blend operation for the frame.
-  uint32_t    pad[1];     // padding for later use
-};
-
-// Sets the (non-animated) image in the mux object.
-// Note: Any existing images (including frames) will be removed.
-// Parameters:
-//   mux - (in/out) object in which the image is to be set
-//   bitstream - (in) can be a raw VP8/VP8L bitstream or a single-image
-//               WebP file (non-animated)
-//   copy_data - (in) value 1 indicates given data WILL be copied to the mux
-//               object and value 0 indicates data will NOT be copied.
-// Returns:
-//   WEBP_MUX_INVALID_ARGUMENT - if mux is NULL or bitstream is NULL.
-//   WEBP_MUX_MEMORY_ERROR - on memory allocation error.
-//   WEBP_MUX_OK - on success.
-WEBP_EXTERN WebPMuxError WebPMuxSetImage(
-    WebPMux* mux, const WebPData* bitstream, int copy_data);
-
-// Adds a frame at the end of the mux object.
-// Notes: (1) frame.id should be WEBP_CHUNK_ANMF
-//        (2) For setting a non-animated image, use WebPMuxSetImage() instead.
-//        (3) Type of frame being pushed must be same as the frames in mux.
-//        (4) As WebP only supports even offsets, any odd offset will be snapped
-//            to an even location using: offset &= ~1
-// Parameters:
-//   mux - (in/out) object to which the frame is to be added
-//   frame - (in) frame data.
-//   copy_data - (in) value 1 indicates given data WILL be copied to the mux
-//               object and value 0 indicates data will NOT be copied.
-// Returns:
-//   WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL
-//                               or if content of 'frame' is invalid.
-//   WEBP_MUX_MEMORY_ERROR - on memory allocation error.
-//   WEBP_MUX_OK - on success.
-WEBP_EXTERN WebPMuxError WebPMuxPushFrame(
-    WebPMux* mux, const WebPMuxFrameInfo* frame, int copy_data);
-
-// Gets the nth frame from the mux object.
-// The content of 'frame->bitstream' is allocated using WebPMalloc(), and NOT
-// owned by the 'mux' object. It MUST be deallocated by the caller by calling
-// WebPDataClear().
-// nth=0 has a special meaning - last position.
-// Parameters:
-//   mux - (in) object from which the info is to be fetched
-//   nth - (in) index of the frame in the mux object
-//   frame - (out) data of the returned frame
-// Returns:
-//   WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL.
-//   WEBP_MUX_NOT_FOUND - if there are less than nth frames in the mux object.
-//   WEBP_MUX_BAD_DATA - if nth frame chunk in mux is invalid.
-//   WEBP_MUX_MEMORY_ERROR - on memory allocation error.
-//   WEBP_MUX_OK - on success.
-WEBP_EXTERN WebPMuxError WebPMuxGetFrame(
-    const WebPMux* mux, uint32_t nth, WebPMuxFrameInfo* frame);
-
-// Deletes a frame from the mux object.
-// nth=0 has a special meaning - last position.
-// Parameters:
-//   mux - (in/out) object from which a frame is to be deleted
-//   nth - (in) The position from which the frame is to be deleted
-// Returns:
-//   WEBP_MUX_INVALID_ARGUMENT - if mux is NULL.
-//   WEBP_MUX_NOT_FOUND - If there are less than nth frames in the mux object
-//                        before deletion.
-//   WEBP_MUX_OK - on success.
-WEBP_EXTERN WebPMuxError WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth);
-
-//------------------------------------------------------------------------------
-// Animation.
-
-// Animation parameters.
-struct WebPMuxAnimParams {
-  uint32_t bgcolor;  // Background color of the canvas stored (in MSB order) as:
-                     // Bits 00 to 07: Alpha.
-                     // Bits 08 to 15: Red.
-                     // Bits 16 to 23: Green.
-                     // Bits 24 to 31: Blue.
-  int loop_count;    // Number of times to repeat the animation [0 = infinite].
-};
-
-// Sets the animation parameters in the mux object. Any existing ANIM chunks
-// will be removed.
-// Parameters:
-//   mux - (in/out) object in which ANIM chunk is to be set/added
-//   params - (in) animation parameters.
-// Returns:
-//   WEBP_MUX_INVALID_ARGUMENT - if mux or params is NULL.
-//   WEBP_MUX_MEMORY_ERROR - on memory allocation error.
-//   WEBP_MUX_OK - on success.
-WEBP_EXTERN WebPMuxError WebPMuxSetAnimationParams(
-    WebPMux* mux, const WebPMuxAnimParams* params);
-
-// Gets the animation parameters from the mux object.
-// Parameters:
-//   mux - (in) object from which the animation parameters to be fetched
-//   params - (out) animation parameters extracted from the ANIM chunk
-// Returns:
-//   WEBP_MUX_INVALID_ARGUMENT - if mux or params is NULL.
-//   WEBP_MUX_NOT_FOUND - if ANIM chunk is not present in mux object.
-//   WEBP_MUX_OK - on success.
-WEBP_EXTERN WebPMuxError WebPMuxGetAnimationParams(
-    const WebPMux* mux, WebPMuxAnimParams* params);
-
-//------------------------------------------------------------------------------
-// Misc Utilities.
-
-// Sets the canvas size for the mux object. The width and height can be
-// specified explicitly or left as zero (0, 0).
-// * When width and height are specified explicitly, then this frame bound is
-//   enforced during subsequent calls to WebPMuxAssemble() and an error is
-//   reported if any animated frame does not completely fit within the canvas.
-// * When unspecified (0, 0), the constructed canvas will get the frame bounds
-//   from the bounding-box over all frames after calling WebPMuxAssemble().
-// Parameters:
-//   mux - (in) object to which the canvas size is to be set
-//   width - (in) canvas width
-//   height - (in) canvas height
-// Returns:
-//   WEBP_MUX_INVALID_ARGUMENT - if mux is NULL; or
-//                               width or height are invalid or out of bounds
-//   WEBP_MUX_OK - on success.
-WEBP_EXTERN WebPMuxError WebPMuxSetCanvasSize(WebPMux* mux,
-                                              int width, int height);
-
-// Gets the canvas size from the mux object.
-// Note: This method assumes that the VP8X chunk, if present, is up-to-date.
-// That is, the mux object hasn't been modified since the last call to
-// WebPMuxAssemble() or WebPMuxCreate().
-// Parameters:
-//   mux - (in) object from which the canvas size is to be fetched
-//   width - (out) canvas width
-//   height - (out) canvas height
-// Returns:
-//   WEBP_MUX_INVALID_ARGUMENT - if mux, width or height is NULL.
-//   WEBP_MUX_BAD_DATA - if VP8X/VP8/VP8L chunk or canvas size is invalid.
-//   WEBP_MUX_OK - on success.
-WEBP_EXTERN WebPMuxError WebPMuxGetCanvasSize(const WebPMux* mux,
-                                              int* width, int* height);
-
-// Gets the feature flags from the mux object.
-// Note: This method assumes that the VP8X chunk, if present, is up-to-date.
-// That is, the mux object hasn't been modified since the last call to
-// WebPMuxAssemble() or WebPMuxCreate().
-// Parameters:
-//   mux - (in) object from which the features are to be fetched
-//   flags - (out) the flags specifying which features are present in the
-//           mux object. This will be an OR of various flag values.
-//           Enum 'WebPFeatureFlags' can be used to test individual flag values.
-// Returns:
-//   WEBP_MUX_INVALID_ARGUMENT - if mux or flags is NULL.
-//   WEBP_MUX_BAD_DATA - if VP8X/VP8/VP8L chunk or canvas size is invalid.
-//   WEBP_MUX_OK - on success.
-WEBP_EXTERN WebPMuxError WebPMuxGetFeatures(const WebPMux* mux,
-                                            uint32_t* flags);
-
-// Gets number of chunks with the given 'id' in the mux object.
-// Parameters:
-//   mux - (in) object from which the info is to be fetched
-//   id - (in) chunk id specifying the type of chunk
-//   num_elements - (out) number of chunks with the given chunk id
-// Returns:
-//   WEBP_MUX_INVALID_ARGUMENT - if mux, or num_elements is NULL.
-//   WEBP_MUX_OK - on success.
-WEBP_EXTERN WebPMuxError WebPMuxNumChunks(const WebPMux* mux,
-                                          WebPChunkId id, int* num_elements);
-
-// Assembles all chunks in WebP RIFF format and returns in 'assembled_data'.
-// This function also validates the mux object.
-// Note: The content of 'assembled_data' will be ignored and overwritten.
-// Also, the content of 'assembled_data' is allocated using WebPMalloc(), and
-// NOT owned by the 'mux' object. It MUST be deallocated by the caller by
-// calling WebPDataClear(). It's always safe to call WebPDataClear() upon
-// return, even in case of error.
-// Parameters:
-//   mux - (in/out) object whose chunks are to be assembled
-//   assembled_data - (out) assembled WebP data
-// Returns:
-//   WEBP_MUX_BAD_DATA - if mux object is invalid.
-//   WEBP_MUX_INVALID_ARGUMENT - if mux or assembled_data is NULL.
-//   WEBP_MUX_MEMORY_ERROR - on memory allocation error.
-//   WEBP_MUX_OK - on success.
-WEBP_EXTERN WebPMuxError WebPMuxAssemble(WebPMux* mux,
-                                         WebPData* assembled_data);
-
-//------------------------------------------------------------------------------
-// WebPAnimEncoder API
-//
-// This API allows encoding (possibly) animated WebP images.
-//
-// Code Example:
-/*
-  WebPAnimEncoderOptions enc_options;
-  WebPAnimEncoderOptionsInit(&enc_options);
-  // Tune 'enc_options' as needed.
-  WebPAnimEncoder* enc = WebPAnimEncoderNew(width, height, &enc_options);
-  while(<there are more frames>) {
-    WebPConfig config;
-    WebPConfigInit(&config);
-    // Tune 'config' as needed.
-    WebPAnimEncoderAdd(enc, frame, timestamp_ms, &config);
-  }
-  WebPAnimEncoderAdd(enc, NULL, timestamp_ms, NULL);
-  WebPAnimEncoderAssemble(enc, webp_data);
-  WebPAnimEncoderDelete(enc);
-  // Write the 'webp_data' to a file, or re-mux it further.
-*/
-
-typedef struct WebPAnimEncoder WebPAnimEncoder;  // Main opaque object.
-
-// Forward declarations. Defined in encode.h.
-struct WebPPicture;
-struct WebPConfig;
-
-// Global options.
-struct WebPAnimEncoderOptions {
-  WebPMuxAnimParams anim_params;  // Animation parameters.
-  int minimize_size;    // If true, minimize the output size (slow). Implicitly
-                        // disables key-frame insertion.
-  int kmin;
-  int kmax;             // Minimum and maximum distance between consecutive key
-                        // frames in the output. The library may insert some key
-                        // frames as needed to satisfy this criteria.
-                        // Note that these conditions should hold: kmax > kmin
-                        // and kmin >= kmax / 2 + 1. Also, if kmax <= 0, then
-                        // key-frame insertion is disabled; and if kmax == 1,
-                        // then all frames will be key-frames (kmin value does
-                        // not matter for these special cases).
-  int allow_mixed;      // If true, use mixed compression mode; may choose
-                        // either lossy and lossless for each frame.
-  int verbose;          // If true, print info and warning messages to stderr.
-
-  uint32_t padding[4];  // Padding for later use.
-};
-
-// Internal, version-checked, entry point.
-WEBP_EXTERN int WebPAnimEncoderOptionsInitInternal(
-    WebPAnimEncoderOptions*, int);
-
-// Should always be called, to initialize a fresh WebPAnimEncoderOptions
-// structure before modification. Returns false in case of version mismatch.
-// WebPAnimEncoderOptionsInit() must have succeeded before using the
-// 'enc_options' object.
-static WEBP_INLINE int WebPAnimEncoderOptionsInit(
-    WebPAnimEncoderOptions* enc_options) {
-  return WebPAnimEncoderOptionsInitInternal(enc_options, WEBP_MUX_ABI_VERSION);
-}
-
-// Internal, version-checked, entry point.
-WEBP_EXTERN WebPAnimEncoder* WebPAnimEncoderNewInternal(
-    int, int, const WebPAnimEncoderOptions*, int);
-
-// Creates and initializes a WebPAnimEncoder object.
-// Parameters:
-//   width/height - (in) canvas width and height of the animation.
-//   enc_options - (in) encoding options; can be passed NULL to pick
-//                      reasonable defaults.
-// Returns:
-//   A pointer to the newly created WebPAnimEncoder object.
-//   Or NULL in case of memory error.
-static WEBP_INLINE WebPAnimEncoder* WebPAnimEncoderNew(
-    int width, int height, const WebPAnimEncoderOptions* enc_options) {
-  return WebPAnimEncoderNewInternal(width, height, enc_options,
-                                    WEBP_MUX_ABI_VERSION);
-}
-
-// Optimize the given frame for WebP, encode it and add it to the
-// WebPAnimEncoder object.
-// The last call to 'WebPAnimEncoderAdd' should be with frame = NULL, which
-// indicates that no more frames are to be added. This call is also used to
-// determine the duration of the last frame.
-// Parameters:
-//   enc - (in/out) object to which the frame is to be added.
-//   frame - (in/out) frame data in ARGB or YUV(A) format. If it is in YUV(A)
-//           format, it will be converted to ARGB, which incurs a small loss.
-//   timestamp_ms - (in) timestamp of this frame in milliseconds.
-//                       Duration of a frame would be calculated as
-//                       "timestamp of next frame - timestamp of this frame".
-//                       Hence, timestamps should be in non-decreasing order.
-//   config - (in) encoding options; can be passed NULL to pick
-//            reasonable defaults.
-// Returns:
-//   On error, returns false and frame->error_code is set appropriately.
-//   Otherwise, returns true.
-WEBP_EXTERN int WebPAnimEncoderAdd(
-    WebPAnimEncoder* enc, struct WebPPicture* frame, int timestamp_ms,
-    const struct WebPConfig* config);
-
-// Assemble all frames added so far into a WebP bitstream.
-// This call should be preceded by  a call to 'WebPAnimEncoderAdd' with
-// frame = NULL; if not, the duration of the last frame will be internally
-// estimated.
-// Parameters:
-//   enc - (in/out) object from which the frames are to be assembled.
-//   webp_data - (out) generated WebP bitstream.
-// Returns:
-//   True on success.
-WEBP_EXTERN int WebPAnimEncoderAssemble(WebPAnimEncoder* enc,
-                                        WebPData* webp_data);
-
-// Get error string corresponding to the most recent call using 'enc'. The
-// returned string is owned by 'enc' and is valid only until the next call to
-// WebPAnimEncoderAdd() or WebPAnimEncoderAssemble() or WebPAnimEncoderDelete().
-// Parameters:
-//   enc - (in/out) object from which the error string is to be fetched.
-// Returns:
-//   NULL if 'enc' is NULL. Otherwise, returns the error string if the last call
-//   to 'enc' had an error, or an empty string if the last call was a success.
-WEBP_EXTERN const char* WebPAnimEncoderGetError(WebPAnimEncoder* enc);
-
-// Deletes the WebPAnimEncoder object.
-// Parameters:
-//   enc - (in/out) object to be deleted
-WEBP_EXTERN void WebPAnimEncoderDelete(WebPAnimEncoder* enc);
-
-//------------------------------------------------------------------------------
-
-#ifdef __cplusplus
-}    // extern "C"
-#endif
-
-#endif  // WEBP_WEBP_MUX_H_
diff --git a/include/webp/mux_types.h b/include/webp/mux_types.h
deleted file mode 100644
index 2fe8195..0000000
--- a/include/webp/mux_types.h
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2012 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-// Data-types common to the mux and demux libraries.
-//
-// Author: Urvang (urvang@google.com)
-
-#ifndef WEBP_WEBP_MUX_TYPES_H_
-#define WEBP_WEBP_MUX_TYPES_H_
-
-#include <string.h>  // memset()
-#include "./types.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Note: forward declaring enumerations is not allowed in (strict) C and C++,
-// the types are left here for reference.
-// typedef enum WebPFeatureFlags WebPFeatureFlags;
-// typedef enum WebPMuxAnimDispose WebPMuxAnimDispose;
-// typedef enum WebPMuxAnimBlend WebPMuxAnimBlend;
-typedef struct WebPData WebPData;
-
-// VP8X Feature Flags.
-typedef enum WebPFeatureFlags {
-  ANIMATION_FLAG  = 0x00000002,
-  XMP_FLAG        = 0x00000004,
-  EXIF_FLAG       = 0x00000008,
-  ALPHA_FLAG      = 0x00000010,
-  ICCP_FLAG       = 0x00000020,
-
-  ALL_VALID_FLAGS = 0x0000003e
-} WebPFeatureFlags;
-
-// Dispose method (animation only). Indicates how the area used by the current
-// frame is to be treated before rendering the next frame on the canvas.
-typedef enum WebPMuxAnimDispose {
-  WEBP_MUX_DISPOSE_NONE,       // Do not dispose.
-  WEBP_MUX_DISPOSE_BACKGROUND  // Dispose to background color.
-} WebPMuxAnimDispose;
-
-// Blend operation (animation only). Indicates how transparent pixels of the
-// current frame are blended with those of the previous canvas.
-typedef enum WebPMuxAnimBlend {
-  WEBP_MUX_BLEND,              // Blend.
-  WEBP_MUX_NO_BLEND            // Do not blend.
-} WebPMuxAnimBlend;
-
-// Data type used to describe 'raw' data, e.g., chunk data
-// (ICC profile, metadata) and WebP compressed image data.
-// 'bytes' memory must be allocated using WebPMalloc() and such.
-struct WebPData {
-  const uint8_t* bytes;
-  size_t size;
-};
-
-// Initializes the contents of the 'webp_data' object with default values.
-static WEBP_INLINE void WebPDataInit(WebPData* webp_data) {
-  if (webp_data != NULL) {
-    memset(webp_data, 0, sizeof(*webp_data));
-  }
-}
-
-// Clears the contents of the 'webp_data' object by calling WebPFree().
-// Does not deallocate the object itself.
-static WEBP_INLINE void WebPDataClear(WebPData* webp_data) {
-  if (webp_data != NULL) {
-    WebPFree((void*)webp_data->bytes);
-    WebPDataInit(webp_data);
-  }
-}
-
-// Allocates necessary storage for 'dst' and copies the contents of 'src'.
-// Returns true on success.
-static WEBP_INLINE int WebPDataCopy(const WebPData* src, WebPData* dst) {
-  if (src == NULL || dst == NULL) return 0;
-  WebPDataInit(dst);
-  if (src->bytes != NULL && src->size != 0) {
-    dst->bytes = (uint8_t*)WebPMalloc(src->size);
-    if (dst->bytes == NULL) return 0;
-    memcpy((void*)dst->bytes, src->bytes, src->size);
-    dst->size = src->size;
-  }
-  return 1;
-}
-
-#ifdef __cplusplus
-}    // extern "C"
-#endif
-
-#endif  // WEBP_WEBP_MUX_TYPES_H_
diff --git a/include/webp/types.h b/include/webp/types.h
deleted file mode 100644
index 47f7f2b..0000000
--- a/include/webp/types.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2010 Google Inc. All Rights Reserved.
-//
-// Use of this source code is governed by a BSD-style license
-// that can be found in the COPYING file in the root of the source
-// tree. An additional intellectual property rights grant can be found
-// in the file PATENTS. All contributing project authors may
-// be found in the AUTHORS file in the root of the source tree.
-// -----------------------------------------------------------------------------
-//
-//  Common types + memory wrappers
-//
-// Author: Skal (pascal.massimino@gmail.com)
-
-#ifndef WEBP_WEBP_TYPES_H_
-#define WEBP_WEBP_TYPES_H_
-
-#include <stddef.h>  // for size_t
-
-#ifndef _MSC_VER
-#include <inttypes.h>
-#if defined(__cplusplus) || !defined(__STRICT_ANSI__) || \
-    (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
-#define WEBP_INLINE inline
-#else
-#define WEBP_INLINE
-#endif
-#else
-typedef signed   char int8_t;
-typedef unsigned char uint8_t;
-typedef signed   short int16_t;
-typedef unsigned short uint16_t;
-typedef signed   int int32_t;
-typedef unsigned int uint32_t;
-typedef unsigned long long int uint64_t;
-typedef long long int int64_t;
-#define WEBP_INLINE __forceinline
-#endif  /* _MSC_VER */
-
-#ifndef WEBP_EXTERN
-// This explicitly marks library functions and allows for changing the
-// signature for e.g., Windows DLL builds.
-# if defined(__GNUC__) && __GNUC__ >= 4
-#  define WEBP_EXTERN extern __attribute__ ((visibility ("default")))
-# else
-#  define WEBP_EXTERN extern
-# endif  /* __GNUC__ >= 4 */
-#endif  /* WEBP_EXTERN */
-
-// Macro to check ABI compatibility (same major revision number)
-#define WEBP_ABI_IS_INCOMPATIBLE(a, b) (((a) >> 8) != ((b) >> 8))
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Allocates 'size' bytes of memory. Returns NULL upon error. Memory
-// must be deallocated by calling WebPFree(). This function is made available
-// by the core 'libwebp' library.
-WEBP_EXTERN void* WebPMalloc(size_t size);
-
-// Releases memory returned by the WebPDecode*() functions (from decode.h).
-WEBP_EXTERN void WebPFree(void* ptr);
-
-#ifdef __cplusplus
-}    // extern "C"
-#endif
-
-#endif  // WEBP_WEBP_TYPES_H_
diff --git a/infra/common.sh b/infra/common.sh
new file mode 100644
index 0000000..b4892d5
--- /dev/null
+++ b/infra/common.sh
@@ -0,0 +1,106 @@
+# Copyright (c) 2021, Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#   * Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#
+#   * Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in
+#     the documentation and/or other materials provided with the
+#     distribution.
+#
+#   * Neither the name of Google nor the names of its contributors may
+#     be used to endorse or promote products derived from this software
+#     without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+log_err() {
+  echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $*" >&2
+}
+
+#######################################
+# Create build directory. Build directory will be deleted if it exists.
+# Arguments:
+#   None.
+# Returns:
+#   mkdir result.
+#######################################
+make_build_dir() {
+  if [[ "$#" -ne 1 ]]; then
+    return 1
+  fi
+
+  local build_dir
+  build_dir="$1"
+  rm -rf "${build_dir}"
+  mkdir -p "${build_dir}"
+}
+
+#######################################
+# Cleanup files from the build directory.
+# Globals:
+#   LIBWEBP_ROOT repository's root path.
+# Arguments:
+#   $1 build directory.
+#######################################
+cleanup() {
+  # $1 is not completely removed to allow for binary artifacts to be
+  # extracted.
+  find "${1:?"Build directory not defined"}" \
+    \( -name "*.[ao]" -o -name "*.l[ao]" \) -exec rm -f {} +
+}
+
+#######################################
+# Setup ccache for toolchain.
+# Globals:
+#   PATH
+# Arguments:
+#   None.
+#######################################
+setup_ccache() {
+  if [[ -x "$(command -v ccache)" ]]; then
+    export CCACHE_CPP2=yes
+    export PATH="/usr/lib/ccache:${PATH}"
+  fi
+}
+
+#######################################
+# Detects whether test block should be run in the current test shard.
+# Globals:
+#   TEST_TOTAL_SHARDS: Valid range: [1, N]. Defaults to 1.
+#   TEST_SHARD_INDEX: Valid range: [0, TEST_TOTAL_SHARDS). Defaults to 0.
+#   libwebp_test_id: current test number; incremented with each call.
+# Arguments:
+#   None
+# Returns:
+#   true if the shard is active
+#   false if the shard is inactive
+#######################################
+shard_should_run() {
+  TEST_TOTAL_SHARDS=${TEST_TOTAL_SHARDS:=1}
+  TEST_SHARD_INDEX=${TEST_SHARD_INDEX:=0}
+  libwebp_test_id=${libwebp_test_id:=-1}
+  : $((libwebp_test_id += 1))
+
+  if [[ "${TEST_SHARD_INDEX}" -lt 0 ||
+    "${TEST_SHARD_INDEX}" -ge "${TEST_TOTAL_SHARDS}" ]]; then
+    log_err "Invalid TEST_SHARD_INDEX (${TEST_SHARD_INDEX})!" \
+      "Expected [0, ${TEST_TOTAL_SHARDS})."
+  fi
+
+  [[ "$((libwebp_test_id % TEST_TOTAL_SHARDS))" -eq "${TEST_SHARD_INDEX}" ]]
+}
diff --git a/infra/compile.sh b/infra/compile.sh
new file mode 100755
index 0000000..18e9ebe
--- /dev/null
+++ b/infra/compile.sh
@@ -0,0 +1,401 @@
+#!/bin/bash
+# Copyright (c) 2021, Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#   * Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#
+#   * Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in
+#     the documentation and/or other materials provided with the
+#     distribution.
+#
+#   * Neither the name of Google nor the names of its contributors may
+#     be used to endorse or promote products derived from this software
+#     without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set -xe
+LIBWEBP_ROOT="$(realpath "$(dirname "$0")/..")"
+WORKSPACE=${WORKSPACE:-"$(mktemp -d -t webp.XXX)"}
+
+# shellcheck source=infra/common.sh
+source "${LIBWEBP_ROOT}/infra/common.sh"
+
+usage() {
+  cat << EOF
+Usage: compile.sh BUILD_TYPE TARGET
+Options:
+BUILD_TYPE  supported build type: (shared, static, static-debug)
+TARGET      supported target platforms:
+              aarch64-linux-clang
+              aarch64-linux-gnu
+              arm-linux-gnueabi
+              arm-neon-linux-gnueabi
+              cmake
+              cmake-aarch64
+              cmake-arm
+              cmake-clang
+              disable-near-lossless
+              disable-sse4.1
+              disable-stats
+              force-aligned-32
+              force-aligned-64
+              gradle
+              i686-linux-asan
+              i686-linux-clang
+              i686-linux-gnu
+              i686-w64-mingw32
+              mips2el-linux-gnu
+              mips32dspr2el-linux-gnu
+              mips32eb-linux-gnu
+              mips32el-linux-gnu
+              mips32r2el-linux-gnu
+              mips32r5el-linux-gnu
+              mips64r2el-linux-gnu
+              mips64r6el-linux-gnu
+              native
+              reduce-csp
+              reduce-size
+              reduce-size-disable-stats
+              visibility-default-gnu
+              visibility-hidden-clang
+              visibility-hidden-gnu
+              wasm
+              x86_64-linux-clang
+              x86_64-linux-gnu
+              x86_64-linux-msan
+              x86_64-w64-mingw32
+Environment variables:
+WORKSPACE   directory where the build is done
+EOF
+}
+
+################################################################################
+echo "Building libwebp in ${WORKSPACE}"
+
+if [[ ! -d "${WORKSPACE}" ]]; then
+  log_err "${WORKSPACE} directory does not exist"
+  exit 1
+fi
+
+BUILD_TYPE=${1:?"Build type not defined.$(
+  echo
+  usage
+)"}
+TARGET=${2:?"Target not defined.$(
+  echo
+  usage
+)"}
+readonly BUILD_DIR="${WORKSPACE}/build-${BUILD_TYPE}"
+
+trap 'cleanup ${BUILD_DIR}' EXIT
+make_build_dir "${BUILD_DIR}"
+
+config_flags=()
+case "${BUILD_TYPE}" in
+  shared*) ;;  # Valid BUILD_TYPE but no setup required
+  static*) config_flags+=("--disable-shared") ;;
+  experimental) config_flags+=("--enable-experimental") ;;
+  *)
+    log_err "Invalid BUILD_TYPE"
+    usage
+    exit 1
+    ;;
+esac
+
+if grep -m 1 -q "enable-asserts" "${LIBWEBP_ROOT}/configure.ac"; then
+  config_flags+=("--enable-asserts")
+fi
+
+case "${TARGET}" in
+  aarch64-linux-clang)
+    TARGET="aarch64-linux-gnu"
+    CC="clang"
+    CC="${CC} --target=aarch64-linux-gnu"
+    export CC
+    export CFLAGS="-isystem /usr/aarch64-linux-gnu/include"
+    ;;
+  arm-linux-gnueabi)
+    export CFLAGS="-O3 -march=armv7-a -mfloat-abi=softfp -ftree-vectorize"
+    ;;
+  arm-neon-linux-gnueabi)
+    TARGET="arm-linux-gnueabi"
+    CFLAGS="-O3 -march=armv7-a -mfpu=neon -mfloat-abi=softfp -ftree-vectorize"
+    export CFLAGS
+    ;;
+  mips2el-linux-gnu)
+    export CFLAGS="-EL -O2 -mips2"
+    TARGET="mipsel-linux-gnu"
+    ;;
+  mips32el-linux-gnu)
+    export CFLAGS="-EL -O2 -mips32"
+    TARGET="mipsel-linux-gnu"
+    ;;
+  mips32r2el-linux-gnu)
+    export CFLAGS="-EL -O2 -mips32r2"
+    TARGET="mipsel-linux-gnu"
+    ;;
+  mips32dspr2el-linux-gnu)
+    export CFLAGS="-EL -O2 -mdspr2"
+    TARGET="mipsel-linux-gnu"
+    ;;
+  mips32r5el-linux-gnu)
+    export CFLAGS="-EL -O2 -mips32r5 -mmsa"
+    TARGET="mipsel-linux-gnu"
+    ;;
+  mips32eb-linux-gnu)
+    export CFLAGS="-EB -O2 -mips32"
+    TARGET="mips-linux-gnu"
+    ;;
+  mips64r2el-linux-gnu)
+    export CFLAGS="-EL -O2 -mips64r2 -mabi=64"
+    TARGET="mips64el-linux-gnuabi64"
+    ;;
+  mips64r6el-linux-gnu)
+    export CFLAGS="-EL -O2 -mips64r6 -mabi=64 -mmsa"
+    TARGET="mips-img-linux-gnu"
+    ;;
+  i686-linux-gnu)
+    export CC="gcc -m32"
+    ;;
+  i686-linux-clang)
+    TARGET="i686-linux-gnu"
+    export CC="clang -m32"
+    ;;
+  i686-linux-asan)
+    TARGET="i686-linux-gnu"
+    export CC="clang -m32 -fsanitize=address"
+    ;;
+  i686-linux-msan)
+    TARGET="i686-linux-gnu"
+    export CC="clang -m32 -fsanitize=memory"
+    ;;
+  x86_64-linux-clang)
+    TARGET="x86_64-linux-gnu"
+    export CC=clang
+    ;;
+  x86_64-linux-msan)
+    TARGET="x86_64-linux-gnu"
+    export CC="clang -fsanitize=memory"
+    ;;
+  force-aligned-32)
+    config_flags+=("--enable-aligned")
+    TARGET="i686-linux-gnu"
+    export CC="gcc -m32"
+    ;;
+  force-aligned-64)
+    config_flags+=("--enable-aligned")
+    TARGET="x86_64-linux-gnu"
+    ;;
+  visibility-default-*)
+    export CFLAGS="-O2 -g -fvisibility=default"
+    TARGET="x86_64-linux-gnu"
+    ;;
+  visibility-hidden-*)
+    export CFLAGS="-O2 -g -fvisibility=hidden"
+    if [[ "${TARGET}" = "visibility-hidden-clang" ]]; then
+      export CC=clang
+    fi
+    TARGET="x86_64-linux-gnu"
+    ;;
+  disable-sse4.1)
+    grep "${TARGET}" "${LIBWEBP_ROOT}/configure.ac" || exit 0
+    config_flags+=("--${TARGET}")
+    TARGET="x86_64-linux-gnu"
+    ;;
+  disable-near-lossless)
+    grep "${TARGET}" "${LIBWEBP_ROOT}/configure.ac" || exit 0
+    config_flags+=("--${TARGET}")
+    TARGET="x86_64-linux-gnu"
+    ;;
+  disable-stats)
+    git -C "${LIBWEBP_ROOT}" grep WEBP_DISABLE_STATS || exit 0
+    export CFLAGS="-O2 -g -DWEBP_DISABLE_STATS"
+    TARGET="x86_64-linux-gnu"
+    ;;
+  reduce-size)
+    git -C "${LIBWEBP_ROOT}" grep WEBP_REDUCE_SIZE || exit 0
+    export CFLAGS="-O2 -g -DWEBP_REDUCE_SIZE"
+    TARGET="x86_64-linux-gnu"
+    ;;
+  reduce-size-disable-stats)
+    git -C "${LIBWEBP_ROOT}" grep -e WEBP_DISABLE_STATS -e WEBP_REDUCE_SIZE \
+      || exit 0
+    export CFLAGS="-O2 -g -DWEBP_DISABLE_STATS -DWEBP_REDUCE_SIZE"
+    TARGET="x86_64-linux-gnu"
+    ;;
+  reduce-csp)
+    git -C "${LIBWEBP_ROOT}" grep WEBP_REDUCE_CSP || exit 0
+    export CFLAGS="-O2 -g -DWEBP_REDUCE_CSP"
+    TARGET="x86_64-linux-gnu"
+    ;;
+  x86_64-linux-gnu | *mingw32 | aarch64*) ;;  # Default target configuration
+  # non-configure based builds
+  native)
+    setup_ccache
+    # exercise makefile.unix then quit
+    make -C "${LIBWEBP_ROOT}" -f makefile.unix -j all
+    for tgt in extras examples/anim_diff; do
+      grep -q -m 1 "${tgt}" "${LIBWEBP_ROOT}/makefile.unix" \
+        && make -C "${LIBWEBP_ROOT}" -f makefile.unix -j "${tgt}"
+    done
+    [[ -d "${LIBWEBP_ROOT}/tests/fuzzer" ]] \
+      && make -j -C "${LIBWEBP_ROOT}/tests/fuzzer" -f makefile.unix
+    exit 0
+    ;;
+  cmake*)
+    setup_ccache
+    # exercise cmake then quit
+    opts=()
+    case "${TARGET}" in
+      cmake-clang)
+        opts+=("-DCMAKE_C_COMPILER=clang")
+        ;;
+      cmake-arm)
+        opts+=("-DCMAKE_C_COMPILER=arm-linux-gnueabi-gcc")
+        case "${GERRIT_BRANCH:-}" in
+          portable-intrinsics | 0.6.1) exit 0 ;;
+          *) ;;  # Skip configuration
+        esac
+        ;;
+      cmake-aarch64)
+        opts+=("-DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc")
+        case "${GERRIT_BRANCH:-}" in
+          portable-intrinsics | 0.6.1) exit 0 ;;
+          *) ;;  # Skip configuration
+        esac
+        ;;
+      *) ;;  # Skip configuration
+    esac
+    case "${BUILD_TYPE}" in
+      static*)
+        opts+=("-DBUILD_SHARED_LIBS=OFF")
+        ;;
+      experimental)
+        opts+=("-DWEBP_EXPERIMENTAL_FEATURES=ON" "-DBUILD_SHARED_LIBS=ON")
+        ;;
+      *)
+        opts+=("-DBUILD_SHARED_LIBS=ON")
+        ;;
+    esac
+    case "${BUILD_TYPE}" in
+      *debug) opts+=("-DCMAKE_BUILD_TYPE=Debug") ;;
+      *) opts+=("-DCMAKE_BUILD_TYPE=RelWithDebInfo") ;;
+    esac
+    cd "${BUILD_DIR}"
+    opts+=("-DWEBP_BUILD_CWEBP=ON" "-DWEBP_BUILD_DWEBP=ON")
+    grep -m 1 -q WEBP_BUILD_GIF2WEBP "${LIBWEBP_ROOT}/CMakeLists.txt" \
+      && opts+=("-DWEBP_BUILD_GIF2WEBP=ON")
+    grep -m 1 -q WEBP_BUILD_IMG2WEBP "${LIBWEBP_ROOT}/CMakeLists.txt" \
+      && opts+=("-DWEBP_BUILD_IMG2WEBP=ON")
+    cmake "${opts[@]}" "${LIBWEBP_ROOT}"
+    make VERBOSE=1 -j
+    case "${BUILD_TYPE}" in
+      static)
+        mkdir -p examples
+        cp [cd]webp examples
+        ;;
+      *) ;;  # Skip configuration.
+    esac
+
+    grep "install" "${LIBWEBP_ROOT}/CMakeLists.txt" || exit 0
+
+    make DESTDIR="${BUILD_DIR}/webp-install" install/strip
+    mkdir tmp
+    cd tmp
+    cat > CMakeLists.txt << EOF
+cmake_minimum_required(VERSION 2.8.7)
+
+project(libwebp C)
+
+find_package(WebP)
+if (NOT WebP_FOUND)
+  message(FATAL_ERROR "WebP package not found")
+endif ()
+message("WebP_FOUND: \${WebP_FOUND}")
+message("WebP_INCLUDE_DIRS: \${WebP_INCLUDE_DIRS}")
+message("WebP_LIBRARIES: \${WebP_LIBRARIES}")
+message("WEBP_INCLUDE_DIRS: \${WEBP_INCLUDE_DIRS}")
+message("WEBP_LIBRARIES: \${WEBP_LIBRARIES}")
+EOF
+    cmake . "${opts[@]}" \
+      "-DCMAKE_PREFIX_PATH=${BUILD_DIR}/webp-install/usr/local"
+    exit 0
+    ;;
+  gradle)
+    setup_ccache
+    # exercise gradle then quit
+    [[ -f "${LIBWEBP_ROOT}/gradlew" ]] || exit 0
+
+    cd "${BUILD_DIR}"
+    # TODO -g / --gradle-user-home could be used if there's a race between jobs
+    "${LIBWEBP_ROOT}/gradlew" -p "${LIBWEBP_ROOT}" buildAllExecutables
+    exit 0
+    ;;
+  wasm)
+    grep -m 1 -q WEBP_ENABLE_WASM "${LIBWEBP_ROOT}/CMakeLists.txt" || exit 0
+    opts+=("-DCMAKE_C_COMPILER=clang" "-DWEBP_ENABLE_WASM=ON")
+    opts+=("-DWEBP_BUILD_CWEBP=ON" "-DWEBP_BUILD_DWEBP=ON")
+    case "${BUILD_TYPE}" in
+      *debug) opts+=("-DCMAKE_BUILD_TYPE=Debug") ;;
+      *) opts+=("-DCMAKE_BUILD_TYPE=RelWithDebInfo") ;;
+    esac
+    cd "${BUILD_DIR}"
+    cmake "${opts[@]}" "${LIBWEBP_ROOT}"
+    make VERBOSE=1 -j
+    mkdir examples
+    case "${BUILD_TYPE}" in
+      static)
+        mkdir -p examples
+        cp [cd]webp examples
+        ;;
+      *) ;;  # Skip configuration
+    esac
+    exit 0
+    ;;
+  *)
+    log_err "Invalid TARGET"
+    usage
+    exit 1
+    ;;
+esac
+
+case "${TARGET}" in
+  *mingw32) ;;  # Skip configuration
+  *)
+    case "${TARGET}-${CC}" in
+      static-debug-gcc* | static-debug-)
+        CFLAGS="${CFLAGS} -fprofile-arcs -ftest-coverage -O0 -g"
+        CXXFLAGS="${CXXFLAGS} -fprofile-arcs -ftest-coverage -O0 -g"
+        export CFLAGS CXXFLAGS
+        ;;
+      *) ;;  # This case should not be reached.
+    esac
+    ;;
+esac
+
+setup_ccache
+
+cd "${LIBWEBP_ROOT}"
+./autogen.sh
+
+cd "${BUILD_DIR}"
+"${LIBWEBP_ROOT}/configure" \
+  --host "${TARGET}" --build "$("${LIBWEBP_ROOT}/config.guess")" \
+  --enable-everything "${config_flags[@]}"
+make -j V=1
diff --git a/infra/compile_android.sh b/infra/compile_android.sh
new file mode 100755
index 0000000..013b97c
--- /dev/null
+++ b/infra/compile_android.sh
@@ -0,0 +1,224 @@
+#!/bin/bash
+# Copyright (c) 2021, Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#   * Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#
+#   * Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in
+#     the documentation and/or other materials provided with the
+#     distribution.
+#
+#   * Neither the name of Google nor the names of its contributors may
+#     be used to endorse or promote products derived from this software
+#     without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set -xe
+LIBWEBP_ROOT="$(realpath "$(dirname "$0")/..")"
+readonly LIBWEBP_ROOT
+readonly WORKSPACE=${WORKSPACE:-"$(mktemp -d -t webp.android.XXX)"}
+# shellcheck source=infra/common.sh
+source "${LIBWEBP_ROOT}/infra/common.sh"
+
+usage() {
+  cat << EOF
+Usage: $(basename "$0") BUILD_TYPE APP_ABI
+Options:
+BUILD_TYPE  supported build types:
+              static
+              static-debug
+              shared
+              shared-debug
+APP_ABI     supported application binary interfaces:
+              armeabi-v7a
+              arm64-v8a
+              x86
+              x86_64
+Environment variables:
+WORKSPACE   directory where the build is done.
+ANDROID_NDK_DIR directory where the android ndk tools are.
+EOF
+}
+
+################################################################################
+echo "Building libwebp for Android in ${WORKSPACE}"
+
+if [[ ! -d "${WORKSPACE}" ]]; then
+  log_err "${WORKSPACE} directory does not exist."
+  exit 1
+fi
+
+readonly BUILD_TYPE=${1:?"BUILD_TYPE is not defined.$(
+  echo
+  usage
+)"}
+readonly APP_ABI=${2:?"APP_ABI not defined.$(
+  echo
+  usage
+)"}
+readonly ANDROID_NDK_DIR=${ANDROID_NDK_DIR:?"ANDROID_NDK_DIR is not defined.$(
+  echo
+  usage
+)"}
+readonly BUILD_DIR="${WORKSPACE}/build-${BUILD_TYPE}"
+readonly STANDALONE_ANDROID_DIR="${WORKSPACE}/android"
+
+if [[ ! -x "${ANDROID_NDK_DIR}/ndk-build" ]]; then
+  log_err "unable to find ndk-build in ANDROID_NDK_DIR: ${ANDROID_NDK_DIR}."
+  exit 1
+fi
+
+CFLAGS=
+LDFLAGS=
+opts=()
+case "${BUILD_TYPE}" in
+  *debug)
+    readonly APP_OPTIM="debug"
+    CFLAGS="-O0 -g"
+    opts+=("--enable-asserts")
+    ;;
+  static* | shared*)
+    readonly APP_OPTIM="release"
+    CFLAGS="-O2 -g"
+    ;;
+  *)
+    usage
+    exit 1
+    ;;
+esac
+
+case "${BUILD_TYPE}" in
+  shared*) readonly SHARED="1" ;;
+  *)
+    readonly SHARED="0"
+    CFLAGS="${CFLAGS} -fPIE"
+    LDFLAGS="${LDFLAGS} -Wl,-pie"
+    opts+=("--disable-shared")
+    ;;
+esac
+
+# Create a fresh build directory
+make_build_dir "${BUILD_DIR}"
+cd "${BUILD_DIR}"
+ln -s "${LIBWEBP_ROOT}" jni
+
+"${ANDROID_NDK_DIR}/ndk-build" -j2 \
+  APP_ABI="${APP_ABI}" \
+  APP_OPTIM="${APP_OPTIM}" \
+  ENABLE_SHARED="${SHARED}"
+
+cd "${LIBWEBP_ROOT}"
+./autogen.sh
+
+case "${APP_ABI}" in
+  armeabi*) arch="arm" ;;
+  arm64*) arch="arm64" ;;
+  *) arch="${APP_ABI}" ;;
+esac
+# TODO(b/185520507): remove this and use the binaries from
+# toolchains/llvm/prebuilt/ directly.
+rm -rf "${STANDALONE_ANDROID_DIR}"
+"${ANDROID_NDK_DIR}/build/tools/make_standalone_toolchain.py" \
+  --api 24 --arch "${arch}" --stl gnustl --install-dir \
+  "${STANDALONE_ANDROID_DIR}"
+export PATH="${STANDALONE_ANDROID_DIR}/bin:${PATH}"
+
+rm -rf "${BUILD_DIR}"
+make_build_dir "${BUILD_DIR}"
+cd "${BUILD_DIR}"
+
+case "${arch}" in
+  arm)
+    host="arm-linux-androideabi"
+    case "${APP_ABI}" in
+      armeabi) ;;
+      armeabi-v7a)
+        CFLAGS="${CFLAGS} -march=armv7-a -mfpu=neon -mfloat-abi=softfp"
+        ;;
+      *) ;;  # No configuration needed
+    esac
+    ;;
+  arm64)
+    host="aarch64-linux-android"
+    ;;
+  x86)
+    host="i686-linux-android"
+    ;;
+  x86_64)
+    host="x86_64-linux-android"
+    ;;
+  *) ;;  # Skip configuration
+esac
+
+setup_ccache
+CC="clang"
+
+"${LIBWEBP_ROOT}/configure" --host "${host}" --build \
+  "$("${LIBWEBP_ROOT}/config.guess")" CC="${CC}" CFLAGS="${CFLAGS}" \
+  LDFLAGS="${LDFLAGS}" "${opts[@]}"
+make -j
+
+if [[ "${GERRIT_REFSPEC:-}" = "refs/heads/portable-intrinsics" ]] \
+  || [[ "${GERRIT_BRANCH:-}" = "portable-intrinsics" ]]; then
+  cd "${WORKSPACE}"
+  rm -rf build && mkdir build
+  cd build
+  standalone="${WORKSPACE}/android"
+  cmake ../libwebp \
+    -DWEBP_BUILD_DWEBP=1 \
+    -DCMAKE_C_COMPILER="${standalone}/bin/clang" \
+    -DCMAKE_PREFIX_PATH="${standalone}/sysroot/usr/lib" \
+    -DCMAKE_C_FLAGS=-fPIE \
+    -DCMAKE_EXE_LINKER_FLAGS=-Wl,-pie \
+    -DCMAKE_BUILD_TYPE=Release \
+    -DWEBP_ENABLE_WASM=1
+  make -j2
+
+  cd "${WORKSPACE}"
+  make_build_dir "${BUILD_DIR}"
+  cd "${BUILD_DIR}"
+  case "${APP_ABI}" in
+    armeabi-v7a | arm64*)
+      cmake "${LIBWEBP_ROOT}" \
+        -DWEBP_BUILD_DWEBP=1 \
+        -DCMAKE_C_COMPILER="${standalone}/bin/clang" \
+        -DCMAKE_PREFIX_PATH="${standalone}/sysroot/usr/lib" \
+        -DCMAKE_C_FLAGS='-fPIE -DENABLE_NEON_BUILTIN_MULHI_INT16X8' \
+        -DCMAKE_EXE_LINKER_FLAGS=-Wl,-pie \
+        -DCMAKE_BUILD_TYPE=Release \
+        -DWEBP_ENABLE_WASM=1
+      make -j2
+      ;;
+    x86*)
+      cmake "${LIBWEBP_ROOT}" \
+        -DWEBP_BUILD_DWEBP=1 \
+        -DCMAKE_C_COMPILER="${standalone}/bin/clang" \
+        -DCMAKE_PREFIX_PATH="${standalone}/sysroot/usr/lib" \
+        -DCMAKE_C_FLAGS='-fPIE -DENABLE_X86_BUILTIN_MULHI_INT16X8' \
+        -DCMAKE_EXE_LINKER_FLAGS=-Wl,-pie \
+        -DCMAKE_BUILD_TYPE=Release \
+        -DWEBP_ENABLE_WASM=1
+      make -j2
+      ;;
+    *)
+      log_err "APP_ABI not supported."
+      exit 1
+      ;;
+  esac
+fi
diff --git a/infra/compile_js.sh b/infra/compile_js.sh
new file mode 100755
index 0000000..6a13fe6
--- /dev/null
+++ b/infra/compile_js.sh
@@ -0,0 +1,75 @@
+#!/bin/bash
+# Copyright (c) 2021, Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#   * Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#
+#   * Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in
+#     the documentation and/or other materials provided with the
+#     distribution.
+#
+#   * Neither the name of Google nor the names of its contributors may
+#     be used to endorse or promote products derived from this software
+#     without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set -ex
+
+readonly WORKSPACE="${WORKSPACE:-"$(mktemp -d -t webp.js.XXX)"}"
+readonly BUILD_DIR="${WORKSPACE}/webp_js/"
+readonly LIBWEBP_ROOT="$(realpath "$(dirname "$0")/..")"
+
+# shellcheck source=infra/common.sh
+source "${LIBWEBP_ROOT}/infra/common.sh"
+
+usage() {
+  cat << EOF
+Usage: $(basename "$0")
+Environment variables:
+WORKSPACE directory where the build is done
+EMSDK_DIR directory where emsdk is installed
+EOF
+}
+
+[[ -d "${EMSDK_DIR:?Not defined}" ]] \
+  || (log_err "${EMSDK_DIR} is not a valid directory." && exit 1)
+
+# shellcheck source=/opt/emsdk/emsdk_env.sh
+source "${EMSDK_DIR}/emsdk_env.sh"
+
+readonly EMSCRIPTEN=${EMSCRIPTEN:-"${EMSDK}/upstream/emscripten"}
+readonly \
+  EMSCRIPTEN_CMAKE_FILE="${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake"
+make_build_dir "${BUILD_DIR}"
+
+pushd "${BUILD_DIR}"
+opts=("-GUnix Makefiles" "-DWEBP_BUILD_WEBP_JS=ON")
+if [[ -z "$(command -v emcmake)" ]]; then
+  opts+=("-DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN_CMAKE_FILE}")
+  cmake \
+    "${opts[@]}" \
+    "${LIBWEBP_ROOT}"
+  make -j
+else
+  emcmake cmake \
+    "${opts[@]}" \
+    "${LIBWEBP_ROOT}"
+  emmake make -j
+fi
+popd
diff --git a/infra/run_static_analysis.sh b/infra/run_static_analysis.sh
new file mode 100755
index 0000000..fa8445c
--- /dev/null
+++ b/infra/run_static_analysis.sh
@@ -0,0 +1,98 @@
+#!/bin/bash
+# Copyright (c) 2021, Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#   * Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#
+#   * Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in
+#     the documentation and/or other materials provided with the
+#     distribution.
+#
+#   * Neither the name of Google nor the names of its contributors may
+#     be used to endorse or promote products derived from this software
+#     without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set -xe
+
+LIBWEBP_ROOT="$(realpath "$(dirname "$0")/..")"
+readonly LIBWEBP_ROOT
+readonly WORKSPACE=${WORKSPACE:-"$(mktemp -d -t webp.scanbuild.XXX)"}
+
+# shellcheck source=infra/common.sh
+source "${LIBWEBP_ROOT}/infra/common.sh"
+
+usage() {
+  cat << EOF
+Usage: $(basename "$0") MODE
+Options:
+MODE supported scan modes: (shallow|deep)
+Environment variables:
+WORKSPACE directory where the build is done.
+EOF
+}
+
+#######################################
+# Wrap clang-tools scan-build.
+# Globals:
+#   OUTPUT_DIR target directory where scan-build report is generated.
+#   MODE scan-build mode
+# Arguments:
+#   $* scan-build additional args.
+# Returns:
+#   scan-build retcode
+#######################################
+scan_build() {
+  scan-build -o "${OUTPUT_DIR}" --use-analyzer="$(command -v clang)" \
+    -analyzer-config mode="${MODE}" "$*"
+}
+
+MODE=${1:?"MODE is not specified.$(
+  echo
+  usage
+)"}
+
+readonly OUTPUT_DIR="${WORKSPACE}/output-${MODE}"
+readonly BUILD_DIR="${WORKSPACE}/build"
+
+make_build_dir "${OUTPUT_DIR}"
+make_build_dir "${BUILD_DIR}"
+
+cd "${LIBWEBP_ROOT}"
+./autogen.sh
+
+cd "${BUILD_DIR}"
+grep -m 1 -q 'enable-asserts' "${LIBWEBP_ROOT}/configure.ac" \
+    && args='--enable-asserts'
+scan_build "${LIBWEBP_ROOT}/configure" --enable-everything "${args}"
+scan_build make -j4
+
+index="$(find "${OUTPUT_DIR}" -name index.html)"
+if [[ -f "${index}" ]]; then
+  mv "$(dirname "${index}")/"* "${OUTPUT_DIR}"
+else
+  # make a empty report to wipe out any old bug reports.
+  cat << EOT > "${OUTPUT_DIR}/index.html"
+<html>
+<body>
+No bugs reported.
+</body>
+</html>
+EOT
+fi
diff --git a/iosbuild.sh b/iosbuild.sh
new file mode 100755
index 0000000..cd3a24c
--- /dev/null
+++ b/iosbuild.sh
@@ -0,0 +1,168 @@
+#!/bin/bash
+#
+# This script generates 'WebP.framework' and 'WebPDecoder.framework',
+# 'WebPDemux.framework' and 'WebPMux.framework'.
+# An iOS app can decode WebP images by including 'WebPDecoder.framework' and
+# both encode and decode WebP images by including 'WebP.framework'.
+#
+# Run ./iosbuild.sh to generate the frameworks under the current directory
+# (the previous build will be erased if it exists).
+#
+# This script is inspired by the build script written by Carson McDonald.
+# (https://www.ioncannon.net/programming/1483/using-webp-to-reduce-native-ios-app-size/).
+
+set -e
+
+# Set this variable based on the desired minimum deployment target.
+readonly IOS_MIN_VERSION=6.0
+
+# Extract the latest SDK version from the final field of the form: iphoneosX.Y
+readonly SDK=$(xcodebuild -showsdks \
+  | grep iphoneos | sort | tail -n 1 | awk '{print substr($NF, 9)}'
+)
+# Extract Xcode version.
+readonly XCODE=$(xcodebuild -version | grep Xcode | cut -d " " -f2)
+if [[ -z "${XCODE}" ]]; then
+  echo "Xcode not available"
+  exit 1
+fi
+
+readonly OLDPATH=${PATH}
+
+# Add iPhoneOS-V6 to the list of platforms below if you need armv6 support.
+# Note that iPhoneOS-V6 support is not available with the iOS6 SDK.
+PLATFORMS="iPhoneSimulator iPhoneSimulator64"
+PLATFORMS+=" iPhoneOS-V7 iPhoneOS-V7s iPhoneOS-V7-arm64"
+readonly PLATFORMS
+readonly SRCDIR=$(dirname $0)
+readonly TOPDIR=$(pwd)
+readonly BUILDDIR="${TOPDIR}/iosbuild"
+readonly TARGETDIR="${TOPDIR}/WebP.framework"
+readonly DECTARGETDIR="${TOPDIR}/WebPDecoder.framework"
+readonly MUXTARGETDIR="${TOPDIR}/WebPMux.framework"
+readonly DEMUXTARGETDIR="${TOPDIR}/WebPDemux.framework"
+readonly DEVELOPER=$(xcode-select --print-path)
+readonly PLATFORMSROOT="${DEVELOPER}/Platforms"
+readonly LIPO=$(xcrun -sdk iphoneos${SDK} -find lipo)
+LIBLIST=''
+DECLIBLIST=''
+MUXLIBLIST=''
+DEMUXLIBLIST=''
+
+if [[ -z "${SDK}" ]]; then
+  echo "iOS SDK not available"
+  exit 1
+elif [[ ${SDK%%.*} -gt 8 ]]; then
+  EXTRA_CFLAGS="-fembed-bitcode"
+elif [[ ${SDK%%.*} -le 6 ]]; then
+  echo "You need iOS SDK version 6.0 or above"
+  exit 1
+fi
+
+echo "Xcode Version: ${XCODE}"
+echo "iOS SDK Version: ${SDK}"
+
+if [[ -e "${BUILDDIR}" || -e "${TARGETDIR}" || -e "${DECTARGETDIR}" \
+      || -e "${MUXTARGETDIR}" || -e "${DEMUXTARGETDIR}" ]]; then
+  cat << EOF
+WARNING: The following directories will be deleted:
+WARNING:   ${BUILDDIR}
+WARNING:   ${TARGETDIR}
+WARNING:   ${DECTARGETDIR}
+WARNING:   ${MUXTARGETDIR}
+WARNING:   ${DEMUXTARGETDIR}
+WARNING: The build will continue in 5 seconds...
+EOF
+  sleep 5
+fi
+rm -rf ${BUILDDIR} ${TARGETDIR} ${DECTARGETDIR} \
+    ${MUXTARGETDIR} ${DEMUXTARGETDIR}
+mkdir -p ${BUILDDIR} ${TARGETDIR}/Headers/ ${DECTARGETDIR}/Headers/ \
+    ${MUXTARGETDIR}/Headers/ ${DEMUXTARGETDIR}/Headers/
+
+if [[ ! -e ${SRCDIR}/configure ]]; then
+  if ! (cd ${SRCDIR} && sh autogen.sh); then
+    cat << EOF
+Error creating configure script!
+This script requires the autoconf/automake and libtool to build. MacPorts can
+be used to obtain these:
+https://www.macports.org/install.php
+EOF
+    exit 1
+  fi
+fi
+
+for PLATFORM in ${PLATFORMS}; do
+  ARCH2=""
+  if [[ "${PLATFORM}" == "iPhoneOS-V7-arm64" ]]; then
+    PLATFORM="iPhoneOS"
+    ARCH="aarch64"
+    ARCH2="arm64"
+  elif [[ "${PLATFORM}" == "iPhoneOS-V7s" ]]; then
+    PLATFORM="iPhoneOS"
+    ARCH="armv7s"
+  elif [[ "${PLATFORM}" == "iPhoneOS-V7" ]]; then
+    PLATFORM="iPhoneOS"
+    ARCH="armv7"
+  elif [[ "${PLATFORM}" == "iPhoneOS-V6" ]]; then
+    PLATFORM="iPhoneOS"
+    ARCH="armv6"
+  elif [[ "${PLATFORM}" == "iPhoneSimulator64" ]]; then
+    PLATFORM="iPhoneSimulator"
+    ARCH="x86_64"
+  else
+    ARCH="i386"
+  fi
+
+  ROOTDIR="${BUILDDIR}/${PLATFORM}-${SDK}-${ARCH}"
+  mkdir -p "${ROOTDIR}"
+
+  DEVROOT="${DEVELOPER}/Toolchains/XcodeDefault.xctoolchain"
+  SDKROOT="${PLATFORMSROOT}/"
+  SDKROOT+="${PLATFORM}.platform/Developer/SDKs/${PLATFORM}${SDK}.sdk/"
+  CFLAGS="-arch ${ARCH2:-${ARCH}} -pipe -isysroot ${SDKROOT} -O3 -DNDEBUG"
+  CFLAGS+=" -miphoneos-version-min=${IOS_MIN_VERSION} ${EXTRA_CFLAGS}"
+
+  set -x
+  export PATH="${DEVROOT}/usr/bin:${OLDPATH}"
+  ${SRCDIR}/configure --host=${ARCH}-apple-darwin --prefix=${ROOTDIR} \
+    --build=$(${SRCDIR}/config.guess) \
+    --disable-shared --enable-static \
+    --enable-libwebpdecoder --enable-swap-16bit-csp \
+    --enable-libwebpmux \
+    CFLAGS="${CFLAGS}"
+  set +x
+
+  # Build only the libraries, skip the examples.
+  make V=0 -C sharpyuv
+  make V=0 -C src install
+
+  LIBLIST+=" ${ROOTDIR}/lib/libwebp.a"
+  DECLIBLIST+=" ${ROOTDIR}/lib/libwebpdecoder.a"
+  MUXLIBLIST+=" ${ROOTDIR}/lib/libwebpmux.a"
+  DEMUXLIBLIST+=" ${ROOTDIR}/lib/libwebpdemux.a"
+
+  make clean
+
+  export PATH=${OLDPATH}
+done
+
+echo "LIBLIST = ${LIBLIST}"
+cp -a ${SRCDIR}/src/webp/{decode,encode,types}.h ${TARGETDIR}/Headers/
+${LIPO} -create ${LIBLIST} -output ${TARGETDIR}/WebP
+
+echo "DECLIBLIST = ${DECLIBLIST}"
+cp -a ${SRCDIR}/src/webp/{decode,types}.h ${DECTARGETDIR}/Headers/
+${LIPO} -create ${DECLIBLIST} -output ${DECTARGETDIR}/WebPDecoder
+
+echo "MUXLIBLIST = ${MUXLIBLIST}"
+cp -a ${SRCDIR}/src/webp/{types,mux,mux_types}.h \
+    ${MUXTARGETDIR}/Headers/
+${LIPO} -create ${MUXLIBLIST} -output ${MUXTARGETDIR}/WebPMux
+
+echo "DEMUXLIBLIST = ${DEMUXLIBLIST}"
+cp -a ${SRCDIR}/src/webp/{decode,types,mux_types,demux}.h \
+    ${DEMUXTARGETDIR}/Headers/
+${LIPO} -create ${DEMUXLIBLIST} -output ${DEMUXTARGETDIR}/WebPDemux
+
+echo  "SUCCESS"
diff --git a/m4/.gitignore b/m4/.gitignore
new file mode 100644
index 0000000..64d9bbc
--- /dev/null
+++ b/m4/.gitignore
@@ -0,0 +1,2 @@
+/libtool.m4
+/lt*.m4
diff --git a/m4/ax_pthread.m4 b/m4/ax_pthread.m4
new file mode 100644
index 0000000..d383ad5
--- /dev/null
+++ b/m4/ax_pthread.m4
@@ -0,0 +1,332 @@
+# ===========================================================================
+#        http://www.gnu.org/software/autoconf-archive/ax_pthread.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+#   AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+#
+# DESCRIPTION
+#
+#   This macro figures out how to build C programs using POSIX threads. It
+#   sets the PTHREAD_LIBS output variable to the threads library and linker
+#   flags, and the PTHREAD_CFLAGS output variable to any special C compiler
+#   flags that are needed. (The user can also force certain compiler
+#   flags/libs to be tested by setting these environment variables.)
+#
+#   Also sets PTHREAD_CC to any special C compiler that is needed for
+#   multi-threaded programs (defaults to the value of CC otherwise). (This
+#   is necessary on AIX to use the special cc_r compiler alias.)
+#
+#   NOTE: You are assumed to not only compile your program with these flags,
+#   but also link it with them as well. e.g. you should link with
+#   $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
+#
+#   If you are only building threads programs, you may wish to use these
+#   variables in your default LIBS, CFLAGS, and CC:
+#
+#     LIBS="$PTHREAD_LIBS $LIBS"
+#     CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+#     CC="$PTHREAD_CC"
+#
+#   In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
+#   has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
+#   (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+#
+#   Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
+#   PTHREAD_PRIO_INHERIT symbol is defined when compiling with
+#   PTHREAD_CFLAGS.
+#
+#   ACTION-IF-FOUND is a list of shell commands to run if a threads library
+#   is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
+#   is not found. If ACTION-IF-FOUND is not specified, the default action
+#   will define HAVE_PTHREAD.
+#
+#   Please let the authors know if this macro fails on any platform, or if
+#   you have any other suggestions or comments. This macro was based on work
+#   by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
+#   from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
+#   Alejandro Forero Cuervo to the autoconf macro repository. We are also
+#   grateful for the helpful feedback of numerous users.
+#
+#   Updated for Autoconf 2.68 by Daniel Richard G.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
+#   Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
+#
+#   This program is free software: you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by the
+#   Free Software Foundation, either version 3 of the License, or (at your
+#   option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+#   Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License along
+#   with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#   As a special exception, the respective Autoconf Macro's copyright owner
+#   gives unlimited permission to copy, distribute and modify the configure
+#   scripts that are the output of Autoconf when processing the Macro. You
+#   need not follow the terms of the GNU General Public License when using
+#   or distributing such scripts, even though portions of the text of the
+#   Macro appear in them. The GNU General Public License (GPL) does govern
+#   all other use of the material that constitutes the Autoconf Macro.
+#
+#   This special exception to the GPL applies to versions of the Autoconf
+#   Macro released by the Autoconf Archive. When you make and distribute a
+#   modified version of the Autoconf Macro, you may extend this special
+#   exception to the GPL to apply to your modified version as well.
+
+#serial 21
+
+AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
+AC_DEFUN([AX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_PUSH([C])
+ax_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+        AC_TRY_LINK_FUNC([pthread_join], [ax_pthread_ok=yes])
+        AC_MSG_RESULT([$ax_pthread_ok])
+        if test x"$ax_pthread_ok" = xno; then
+                PTHREAD_LIBS=""
+                PTHREAD_CFLAGS=""
+        fi
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try.  Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important.  Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+#       other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+#      doesn't hurt to check since this sometimes defines pthreads too;
+#      also defines -D_REENTRANT)
+#      ... -mt is also the pthreads flag for HP/aCC
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case ${host_os} in
+        solaris*)
+
+        # On Solaris (at least, for some versions), libc contains stubbed
+        # (non-functional) versions of the pthreads routines, so link-based
+        # tests will erroneously succeed.  (We need to link with -pthreads/-mt/
+        # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather
+        # a function called by this macro, so we could check for that, but
+        # who knows whether they'll stub that too in a future libc.)  So,
+        # we'll just look for -pthreads and -lpthread first:
+
+        ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
+        ;;
+
+        darwin*)
+        ax_pthread_flags="-pthread $ax_pthread_flags"
+        ;;
+esac
+
+# Clang doesn't consider unrecognized options an error unless we specify
+# -Werror. We throw in some extra Clang-specific options to ensure that
+# this doesn't happen for GCC, which also accepts -Werror.
+
+AC_MSG_CHECKING([if compiler needs -Werror to reject unknown flags])
+save_CFLAGS="$CFLAGS"
+ax_pthread_extra_flags="-Werror"
+CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument"
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int foo(void);],[foo()])],
+                  [AC_MSG_RESULT([yes])],
+                  [ax_pthread_extra_flags=
+                   AC_MSG_RESULT([no])])
+CFLAGS="$save_CFLAGS"
+
+if test x"$ax_pthread_ok" = xno; then
+for flag in $ax_pthread_flags; do
+
+        case $flag in
+                none)
+                AC_MSG_CHECKING([whether pthreads work without any flags])
+                ;;
+
+                -*)
+                AC_MSG_CHECKING([whether pthreads work with $flag])
+                PTHREAD_CFLAGS="$flag"
+                ;;
+
+                pthread-config)
+                AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
+                if test x"$ax_pthread_config" = xno; then continue; fi
+                PTHREAD_CFLAGS="`pthread-config --cflags`"
+                PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+                ;;
+
+                *)
+                AC_MSG_CHECKING([for the pthreads library -l$flag])
+                PTHREAD_LIBS="-l$flag"
+                ;;
+        esac
+
+        save_LIBS="$LIBS"
+        save_CFLAGS="$CFLAGS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags"
+
+        # Check for various functions.  We must include pthread.h,
+        # since some functions may be macros.  (On the Sequent, we
+        # need a special flag -Kthread to make this header compile.)
+        # We check for pthread_join because it is in -lpthread on IRIX
+        # while pthread_create is in libc.  We check for pthread_attr_init
+        # due to DEC craziness with -lpthreads.  We check for
+        # pthread_cleanup_push because it is one of the few pthread
+        # functions on Solaris that doesn't have a non-functional libc stub.
+        # We try pthread_create on general principles.
+        AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
+                        static void routine(void *a) { a = 0; }
+                        static void *start_routine(void *a) { return a; }],
+                       [pthread_t th; pthread_attr_t attr;
+                        pthread_create(&th, 0, start_routine, 0);
+                        pthread_join(th, 0);
+                        pthread_attr_init(&attr);
+                        pthread_cleanup_push(routine, 0);
+                        pthread_cleanup_pop(0) /* ; */])],
+                [ax_pthread_ok=yes],
+                [])
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        AC_MSG_RESULT([$ax_pthread_ok])
+        if test "x$ax_pthread_ok" = xyes; then
+                break;
+        fi
+
+        PTHREAD_LIBS=""
+        PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$ax_pthread_ok" = xyes; then
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+        AC_MSG_CHECKING([for joinable pthread attribute])
+        attr_name=unknown
+        for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+            AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
+                           [int attr = $attr; return attr /* ; */])],
+                [attr_name=$attr; break],
+                [])
+        done
+        AC_MSG_RESULT([$attr_name])
+        if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+            AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$attr_name],
+                               [Define to necessary symbol if this constant
+                                uses a non-standard name on your system.])
+        fi
+
+        AC_MSG_CHECKING([if more special flags are required for pthreads])
+        flag=no
+        case ${host_os} in
+            aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";;
+            osf* | hpux*) flag="-D_REENTRANT";;
+            solaris*)
+            if test "$GCC" = "yes"; then
+                flag="-D_REENTRANT"
+            else
+                # TODO: What about Clang on Solaris?
+                flag="-mt -D_REENTRANT"
+            fi
+            ;;
+        esac
+        AC_MSG_RESULT([$flag])
+        if test "x$flag" != xno; then
+            PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+        fi
+
+        AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
+            [ax_cv_PTHREAD_PRIO_INHERIT], [
+                AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
+                                                [[int i = PTHREAD_PRIO_INHERIT;]])],
+                    [ax_cv_PTHREAD_PRIO_INHERIT=yes],
+                    [ax_cv_PTHREAD_PRIO_INHERIT=no])
+            ])
+        AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
+            [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])])
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        # More AIX lossage: compile with *_r variant
+        if test "x$GCC" != xyes; then
+            case $host_os in
+                aix*)
+                AS_CASE(["x/$CC"],
+                  [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
+                  [#handle absolute path differently from PATH based program lookup
+                   AS_CASE(["x$CC"],
+                     [x/*],
+                     [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
+                     [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
+                ;;
+            esac
+        fi
+fi
+
+test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
+
+AC_SUBST([PTHREAD_LIBS])
+AC_SUBST([PTHREAD_CFLAGS])
+AC_SUBST([PTHREAD_CC])
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$ax_pthread_ok" = xyes; then
+        ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1])
+        :
+else
+        ax_pthread_ok=no
+        $2
+fi
+AC_LANG_POP
+])dnl AX_PTHREAD
diff --git a/makefile.unix b/makefile.unix
new file mode 100644
index 0000000..8348d10
--- /dev/null
+++ b/makefile.unix
@@ -0,0 +1,536 @@
+# This makefile is a simpler alternative to the autoconf-based build
+# system, for simple local building of the libraries and tools.
+# It will not install the libraries system-wide, but just create the 'cwebp'
+# and 'dwebp' tools in the examples/ directory, along with the static
+# libraries 'src/libwebp.a', 'src/libwebpdecoder.a', 'src/mux/libwebpmux.a',
+# 'src/demux/libwebpdemux.a', 'extras/libwebpextras.a' and
+# 'sharpyuv/libsharpyuv.a'.
+#
+# To build the library and examples, use:
+#    make -f makefile.unix
+# from this top directory.
+
+#### Customizable part ####
+
+# These flags assume you have libpng, libjpeg, libtiff and libgif installed. If
+# not, either follow the install instructions below or just comment out the next
+# four lines.
+EXTRA_FLAGS= -DWEBP_HAVE_PNG -DWEBP_HAVE_JPEG -DWEBP_HAVE_TIFF
+DWEBP_LIBS= -lpng -lz
+CWEBP_LIBS= $(DWEBP_LIBS) -ljpeg -ltiff
+GIF_LIBS = -lgif
+
+ifeq ($(strip $(shell uname)), Darwin)
+  # Work around a problem linking tables marked as common symbols,
+  # cf., src/enc/yuv.[hc]
+  # Failure observed with: gcc 4.2.1 and 4.0.1.
+  EXTRA_FLAGS += -fno-common
+  EXTRA_FLAGS += -DHAVE_GLUT_GLUT_H
+  EXTRA_FLAGS += -Wno-deprecated-declarations
+  EXTRA_FLAGS += -I/opt/local/include
+  EXTRA_LIBS  += -L/opt/local/lib
+  GL_LIBS = -framework GLUT -framework OpenGL
+else
+  EXTRA_FLAGS += -I/usr/local/include
+  EXTRA_LIBS  += -L/usr/local/lib
+  GL_LIBS = -lglut -lGL
+endif
+
+# SDL flags: use sdl-config if it exists
+SDL_CONFIG = $(shell sdl-config --version 2> /dev/null)
+ifneq ($(SDL_CONFIG),)
+  SDL_LIBS = $(shell sdl-config --libs)
+  SDL_FLAGS = $(shell sdl-config --cflags)
+else
+  # use best-guess
+  SDL_LIBS = -lSDL
+  SDL_FLAGS =
+endif
+
+# To install libraries on Mac OS X:
+# 1. Install MacPorts (https://www.macports.org/install.php)
+# 2. Run "sudo port install jpeg"
+# 3. Run "sudo port install libpng"
+# 4. Run "sudo port install tiff"
+# 5. Run "sudo port install giflib"
+
+# To install libraries on Linux:
+# 1. Run "sudo apt-get install libjpeg62-dev"
+# 2. Run "sudo apt-get install libpng12-dev"
+# 3. Run "sudo apt-get install libtiff4-dev"
+# 4. Run "sudo apt-get install libgif-dev"
+
+# Uncomment for build for 32bit platform
+# Alternatively, you can just use the command
+# 'make -f makefile.unix EXTRA_FLAGS=-m32' to that effect.
+# EXTRA_FLAGS += -m32
+
+# Extra flags to enable byte swap for 16 bit colorspaces.
+# EXTRA_FLAGS += -DWEBP_SWAP_16BIT_CSP=1
+
+# Extra flags to enable multi-threading
+EXTRA_FLAGS += -DWEBP_USE_THREAD
+EXTRA_LIBS += -lpthread
+
+# Control symbol visibility. Comment out if your compiler doesn't support it.
+EXTRA_FLAGS += -fvisibility=hidden
+
+# Extra flags to emulate C89 strictness with the full ANSI
+EXTRA_FLAGS += -Wextra -Wold-style-definition
+EXTRA_FLAGS += -Wmissing-prototypes
+EXTRA_FLAGS += -Wmissing-declarations
+EXTRA_FLAGS += -Wdeclaration-after-statement
+EXTRA_FLAGS += -Wshadow
+EXTRA_FLAGS += -Wformat-security -Wformat-nonliteral
+# EXTRA_FLAGS += -Wvla
+
+# SSE4.1-specific flags:
+ifeq ($(HAVE_SSE41), 1)
+EXTRA_FLAGS += -DWEBP_HAVE_SSE41
+src/dsp/%_sse41.o: EXTRA_FLAGS += -msse4.1
+endif
+
+# NEON-specific flags:
+# EXTRA_FLAGS += -march=armv7-a -mfloat-abi=hard -mfpu=neon -mtune=cortex-a8
+# -> seems to make the overall lib slower: -fno-split-wide-types
+
+# MIPS (MSA) 32-bit build specific flags for mips32r5 (p5600):
+# EXTRA_FLAGS += -mips32r5 -mabi=32 -mtune=p5600 -mmsa -mfp64
+# EXTRA_FLAGS += -msched-weight -mload-store-pairs
+
+# MIPS (MSA) 64-bit build specific flags for mips64r6 (i6400):
+# EXTRA_FLAGS += -mips64r6 -mabi=64 -mtune=i6400 -mmsa -mfp64
+# EXTRA_FLAGS += -msched-weight -mload-store-pairs
+
+#### Nothing should normally be changed below this line ####
+
+AR = ar
+ARFLAGS = r
+CPPFLAGS = -I. -Isrc/ -Wall
+ifeq ($(DEBUG), 1)
+  CFLAGS = -g
+else
+  CFLAGS = -O3 -DNDEBUG
+endif
+CFLAGS += $(EXTRA_FLAGS)
+CC = gcc
+INSTALL = install
+GROFF = /usr/bin/groff
+COL = /usr/bin/col
+LDFLAGS = $(EXTRA_LIBS) $(EXTRA_FLAGS) -lm
+
+ifdef BITTRACE
+CFLAGS += -DBITTRACE=$(BITTRACE)
+endif
+
+ANIM_UTIL_OBJS = \
+    examples/anim_util.o \
+
+SHARPYUV_OBJS = \
+    sharpyuv/sharpyuv.o \
+    sharpyuv/sharpyuv_cpu.o \
+    sharpyuv/sharpyuv_csp.o \
+    sharpyuv/sharpyuv_dsp.o \
+    sharpyuv/sharpyuv_gamma.o \
+    sharpyuv/sharpyuv_neon.o \
+    sharpyuv/sharpyuv_sse2.o \
+
+DEC_OBJS = \
+    src/dec/alpha_dec.o \
+    src/dec/buffer_dec.o \
+    src/dec/frame_dec.o \
+    src/dec/idec_dec.o \
+    src/dec/io_dec.o \
+    src/dec/quant_dec.o \
+    src/dec/tree_dec.o \
+    src/dec/vp8_dec.o \
+    src/dec/vp8l_dec.o \
+    src/dec/webp_dec.o \
+
+DEMUX_OBJS = \
+    src/demux/anim_decode.o \
+    src/demux/demux.o \
+
+DSP_DEC_OBJS = \
+    src/dsp/alpha_processing.o \
+    src/dsp/alpha_processing_mips_dsp_r2.o \
+    src/dsp/alpha_processing_neon.o \
+    src/dsp/alpha_processing_sse2.o \
+    src/dsp/alpha_processing_sse41.o \
+    src/dsp/cpu.o \
+    src/dsp/dec.o \
+    src/dsp/dec_clip_tables.o \
+    src/dsp/dec_mips32.o \
+    src/dsp/dec_mips_dsp_r2.o \
+    src/dsp/dec_msa.o \
+    src/dsp/dec_neon.o \
+    src/dsp/dec_sse2.o \
+    src/dsp/dec_sse41.o \
+    src/dsp/filters.o \
+    src/dsp/filters_mips_dsp_r2.o \
+    src/dsp/filters_msa.o \
+    src/dsp/filters_neon.o \
+    src/dsp/filters_sse2.o \
+    src/dsp/lossless.o \
+    src/dsp/lossless_mips_dsp_r2.o \
+    src/dsp/lossless_msa.o \
+    src/dsp/lossless_neon.o \
+    src/dsp/lossless_sse2.o \
+    src/dsp/lossless_sse41.o \
+    src/dsp/rescaler.o \
+    src/dsp/rescaler_mips32.o \
+    src/dsp/rescaler_mips_dsp_r2.o \
+    src/dsp/rescaler_msa.o \
+    src/dsp/rescaler_neon.o \
+    src/dsp/rescaler_sse2.o \
+    src/dsp/upsampling.o \
+    src/dsp/upsampling_mips_dsp_r2.o \
+    src/dsp/upsampling_msa.o \
+    src/dsp/upsampling_neon.o \
+    src/dsp/upsampling_sse2.o \
+    src/dsp/upsampling_sse41.o \
+    src/dsp/yuv.o \
+    src/dsp/yuv_mips32.o \
+    src/dsp/yuv_mips_dsp_r2.o \
+    src/dsp/yuv_neon.o \
+    src/dsp/yuv_sse2.o \
+    src/dsp/yuv_sse41.o \
+
+DSP_ENC_OBJS = \
+    src/dsp/cost.o \
+    src/dsp/cost_mips32.o \
+    src/dsp/cost_mips_dsp_r2.o \
+    src/dsp/cost_neon.o \
+    src/dsp/cost_sse2.o \
+    src/dsp/enc.o \
+    src/dsp/enc_mips32.o \
+    src/dsp/enc_mips_dsp_r2.o \
+    src/dsp/enc_msa.o \
+    src/dsp/enc_neon.o \
+    src/dsp/enc_sse2.o \
+    src/dsp/enc_sse41.o \
+    src/dsp/lossless_enc.o \
+    src/dsp/lossless_enc_mips32.o \
+    src/dsp/lossless_enc_mips_dsp_r2.o \
+    src/dsp/lossless_enc_msa.o \
+    src/dsp/lossless_enc_neon.o \
+    src/dsp/lossless_enc_sse2.o \
+    src/dsp/lossless_enc_sse41.o \
+    src/dsp/ssim.o \
+    src/dsp/ssim_sse2.o \
+
+ENC_OBJS = \
+    src/enc/alpha_enc.o \
+    src/enc/analysis_enc.o \
+    src/enc/backward_references_cost_enc.o \
+    src/enc/backward_references_enc.o \
+    src/enc/config_enc.o \
+    src/enc/cost_enc.o \
+    src/enc/filter_enc.o \
+    src/enc/frame_enc.o \
+    src/enc/histogram_enc.o \
+    src/enc/iterator_enc.o \
+    src/enc/near_lossless_enc.o \
+    src/enc/picture_enc.o \
+    src/enc/picture_csp_enc.o \
+    src/enc/picture_psnr_enc.o \
+    src/enc/picture_rescale_enc.o \
+    src/enc/picture_tools_enc.o \
+    src/enc/predictor_enc.o \
+    src/enc/quant_enc.o \
+    src/enc/syntax_enc.o \
+    src/enc/token_enc.o \
+    src/enc/tree_enc.o \
+    src/enc/vp8l_enc.o \
+    src/enc/webp_enc.o \
+
+EX_FORMAT_DEC_OBJS = \
+    imageio/image_dec.o \
+    imageio/jpegdec.o \
+    imageio/metadata.o \
+    imageio/pngdec.o \
+    imageio/pnmdec.o \
+    imageio/tiffdec.o \
+    imageio/webpdec.o \
+
+EX_FORMAT_ENC_OBJS = \
+    imageio/image_enc.o \
+
+EX_UTIL_OBJS = \
+    examples/example_util.o \
+
+GIFDEC_OBJS = \
+    examples/gifdec.o \
+
+IMAGE_UTIL_OBJS = \
+    imageio/imageio_util.o \
+
+MUX_OBJS = \
+    src/mux/anim_encode.o \
+    src/mux/muxedit.o \
+    src/mux/muxinternal.o \
+    src/mux/muxread.o \
+
+UTILS_DEC_OBJS = \
+    src/utils/bit_reader_utils.o \
+    src/utils/color_cache_utils.o \
+    src/utils/filters_utils.o \
+    src/utils/huffman_utils.o \
+    src/utils/quant_levels_dec_utils.o \
+    src/utils/random_utils.o \
+    src/utils/rescaler_utils.o \
+    src/utils/thread_utils.o \
+    src/utils/utils.o \
+
+UTILS_ENC_OBJS = \
+    src/utils/bit_writer_utils.o \
+    src/utils/huffman_encode_utils.o \
+    src/utils/quant_levels_utils.o \
+
+EXTRA_OBJS = \
+    extras/extras.o \
+    extras/quality_estimate.o \
+
+LIBWEBPDECODER_OBJS = $(DEC_OBJS) $(DSP_DEC_OBJS) $(UTILS_DEC_OBJS)
+LIBWEBP_OBJS = $(LIBWEBPDECODER_OBJS) $(ENC_OBJS) \
+               $(DSP_ENC_OBJS) $(UTILS_ENC_OBJS)
+LIBWEBPMUX_OBJS = $(MUX_OBJS)
+LIBWEBPDEMUX_OBJS = $(DEMUX_OBJS)
+LIBWEBPEXTRA_OBJS = $(EXTRA_OBJS)
+LIBSHARPYUV_OBJS = $(SHARPYUV_OBJS)
+
+HDRS_INSTALLED = \
+    src/webp/decode.h \
+    src/webp/demux.h \
+    src/webp/encode.h \
+    src/webp/mux.h \
+    src/webp/mux_types.h \
+    src/webp/types.h \
+
+SHARPYUV_HDRS_INSTALLED = \
+    sharpyuv/sharpyuv.h \
+    sharpyuv/sharpyuv_cpu.h \
+    sharpyuv/sharpyuv_csp.h \
+
+HDRS = \
+    src/dec/alphai_dec.h \
+    src/dec/common_dec.h \
+    src/dec/vp8_dec.h \
+    src/dec/vp8i_dec.h \
+    src/dec/vp8li_dec.h \
+    src/dec/webpi_dec.h \
+    src/dsp/common_sse2.h \
+    src/dsp/cpu.h \
+    src/dsp/dsp.h \
+    src/dsp/lossless.h \
+    src/dsp/lossless_common.h \
+    src/dsp/mips_macro.h \
+    src/dsp/msa_macro.h \
+    src/dsp/neon.h \
+    src/dsp/yuv.h \
+    src/enc/backward_references_enc.h \
+    src/enc/cost_enc.h \
+    src/enc/histogram_enc.h \
+    src/enc/vp8i_enc.h \
+    src/enc/vp8li_enc.h \
+    src/mux/animi.h \
+    src/mux/muxi.h \
+    src/utils/bit_reader_utils.h \
+    src/utils/bit_reader_inl_utils.h \
+    src/utils/bit_writer_utils.h \
+    src/utils/color_cache_utils.h \
+    src/utils/endian_inl_utils.h \
+    src/utils/filters_utils.h \
+    src/utils/huffman_utils.h \
+    src/utils/huffman_encode_utils.h \
+    src/utils/quant_levels_utils.h \
+    src/utils/quant_levels_dec_utils.h \
+    src/utils/random_utils.h \
+    src/utils/rescaler_utils.h \
+    src/utils/thread_utils.h \
+    src/utils/utils.h \
+    src/webp/format_constants.h \
+    $(HDRS_INSTALLED) \
+    $(SHARPYUV_HDRS_INSTALLED) \
+
+OUT_LIBS = examples/libexample_util.a
+OUT_LIBS += imageio/libimageio_util.a
+OUT_LIBS += imageio/libimagedec.a
+OUT_LIBS += imageio/libimageenc.a
+OUT_LIBS += src/libwebpdecoder.a
+OUT_LIBS += src/libwebp.a
+OUT_LIBS += sharpyuv/libsharpyuv.a
+EXTRA_LIB = extras/libwebpextras.a
+OUT_EXAMPLES = examples/cwebp examples/dwebp
+EXTRA_EXAMPLES = examples/gif2webp examples/vwebp examples/webpmux \
+                 examples/anim_diff examples/anim_dump \
+                 examples/img2webp examples/webpinfo
+OTHER_EXAMPLES = extras/get_disto extras/webp_quality extras/vwebp_sdl
+
+OUTPUT = $(OUT_LIBS) $(OUT_EXAMPLES)
+ifeq ($(MAKECMDGOALS),clean)
+  OUTPUT += $(EXTRA_EXAMPLES) $(OTHER_EXAMPLES)
+  OUTPUT += src/demux/libwebpdemux.a src/mux/libwebpmux.a $(EXTRA_LIB)
+  OUTPUT += examples/libgifdec.a examples/libanim_util.a
+endif
+
+ex: $(OUT_EXAMPLES)
+all: ex $(EXTRA_EXAMPLES) $(OTHER_EXAMPLES)
+extras: $(EXTRA_LIB)
+
+$(EX_FORMAT_DEC_OBJS): %.o: %.h
+
+# special dependencies:
+#   tree_dec.c/vp8_dec.c/bit_reader_utils.c <->
+#     bit_reader_inl_utils.h, endian_inl_utils.h
+#   bit_writer_utils.c <-> endian_inl_utils.h
+src/dec/tree_dec.o: src/utils/bit_reader_inl_utils.h
+src/dec/tree_dec.o: src/utils/endian_inl_utils.h
+src/dec/vp8_dec.o: src/utils/bit_reader_inl_utils.h src/utils/endian_inl_utils.h
+src/utils/bit_reader_utils.o: src/utils/bit_reader_inl_utils.h
+src/utils/bit_reader_utils.o: src/utils/endian_inl_utils.h
+src/utils/bit_writer_utils.o: src/utils/endian_inl_utils.h
+
+%.o: %.c $(HDRS)
+	$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
+
+examples/libanim_util.a: $(ANIM_UTIL_OBJS)
+examples/libexample_util.a: $(EX_UTIL_OBJS)
+examples/libgifdec.a: $(GIFDEC_OBJS)
+extras/libwebpextras.a: $(LIBWEBPEXTRA_OBJS)
+imageio/libimagedec.a: $(EX_FORMAT_DEC_OBJS)
+imageio/libimageenc.a: $(EX_FORMAT_ENC_OBJS)
+imageio/libimageio_util.a: $(IMAGE_UTIL_OBJS)
+src/libwebpdecoder.a: $(LIBWEBPDECODER_OBJS)
+src/libwebp.a: $(LIBWEBP_OBJS)
+src/mux/libwebpmux.a: $(LIBWEBPMUX_OBJS)
+src/demux/libwebpdemux.a: $(LIBWEBPDEMUX_OBJS)
+sharpyuv/libsharpyuv.a: $(LIBSHARPYUV_OBJS)
+
+%.a:
+	$(AR) $(ARFLAGS) $@ $^
+
+examples/anim_diff: examples/anim_diff.o $(ANIM_UTIL_OBJS) $(GIFDEC_OBJS)
+examples/anim_dump: examples/anim_dump.o $(ANIM_UTIL_OBJS) $(GIFDEC_OBJS)
+examples/cwebp: examples/cwebp.o
+examples/dwebp: examples/dwebp.o
+examples/gif2webp: examples/gif2webp.o $(GIFDEC_OBJS)
+examples/vwebp: examples/vwebp.o
+examples/webpmux: examples/webpmux.o
+examples/img2webp: examples/img2webp.o
+examples/webpinfo: examples/webpinfo.o
+
+examples/anim_diff: examples/libanim_util.a examples/libgifdec.a
+examples/anim_diff: src/demux/libwebpdemux.a examples/libexample_util.a
+examples/anim_diff: imageio/libimageio_util.a src/libwebp.a
+examples/anim_diff: sharpyuv/libsharpyuv.a
+examples/anim_diff: override EXTRA_LIBS += $(GIF_LIBS)
+examples/anim_diff: EXTRA_FLAGS += -DWEBP_HAVE_GIF
+examples/anim_dump: examples/libanim_util.a examples/libgifdec.a
+examples/anim_dump: src/demux/libwebpdemux.a
+examples/anim_dump: examples/libexample_util.a
+examples/anim_dump: imageio/libimageio_util.a
+examples/anim_dump: imageio/libimageenc.a
+examples/anim_dump: src/libwebp.a
+examples/anim_dump: sharpyuv/libsharpyuv.a
+examples/anim_dump: override EXTRA_LIBS += $(GIF_LIBS) $(DWEBP_LIBS)
+examples/cwebp: examples/libexample_util.a
+examples/cwebp: imageio/libimagedec.a
+examples/cwebp: src/demux/libwebpdemux.a
+examples/cwebp: imageio/libimageio_util.a
+examples/cwebp: src/libwebp.a
+examples/cwebp: sharpyuv/libsharpyuv.a
+examples/cwebp: override EXTRA_LIBS += $(CWEBP_LIBS)
+examples/dwebp: examples/libexample_util.a
+examples/dwebp: imageio/libimagedec.a
+examples/dwebp: src/demux/libwebpdemux.a
+examples/dwebp: imageio/libimageenc.a
+examples/dwebp: imageio/libimageio_util.a
+examples/dwebp: src/libwebp.a
+examples/dwebp: sharpyuv/libsharpyuv.a
+examples/dwebp: override EXTRA_LIBS += $(DWEBP_LIBS)
+examples/gif2webp: examples/libexample_util.a imageio/libimageio_util.a
+examples/gif2webp: examples/libgifdec.a src/mux/libwebpmux.a src/libwebp.a
+examples/gif2webp: sharpyuv/libsharpyuv.a
+examples/gif2webp: override EXTRA_LIBS += $(GIF_LIBS)
+examples/gif2webp: EXTRA_FLAGS += -DWEBP_HAVE_GIF
+examples/vwebp: examples/libexample_util.a src/demux/libwebpdemux.a
+examples/vwebp: imageio/libimageio_util.a src/libwebp.a
+examples/vwebp: sharpyuv/libsharpyuv.a
+examples/vwebp: override EXTRA_LIBS += $(GL_LIBS)
+examples/vwebp: EXTRA_FLAGS += -DWEBP_HAVE_GL
+examples/webpmux: examples/libexample_util.a imageio/libimageio_util.a
+examples/webpmux: src/mux/libwebpmux.a src/libwebpdecoder.a
+examples/img2webp: examples/libexample_util.a imageio/libimageio_util.a
+examples/img2webp: imageio/libimagedec.a
+examples/img2webp: src/demux/libwebpdemux.a
+examples/img2webp: src/mux/libwebpmux.a
+examples/img2webp: src/libwebp.a
+examples/img2webp: sharpyuv/libsharpyuv.a
+examples/img2webp: override EXTRA_LIBS += $(CWEBP_LIBS)
+examples/webpinfo: examples/libexample_util.a imageio/libimageio_util.a
+examples/webpinfo: src/libwebpdecoder.a
+
+extras/get_disto: extras/get_disto.o
+extras/get_disto: imageio/libimagedec.a
+extras/get_disto: src/demux/libwebpdemux.a
+extras/get_disto: imageio/libimageio_util.a
+extras/get_disto: src/libwebp.a
+extras/get_disto: sharpyuv/libsharpyuv.a
+extras/get_disto: override EXTRA_LIBS += $(CWEBP_LIBS)
+
+extras/webp_quality: extras/webp_quality.o
+extras/webp_quality: imageio/libimageio_util.a
+extras/webp_quality: $(EXTRA_LIB) src/libwebp.a
+extras/webp_quality: sharpyuv/libsharpyuv.a
+
+extras/vwebp_sdl: extras/vwebp_sdl.o
+extras/vwebp_sdl: extras/webp_to_sdl.o
+extras/vwebp_sdl: imageio/libimageio_util.a
+extras/vwebp_sdl: src/libwebp.a
+extras/vwebp_sdl: sharpyuv/libsharpyuv.a
+extras/vwebp_sdl: EXTRA_FLAGS += -DWEBP_HAVE_SDL $(SDL_FLAGS)
+extras/vwebp_sdl: override EXTRA_LIBS += $(SDL_LIBS)
+
+$(OUT_EXAMPLES) $(EXTRA_EXAMPLES) $(OTHER_EXAMPLES):
+	$(CC) -o $@ $^ $(LDFLAGS)
+
+dist: DESTDIR := dist
+dist: OUT_EXAMPLES += $(EXTRA_EXAMPLES)
+dist: all
+	$(INSTALL) -m755 -d $(DESTDIR)/include/webp \
+	            $(DESTDIR)/include/webp/sharpyuv \
+	            $(DESTDIR)/bin $(DESTDIR)/doc $(DESTDIR)/lib
+	$(INSTALL) -m755 -s $(OUT_EXAMPLES) $(DESTDIR)/bin
+	$(INSTALL) -m644 $(HDRS_INSTALLED) $(DESTDIR)/include/webp
+	$(INSTALL) -m644 $(SHARPYUV_HDRS_INSTALLED) $(DESTDIR)/include/webp/sharpyuv
+	$(INSTALL) -m644 src/libwebp.a $(DESTDIR)/lib
+	$(INSTALL) -m644 src/demux/libwebpdemux.a $(DESTDIR)/lib
+	$(INSTALL) -m644 src/mux/libwebpmux.a $(DESTDIR)/lib
+	$(INSTALL) -m644 sharpyuv/libsharpyuv.a $(DESTDIR)/lib
+	umask 022; \
+	for m in man/[cdv]webp.1 man/gif2webp.1 man/webpmux.1 \
+                 man/img2webp.1 man/webpinfo.1; do \
+	  basenam=$$(basename $$m .1); \
+	  $(GROFF) -t -e -man -T ascii $$m \
+	    | $(COL) -bx >$(DESTDIR)/doc/$${basenam}.txt; \
+	  $(GROFF) -t -e -man -T html $$m \
+	    | $(COL) -bx >$(DESTDIR)/doc/$${basenam}.html; \
+	done
+
+clean:
+	$(RM) $(OUTPUT) *~ \
+              examples/*.o examples/*~ \
+              extras/*.o extras/*~ \
+              imageio/*.o imageio/*~ \
+              sharpyuv/*.o sharpyuv/*~ \
+              src/dec/*.o src/dec/*~ \
+              src/demux/*.o src/demux/*~ \
+              src/dsp/*.o src/dsp/*~ \
+              src/enc/*.o src/enc/*~ \
+              src/mux/*.o src/mux/*~ \
+              src/utils/*.o src/utils/*~ \
+              src/webp/*~ man/*~ doc/*~ swig/*~ \
+
+.PHONY: all clean dist ex
+.SUFFIXES:
diff --git a/man/Makefile.am b/man/Makefile.am
new file mode 100644
index 0000000..57e2483
--- /dev/null
+++ b/man/Makefile.am
@@ -0,0 +1,17 @@
+man_MANS = cwebp.1 dwebp.1
+if BUILD_MUX
+  man_MANS += webpmux.1
+endif
+if BUILD_GIF2WEBP
+  man_MANS += gif2webp.1
+endif
+if BUILD_IMG2WEBP
+  man_MANS += img2webp.1
+endif
+if BUILD_VWEBP
+  man_MANS += vwebp.1
+endif
+if BUILD_WEBPINFO
+  man_MANS += webpinfo.1
+endif
+EXTRA_DIST = $(man_MANS)
diff --git a/man/cwebp.1 b/man/cwebp.1
new file mode 100644
index 0000000..28de4c9
--- /dev/null
+++ b/man/cwebp.1
@@ -0,0 +1,328 @@
+.\"                                      Hey, EMACS: -*- nroff -*-
+.TH CWEBP 1 "March 17, 2022"
+.SH NAME
+cwebp \- compress an image file to a WebP file
+.SH SYNOPSIS
+.B cwebp
+.RI [ options ] " input_file \-o output_file.webp
+.br
+.SH DESCRIPTION
+This manual page documents the
+.B cwebp
+command.
+.PP
+\fBcwebp\fP compresses an image using the WebP format.
+Input format can be either PNG, JPEG, TIFF, WebP or raw Y'CbCr samples.
+Note: Animated PNG and WebP files are not supported.
+.SH OPTIONS
+The basic options are:
+.TP
+.BI \-o " string
+Specify the name of the output WebP file. If omitted, \fBcwebp\fP will
+perform compression but only report statistics.
+Using "\-" as output name will direct output to 'stdout'.
+.TP
+.BI \-\- " string
+Explicitly specify the input file. This option is useful if the input
+file starts with a '\-' for instance. This option must appear \fBlast\fP.
+Any other options afterward will be ignored.
+.TP
+.B \-h, \-help
+A short usage summary.
+.TP
+.B \-H, \-longhelp
+A summary of all the possible options.
+.TP
+.B \-version
+Print the version number (as major.minor.revision) and exit.
+.TP
+.B \-lossless
+Encode the image without any loss. For images with fully transparent area,
+the invisible pixel values (R/G/B or Y/U/V) will be preserved only if the
+\-exact option is used.
+.TP
+.BI \-near_lossless " int
+Specify the level of near\-lossless image preprocessing. This option adjusts
+pixel values to help compressibility, but has minimal impact on the visual
+quality. It triggers lossless compression mode automatically. The range is 0
+(maximum preprocessing) to 100 (no preprocessing, the default). The typical
+value is around 60. Note that lossy with \fB\-q 100\fP can at times yield
+better results.
+.TP
+.BI \-q " float
+Specify the compression factor for RGB channels between 0 and 100. The default
+is 75.
+.br
+In case of lossy compression (default), a small factor produces a smaller file
+with lower quality. Best quality is achieved by using a value of 100.
+.br
+In case of lossless compression (specified by the \fB\-lossless\fP option), a
+small factor enables faster compression speed, but produces a larger file.
+Maximum compression is achieved by using a value of 100.
+.TP
+.BI \-z " int
+Switch on \fBlossless\fP compression mode with the specified level between 0
+and 9, with level 0 being the fastest, 9 being the slowest. Fast mode
+produces larger file size than slower ones. A good default is \fB\-z 6\fP.
+This option is actually a shortcut for some predefined settings for quality
+and method. If options \fB\-q\fP  or \fB\-m\fP are subsequently used, they will
+invalidate the effect of this option.
+.TP
+.BI \-alpha_q " int
+Specify the compression factor for alpha compression between 0 and 100.
+Lossless compression of alpha is achieved using a value of 100, while the lower
+values result in a lossy compression. The default is 100.
+.TP
+.BI \-preset " string
+Specify a set of pre\-defined parameters to suit a particular type of
+source material. Possible values are:  \fBdefault\fP, \fBphoto\fP,
+\fBpicture\fP, \fBdrawing\fP, \fBicon\fP, \fBtext\fP. Since
+\fB\-preset\fP overwrites the other parameters' values (except the
+\fB\-q\fP one), this option should preferably appear first in the
+order of the arguments.
+.TP
+.BI \-m " int
+Specify the compression method to use. This parameter controls the
+trade off between encoding speed and the compressed file size and quality.
+Possible values range from 0 to 6. Default value is 4.
+When higher values are used, the encoder will spend more time inspecting
+additional encoding possibilities and decide on the quality gain.
+Lower value can result in faster processing time at the expense of
+larger file size and lower compression quality.
+.TP
+.BI \-crop " x_position y_position width height
+Crop the source to a rectangle with top\-left corner at coordinates
+(\fBx_position\fP, \fBy_position\fP) and size \fBwidth\fP x \fBheight\fP.
+This cropping area must be fully contained within the source rectangle.
+Note: the cropping is applied \fIbefore\fP any scaling.
+.TP
+.BI \-resize " width height
+Resize the source to a rectangle with size \fBwidth\fP x \fBheight\fP.
+If either (but not both) of the \fBwidth\fP or \fBheight\fP parameters is 0,
+the value will be calculated preserving the aspect\-ratio. Note: scaling
+is applied \fIafter\fP cropping.
+.TP
+.B \-mt
+Use multi\-threading for encoding, if possible.
+.TP
+.B \-low_memory
+Reduce memory usage of lossy encoding by saving four times the compressed
+size (typically). This will make the encoding slower and the output slightly
+different in size and distortion. This flag is only effective for methods
+3 and up, and is off by default. Note that leaving this flag off will have
+some side effects on the bitstream: it forces certain bitstream features
+like number of partitions (forced to 1). Note that a more detailed report
+of bitstream size is printed by \fBcwebp\fP when using this option.
+
+.SS LOSSY OPTIONS
+These options are only effective when doing lossy encoding (the default, with
+or without alpha).
+
+.TP
+.BI \-size " int
+Specify a target size (in bytes) to try and reach for the compressed output.
+The compressor will make several passes of partial encoding in order to get as
+close as possible to this target. If both \fB\-size\fP and \fB\-psnr\fP
+are used, \fB\-size\fP value will prevail.
+.TP
+.BI \-psnr " float
+Specify a target PSNR (in dB) to try and reach for the compressed output.
+The compressor will make several passes of partial encoding in order to get as
+close as possible to this target. If both \fB\-size\fP and \fB\-psnr\fP
+are used, \fB\-size\fP value will prevail.
+.TP
+.BI \-pass " int
+Set a maximum number of passes to use during the dichotomy used by
+options \fB\-size\fP or \fB\-psnr\fP. Maximum value is 10, default is 1.
+If options \fB\-size\fP or \fB\-psnr\fP were used, but \fB\-pass\fP wasn't
+specified, a default value of '6' passes will be used.
+.TP
+.BI \-qrange " int int
+Specifies the permissible interval for the quality factor. This is particularly
+useful when using multi-pass (\fB\-size\fP or \fB\-psnr\fP options).
+Default is 0 100.
+If the quality factor is outside this range, it will be clamped.
+If the minimum value must be less or equal to the maximum one.
+.TP
+.B \-af
+Turns auto\-filter on. This algorithm will spend additional time optimizing
+the filtering strength to reach a well\-balanced quality.
+.TP
+.B \-jpeg_like
+Change the internal parameter mapping to better match the expected size
+of JPEG compression. This flag will generally produce an output file of
+similar size to its JPEG equivalent (for the same \fB\-q\fP setting), but
+with less visual distortion.
+
+.TP
+Advanced options:
+
+.TP
+.BI \-f " int
+Specify the strength of the deblocking filter, between 0 (no filtering)
+and 100 (maximum filtering). A value of 0 will turn off any filtering.
+Higher value will increase the strength of the filtering process applied
+after decoding the picture. The higher the value the smoother the picture will
+appear. Typical values are usually in the range of 20 to 50.
+.TP
+.BI \-sharpness " int
+Specify the sharpness of the filtering (if used).
+Range is 0 (sharpest) to 7 (least sharp). Default is 0.
+.TP
+.B \-strong
+Use strong filtering (if filtering is being used thanks to the
+\fB\-f\fP option). Strong filtering is on by default.
+.TP
+.B \-nostrong
+Disable strong filtering (if filtering is being used thanks to the
+\fB\-f\fP option) and use simple filtering instead.
+.TP
+.B \-sharp_yuv
+Use more accurate and sharper RGB->YUV conversion if needed. Note that this
+process is slower than the default 'fast' RGB->YUV conversion.
+.TP
+.BI \-sns " int
+Specify the amplitude of the spatial noise shaping. Spatial noise shaping
+(or \fBsns\fP for short) refers to a general collection of built\-in algorithms
+used to decide which area of the picture should use relatively less bits,
+and where else to better transfer these bits. The possible range goes from
+0 (algorithm is off) to 100 (the maximal effect). The default value is 50.
+.TP
+.BI \-segments " int
+Change the number of partitions to use during the segmentation of the
+sns algorithm. Segments should be in range 1 to 4. Default value is 4.
+This option has no effect for methods 3 and up, unless \fB\-low_memory\fP
+is used.
+.TP
+.BI \-partition_limit " int
+Degrade quality by limiting the number of bits used by some macroblocks.
+Range is 0 (no degradation, the default) to 100 (full degradation).
+Useful values are usually around 30\-70 for moderately large images.
+In the VP8 format, the so\-called control partition has a limit of 512k and
+is used to store the following information: whether the macroblock is skipped,
+which segment it belongs to, whether it is coded as intra 4x4 or intra 16x16
+mode, and finally the prediction modes to use for each of the sub\-blocks.
+For a very large image, 512k only leaves room to few bits per 16x16 macroblock.
+The absolute minimum is 4 bits per macroblock. Skip, segment, and mode
+information can use up almost all these 4 bits (although the case is unlikely),
+which is problematic for very large images. The partition_limit factor controls
+how frequently the most bit\-costly mode (intra 4x4) will be used. This is
+useful in case the 512k limit is reached and the following message is displayed:
+\fIError code: 6 (PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k)\fP.
+If using \fB\-partition_limit\fP is not enough to meet the 512k constraint, one
+should use less segments in order to save more header bits per macroblock.
+See the \fB\-segments\fP option.
+
+.SS LOGGING OPTIONS
+These options control the level of output:
+.TP
+.B \-v
+Print extra information (encoding time in particular).
+.TP
+.B \-print_psnr
+Compute and report average PSNR (Peak\-Signal\-To\-Noise ratio).
+.TP
+.B \-print_ssim
+Compute and report average SSIM (structural similarity
+metric, see https://en.wikipedia.org/wiki/SSIM for additional details).
+.TP
+.B \-print_lsim
+Compute and report local similarity metric (sum of lowest error amongst the
+collocated pixel neighbors).
+.TP
+.B \-progress
+Report encoding progress in percent.
+.TP
+.B \-quiet
+Do not print anything.
+.TP
+.B \-short
+Only print brief information (output file size and PSNR) for testing purposes.
+.TP
+.BI \-map " int
+Output additional ASCII\-map of encoding information. Possible map values
+range from 1 to 6. This is only meant to help debugging.
+
+.SS ADDITIONAL OPTIONS
+More advanced options are:
+.TP
+.BI \-s " width height
+Specify that the input file actually consists of raw Y'CbCr samples following
+the ITU\-R BT.601 recommendation, in 4:2:0 linear format.
+The luma plane has size \fBwidth\fP x \fBheight\fP.
+.TP
+.BI \-pre " int
+Specify some preprocessing steps. Using a value of '2' will trigger
+quality\-dependent pseudo\-random dithering during RGBA\->YUVA conversion
+(lossy compression only).
+.TP
+.BI \-alpha_filter " string
+Specify the predictive filtering method for the alpha plane. One of 'none',
+\&'fast' or 'best', in increasing complexity and slowness order. Default is
+\&'fast'. Internally, alpha filtering is performed using four possible
+predictions (none, horizontal, vertical, gradient). The 'best' mode will try
+each mode in turn and pick the one which gives the smaller size. The 'fast'
+mode will just try to form an a priori guess without testing all modes.
+.TP
+.BI \-alpha_method " int
+Specify the algorithm used for alpha compression: 0 or 1. Algorithm 0 denotes
+no compression, 1 uses WebP lossless format for compression. The default is 1.
+.TP
+.B \-exact
+Preserve RGB values in transparent area. The default is off, to help
+compressibility.
+.TP
+.BI \-blend_alpha " int
+This option blends the alpha channel (if present) with the source using the
+background color specified in hexadecimal as 0xrrggbb. The alpha channel is
+afterward reset to the opaque value 255.
+.TP
+.B \-noalpha
+Using this option will discard the alpha channel.
+.TP
+.BI \-hint " string
+Specify the hint about input image type. Possible values are:
+\fBphoto\fP, \fBpicture\fP or \fBgraph\fP.
+.TP
+.BI \-metadata " string
+A comma separated list of metadata to copy from the input to the output if
+present.
+Valid values: \fBall\fP, \fBnone\fP, \fBexif\fP, \fBicc\fP, \fBxmp\fP.
+The default is \fBnone\fP.
+
+Note: each input format may not support all combinations.
+.TP
+.B \-noasm
+Disable all assembly optimizations.
+
+.SH BUGS
+Please report all bugs to the issue tracker:
+https://bugs.chromium.org/p/webp
+.br
+Patches welcome! See this page to get started:
+https://www.webmproject.org/code/contribute/submitting\-patches/
+
+.SH EXAMPLES
+cwebp \-q 50 -lossless picture.png \-o picture_lossless.webp
+.br
+cwebp \-q 70 picture_with_alpha.png \-o picture_with_alpha.webp
+.br
+cwebp \-sns 70 \-f 50 \-size 60000 picture.png \-o picture.webp
+.br
+cwebp \-o picture.webp \-\- \-\-\-picture.png
+
+.SH AUTHORS
+\fBcwebp\fP is a part of libwebp and was written by the WebP team.
+.br
+The latest source tree is available at
+https://chromium.googlesource.com/webm/libwebp
+.PP
+This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>,
+for the Debian project (and may be used by others).
+
+.SH SEE ALSO
+.BR dwebp (1),
+.BR gif2webp (1)
+.br
+Please refer to https://developers.google.com/speed/webp/ for additional
+information.
diff --git a/man/dwebp.1 b/man/dwebp.1
new file mode 100644
index 0000000..e718aba
--- /dev/null
+++ b/man/dwebp.1
@@ -0,0 +1,150 @@
+.\"                                      Hey, EMACS: -*- nroff -*-
+.TH DWEBP 1 "November 17, 2021"
+.SH NAME
+dwebp \- decompress a WebP file to an image file
+.SH SYNOPSIS
+.B dwebp
+.RI [ options ] " input_file.webp
+.br
+.SH DESCRIPTION
+This manual page documents the
+.B dwebp
+command.
+.PP
+\fBdwebp\fP decompresses WebP files into PNG, PAM, PPM or PGM images.
+Note: Animated WebP files are not supported.
+.SH OPTIONS
+The basic options are:
+.TP
+.B \-h
+Print usage summary.
+.TP
+.B \-version
+Print the version number (as major.minor.revision) and exit.
+.TP
+.BI \-o " string
+Specify the name of the output file (as PNG format by default).
+Using "-" as output name will direct output to 'stdout'.
+.TP
+.BI \-\- " string
+Explicitly specify the input file. This option is useful if the input
+file starts with an '\-' for instance. This option must appear \fBlast\fP.
+Any other options afterward will be ignored. If the input file is "\-",
+the data will be read from \fIstdin\fP instead of a file.
+.TP
+.B \-bmp
+Change the output format to uncompressed BMP.
+.TP
+.B \-tiff
+Change the output format to uncompressed TIFF.
+.TP
+.B \-pam
+Change the output format to PAM (retains alpha).
+.TP
+.B \-ppm
+Change the output format to PPM (discards alpha).
+.TP
+.B \-pgm
+Change the output format to PGM. The output consists of luma/chroma
+samples instead of RGB, using the IMC4 layout. This option is mainly
+for verification and debugging purposes.
+.TP
+.B \-yuv
+Change the output format to raw YUV. The output consists of
+luma/chroma-U/chroma-V samples instead of RGB, saved sequentially as
+individual planes. This option is mainly for verification and debugging
+purposes.
+.TP
+.B \-nofancy
+Don't use the fancy upscaler for YUV420. This may lead to jaggy
+edges (especially the red ones), but should be faster.
+.TP
+.B \-nofilter
+Don't use the in-loop filtering process even if it is required by
+the bitstream. This may produce visible blocks on the non-compliant output,
+but it will make the decoding faster.
+.TP
+.BI \-dither " strength
+Specify a dithering \fBstrength\fP between 0 and 100. Dithering is a
+post-processing effect applied to chroma components in lossy compression.
+It helps by smoothing gradients and avoiding banding artifacts.
+.TP
+.BI \-alpha_dither
+If the compressed file contains a transparency plane that was quantized
+during compression, this flag will allow dithering the reconstructed plane
+in order to generate smoother transparency gradients.
+.TP
+.B \-nodither
+Disable all dithering (default).
+.TP
+.B \-mt
+Use multi-threading for decoding, if possible.
+.TP
+.BI \-crop " x_position y_position width height
+Crop the decoded picture to a rectangle with top-left corner at coordinates
+(\fBx_position\fP, \fBy_position\fP) and size \fBwidth\fP x \fBheight\fP.
+This cropping area must be fully contained within the source rectangle.
+The top-left corner will be snapped to even coordinates if needed.
+This option is meant to reduce the memory needed for cropping large images.
+Note: the cropping is applied \fIbefore\fP any scaling.
+.TP
+.B \-flip
+Flip decoded image vertically (can be useful for OpenGL textures for instance).
+.TP
+\fB\-resize\fR, \fB\-scale\fI width height\fR
+Rescale the decoded picture to dimension \fBwidth\fP x \fBheight\fP. This
+option is mostly intended to reducing the memory needed to decode large images,
+when only a small version is needed (thumbnail, preview, etc.). Note: scaling
+is applied \fIafter\fP cropping.
+If either (but not both) of the \fBwidth\fP or \fBheight\fP parameters is 0,
+the value will be calculated preserving the aspect-ratio.
+.TP
+.B \-quiet
+Do not print anything.
+.TP
+.B \-v
+Print extra information (decoding time in particular).
+.TP
+.B \-noasm
+Disable all assembly optimizations.
+
+.SH BUGS
+Please report all bugs to the issue tracker:
+https://bugs.chromium.org/p/webp
+.br
+Patches welcome! See this page to get started:
+https://www.webmproject.org/code/contribute/submitting\-patches/
+
+.SH EXAMPLES
+dwebp picture.webp \-o output.png
+.br
+dwebp picture.webp \-ppm \-o output.ppm
+.br
+dwebp \-o output.ppm \-\- \-\-\-picture.webp
+.br
+cat picture.webp | dwebp \-o \- \-\- \- > output.ppm
+
+.SH AUTHORS
+\fBdwebp\fP is a part of libwebp and was written by the WebP team.
+.br
+The latest source tree is available at
+https://chromium.googlesource.com/webm/libwebp
+.PP
+This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>,
+for the Debian project (and may be used by others).
+
+.SH SEE ALSO
+.BR cwebp (1),
+.BR gif2webp (1),
+.BR webpmux (1)
+.br
+Please refer to https://developers.google.com/speed/webp/ for additional
+information.
+.SS Output file format details
+PAM: http://netpbm.sourceforge.net/doc/pam.html
+.br
+PGM: http://netpbm.sourceforge.net/doc/pgm.html
+.br
+PPM: http://netpbm.sourceforge.net/doc/ppm.html
+.br
+PNG: http://www.libpng.org/pub/png/png-sitemap.html#info
diff --git a/man/gif2webp.1 b/man/gif2webp.1
new file mode 100644
index 0000000..3bf43bc
--- /dev/null
+++ b/man/gif2webp.1
@@ -0,0 +1,164 @@
+.\"                                      Hey, EMACS: -*- nroff -*-
+.TH GIF2WEBP 1 "November 17, 2021"
+.SH NAME
+gif2webp \- Convert a GIF image to WebP
+.SH SYNOPSIS
+.B gif2webp
+.RI [ options ] " input_file.gif \-o output_file.webp
+.br
+.SH DESCRIPTION
+This manual page documents the
+.B gif2webp
+command.
+.PP
+\fBgif2webp\fP converts a GIF image to a WebP image.
+.SH OPTIONS
+The basic options are:
+.TP
+.BI \-o " string
+Specify the name of the output WebP file. If omitted, \fBgif2webp\fP will
+perform conversion but only report statistics.
+Using "\-" as output name will direct output to 'stdout'.
+.TP
+.BI \-\- " string
+Explicitly specify the input file. This option is useful if the input
+file starts with an '\-' for instance. This option must appear \fBlast\fP.
+Any other options afterward will be ignored. If the input file is "\-",
+the data will be read from \fIstdin\fP instead of a file.
+.TP
+.B \-h, \-help
+Usage information.
+.TP
+.B \-version
+Print the version number (as major.minor.revision) and exit.
+.TP
+.B \-lossy
+Encode the image using lossy compression.
+.TP
+.B \-mixed
+Mixed compression mode: optimize compression of the image by picking either
+lossy or lossless compression for each frame heuristically.
+.TP
+.BI \-q " float
+Specify the compression factor for RGB channels between 0 and 100. The default
+is 75.
+.br
+In case of lossless compression (default), a small factor enables faster
+compression speed, but produces a larger file. Maximum compression is achieved
+by using a value of 100.
+.br
+In case of lossy compression (specified by the \-lossy option), a small factor
+produces a smaller file with lower quality. Best quality is achieved by using a
+value of 100.
+.TP
+.BI \-m " int
+Specify the compression method to use. This parameter controls the
+trade off between encoding speed and the compressed file size and quality.
+Possible values range from 0 to 6. Default value is 4.
+When higher values are used, the encoder will spend more time inspecting
+additional encoding possibilities and decide on the quality gain.
+Lower value can result is faster processing time at the expense of
+larger file size and lower compression quality.
+.TP
+.BI \-min_size
+Encode image to achieve smallest size. This disables key frame insertion and
+picks the dispose method resulting in the smallest output for each frame. It
+uses lossless compression by default, but can be combined with \-q, \-m,
+\-lossy or \-mixed options.
+.TP
+.BI \-kmin " int
+.TP
+.BI \-kmax " int
+Specify the minimum and maximum distance between consecutive key frames
+(independently decodable frames) in the output animation. The tool will insert
+some key frames into the output animation as needed so that this criteria is
+satisfied.
+.br
+A 'kmax' value of 0 will turn off insertion of key frames. A 'kmax' value of 1
+will result in all frames being key frames. 'kmin' value is not taken into
+account in both these special cases.
+Typical values are in the range 3 to 30. Default values are kmin = 9,
+kmax = 17 for lossless compression and kmin = 3, kmax = 5 for lossy compression.
+.br
+These two options are relevant only for animated images with large number of
+frames (>50).
+.br
+When lower values are used, more frames will be converted to key frames. This
+may lead to smaller number of frames required to decode a frame on average,
+thereby improving the decoding performance. But this may lead to slightly bigger
+file sizes.
+Higher values may lead to worse decoding performance, but smaller file sizes.
+.br
+Some restrictions:
+.br
+(i) kmin < kmax,
+.br
+(ii) kmin >= kmax / 2 + 1 and
+.br
+(iii) kmax - kmin <= 30.
+.br
+If any of these restrictions are not met, they will be enforced automatically.
+.TP
+.BI \-metadata " string
+A comma separated list of metadata to copy from the input to the output if
+present.
+Valid values: \fBall\fP, \fBnone\fP, \fBicc\fP, \fBxmp\fP.
+The default is \fBxmp\fP.
+.TP
+.BI \-f " int
+For lossy encoding only (specified by the \-lossy option). Specify the strength
+of the deblocking filter, between 0 (no filtering) and 100 (maximum filtering).
+A value of 0 will turn off any filtering. Higher value will increase the
+strength of the filtering process applied after decoding the picture. The higher
+the value the smoother the picture will appear. Typical values are usually in
+the range of 20 to 50.
+.TP
+.B \-mt
+Use multi-threading for encoding, if possible.
+.TP
+.B \-loop_compatibility
+If enabled, handle the loop information in a compatible fashion for Chrome
+version prior to M62 (inclusive) and Firefox.
+.TP
+.B \-v
+Print extra information.
+.TP
+.B \-quiet
+Do not print anything.
+
+.SH BUGS
+Please report all bugs to the issue tracker:
+https://bugs.chromium.org/p/webp
+.br
+Patches welcome! See this page to get started:
+https://www.webmproject.org/code/contribute/submitting\-patches/
+
+.SH EXAMPLES
+gif2webp picture.gif \-o picture.webp
+.br
+gif2webp \-q 70 picture.gif \-o picture.webp
+.br
+gif2webp \-lossy \-m 3 picture.gif \-o picture_lossy.webp
+.br
+gif2webp \-lossy \-f 50 picture.gif \-o picture.webp
+.br
+gif2webp \-q 70 \-o picture.webp \-\- \-\-\-picture.gif
+.br
+cat picture.gif | gif2webp \-o \- \-\- \- > output.webp
+
+.SH AUTHORS
+\fBgif2webp\fP is a part of libwebp and was written by the WebP team.
+.br
+The latest source tree is available at
+https://chromium.googlesource.com/webm/libwebp
+.PP
+This manual page was written by Urvang Joshi <urvang@google.com>, for the
+Debian project (and may be used by others).
+
+.SH SEE ALSO
+.BR cwebp (1),
+.BR dwebp (1),
+.BR webpmux (1)
+.br
+Please refer to https://developers.google.com/speed/webp/ for additional
+information.
diff --git a/man/img2webp.1 b/man/img2webp.1
new file mode 100644
index 0000000..5b28ced
--- /dev/null
+++ b/man/img2webp.1
@@ -0,0 +1,105 @@
+.\"                                      Hey, EMACS: -*- nroff -*-
+.TH IMG2WEBP 1 "January 5, 2022"
+.SH NAME
+img2webp \- create animated WebP file from a sequence of input images.
+.SH SYNOPSIS
+.B img2webp
+[file_options] [[frame_options] frame_file]...
+.br
+.B img2webp argument_file_name
+.br
+.SH DESCRIPTION
+This manual page documents the
+.B img2webp
+command.
+.PP
+\fBimg2webp\fP compresses a sequence of images using the animated WebP format.
+Input images can either be PNG, JPEG, TIFF or WebP.
+If a single file name (not starting with the character '\-') is supplied as
+the argument, the command line arguments are actually tokenized from this file.
+This allows for easy scripting or using a large number of arguments.
+.SH FILE-LEVEL OPTIONS
+The file-level options are applied at the beginning of the compression process,
+before the input frames are read.
+.TP
+.BI \-o " string
+Specify the name of the output WebP file.
+.TP
+.BI \-min_size
+Encode images to achieve smallest size. This disables key frame insertion and
+picks the parameters resulting in the smallest output for each frame. It uses
+lossless compression by default, but can be combined with \-q, \-m, \-lossy or
+\-mixed options.
+.TP
+.BI \-kmin " int
+.TP
+.BI \-kmax " int
+Specify the minimum and maximum distance between consecutive key frames
+(independently decodable frames) in the output animation. The tool will insert
+some key frames into the output animation as needed so that this criteria is
+satisfied.
+.br
+.B \-mixed
+Mixed compression mode: optimize compression of the image by picking either
+lossy or lossless compression for each frame heuristically. This global
+option disables the local option \fB-lossy\fP and \fB-lossless\fP .
+.TP
+.BI \-loop " int
+Specifies the number of times the animation should loop. Using '0'
+means 'loop indefinitely'.
+.TP
+.BI \-v
+Be more verbose.
+.TP
+.B \-h, \-help
+A short usage summary.
+.TP
+.B \-version
+Print the version numbers of the relevant libraries used.
+
+.SH PER-FRAME OPTIONS
+The per-frame options are applied for the images following as arguments in the
+command line. They can be modified any number of times preceding each particular
+input image.
+.TP
+.BI \-d " int
+Specify the image duration in milliseconds.
+.TP
+.B \-lossless, \-lossy
+Compress the next image(s) using lossless or lossy compression mode. The
+default mode is lossless.
+.TP
+.BI \-q " float
+Specify the compression factor between 0 and 100. The default is 75.
+.TP
+.BI \-m " int
+Specify the compression method to use. This parameter controls the
+trade off between encoding speed and the compressed file size and quality.
+Possible values range from 0 to 6. Default value is 4.
+
+.SH EXAMPLE
+img2webp -loop 2 in0.png -lossy in1.jpg -d 80 in2.tiff -o out.webp
+.br
+
+.SH BUGS
+Please report all bugs to the issue tracker:
+https://bugs.chromium.org/p/webp
+.br
+Patches welcome! See this page to get started:
+https://www.webmproject.org/code/contribute/submitting\-patches/
+
+.SH AUTHORS
+\fBimg2webp\fP is a part of libwebp and was written by the WebP team.
+.br
+The latest source tree is available at
+https://chromium.googlesource.com/webm/libwebp
+.PP
+This manual page was written by Pascal Massimino <pascal.massimino@gmail.com>,
+for the Debian project (and may be used by others).
+
+.SH SEE ALSO
+.BR webpmux (1),
+.BR gif2webp (1)
+.br
+Please refer to https://developers.google.com/speed/webp/ for additional
+information.
diff --git a/man/vwebp.1 b/man/vwebp.1
new file mode 100644
index 0000000..fa48db6
--- /dev/null
+++ b/man/vwebp.1
@@ -0,0 +1,101 @@
+.\"                                      Hey, EMACS: -*- nroff -*-
+.TH VWEBP 1 "November 17, 2021"
+.SH NAME
+vwebp \- decompress a WebP file and display it in a window
+.SH SYNOPSIS
+.B vwebp
+.RI [ options ] " input_file.webp
+.br
+.SH DESCRIPTION
+This manual page documents the
+.B vwebp
+command.
+.PP
+\fBvwebp\fP decompresses a WebP file and displays it in a window using OpenGL.
+.SH OPTIONS
+.TP
+.B \-h
+Print usage summary.
+.TP
+.B \-version
+Print version number and exit.
+.TP
+.B \-noicc
+Don't use the ICC profile if present.
+.TP
+.B \-nofancy
+Don't use the fancy YUV420 upscaler.
+.TP
+.B \-nofilter
+Disable in-loop filtering.
+.TP
+.BI \-dither " strength
+Specify a dithering \fBstrength\fP between 0 and 100. Dithering is a
+post-processing effect applied to chroma components in lossy compression.
+It helps by smoothing gradients and avoiding banding artifacts. Default: 50.
+.TP
+.BI \-noalphadither
+By default, quantized transparency planes are dithered during decompression,
+to smooth the gradients. This flag will prevent this dithering.
+.TP
+.B \-usebgcolor
+Fill transparent areas with the bitstream's own background color instead of
+checkerboard only. Default is white for non-animated images.
+.TP
+.B \-mt
+Use multi-threading for decoding, if possible.
+.TP
+.B \-info
+Display image information on top of the decoded image.
+.TP
+.BI \-\- " string
+Explicitly specify the input file. This option is useful if the input
+file starts with an '\-' for instance. This option must appear \fBlast\fP.
+Any other options afterward will be ignored. If the input file is "\-",
+the data will be read from \fIstdin\fP instead of a file.
+.TP
+
+.SH KEYBOARD SHORTCUTS
+.TP
+.B 'c'
+Toggle use of color profile.
+.TP
+.B 'b'
+Toggle display of background color.
+.TP
+.B 'i'
+Overlay file information.
+.TP
+.B 'd'
+Disable blending and disposal process, for debugging purposes.
+.TP
+.B 'q' / 'Q' / ESC
+Quit.
+
+.SH BUGS
+Please report all bugs to the issue tracker:
+https://bugs.chromium.org/p/webp
+.br
+Patches welcome! See this page to get started:
+https://www.webmproject.org/code/contribute/submitting\-patches/
+
+.SH EXAMPLES
+vwebp picture.webp
+.br
+vwebp picture.webp -mt -dither 0
+.br
+vwebp \-\- \-\-\-picture.webp
+
+.SH AUTHORS
+\fBvwebp\fP is a part of libwebp and was written by the WebP team.
+.br
+The latest source tree is available at
+https://chromium.googlesource.com/webm/libwebp
+.PP
+This manual page was written for the Debian project (and may be used by others).
+
+.SH SEE ALSO
+.BR dwebp (1)
+.br
+Please refer to https://developers.google.com/speed/webp/ for additional
+information.
diff --git a/man/webpinfo.1 b/man/webpinfo.1
new file mode 100644
index 0000000..35d6d92
--- /dev/null
+++ b/man/webpinfo.1
@@ -0,0 +1,80 @@
+.\"                                      Hey, EMACS: -*- nroff -*-
+.TH WEBPINFO 1 "November 17, 2021"
+.SH NAME
+webpinfo \- print out the chunk level structure of WebP files
+along with basic integrity checks.
+.SH SYNOPSIS
+.B webpinfo
+.I OPTIONS
+.I INPUT
+.br
+.B webpinfo [\-h|\-help|\-H|\-longhelp]
+.br
+
+.SH DESCRIPTION
+This manual page documents the
+.B webpinfo
+command.
+.PP
+\fBwebpinfo\fP can be used to print out the chunk level structure and bitstream
+header information of WebP files. It can also check if the files are of valid
+WebP format.
+
+.SH OPTIONS
+.TP
+.B \-version
+Print the version number (as major.minor.revision) and exit.
+.TP
+.B \-quiet
+Do not show chunk parsing information.
+.TP
+.B \-diag
+Show parsing error diagnosis.
+.TP
+.B \-summary
+Show chunk stats summary.
+.TP
+.BI \-bitstream_info
+Parse bitstream header.
+.TP
+.B \-h, \-help
+A short usage summary.
+.TP
+.B \-H, \-longhelp
+Detailed usage instructions.
+
+.SH INPUT
+Input files in WebP format. Input files must come last, following
+options (if any). There can be multiple input files.
+
+.SH BUGS
+Please report all bugs to the issue tracker:
+https://bugs.chromium.org/p/webp
+.br
+Patches welcome! See this page to get started:
+https://www.webmproject.org/code/contribute/submitting\-patches/
+
+.SH EXAMPLES
+.br
+webpinfo \-h
+.br
+webpinfo \-diag \-summary input_file.webp
+.br
+webpinfo \-bitstream_info input_file_1.webp input_file_2.webp
+.br
+webpinfo *.webp
+
+.SH AUTHORS
+\fBwebpinfo\fP is a part of libwebp and was written by the WebP team.
+.br
+The latest source tree is available at
+https://chromium.googlesource.com/webm/libwebp
+.PP
+This manual page was written by Hui Su <huisu@google.com>,
+for the Debian project (and may be used by others).
+
+.SH SEE ALSO
+.BR webpmux (1)
+.br
+Please refer to https://developers.google.com/speed/webp/ for additional
+information.
diff --git a/man/webpmux.1 b/man/webpmux.1
new file mode 100644
index 0000000..07e8738
--- /dev/null
+++ b/man/webpmux.1
@@ -0,0 +1,271 @@
+.\"                                      Hey, EMACS: -*- nroff -*-
+.TH WEBPMUX 1 "November 17, 2021"
+.SH NAME
+webpmux \- create animated WebP files from non\-animated WebP images, extract
+frames from animated WebP images, and manage XMP/EXIF metadata and ICC profile.
+.SH SYNOPSIS
+.B webpmux \-get
+.I GET_OPTIONS
+.I INPUT
+.B \-o
+.I OUTPUT
+.br
+.B webpmux \-set
+.I SET_OPTIONS
+.I INPUT
+.B \-o
+.I OUTPUT
+.br
+.B webpmux \-strip
+.I STRIP_OPTIONS
+.I INPUT
+.B \-o
+.I OUTPUT
+.br
+.B webpmux \-frame
+.I FRAME_OPTIONS
+.B [ \-frame ... ] [ \-loop
+.I LOOP_COUNT
+.B ]
+.br
+.RS 8
+.B [ \-bgcolor
+.I BACKGROUND_COLOR
+.B ] \-o
+.I OUTPUT
+.RE
+.br
+.B webpmux \-duration
+.I DURATION OPTIONS
+.B [ \-duration ... ]
+.I INPUT
+.B \-o
+.I OUTPUT
+.br
+.B webpmux \-info
+.I INPUT
+.br
+.B webpmux [\-h|\-help]
+.br
+.B webpmux \-version
+.br
+.B webpmux argument_file_name
+.SH DESCRIPTION
+This manual page documents the
+.B webpmux
+command.
+.PP
+\fBwebpmux\fP can be used to create/extract from animated WebP files, as well as
+to add/extract/strip XMP/EXIF metadata and ICC profile.
+If a single file name (not starting with the character '\-') is supplied as
+the argument, the command line arguments are actually tokenized from this file.
+This allows for easy scripting or using a large number of arguments.
+.SH OPTIONS
+.SS GET_OPTIONS (\-get):
+.TP
+.B icc
+Get ICC profile.
+.TP
+.B exif
+Get EXIF metadata.
+.TP
+.B xmp
+Get XMP metadata.
+.TP
+.BI frame " n
+Get nth frame from an animated image. (n = 0 has a special meaning: last frame).
+
+.SS SET_OPTIONS (\-set)
+.TP
+.BI loop " loop_count
+Set loop count on an animated file.
+.P
+Where: 'loop_count' must be in range [0, 65535].
+.TP
+.BI bgcolor " A,R,G,B
+Set the background color of the canvas on an animated file.
+.P
+where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 specifying the
+Alpha, Red, Green and Blue component values respectively.
+.TP
+.BI icc " file.icc
+Set ICC profile.
+.P
+Where: 'file.icc' contains the ICC profile to be set.
+.TP
+.BI exif " file.exif
+Set EXIF metadata.
+.P
+Where: 'file.exif' contains the EXIF metadata to be set.
+.TP
+.BI xmp " file.xmp
+Set XMP metadata.
+.P
+Where: 'file.xmp' contains the XMP metadata to be set.
+
+.SS STRIP_OPTIONS (\-strip)
+.TP
+.B icc
+Strip ICC profile.
+.TP
+.B exif
+Strip EXIF metadata.
+.TP
+.B xmp
+Strip XMP metadata.
+
+.SS DURATION_OPTIONS (\-duration)
+Amend the duration of a specific interval of frames. This option is only
+effective on animated WebP and has no effect on a single-frame file.
+.TP
+.I duration[,start[,end]]
+Where:
+.br
+.B duration
+is the duration for the interval in milliseconds (mandatory).
+Must be non-negative.
+.br
+.B start
+is the starting frame index of the interval (optional).
+.br
+.B end
+is the ending frame index (inclusive) of the interval (optional).
+.TP
+The three typical usages of this option are:
+.br
+.B -duration d
+     set the duration to 'd' for the whole animation.
+.br
+.B -duration d,f
+     set the duration of frame 'f' to 'd'.
+.br
+.B -duration d,start,end
+     set the duration to 'd' for the whole [start,end] interval.
+.TP
+.P
+Note that the frames outside of the [start, end] interval will remain untouched.
+The 'end' value '0' has the special meaning 'last frame of the animation'.
+.TP
+.I Reminder:
+frame indexing starts at '1'.
+.br
+
+.SS FRAME_OPTIONS (\-frame)
+Create an animated WebP file from multiple (non\-animated) WebP images.
+.TP
+.I file_i +di[+xi+yi[+mi[bi]]]
+Where: 'file_i' is the i'th frame (WebP format), 'xi','yi' specify the image
+offset for this frame, 'di' is the pause duration before next frame, 'mi' is
+the dispose method for this frame (0 for NONE or 1 for BACKGROUND) and 'bi' is
+the blending method for this frame (+b for BLEND or \-b for NO_BLEND).
+Argument 'bi' can be omitted and will default to +b (BLEND).
+Also, 'mi' can be omitted if 'bi' is omitted and will default to 0 (NONE).
+Finally, if 'mi' and 'bi' are omitted then 'xi' and 'yi' can be omitted and will
+default to +0+0.
+.TP
+.BI \-loop " n
+Loop the frames n number of times. 0 indicates the frames should loop forever.
+Valid range is 0 to 65535 [Default: 0 (infinite)].
+.TP
+.BI \-bgcolor " A,R,G,B
+Background color of the canvas.
+.br
+where: 'A', 'R', 'G' and 'B' are integers in the range 0 to 255 specifying the
+Alpha, Red, Green and Blue component values respectively
+[Default: 255,255,255,255].
+
+.SS INPUT
+.TP
+Input file in WebP format.
+
+.SS OUTPUT (\-o)
+.TP
+Output file in WebP format.
+
+.SS Note:
+.TP
+The nature of EXIF, XMP and ICC data is not checked and is assumed to be valid.
+
+.SH BUGS
+Please report all bugs to the issue tracker:
+https://bugs.chromium.org/p/webp
+.br
+Patches welcome! See this page to get started:
+https://www.webmproject.org/code/contribute/submitting\-patches/
+
+.SH EXAMPLES
+.P
+Add ICC profile:
+.br
+webpmux \-set icc image_profile.icc in.webp \-o icc_container.webp
+.P
+Extract ICC profile:
+.br
+webpmux \-get icc icc_container.webp \-o image_profile.icc
+.P
+Strip ICC profile:
+.br
+webpmux \-strip icc icc_container.webp \-o without_icc.webp
+.P
+Add XMP metadata:
+.br
+webpmux \-set xmp image_metadata.xmp in.webp \-o xmp_container.webp
+.P
+Extract XMP metadata:
+.br
+webpmux \-get xmp xmp_container.webp \-o image_metadata.xmp
+.P
+Strip XMP metadata:
+.br
+webpmux \-strip xmp xmp_container.webp \-o without_xmp.webp
+.P
+Add EXIF metadata:
+.br
+webpmux \-set exif image_metadata.exif in.webp \-o exif_container.webp
+.P
+Extract EXIF metadata:
+.br
+webpmux \-get exif exif_container.webp \-o image_metadata.exif
+.P
+Strip EXIF metadata:
+.br
+webpmux \-strip exif exif_container.webp \-o without_exif.webp
+.P
+Create an animated WebP file from 3 (non\-animated) WebP images:
+.br
+webpmux \-frame 1.webp +100 \-frame 2.webp +100+50+50
+.br
+.RS 8
+\-frame 3.webp +100+50+50+1+b \-loop 10 \-bgcolor 255,255,255,255
+.br
+\-o anim_container.webp
+.RE
+.P
+Get the 2nd frame from an animated WebP file:
+.br
+webpmux \-get frame 2 anim_container.webp \-o frame_2.webp
+.P
+Using \-get/\-set/\-strip with input file name starting with '\-':
+.br
+webpmux \-set icc image_profile.icc \-o icc_container.webp \-\- \-\-\-in.webp
+.br
+webpmux \-get icc \-o image_profile.icc \-\- \-\-\-icc_container.webp
+.br
+webpmux \-strip icc \-o without_icc.webp \-\- \-\-\-icc_container.webp
+
+.SH AUTHORS
+\fBwebpmux\fP is a part of libwebp and was written by the WebP team.
+.br
+The latest source tree is available at
+https://chromium.googlesource.com/webm/libwebp
+.PP
+This manual page was written by Vikas Arora <vikaas.arora@gmail.com>,
+for the Debian project (and may be used by others).
+
+.SH SEE ALSO
+.BR cwebp (1),
+.BR dwebp (1),
+.BR gif2webp (1)
+.br
+Please refer to https://developers.google.com/speed/webp/ for additional
+information.
diff --git a/sharpyuv/Makefile.am b/sharpyuv/Makefile.am
new file mode 100644
index 0000000..b6b4675
--- /dev/null
+++ b/sharpyuv/Makefile.am
@@ -0,0 +1,41 @@
+AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir)
+AM_CPPFLAGS += -I$(top_builddir)/src -I$(top_srcdir)/src
+
+lib_LTLIBRARIES = libsharpyuv.la
+
+noinst_LTLIBRARIES =
+noinst_LTLIBRARIES += libsharpyuv_sse2.la
+noinst_LTLIBRARIES += libsharpyuv_neon.la
+
+libsharpyuvinclude_HEADERS =
+libsharpyuvinclude_HEADERS += sharpyuv.h
+libsharpyuvinclude_HEADERS += sharpyuv_csp.h
+noinst_HEADERS =
+noinst_HEADERS += ../src/dsp/cpu.c
+noinst_HEADERS += ../src/dsp/cpu.h
+noinst_HEADERS += ../src/webp/types.h
+
+libsharpyuv_sse2_la_SOURCES =
+libsharpyuv_sse2_la_SOURCES += sharpyuv_sse2.c
+libsharpyuv_sse2_la_CPPFLAGS = $(libsharpyuv_la_CPPFLAGS)
+libsharpyuv_sse2_la_CFLAGS = $(AM_CFLAGS) $(SSE2_FLAGS)
+
+libsharpyuv_neon_la_SOURCES =
+libsharpyuv_neon_la_SOURCES += sharpyuv_neon.c
+libsharpyuv_neon_la_CPPFLAGS = $(libsharpyuv_la_CPPFLAGS)
+libsharpyuv_neon_la_CFLAGS = $(AM_CFLAGS) $(NEON_FLAGS)
+
+libsharpyuv_la_SOURCES =
+libsharpyuv_la_SOURCES += sharpyuv_cpu.c sharpyuv_cpu.h
+libsharpyuv_la_SOURCES += sharpyuv_csp.c sharpyuv_csp.h
+libsharpyuv_la_SOURCES += sharpyuv_dsp.c sharpyuv_dsp.h
+libsharpyuv_la_SOURCES += sharpyuv_gamma.c sharpyuv_gamma.h
+libsharpyuv_la_SOURCES += sharpyuv.c sharpyuv.h
+
+libsharpyuv_la_CPPFLAGS = $(AM_CPPFLAGS)
+libsharpyuv_la_LDFLAGS = -no-undefined -version-info 0:0:0 -lm
+libsharpyuv_la_LIBADD =
+libsharpyuv_la_LIBADD += libsharpyuv_sse2.la
+libsharpyuv_la_LIBADD += libsharpyuv_neon.la
+libsharpyuvincludedir = $(includedir)/webp/sharpyuv
+pkgconfig_DATA = libsharpyuv.pc
diff --git a/sharpyuv/libsharpyuv.pc.in b/sharpyuv/libsharpyuv.pc.in
new file mode 100644
index 0000000..0fb565a
--- /dev/null
+++ b/sharpyuv/libsharpyuv.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@/webp
+
+Name: libsharpyuv
+Description: Library for sharp RGB to YUV conversion
+Version: @PACKAGE_VERSION@
+Cflags: -I${includedir}
+Libs: -L${libdir} -l@webp_libname_prefix@sharpyuv
+Libs.private: -lm @PTHREAD_CFLAGS@ @PTHREAD_LIBS@
diff --git a/sharpyuv/libsharpyuv.rc b/sharpyuv/libsharpyuv.rc
new file mode 100644
index 0000000..7f1df72
--- /dev/null
+++ b/sharpyuv/libsharpyuv.rc
@@ -0,0 +1,41 @@
+#define APSTUDIO_READONLY_SYMBOLS
+#include "winres.h"
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,0,2,0
+ PRODUCTVERSION 0,0,2,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", "Google, Inc."
+            VALUE "FileDescription", "libsharpyuv DLL"
+            VALUE "FileVersion", "0.2.0"
+            VALUE "InternalName", "libsharpyuv.dll"
+            VALUE "LegalCopyright", "Copyright (C) 2022"
+            VALUE "OriginalFilename", "libsharpyuv.dll"
+            VALUE "ProductName", "SharpYuv Library"
+            VALUE "ProductVersion", "0.2.0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+#endif    // English (United States) resources
diff --git a/sharpyuv/sharpyuv.c b/sharpyuv/sharpyuv.c
new file mode 100644
index 0000000..7de34fb
--- /dev/null
+++ b/sharpyuv/sharpyuv.c
@@ -0,0 +1,526 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Sharp RGB to YUV conversion.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "sharpyuv/sharpyuv.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "src/webp/types.h"
+#include "sharpyuv/sharpyuv_cpu.h"
+#include "sharpyuv/sharpyuv_dsp.h"
+#include "sharpyuv/sharpyuv_gamma.h"
+
+//------------------------------------------------------------------------------
+
+int SharpYuvGetVersion(void) {
+  return SHARPYUV_VERSION;
+}
+
+//------------------------------------------------------------------------------
+// Sharp RGB->YUV conversion
+
+static const int kNumIterations = 4;
+
+#define YUV_FIX 16  // fixed-point precision for RGB->YUV
+static const int kYuvHalf = 1 << (YUV_FIX - 1);
+
+// Max bit depth so that intermediate calculations fit in 16 bits.
+static const int kMaxBitDepth = 14;
+
+// Returns the precision shift to use based on the input rgb_bit_depth.
+static int GetPrecisionShift(int rgb_bit_depth) {
+  // Try to add 2 bits of precision if it fits in kMaxBitDepth. Otherwise remove
+  // bits if needed.
+  return ((rgb_bit_depth + 2) <= kMaxBitDepth) ? 2
+                                               : (kMaxBitDepth - rgb_bit_depth);
+}
+
+typedef int16_t fixed_t;      // signed type with extra precision for UV
+typedef uint16_t fixed_y_t;   // unsigned type with extra precision for W
+
+//------------------------------------------------------------------------------
+
+static uint8_t clip_8b(fixed_t v) {
+  return (!(v & ~0xff)) ? (uint8_t)v : (v < 0) ? 0u : 255u;
+}
+
+static uint16_t clip(fixed_t v, int max) {
+  return (v < 0) ? 0 : (v > max) ? max : (uint16_t)v;
+}
+
+static fixed_y_t clip_bit_depth(int y, int bit_depth) {
+  const int max = (1 << bit_depth) - 1;
+  return (!(y & ~max)) ? (fixed_y_t)y : (y < 0) ? 0 : max;
+}
+
+//------------------------------------------------------------------------------
+
+static int RGBToGray(int64_t r, int64_t g, int64_t b) {
+  const int64_t luma = 13933 * r + 46871 * g + 4732 * b + kYuvHalf;
+  return (int)(luma >> YUV_FIX);
+}
+
+static uint32_t ScaleDown(uint16_t a, uint16_t b, uint16_t c, uint16_t d,
+                          int rgb_bit_depth) {
+  const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
+  const uint32_t A = SharpYuvGammaToLinear(a, bit_depth);
+  const uint32_t B = SharpYuvGammaToLinear(b, bit_depth);
+  const uint32_t C = SharpYuvGammaToLinear(c, bit_depth);
+  const uint32_t D = SharpYuvGammaToLinear(d, bit_depth);
+  return SharpYuvLinearToGamma((A + B + C + D + 2) >> 2, bit_depth);
+}
+
+static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int w,
+                                int rgb_bit_depth) {
+  const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
+  int i;
+  for (i = 0; i < w; ++i) {
+    const uint32_t R = SharpYuvGammaToLinear(src[0 * w + i], bit_depth);
+    const uint32_t G = SharpYuvGammaToLinear(src[1 * w + i], bit_depth);
+    const uint32_t B = SharpYuvGammaToLinear(src[2 * w + i], bit_depth);
+    const uint32_t Y = RGBToGray(R, G, B);
+    dst[i] = (fixed_y_t)SharpYuvLinearToGamma(Y, bit_depth);
+  }
+}
+
+static void UpdateChroma(const fixed_y_t* src1, const fixed_y_t* src2,
+                         fixed_t* dst, int uv_w, int rgb_bit_depth) {
+  int i;
+  for (i = 0; i < uv_w; ++i) {
+    const int r =
+        ScaleDown(src1[0 * uv_w + 0], src1[0 * uv_w + 1], src2[0 * uv_w + 0],
+                  src2[0 * uv_w + 1], rgb_bit_depth);
+    const int g =
+        ScaleDown(src1[2 * uv_w + 0], src1[2 * uv_w + 1], src2[2 * uv_w + 0],
+                  src2[2 * uv_w + 1], rgb_bit_depth);
+    const int b =
+        ScaleDown(src1[4 * uv_w + 0], src1[4 * uv_w + 1], src2[4 * uv_w + 0],
+                  src2[4 * uv_w + 1], rgb_bit_depth);
+    const int W = RGBToGray(r, g, b);
+    dst[0 * uv_w] = (fixed_t)(r - W);
+    dst[1 * uv_w] = (fixed_t)(g - W);
+    dst[2 * uv_w] = (fixed_t)(b - W);
+    dst  += 1;
+    src1 += 2;
+    src2 += 2;
+  }
+}
+
+static void StoreGray(const fixed_y_t* rgb, fixed_y_t* y, int w) {
+  int i;
+  assert(w > 0);
+  for (i = 0; i < w; ++i) {
+    y[i] = RGBToGray(rgb[0 * w + i], rgb[1 * w + i], rgb[2 * w + i]);
+  }
+}
+
+//------------------------------------------------------------------------------
+
+static WEBP_INLINE fixed_y_t Filter2(int A, int B, int W0, int bit_depth) {
+  const int v0 = (A * 3 + B + 2) >> 2;
+  return clip_bit_depth(v0 + W0, bit_depth);
+}
+
+//------------------------------------------------------------------------------
+
+static WEBP_INLINE int Shift(int v, int shift) {
+  return (shift >= 0) ? (v << shift) : (v >> -shift);
+}
+
+static void ImportOneRow(const uint8_t* const r_ptr,
+                         const uint8_t* const g_ptr,
+                         const uint8_t* const b_ptr,
+                         int rgb_step,
+                         int rgb_bit_depth,
+                         int pic_width,
+                         fixed_y_t* const dst) {
+  // Convert the rgb_step from a number of bytes to a number of uint8_t or
+  // uint16_t values depending the bit depth.
+  const int step = (rgb_bit_depth > 8) ? rgb_step / 2 : rgb_step;
+  int i;
+  const int w = (pic_width + 1) & ~1;
+  for (i = 0; i < pic_width; ++i) {
+    const int off = i * step;
+    const int shift = GetPrecisionShift(rgb_bit_depth);
+    if (rgb_bit_depth == 8) {
+      dst[i + 0 * w] = Shift(r_ptr[off], shift);
+      dst[i + 1 * w] = Shift(g_ptr[off], shift);
+      dst[i + 2 * w] = Shift(b_ptr[off], shift);
+    } else {
+      dst[i + 0 * w] = Shift(((uint16_t*)r_ptr)[off], shift);
+      dst[i + 1 * w] = Shift(((uint16_t*)g_ptr)[off], shift);
+      dst[i + 2 * w] = Shift(((uint16_t*)b_ptr)[off], shift);
+    }
+  }
+  if (pic_width & 1) {  // replicate rightmost pixel
+    dst[pic_width + 0 * w] = dst[pic_width + 0 * w - 1];
+    dst[pic_width + 1 * w] = dst[pic_width + 1 * w - 1];
+    dst[pic_width + 2 * w] = dst[pic_width + 2 * w - 1];
+  }
+}
+
+static void InterpolateTwoRows(const fixed_y_t* const best_y,
+                               const fixed_t* prev_uv,
+                               const fixed_t* cur_uv,
+                               const fixed_t* next_uv,
+                               int w,
+                               fixed_y_t* out1,
+                               fixed_y_t* out2,
+                               int rgb_bit_depth) {
+  const int uv_w = w >> 1;
+  const int len = (w - 1) >> 1;   // length to filter
+  int k = 3;
+  const int bit_depth = rgb_bit_depth + GetPrecisionShift(rgb_bit_depth);
+  while (k-- > 0) {   // process each R/G/B segments in turn
+    // special boundary case for i==0
+    out1[0] = Filter2(cur_uv[0], prev_uv[0], best_y[0], bit_depth);
+    out2[0] = Filter2(cur_uv[0], next_uv[0], best_y[w], bit_depth);
+
+    SharpYuvFilterRow(cur_uv, prev_uv, len, best_y + 0 + 1, out1 + 1,
+                      bit_depth);
+    SharpYuvFilterRow(cur_uv, next_uv, len, best_y + w + 1, out2 + 1,
+                      bit_depth);
+
+    // special boundary case for i == w - 1 when w is even
+    if (!(w & 1)) {
+      out1[w - 1] = Filter2(cur_uv[uv_w - 1], prev_uv[uv_w - 1],
+                            best_y[w - 1 + 0], bit_depth);
+      out2[w - 1] = Filter2(cur_uv[uv_w - 1], next_uv[uv_w - 1],
+                            best_y[w - 1 + w], bit_depth);
+    }
+    out1 += w;
+    out2 += w;
+    prev_uv += uv_w;
+    cur_uv  += uv_w;
+    next_uv += uv_w;
+  }
+}
+
+static WEBP_INLINE int RGBToYUVComponent(int r, int g, int b,
+                                         const int coeffs[4], int sfix) {
+  const int srounder = 1 << (YUV_FIX + sfix - 1);
+  const int luma = coeffs[0] * r + coeffs[1] * g + coeffs[2] * b +
+                   coeffs[3] + srounder;
+  return (luma >> (YUV_FIX + sfix));
+}
+
+static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv,
+                            uint8_t* y_ptr, int y_stride, uint8_t* u_ptr,
+                            int u_stride, uint8_t* v_ptr, int v_stride,
+                            int rgb_bit_depth,
+                            int yuv_bit_depth, int width, int height,
+                            const SharpYuvConversionMatrix* yuv_matrix) {
+  int i, j;
+  const fixed_t* const best_uv_base = best_uv;
+  const int w = (width + 1) & ~1;
+  const int h = (height + 1) & ~1;
+  const int uv_w = w >> 1;
+  const int uv_h = h >> 1;
+  const int sfix = GetPrecisionShift(rgb_bit_depth);
+  const int yuv_max = (1 << yuv_bit_depth) - 1;
+
+  for (best_uv = best_uv_base, j = 0; j < height; ++j) {
+    for (i = 0; i < width; ++i) {
+      const int off = (i >> 1);
+      const int W = best_y[i];
+      const int r = best_uv[off + 0 * uv_w] + W;
+      const int g = best_uv[off + 1 * uv_w] + W;
+      const int b = best_uv[off + 2 * uv_w] + W;
+      const int y = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_y, sfix);
+      if (yuv_bit_depth <= 8) {
+        y_ptr[i] = clip_8b(y);
+      } else {
+        ((uint16_t*)y_ptr)[i] = clip(y, yuv_max);
+      }
+    }
+    best_y += w;
+    best_uv += (j & 1) * 3 * uv_w;
+    y_ptr += y_stride;
+  }
+  for (best_uv = best_uv_base, j = 0; j < uv_h; ++j) {
+    for (i = 0; i < uv_w; ++i) {
+      const int off = i;
+      // Note r, g and b values here are off by W, but a constant offset on all
+      // 3 components doesn't change the value of u and v with a YCbCr matrix.
+      const int r = best_uv[off + 0 * uv_w];
+      const int g = best_uv[off + 1 * uv_w];
+      const int b = best_uv[off + 2 * uv_w];
+      const int u = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_u, sfix);
+      const int v = RGBToYUVComponent(r, g, b, yuv_matrix->rgb_to_v, sfix);
+      if (yuv_bit_depth <= 8) {
+        u_ptr[i] = clip_8b(u);
+        v_ptr[i] = clip_8b(v);
+      } else {
+        ((uint16_t*)u_ptr)[i] = clip(u, yuv_max);
+        ((uint16_t*)v_ptr)[i] = clip(v, yuv_max);
+      }
+    }
+    best_uv += 3 * uv_w;
+    u_ptr += u_stride;
+    v_ptr += v_stride;
+  }
+  return 1;
+}
+
+//------------------------------------------------------------------------------
+// Main function
+
+static void* SafeMalloc(uint64_t nmemb, size_t size) {
+  const uint64_t total_size = nmemb * (uint64_t)size;
+  if (total_size != (size_t)total_size) return NULL;
+  return malloc((size_t)total_size);
+}
+
+#define SAFE_ALLOC(W, H, T) ((T*)SafeMalloc((W) * (H), sizeof(T)))
+
+static int DoSharpArgbToYuv(const uint8_t* r_ptr, const uint8_t* g_ptr,
+                            const uint8_t* b_ptr, int rgb_step, int rgb_stride,
+                            int rgb_bit_depth, uint8_t* y_ptr, int y_stride,
+                            uint8_t* u_ptr, int u_stride, uint8_t* v_ptr,
+                            int v_stride, int yuv_bit_depth, int width,
+                            int height,
+                            const SharpYuvConversionMatrix* yuv_matrix) {
+  // we expand the right/bottom border if needed
+  const int w = (width + 1) & ~1;
+  const int h = (height + 1) & ~1;
+  const int uv_w = w >> 1;
+  const int uv_h = h >> 1;
+  uint64_t prev_diff_y_sum = ~0;
+  int j, iter;
+
+  // TODO(skal): allocate one big memory chunk. But for now, it's easier
+  // for valgrind debugging to have several chunks.
+  fixed_y_t* const tmp_buffer = SAFE_ALLOC(w * 3, 2, fixed_y_t);   // scratch
+  fixed_y_t* const best_y_base = SAFE_ALLOC(w, h, fixed_y_t);
+  fixed_y_t* const target_y_base = SAFE_ALLOC(w, h, fixed_y_t);
+  fixed_y_t* const best_rgb_y = SAFE_ALLOC(w, 2, fixed_y_t);
+  fixed_t* const best_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);
+  fixed_t* const target_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);
+  fixed_t* const best_rgb_uv = SAFE_ALLOC(uv_w * 3, 1, fixed_t);
+  fixed_y_t* best_y = best_y_base;
+  fixed_y_t* target_y = target_y_base;
+  fixed_t* best_uv = best_uv_base;
+  fixed_t* target_uv = target_uv_base;
+  const uint64_t diff_y_threshold = (uint64_t)(3.0 * w * h);
+  int ok;
+  assert(w > 0);
+  assert(h > 0);
+
+  if (best_y_base == NULL || best_uv_base == NULL ||
+      target_y_base == NULL || target_uv_base == NULL ||
+      best_rgb_y == NULL || best_rgb_uv == NULL ||
+      tmp_buffer == NULL) {
+    ok = 0;
+    goto End;
+  }
+
+  // Import RGB samples to W/RGB representation.
+  for (j = 0; j < height; j += 2) {
+    const int is_last_row = (j == height - 1);
+    fixed_y_t* const src1 = tmp_buffer + 0 * w;
+    fixed_y_t* const src2 = tmp_buffer + 3 * w;
+
+    // prepare two rows of input
+    ImportOneRow(r_ptr, g_ptr, b_ptr, rgb_step, rgb_bit_depth, width,
+                 src1);
+    if (!is_last_row) {
+      ImportOneRow(r_ptr + rgb_stride, g_ptr + rgb_stride, b_ptr + rgb_stride,
+                   rgb_step, rgb_bit_depth, width, src2);
+    } else {
+      memcpy(src2, src1, 3 * w * sizeof(*src2));
+    }
+    StoreGray(src1, best_y + 0, w);
+    StoreGray(src2, best_y + w, w);
+
+    UpdateW(src1, target_y, w, rgb_bit_depth);
+    UpdateW(src2, target_y + w, w, rgb_bit_depth);
+    UpdateChroma(src1, src2, target_uv, uv_w, rgb_bit_depth);
+    memcpy(best_uv, target_uv, 3 * uv_w * sizeof(*best_uv));
+    best_y += 2 * w;
+    best_uv += 3 * uv_w;
+    target_y += 2 * w;
+    target_uv += 3 * uv_w;
+    r_ptr += 2 * rgb_stride;
+    g_ptr += 2 * rgb_stride;
+    b_ptr += 2 * rgb_stride;
+  }
+
+  // Iterate and resolve clipping conflicts.
+  for (iter = 0; iter < kNumIterations; ++iter) {
+    const fixed_t* cur_uv = best_uv_base;
+    const fixed_t* prev_uv = best_uv_base;
+    uint64_t diff_y_sum = 0;
+
+    best_y = best_y_base;
+    best_uv = best_uv_base;
+    target_y = target_y_base;
+    target_uv = target_uv_base;
+    for (j = 0; j < h; j += 2) {
+      fixed_y_t* const src1 = tmp_buffer + 0 * w;
+      fixed_y_t* const src2 = tmp_buffer + 3 * w;
+      {
+        const fixed_t* const next_uv = cur_uv + ((j < h - 2) ? 3 * uv_w : 0);
+        InterpolateTwoRows(best_y, prev_uv, cur_uv, next_uv, w,
+                           src1, src2, rgb_bit_depth);
+        prev_uv = cur_uv;
+        cur_uv = next_uv;
+      }
+
+      UpdateW(src1, best_rgb_y + 0 * w, w, rgb_bit_depth);
+      UpdateW(src2, best_rgb_y + 1 * w, w, rgb_bit_depth);
+      UpdateChroma(src1, src2, best_rgb_uv, uv_w, rgb_bit_depth);
+
+      // update two rows of Y and one row of RGB
+      diff_y_sum +=
+          SharpYuvUpdateY(target_y, best_rgb_y, best_y, 2 * w,
+                          rgb_bit_depth + GetPrecisionShift(rgb_bit_depth));
+      SharpYuvUpdateRGB(target_uv, best_rgb_uv, best_uv, 3 * uv_w);
+
+      best_y += 2 * w;
+      best_uv += 3 * uv_w;
+      target_y += 2 * w;
+      target_uv += 3 * uv_w;
+    }
+    // test exit condition
+    if (iter > 0) {
+      if (diff_y_sum < diff_y_threshold) break;
+      if (diff_y_sum > prev_diff_y_sum) break;
+    }
+    prev_diff_y_sum = diff_y_sum;
+  }
+
+  // final reconstruction
+  ok = ConvertWRGBToYUV(best_y_base, best_uv_base, y_ptr, y_stride, u_ptr,
+                        u_stride, v_ptr, v_stride, rgb_bit_depth, yuv_bit_depth,
+                        width, height, yuv_matrix);
+
+ End:
+  free(best_y_base);
+  free(best_uv_base);
+  free(target_y_base);
+  free(target_uv_base);
+  free(best_rgb_y);
+  free(best_rgb_uv);
+  free(tmp_buffer);
+  return ok;
+}
+#undef SAFE_ALLOC
+
+#if defined(WEBP_USE_THREAD) && !defined(_WIN32)
+#include <pthread.h>  // NOLINT
+
+#define LOCK_ACCESS \
+    static pthread_mutex_t sharpyuv_lock = PTHREAD_MUTEX_INITIALIZER; \
+    if (pthread_mutex_lock(&sharpyuv_lock)) return
+#define UNLOCK_ACCESS_AND_RETURN                  \
+    do {                                          \
+      (void)pthread_mutex_unlock(&sharpyuv_lock); \
+      return;                                     \
+    } while (0)
+#else  // !(defined(WEBP_USE_THREAD) && !defined(_WIN32))
+#define LOCK_ACCESS do {} while (0)
+#define UNLOCK_ACCESS_AND_RETURN return
+#endif  // defined(WEBP_USE_THREAD) && !defined(_WIN32)
+
+// Hidden exported init function.
+// By default SharpYuvConvert calls it with SharpYuvGetCPUInfo. If needed,
+// users can declare it as extern and call it with an alternate VP8CPUInfo
+// function.
+SHARPYUV_EXTERN void SharpYuvInit(VP8CPUInfo cpu_info_func);
+void SharpYuvInit(VP8CPUInfo cpu_info_func) {
+  static volatile VP8CPUInfo sharpyuv_last_cpuinfo_used =
+      (VP8CPUInfo)&sharpyuv_last_cpuinfo_used;
+  LOCK_ACCESS;
+  // Only update SharpYuvGetCPUInfo when called from external code to avoid a
+  // race on reading the value in SharpYuvConvert().
+  if (cpu_info_func != (VP8CPUInfo)&SharpYuvGetCPUInfo) {
+    SharpYuvGetCPUInfo = cpu_info_func;
+  }
+  if (sharpyuv_last_cpuinfo_used == SharpYuvGetCPUInfo) {
+    UNLOCK_ACCESS_AND_RETURN;
+  }
+
+  SharpYuvInitDsp();
+  SharpYuvInitGammaTables();
+
+  sharpyuv_last_cpuinfo_used = SharpYuvGetCPUInfo;
+  UNLOCK_ACCESS_AND_RETURN;
+}
+
+int SharpYuvConvert(const void* r_ptr, const void* g_ptr,
+                    const void* b_ptr, int rgb_step, int rgb_stride,
+                    int rgb_bit_depth, void* y_ptr, int y_stride,
+                    void* u_ptr, int u_stride, void* v_ptr,
+                    int v_stride, int yuv_bit_depth, int width,
+                    int height, const SharpYuvConversionMatrix* yuv_matrix) {
+  SharpYuvConversionMatrix scaled_matrix;
+  const int rgb_max = (1 << rgb_bit_depth) - 1;
+  const int rgb_round = 1 << (rgb_bit_depth - 1);
+  const int yuv_max = (1 << yuv_bit_depth) - 1;
+  const int sfix = GetPrecisionShift(rgb_bit_depth);
+
+  if (width < 1 || height < 1 || width == INT_MAX || height == INT_MAX ||
+      r_ptr == NULL || g_ptr == NULL || b_ptr == NULL || y_ptr == NULL ||
+      u_ptr == NULL || v_ptr == NULL) {
+    return 0;
+  }
+  if (rgb_bit_depth != 8 && rgb_bit_depth != 10 && rgb_bit_depth != 12 &&
+      rgb_bit_depth != 16) {
+    return 0;
+  }
+  if (yuv_bit_depth != 8 && yuv_bit_depth != 10 && yuv_bit_depth != 12) {
+    return 0;
+  }
+  if (rgb_bit_depth > 8 && (rgb_step % 2 != 0 || rgb_stride %2 != 0)) {
+    // Step/stride should be even for uint16_t buffers.
+    return 0;
+  }
+  if (yuv_bit_depth > 8 &&
+      (y_stride % 2 != 0 || u_stride % 2 != 0 || v_stride % 2 != 0)) {
+    // Stride should be even for uint16_t buffers.
+    return 0;
+  }
+  // The address of the function pointer is used to avoid a read race.
+  SharpYuvInit((VP8CPUInfo)&SharpYuvGetCPUInfo);
+
+  // Add scaling factor to go from rgb_bit_depth to yuv_bit_depth, to the
+  // rgb->yuv conversion matrix.
+  if (rgb_bit_depth == yuv_bit_depth) {
+    memcpy(&scaled_matrix, yuv_matrix, sizeof(scaled_matrix));
+  } else {
+    int i;
+    for (i = 0; i < 3; ++i) {
+      scaled_matrix.rgb_to_y[i] =
+          (yuv_matrix->rgb_to_y[i] * yuv_max + rgb_round) / rgb_max;
+      scaled_matrix.rgb_to_u[i] =
+          (yuv_matrix->rgb_to_u[i] * yuv_max + rgb_round) / rgb_max;
+      scaled_matrix.rgb_to_v[i] =
+          (yuv_matrix->rgb_to_v[i] * yuv_max + rgb_round) / rgb_max;
+    }
+  }
+  // Also incorporate precision change scaling.
+  scaled_matrix.rgb_to_y[3] = Shift(yuv_matrix->rgb_to_y[3], sfix);
+  scaled_matrix.rgb_to_u[3] = Shift(yuv_matrix->rgb_to_u[3], sfix);
+  scaled_matrix.rgb_to_v[3] = Shift(yuv_matrix->rgb_to_v[3], sfix);
+
+  return DoSharpArgbToYuv(r_ptr, g_ptr, b_ptr, rgb_step, rgb_stride,
+                          rgb_bit_depth, y_ptr, y_stride, u_ptr, u_stride,
+                          v_ptr, v_stride, yuv_bit_depth, width, height,
+                          &scaled_matrix);
+}
+
+//------------------------------------------------------------------------------
diff --git a/sharpyuv/sharpyuv.h b/sharpyuv/sharpyuv.h
new file mode 100644
index 0000000..181b20a
--- /dev/null
+++ b/sharpyuv/sharpyuv.h
@@ -0,0 +1,103 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Sharp RGB to YUV conversion.
+
+#ifndef WEBP_SHARPYUV_SHARPYUV_H_
+#define WEBP_SHARPYUV_SHARPYUV_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef SHARPYUV_EXTERN
+#ifdef WEBP_EXTERN
+#define SHARPYUV_EXTERN WEBP_EXTERN
+#else
+// This explicitly marks library functions and allows for changing the
+// signature for e.g., Windows DLL builds.
+#if defined(__GNUC__) && __GNUC__ >= 4
+#define SHARPYUV_EXTERN extern __attribute__((visibility("default")))
+#else
+#if defined(_MSC_VER) && defined(WEBP_DLL)
+#define SHARPYUV_EXTERN __declspec(dllexport)
+#else
+#define SHARPYUV_EXTERN extern
+#endif /* _MSC_VER && WEBP_DLL */
+#endif /* __GNUC__ >= 4 */
+#endif /* WEBP_EXTERN */
+#endif /* SHARPYUV_EXTERN */
+
+// SharpYUV API version following the convention from semver.org
+#define SHARPYUV_VERSION_MAJOR 0
+#define SHARPYUV_VERSION_MINOR 2
+#define SHARPYUV_VERSION_PATCH 0
+// Version as a uint32_t. The major number is the high 8 bits.
+// The minor number is the middle 8 bits. The patch number is the low 16 bits.
+#define SHARPYUV_MAKE_VERSION(MAJOR, MINOR, PATCH) \
+  (((MAJOR) << 24) | ((MINOR) << 16) | (PATCH))
+#define SHARPYUV_VERSION                                                \
+  SHARPYUV_MAKE_VERSION(SHARPYUV_VERSION_MAJOR, SHARPYUV_VERSION_MINOR, \
+                        SHARPYUV_VERSION_PATCH)
+
+// Returns the library's version number, packed in hexadecimal. See
+// SHARPYUV_VERSION.
+SHARPYUV_EXTERN int SharpYuvGetVersion(void);
+
+// RGB to YUV conversion matrix, in 16 bit fixed point.
+// y = rgb_to_y[0] * r + rgb_to_y[1] * g + rgb_to_y[2] * b + rgb_to_y[3]
+// u = rgb_to_u[0] * r + rgb_to_u[1] * g + rgb_to_u[2] * b + rgb_to_u[3]
+// v = rgb_to_v[0] * r + rgb_to_v[1] * g + rgb_to_v[2] * b + rgb_to_v[3]
+// Then y, u and v values are divided by 1<<16 and rounded.
+typedef struct {
+  int rgb_to_y[4];
+  int rgb_to_u[4];
+  int rgb_to_v[4];
+} SharpYuvConversionMatrix;
+
+// Converts RGB to YUV420 using a downsampling algorithm that minimizes
+// artefacts caused by chroma subsampling.
+// This is slower than standard downsampling (averaging of 4 UV values).
+// Assumes that the image will be upsampled using a bilinear filter. If nearest
+// neighbor is used instead, the upsampled image might look worse than with
+// standard downsampling.
+// r_ptr, g_ptr, b_ptr: pointers to the source r, g and b channels. Should point
+//     to uint8_t buffers if rgb_bit_depth is 8, or uint16_t buffers otherwise.
+// rgb_step: distance in bytes between two horizontally adjacent pixels on the
+//     r, g and b channels. If rgb_bit_depth is > 8, it should be a
+//     multiple of 2.
+// rgb_stride: distance in bytes between two vertically adjacent pixels on the
+//     r, g, and b channels. If rgb_bit_depth is > 8, it should be a
+//     multiple of 2.
+// rgb_bit_depth: number of bits for each r/g/b value. One of: 8, 10, 12, 16.
+//     Note: 16 bit input is truncated to 14 bits before conversion to yuv.
+// yuv_bit_depth: number of bits for each y/u/v value. One of: 8, 10, 12.
+// y_ptr, u_ptr, v_ptr: pointers to the destination y, u and v channels.  Should
+//     point to uint8_t buffers if yuv_bit_depth is 8, or uint16_t buffers
+//     otherwise.
+// y_stride, u_stride, v_stride: distance in bytes between two vertically
+//     adjacent pixels on the y, u and v channels. If yuv_bit_depth > 8, they
+//     should be multiples of 2.
+// width, height: width and height of the image in pixels
+SHARPYUV_EXTERN int SharpYuvConvert(const void* r_ptr, const void* g_ptr,
+                                    const void* b_ptr, int rgb_step,
+                                    int rgb_stride, int rgb_bit_depth,
+                                    void* y_ptr, int y_stride, void* u_ptr,
+                                    int u_stride, void* v_ptr, int v_stride,
+                                    int yuv_bit_depth, int width, int height,
+                                    const SharpYuvConversionMatrix* yuv_matrix);
+
+// TODO(b/194336375): Add YUV444 to YUV420 conversion. Maybe also add 422
+// support (it's rarely used in practice, especially for images).
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // WEBP_SHARPYUV_SHARPYUV_H_
diff --git a/sharpyuv/sharpyuv_cpu.c b/sharpyuv/sharpyuv_cpu.c
new file mode 100644
index 0000000..29425a0
--- /dev/null
+++ b/sharpyuv/sharpyuv_cpu.c
@@ -0,0 +1,14 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+#include "sharpyuv/sharpyuv_cpu.h"
+
+// Include src/dsp/cpu.c to create SharpYuvGetCPUInfo from VP8GetCPUInfo. The
+// function pointer is renamed in sharpyuv_cpu.h.
+#include "src/dsp/cpu.c"
diff --git a/sharpyuv/sharpyuv_cpu.h b/sharpyuv/sharpyuv_cpu.h
new file mode 100644
index 0000000..176ca3e
--- /dev/null
+++ b/sharpyuv/sharpyuv_cpu.h
@@ -0,0 +1,22 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+#ifndef WEBP_SHARPYUV_SHARPYUV_CPU_H_
+#define WEBP_SHARPYUV_SHARPYUV_CPU_H_
+
+#include "sharpyuv/sharpyuv.h"
+
+// Avoid exporting SharpYuvGetCPUInfo in shared object / DLL builds.
+// SharpYuvInit() replaces the use of the function pointer.
+#undef WEBP_EXTERN
+#define WEBP_EXTERN extern
+#define VP8GetCPUInfo SharpYuvGetCPUInfo
+#include "src/dsp/cpu.h"
+
+#endif  // WEBP_SHARPYUV_SHARPYUV_CPU_H_
diff --git a/sharpyuv/sharpyuv_csp.c b/sharpyuv/sharpyuv_csp.c
new file mode 100644
index 0000000..0ad22be
--- /dev/null
+++ b/sharpyuv/sharpyuv_csp.c
@@ -0,0 +1,110 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Colorspace utilities.
+
+#include "sharpyuv/sharpyuv_csp.h"
+
+#include <assert.h>
+#include <math.h>
+#include <stddef.h>
+
+static int ToFixed16(float f) { return (int)floor(f * (1 << 16) + 0.5f); }
+
+void SharpYuvComputeConversionMatrix(const SharpYuvColorSpace* yuv_color_space,
+                                     SharpYuvConversionMatrix* matrix) {
+  const float kr = yuv_color_space->kr;
+  const float kb = yuv_color_space->kb;
+  const float kg = 1.0f - kr - kb;
+  const float cr = 0.5f / (1.0f - kb);
+  const float cb = 0.5f / (1.0f - kr);
+
+  const int shift = yuv_color_space->bit_depth - 8;
+
+  const float denom = (float)((1 << yuv_color_space->bit_depth) - 1);
+  float scale_y = 1.0f;
+  float add_y = 0.0f;
+  float scale_u = cr;
+  float scale_v = cb;
+  float add_uv = (float)(128 << shift);
+  assert(yuv_color_space->bit_depth >= 8);
+
+  if (yuv_color_space->range == kSharpYuvRangeLimited) {
+    scale_y *= (219 << shift) / denom;
+    scale_u *= (224 << shift) / denom;
+    scale_v *= (224 << shift) / denom;
+    add_y = (float)(16 << shift);
+  }
+
+  matrix->rgb_to_y[0] = ToFixed16(kr * scale_y);
+  matrix->rgb_to_y[1] = ToFixed16(kg * scale_y);
+  matrix->rgb_to_y[2] = ToFixed16(kb * scale_y);
+  matrix->rgb_to_y[3] = ToFixed16(add_y);
+
+  matrix->rgb_to_u[0] = ToFixed16(-kr * scale_u);
+  matrix->rgb_to_u[1] = ToFixed16(-kg * scale_u);
+  matrix->rgb_to_u[2] = ToFixed16((1 - kb) * scale_u);
+  matrix->rgb_to_u[3] = ToFixed16(add_uv);
+
+  matrix->rgb_to_v[0] = ToFixed16((1 - kr) * scale_v);
+  matrix->rgb_to_v[1] = ToFixed16(-kg * scale_v);
+  matrix->rgb_to_v[2] = ToFixed16(-kb * scale_v);
+  matrix->rgb_to_v[3] = ToFixed16(add_uv);
+}
+
+// Matrices are in YUV_FIX fixed point precision.
+// WebP's matrix, similar but not identical to kRec601LimitedMatrix.
+static const SharpYuvConversionMatrix kWebpMatrix = {
+  {16839, 33059, 6420, 16 << 16},
+  {-9719, -19081, 28800, 128 << 16},
+  {28800, -24116, -4684, 128 << 16},
+};
+// Kr=0.2990f Kb=0.1140f bits=8 range=kSharpYuvRangeLimited
+static const SharpYuvConversionMatrix kRec601LimitedMatrix = {
+  {16829, 33039, 6416, 16 << 16},
+  {-9714, -19071, 28784, 128 << 16},
+  {28784, -24103, -4681, 128 << 16},
+};
+// Kr=0.2990f Kb=0.1140f bits=8 range=kSharpYuvRangeFull
+static const SharpYuvConversionMatrix kRec601FullMatrix = {
+  {19595, 38470, 7471, 0},
+  {-11058, -21710, 32768, 128 << 16},
+  {32768, -27439, -5329, 128 << 16},
+};
+// Kr=0.2126f Kb=0.0722f bits=8 range=kSharpYuvRangeLimited
+static const SharpYuvConversionMatrix kRec709LimitedMatrix = {
+  {11966, 40254, 4064, 16 << 16},
+  {-6596, -22189, 28784, 128 << 16},
+  {28784, -26145, -2639, 128 << 16},
+};
+// Kr=0.2126f Kb=0.0722f bits=8 range=kSharpYuvRangeFull
+static const SharpYuvConversionMatrix kRec709FullMatrix = {
+  {13933, 46871, 4732, 0},
+  {-7509, -25259, 32768, 128 << 16},
+  {32768, -29763, -3005, 128 << 16},
+};
+
+const SharpYuvConversionMatrix* SharpYuvGetConversionMatrix(
+    SharpYuvMatrixType matrix_type) {
+  switch (matrix_type) {
+    case kSharpYuvMatrixWebp:
+      return &kWebpMatrix;
+    case kSharpYuvMatrixRec601Limited:
+      return &kRec601LimitedMatrix;
+    case kSharpYuvMatrixRec601Full:
+      return &kRec601FullMatrix;
+    case kSharpYuvMatrixRec709Limited:
+      return &kRec709LimitedMatrix;
+    case kSharpYuvMatrixRec709Full:
+      return &kRec709FullMatrix;
+    case kSharpYuvMatrixNum:
+      return NULL;
+  }
+  return NULL;
+}
diff --git a/sharpyuv/sharpyuv_csp.h b/sharpyuv/sharpyuv_csp.h
new file mode 100644
index 0000000..3214e3a
--- /dev/null
+++ b/sharpyuv/sharpyuv_csp.h
@@ -0,0 +1,60 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Colorspace utilities.
+
+#ifndef WEBP_SHARPYUV_SHARPYUV_CSP_H_
+#define WEBP_SHARPYUV_SHARPYUV_CSP_H_
+
+#include "sharpyuv/sharpyuv.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Range of YUV values.
+typedef enum {
+  kSharpYuvRangeFull,     // YUV values between [0;255] (for 8 bit)
+  kSharpYuvRangeLimited   // Y in [16;235], YUV in [16;240] (for 8 bit)
+} SharpYuvRange;
+
+// Constants that define a YUV color space.
+typedef struct {
+  // Kr and Kb are defined such that:
+  // Y = Kr * r + Kg * g + Kb * b where Kg = 1 - Kr - Kb.
+  float kr;
+  float kb;
+  int bit_depth;  // 8, 10 or 12
+  SharpYuvRange range;
+} SharpYuvColorSpace;
+
+// Fills in 'matrix' for the given YUVColorSpace.
+SHARPYUV_EXTERN void SharpYuvComputeConversionMatrix(
+    const SharpYuvColorSpace* yuv_color_space,
+    SharpYuvConversionMatrix* matrix);
+
+// Enums for precomputed conversion matrices.
+typedef enum {
+  kSharpYuvMatrixWebp = 0,
+  kSharpYuvMatrixRec601Limited,
+  kSharpYuvMatrixRec601Full,
+  kSharpYuvMatrixRec709Limited,
+  kSharpYuvMatrixRec709Full,
+  kSharpYuvMatrixNum
+} SharpYuvMatrixType;
+
+// Returns a pointer to a matrix for one of the predefined colorspaces.
+SHARPYUV_EXTERN const SharpYuvConversionMatrix* SharpYuvGetConversionMatrix(
+    SharpYuvMatrixType matrix_type);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // WEBP_SHARPYUV_SHARPYUV_CSP_H_
diff --git a/sharpyuv/sharpyuv_dsp.c b/sharpyuv/sharpyuv_dsp.c
new file mode 100644
index 0000000..31c272c
--- /dev/null
+++ b/sharpyuv/sharpyuv_dsp.c
@@ -0,0 +1,103 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Speed-critical functions for Sharp YUV.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "sharpyuv/sharpyuv_dsp.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "sharpyuv/sharpyuv_cpu.h"
+
+//-----------------------------------------------------------------------------
+
+#if !WEBP_NEON_OMIT_C_CODE
+static uint16_t clip(int v, int max) {
+  return (v < 0) ? 0 : (v > max) ? max : (uint16_t)v;
+}
+
+static uint64_t SharpYuvUpdateY_C(const uint16_t* ref, const uint16_t* src,
+                                  uint16_t* dst, int len, int bit_depth) {
+  uint64_t diff = 0;
+  int i;
+  const int max_y = (1 << bit_depth) - 1;
+  for (i = 0; i < len; ++i) {
+    const int diff_y = ref[i] - src[i];
+    const int new_y = (int)dst[i] + diff_y;
+    dst[i] = clip(new_y, max_y);
+    diff += (uint64_t)abs(diff_y);
+  }
+  return diff;
+}
+
+static void SharpYuvUpdateRGB_C(const int16_t* ref, const int16_t* src,
+                                int16_t* dst, int len) {
+  int i;
+  for (i = 0; i < len; ++i) {
+    const int diff_uv = ref[i] - src[i];
+    dst[i] += diff_uv;
+  }
+}
+
+static void SharpYuvFilterRow_C(const int16_t* A, const int16_t* B, int len,
+                                const uint16_t* best_y, uint16_t* out,
+                                int bit_depth) {
+  int i;
+  const int max_y = (1 << bit_depth) - 1;
+  for (i = 0; i < len; ++i, ++A, ++B) {
+    const int v0 = (A[0] * 9 + A[1] * 3 + B[0] * 3 + B[1] + 8) >> 4;
+    const int v1 = (A[1] * 9 + A[0] * 3 + B[1] * 3 + B[0] + 8) >> 4;
+    out[2 * i + 0] = clip(best_y[2 * i + 0] + v0, max_y);
+    out[2 * i + 1] = clip(best_y[2 * i + 1] + v1, max_y);
+  }
+}
+#endif  // !WEBP_NEON_OMIT_C_CODE
+
+//-----------------------------------------------------------------------------
+
+uint64_t (*SharpYuvUpdateY)(const uint16_t* src, const uint16_t* ref,
+                            uint16_t* dst, int len, int bit_depth);
+void (*SharpYuvUpdateRGB)(const int16_t* src, const int16_t* ref, int16_t* dst,
+                          int len);
+void (*SharpYuvFilterRow)(const int16_t* A, const int16_t* B, int len,
+                          const uint16_t* best_y, uint16_t* out,
+                          int bit_depth);
+
+extern void InitSharpYuvSSE2(void);
+extern void InitSharpYuvNEON(void);
+
+void SharpYuvInitDsp(void) {
+#if !WEBP_NEON_OMIT_C_CODE
+  SharpYuvUpdateY = SharpYuvUpdateY_C;
+  SharpYuvUpdateRGB = SharpYuvUpdateRGB_C;
+  SharpYuvFilterRow = SharpYuvFilterRow_C;
+#endif
+
+  if (SharpYuvGetCPUInfo != NULL) {
+#if defined(WEBP_HAVE_SSE2)
+    if (SharpYuvGetCPUInfo(kSSE2)) {
+      InitSharpYuvSSE2();
+    }
+#endif  // WEBP_HAVE_SSE2
+  }
+
+#if defined(WEBP_HAVE_NEON)
+  if (WEBP_NEON_OMIT_C_CODE ||
+      (SharpYuvGetCPUInfo != NULL && SharpYuvGetCPUInfo(kNEON))) {
+    InitSharpYuvNEON();
+  }
+#endif  // WEBP_HAVE_NEON
+
+  assert(SharpYuvUpdateY != NULL);
+  assert(SharpYuvUpdateRGB != NULL);
+  assert(SharpYuvFilterRow != NULL);
+}
diff --git a/sharpyuv/sharpyuv_dsp.h b/sharpyuv/sharpyuv_dsp.h
new file mode 100644
index 0000000..805fbad
--- /dev/null
+++ b/sharpyuv/sharpyuv_dsp.h
@@ -0,0 +1,28 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Speed-critical functions for Sharp YUV.
+
+#ifndef WEBP_SHARPYUV_SHARPYUV_DSP_H_
+#define WEBP_SHARPYUV_SHARPYUV_DSP_H_
+
+#include "sharpyuv/sharpyuv_cpu.h"
+#include "src/webp/types.h"
+
+extern uint64_t (*SharpYuvUpdateY)(const uint16_t* src, const uint16_t* ref,
+                                   uint16_t* dst, int len, int bit_depth);
+extern void (*SharpYuvUpdateRGB)(const int16_t* src, const int16_t* ref,
+                                 int16_t* dst, int len);
+extern void (*SharpYuvFilterRow)(const int16_t* A, const int16_t* B, int len,
+                                 const uint16_t* best_y, uint16_t* out,
+                                 int bit_depth);
+
+void SharpYuvInitDsp(void);
+
+#endif  // WEBP_SHARPYUV_SHARPYUV_DSP_H_
diff --git a/sharpyuv/sharpyuv_gamma.c b/sharpyuv/sharpyuv_gamma.c
new file mode 100644
index 0000000..20ab2da
--- /dev/null
+++ b/sharpyuv/sharpyuv_gamma.c
@@ -0,0 +1,113 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Gamma correction utilities.
+
+#include "sharpyuv/sharpyuv_gamma.h"
+
+#include <assert.h>
+#include <math.h>
+
+#include "src/webp/types.h"
+
+// Gamma correction compensates loss of resolution during chroma subsampling.
+// Size of pre-computed table for converting from gamma to linear.
+#define GAMMA_TO_LINEAR_TAB_BITS 10
+#define GAMMA_TO_LINEAR_TAB_SIZE (1 << GAMMA_TO_LINEAR_TAB_BITS)
+static uint32_t kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE + 2];
+#define LINEAR_TO_GAMMA_TAB_BITS 9
+#define LINEAR_TO_GAMMA_TAB_SIZE (1 << LINEAR_TO_GAMMA_TAB_BITS)
+static uint32_t kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE + 2];
+
+static const double kGammaF = 1. / 0.45;
+#define GAMMA_TO_LINEAR_BITS 16
+
+static volatile int kGammaTablesSOk = 0;
+void SharpYuvInitGammaTables(void) {
+  assert(GAMMA_TO_LINEAR_BITS <= 16);
+  if (!kGammaTablesSOk) {
+    int v;
+    const double a = 0.09929682680944;
+    const double thresh = 0.018053968510807;
+    const double final_scale = 1 << GAMMA_TO_LINEAR_BITS;
+    // Precompute gamma to linear table.
+    {
+      const double norm = 1. / GAMMA_TO_LINEAR_TAB_SIZE;
+      const double a_rec = 1. / (1. + a);
+      for (v = 0; v <= GAMMA_TO_LINEAR_TAB_SIZE; ++v) {
+        const double g = norm * v;
+        double value;
+        if (g <= thresh * 4.5) {
+          value = g / 4.5;
+        } else {
+          value = pow(a_rec * (g + a), kGammaF);
+        }
+        kGammaToLinearTabS[v] = (uint32_t)(value * final_scale + .5);
+      }
+      // to prevent small rounding errors to cause read-overflow:
+      kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE + 1] =
+          kGammaToLinearTabS[GAMMA_TO_LINEAR_TAB_SIZE];
+    }
+    // Precompute linear to gamma table.
+    {
+      const double scale = 1. / LINEAR_TO_GAMMA_TAB_SIZE;
+      for (v = 0; v <= LINEAR_TO_GAMMA_TAB_SIZE; ++v) {
+        const double g = scale * v;
+        double value;
+        if (g <= thresh) {
+          value = 4.5 * g;
+        } else {
+          value = (1. + a) * pow(g, 1. / kGammaF) - a;
+        }
+        kLinearToGammaTabS[v] =
+            (uint32_t)(final_scale * value + 0.5);
+      }
+      // to prevent small rounding errors to cause read-overflow:
+      kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE + 1] =
+          kLinearToGammaTabS[LINEAR_TO_GAMMA_TAB_SIZE];
+    }
+    kGammaTablesSOk = 1;
+  }
+}
+
+static WEBP_INLINE int Shift(int v, int shift) {
+  return (shift >= 0) ? (v << shift) : (v >> -shift);
+}
+
+static WEBP_INLINE uint32_t FixedPointInterpolation(int v, uint32_t* tab,
+                                                    int tab_pos_shift_right,
+                                                    int tab_value_shift) {
+  const uint32_t tab_pos = Shift(v, -tab_pos_shift_right);
+  // fractional part, in 'tab_pos_shift' fixed-point precision
+  const uint32_t x = v - (tab_pos << tab_pos_shift_right);  // fractional part
+  // v0 / v1 are in kGammaToLinearBits fixed-point precision (range [0..1])
+  const uint32_t v0 = Shift(tab[tab_pos + 0], tab_value_shift);
+  const uint32_t v1 = Shift(tab[tab_pos + 1], tab_value_shift);
+  // Final interpolation.
+  const uint32_t v2 = (v1 - v0) * x;  // note: v1 >= v0.
+  const int half =
+      (tab_pos_shift_right > 0) ? 1 << (tab_pos_shift_right - 1) : 0;
+  const uint32_t result = v0 + ((v2 + half) >> tab_pos_shift_right);
+  return result;
+}
+
+uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth) {
+  const int shift = GAMMA_TO_LINEAR_TAB_BITS - bit_depth;
+  if (shift > 0) {
+    return kGammaToLinearTabS[v << shift];
+  }
+  return FixedPointInterpolation(v, kGammaToLinearTabS, -shift, 0);
+}
+
+uint16_t SharpYuvLinearToGamma(uint32_t value, int bit_depth) {
+  return FixedPointInterpolation(
+      value, kLinearToGammaTabS,
+      (GAMMA_TO_LINEAR_BITS - LINEAR_TO_GAMMA_TAB_BITS),
+      bit_depth - GAMMA_TO_LINEAR_BITS);
+}
diff --git a/sharpyuv/sharpyuv_gamma.h b/sharpyuv/sharpyuv_gamma.h
new file mode 100644
index 0000000..d13aff5
--- /dev/null
+++ b/sharpyuv/sharpyuv_gamma.h
@@ -0,0 +1,35 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Gamma correction utilities.
+
+#ifndef WEBP_SHARPYUV_SHARPYUV_GAMMA_H_
+#define WEBP_SHARPYUV_SHARPYUV_GAMMA_H_
+
+#include "src/webp/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Initializes precomputed tables. Must be called once before calling
+// SharpYuvGammaToLinear or SharpYuvLinearToGamma.
+void SharpYuvInitGammaTables(void);
+
+// Converts a gamma color value on 'bit_depth' bits to a 16 bit linear value.
+uint32_t SharpYuvGammaToLinear(uint16_t v, int bit_depth);
+
+// Converts a 16 bit linear color value to a gamma value on 'bit_depth' bits.
+uint16_t SharpYuvLinearToGamma(uint32_t value, int bit_depth);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // WEBP_SHARPYUV_SHARPYUV_GAMMA_H_
diff --git a/sharpyuv/sharpyuv_neon.c b/sharpyuv/sharpyuv_neon.c
new file mode 100644
index 0000000..5840914
--- /dev/null
+++ b/sharpyuv/sharpyuv_neon.c
@@ -0,0 +1,181 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Speed-critical functions for Sharp YUV.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "sharpyuv/sharpyuv_dsp.h"
+
+#if defined(WEBP_USE_NEON)
+#include <assert.h>
+#include <stdlib.h>
+#include <arm_neon.h>
+
+static uint16_t clip_NEON(int v, int max) {
+  return (v < 0) ? 0 : (v > max) ? max : (uint16_t)v;
+}
+
+static uint64_t SharpYuvUpdateY_NEON(const uint16_t* ref, const uint16_t* src,
+                                     uint16_t* dst, int len, int bit_depth) {
+  const int max_y = (1 << bit_depth) - 1;
+  int i;
+  const int16x8_t zero = vdupq_n_s16(0);
+  const int16x8_t max = vdupq_n_s16(max_y);
+  uint64x2_t sum = vdupq_n_u64(0);
+  uint64_t diff;
+
+  for (i = 0; i + 8 <= len; i += 8) {
+    const int16x8_t A = vreinterpretq_s16_u16(vld1q_u16(ref + i));
+    const int16x8_t B = vreinterpretq_s16_u16(vld1q_u16(src + i));
+    const int16x8_t C = vreinterpretq_s16_u16(vld1q_u16(dst + i));
+    const int16x8_t D = vsubq_s16(A, B);       // diff_y
+    const int16x8_t F = vaddq_s16(C, D);       // new_y
+    const uint16x8_t H =
+        vreinterpretq_u16_s16(vmaxq_s16(vminq_s16(F, max), zero));
+    const int16x8_t I = vabsq_s16(D);          // abs(diff_y)
+    vst1q_u16(dst + i, H);
+    sum = vpadalq_u32(sum, vpaddlq_u16(vreinterpretq_u16_s16(I)));
+  }
+  diff = vgetq_lane_u64(sum, 0) + vgetq_lane_u64(sum, 1);
+  for (; i < len; ++i) {
+    const int diff_y = ref[i] - src[i];
+    const int new_y = (int)(dst[i]) + diff_y;
+    dst[i] = clip_NEON(new_y, max_y);
+    diff += (uint64_t)(abs(diff_y));
+  }
+  return diff;
+}
+
+static void SharpYuvUpdateRGB_NEON(const int16_t* ref, const int16_t* src,
+                                   int16_t* dst, int len) {
+  int i;
+  for (i = 0; i + 8 <= len; i += 8) {
+    const int16x8_t A = vld1q_s16(ref + i);
+    const int16x8_t B = vld1q_s16(src + i);
+    const int16x8_t C = vld1q_s16(dst + i);
+    const int16x8_t D = vsubq_s16(A, B);   // diff_uv
+    const int16x8_t E = vaddq_s16(C, D);   // new_uv
+    vst1q_s16(dst + i, E);
+  }
+  for (; i < len; ++i) {
+    const int diff_uv = ref[i] - src[i];
+    dst[i] += diff_uv;
+  }
+}
+
+static void SharpYuvFilterRow16_NEON(const int16_t* A, const int16_t* B,
+                                     int len, const uint16_t* best_y,
+                                     uint16_t* out, int bit_depth) {
+  const int max_y = (1 << bit_depth) - 1;
+  int i;
+  const int16x8_t max = vdupq_n_s16(max_y);
+  const int16x8_t zero = vdupq_n_s16(0);
+  for (i = 0; i + 8 <= len; i += 8) {
+    const int16x8_t a0 = vld1q_s16(A + i + 0);
+    const int16x8_t a1 = vld1q_s16(A + i + 1);
+    const int16x8_t b0 = vld1q_s16(B + i + 0);
+    const int16x8_t b1 = vld1q_s16(B + i + 1);
+    const int16x8_t a0b1 = vaddq_s16(a0, b1);
+    const int16x8_t a1b0 = vaddq_s16(a1, b0);
+    const int16x8_t a0a1b0b1 = vaddq_s16(a0b1, a1b0);  // A0+A1+B0+B1
+    const int16x8_t a0b1_2 = vaddq_s16(a0b1, a0b1);    // 2*(A0+B1)
+    const int16x8_t a1b0_2 = vaddq_s16(a1b0, a1b0);    // 2*(A1+B0)
+    const int16x8_t c0 = vshrq_n_s16(vaddq_s16(a0b1_2, a0a1b0b1), 3);
+    const int16x8_t c1 = vshrq_n_s16(vaddq_s16(a1b0_2, a0a1b0b1), 3);
+    const int16x8_t e0 = vrhaddq_s16(c1, a0);
+    const int16x8_t e1 = vrhaddq_s16(c0, a1);
+    const int16x8x2_t f = vzipq_s16(e0, e1);
+    const int16x8_t g0 = vreinterpretq_s16_u16(vld1q_u16(best_y + 2 * i + 0));
+    const int16x8_t g1 = vreinterpretq_s16_u16(vld1q_u16(best_y + 2 * i + 8));
+    const int16x8_t h0 = vaddq_s16(g0, f.val[0]);
+    const int16x8_t h1 = vaddq_s16(g1, f.val[1]);
+    const int16x8_t i0 = vmaxq_s16(vminq_s16(h0, max), zero);
+    const int16x8_t i1 = vmaxq_s16(vminq_s16(h1, max), zero);
+    vst1q_u16(out + 2 * i + 0, vreinterpretq_u16_s16(i0));
+    vst1q_u16(out + 2 * i + 8, vreinterpretq_u16_s16(i1));
+  }
+  for (; i < len; ++i) {
+    const int a0b1 = A[i + 0] + B[i + 1];
+    const int a1b0 = A[i + 1] + B[i + 0];
+    const int a0a1b0b1 = a0b1 + a1b0 + 8;
+    const int v0 = (8 * A[i + 0] + 2 * a1b0 + a0a1b0b1) >> 4;
+    const int v1 = (8 * A[i + 1] + 2 * a0b1 + a0a1b0b1) >> 4;
+    out[2 * i + 0] = clip_NEON(best_y[2 * i + 0] + v0, max_y);
+    out[2 * i + 1] = clip_NEON(best_y[2 * i + 1] + v1, max_y);
+  }
+}
+
+static void SharpYuvFilterRow32_NEON(const int16_t* A, const int16_t* B,
+                                     int len, const uint16_t* best_y,
+                                     uint16_t* out, int bit_depth) {
+  const int max_y = (1 << bit_depth) - 1;
+  int i;
+  const uint16x8_t max = vdupq_n_u16(max_y);
+  for (i = 0; i + 4 <= len; i += 4) {
+    const int16x4_t a0 = vld1_s16(A + i + 0);
+    const int16x4_t a1 = vld1_s16(A + i + 1);
+    const int16x4_t b0 = vld1_s16(B + i + 0);
+    const int16x4_t b1 = vld1_s16(B + i + 1);
+    const int32x4_t a0b1 = vaddl_s16(a0, b1);
+    const int32x4_t a1b0 = vaddl_s16(a1, b0);
+    const int32x4_t a0a1b0b1 = vaddq_s32(a0b1, a1b0);  // A0+A1+B0+B1
+    const int32x4_t a0b1_2 = vaddq_s32(a0b1, a0b1);    // 2*(A0+B1)
+    const int32x4_t a1b0_2 = vaddq_s32(a1b0, a1b0);    // 2*(A1+B0)
+    const int32x4_t c0 = vshrq_n_s32(vaddq_s32(a0b1_2, a0a1b0b1), 3);
+    const int32x4_t c1 = vshrq_n_s32(vaddq_s32(a1b0_2, a0a1b0b1), 3);
+    const int32x4_t e0 = vrhaddq_s32(c1, vmovl_s16(a0));
+    const int32x4_t e1 = vrhaddq_s32(c0, vmovl_s16(a1));
+    const int32x4x2_t f = vzipq_s32(e0, e1);
+
+    const int16x8_t g = vreinterpretq_s16_u16(vld1q_u16(best_y + 2 * i));
+    const int32x4_t h0 = vaddw_s16(f.val[0], vget_low_s16(g));
+    const int32x4_t h1 = vaddw_s16(f.val[1], vget_high_s16(g));
+    const uint16x8_t i_16 = vcombine_u16(vqmovun_s32(h0), vqmovun_s32(h1));
+    const uint16x8_t i_clamped = vminq_u16(i_16, max);
+    vst1q_u16(out + 2 * i + 0, i_clamped);
+  }
+  for (; i < len; ++i) {
+    const int a0b1 = A[i + 0] + B[i + 1];
+    const int a1b0 = A[i + 1] + B[i + 0];
+    const int a0a1b0b1 = a0b1 + a1b0 + 8;
+    const int v0 = (8 * A[i + 0] + 2 * a1b0 + a0a1b0b1) >> 4;
+    const int v1 = (8 * A[i + 1] + 2 * a0b1 + a0a1b0b1) >> 4;
+    out[2 * i + 0] = clip_NEON(best_y[2 * i + 0] + v0, max_y);
+    out[2 * i + 1] = clip_NEON(best_y[2 * i + 1] + v1, max_y);
+  }
+}
+
+static void SharpYuvFilterRow_NEON(const int16_t* A, const int16_t* B, int len,
+                                   const uint16_t* best_y, uint16_t* out,
+                                   int bit_depth) {
+  if (bit_depth <= 10) {
+    SharpYuvFilterRow16_NEON(A, B, len, best_y, out, bit_depth);
+  } else {
+    SharpYuvFilterRow32_NEON(A, B, len, best_y, out, bit_depth);
+  }
+}
+
+//------------------------------------------------------------------------------
+
+extern void InitSharpYuvNEON(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void InitSharpYuvNEON(void) {
+  SharpYuvUpdateY = SharpYuvUpdateY_NEON;
+  SharpYuvUpdateRGB = SharpYuvUpdateRGB_NEON;
+  SharpYuvFilterRow = SharpYuvFilterRow_NEON;
+}
+
+#else  // !WEBP_USE_NEON
+
+extern void InitSharpYuvNEON(void);
+
+void InitSharpYuvNEON(void) {}
+
+#endif  // WEBP_USE_NEON
diff --git a/sharpyuv/sharpyuv_sse2.c b/sharpyuv/sharpyuv_sse2.c
new file mode 100644
index 0000000..9744d1b
--- /dev/null
+++ b/sharpyuv/sharpyuv_sse2.c
@@ -0,0 +1,201 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// Speed-critical functions for Sharp YUV.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#include "sharpyuv/sharpyuv_dsp.h"
+
+#if defined(WEBP_USE_SSE2)
+#include <stdlib.h>
+#include <emmintrin.h>
+
+static uint16_t clip_SSE2(int v, int max) {
+  return (v < 0) ? 0 : (v > max) ? max : (uint16_t)v;
+}
+
+static uint64_t SharpYuvUpdateY_SSE2(const uint16_t* ref, const uint16_t* src,
+                                     uint16_t* dst, int len, int bit_depth) {
+  const int max_y = (1 << bit_depth) - 1;
+  uint64_t diff = 0;
+  uint32_t tmp[4];
+  int i;
+  const __m128i zero = _mm_setzero_si128();
+  const __m128i max = _mm_set1_epi16(max_y);
+  const __m128i one = _mm_set1_epi16(1);
+  __m128i sum = zero;
+
+  for (i = 0; i + 8 <= len; i += 8) {
+    const __m128i A = _mm_loadu_si128((const __m128i*)(ref + i));
+    const __m128i B = _mm_loadu_si128((const __m128i*)(src + i));
+    const __m128i C = _mm_loadu_si128((const __m128i*)(dst + i));
+    const __m128i D = _mm_sub_epi16(A, B);       // diff_y
+    const __m128i E = _mm_cmpgt_epi16(zero, D);  // sign (-1 or 0)
+    const __m128i F = _mm_add_epi16(C, D);       // new_y
+    const __m128i G = _mm_or_si128(E, one);      // -1 or 1
+    const __m128i H = _mm_max_epi16(_mm_min_epi16(F, max), zero);
+    const __m128i I = _mm_madd_epi16(D, G);      // sum(abs(...))
+    _mm_storeu_si128((__m128i*)(dst + i), H);
+    sum = _mm_add_epi32(sum, I);
+  }
+  _mm_storeu_si128((__m128i*)tmp, sum);
+  diff = tmp[3] + tmp[2] + tmp[1] + tmp[0];
+  for (; i < len; ++i) {
+    const int diff_y = ref[i] - src[i];
+    const int new_y = (int)dst[i] + diff_y;
+    dst[i] = clip_SSE2(new_y, max_y);
+    diff += (uint64_t)abs(diff_y);
+  }
+  return diff;
+}
+
+static void SharpYuvUpdateRGB_SSE2(const int16_t* ref, const int16_t* src,
+                                   int16_t* dst, int len) {
+  int i = 0;
+  for (i = 0; i + 8 <= len; i += 8) {
+    const __m128i A = _mm_loadu_si128((const __m128i*)(ref + i));
+    const __m128i B = _mm_loadu_si128((const __m128i*)(src + i));
+    const __m128i C = _mm_loadu_si128((const __m128i*)(dst + i));
+    const __m128i D = _mm_sub_epi16(A, B);   // diff_uv
+    const __m128i E = _mm_add_epi16(C, D);   // new_uv
+    _mm_storeu_si128((__m128i*)(dst + i), E);
+  }
+  for (; i < len; ++i) {
+    const int diff_uv = ref[i] - src[i];
+    dst[i] += diff_uv;
+  }
+}
+
+static void SharpYuvFilterRow16_SSE2(const int16_t* A, const int16_t* B,
+                                     int len, const uint16_t* best_y,
+                                     uint16_t* out, int bit_depth) {
+  const int max_y = (1 << bit_depth) - 1;
+  int i;
+  const __m128i kCst8 = _mm_set1_epi16(8);
+  const __m128i max = _mm_set1_epi16(max_y);
+  const __m128i zero = _mm_setzero_si128();
+  for (i = 0; i + 8 <= len; i += 8) {
+    const __m128i a0 = _mm_loadu_si128((const __m128i*)(A + i + 0));
+    const __m128i a1 = _mm_loadu_si128((const __m128i*)(A + i + 1));
+    const __m128i b0 = _mm_loadu_si128((const __m128i*)(B + i + 0));
+    const __m128i b1 = _mm_loadu_si128((const __m128i*)(B + i + 1));
+    const __m128i a0b1 = _mm_add_epi16(a0, b1);
+    const __m128i a1b0 = _mm_add_epi16(a1, b0);
+    const __m128i a0a1b0b1 = _mm_add_epi16(a0b1, a1b0);  // A0+A1+B0+B1
+    const __m128i a0a1b0b1_8 = _mm_add_epi16(a0a1b0b1, kCst8);
+    const __m128i a0b1_2 = _mm_add_epi16(a0b1, a0b1);    // 2*(A0+B1)
+    const __m128i a1b0_2 = _mm_add_epi16(a1b0, a1b0);    // 2*(A1+B0)
+    const __m128i c0 = _mm_srai_epi16(_mm_add_epi16(a0b1_2, a0a1b0b1_8), 3);
+    const __m128i c1 = _mm_srai_epi16(_mm_add_epi16(a1b0_2, a0a1b0b1_8), 3);
+    const __m128i d0 = _mm_add_epi16(c1, a0);
+    const __m128i d1 = _mm_add_epi16(c0, a1);
+    const __m128i e0 = _mm_srai_epi16(d0, 1);
+    const __m128i e1 = _mm_srai_epi16(d1, 1);
+    const __m128i f0 = _mm_unpacklo_epi16(e0, e1);
+    const __m128i f1 = _mm_unpackhi_epi16(e0, e1);
+    const __m128i g0 = _mm_loadu_si128((const __m128i*)(best_y + 2 * i + 0));
+    const __m128i g1 = _mm_loadu_si128((const __m128i*)(best_y + 2 * i + 8));
+    const __m128i h0 = _mm_add_epi16(g0, f0);
+    const __m128i h1 = _mm_add_epi16(g1, f1);
+    const __m128i i0 = _mm_max_epi16(_mm_min_epi16(h0, max), zero);
+    const __m128i i1 = _mm_max_epi16(_mm_min_epi16(h1, max), zero);
+    _mm_storeu_si128((__m128i*)(out + 2 * i + 0), i0);
+    _mm_storeu_si128((__m128i*)(out + 2 * i + 8), i1);
+  }
+  for (; i < len; ++i) {
+    //   (9 * A0 + 3 * A1 + 3 * B0 + B1 + 8) >> 4 =
+    // = (8 * A0 + 2 * (A1 + B0) + (A0 + A1 + B0 + B1 + 8)) >> 4
+    // We reuse the common sub-expressions.
+    const int a0b1 = A[i + 0] + B[i + 1];
+    const int a1b0 = A[i + 1] + B[i + 0];
+    const int a0a1b0b1 = a0b1 + a1b0 + 8;
+    const int v0 = (8 * A[i + 0] + 2 * a1b0 + a0a1b0b1) >> 4;
+    const int v1 = (8 * A[i + 1] + 2 * a0b1 + a0a1b0b1) >> 4;
+    out[2 * i + 0] = clip_SSE2(best_y[2 * i + 0] + v0, max_y);
+    out[2 * i + 1] = clip_SSE2(best_y[2 * i + 1] + v1, max_y);
+  }
+}
+
+static WEBP_INLINE __m128i s16_to_s32(__m128i in) {
+  return _mm_srai_epi32(_mm_unpacklo_epi16(in, in), 16);
+}
+
+static void SharpYuvFilterRow32_SSE2(const int16_t* A, const int16_t* B,
+                                     int len, const uint16_t* best_y,
+                                     uint16_t* out, int bit_depth) {
+  const int max_y = (1 << bit_depth) - 1;
+  int i;
+  const __m128i kCst8 = _mm_set1_epi32(8);
+  const __m128i max = _mm_set1_epi16(max_y);
+  const __m128i zero = _mm_setzero_si128();
+  for (i = 0; i + 4 <= len; i += 4) {
+    const __m128i a0 = s16_to_s32(_mm_loadl_epi64((const __m128i*)(A + i + 0)));
+    const __m128i a1 = s16_to_s32(_mm_loadl_epi64((const __m128i*)(A + i + 1)));
+    const __m128i b0 = s16_to_s32(_mm_loadl_epi64((const __m128i*)(B + i + 0)));
+    const __m128i b1 = s16_to_s32(_mm_loadl_epi64((const __m128i*)(B + i + 1)));
+    const __m128i a0b1 = _mm_add_epi32(a0, b1);
+    const __m128i a1b0 = _mm_add_epi32(a1, b0);
+    const __m128i a0a1b0b1 = _mm_add_epi32(a0b1, a1b0);  // A0+A1+B0+B1
+    const __m128i a0a1b0b1_8 = _mm_add_epi32(a0a1b0b1, kCst8);
+    const __m128i a0b1_2 = _mm_add_epi32(a0b1, a0b1);  // 2*(A0+B1)
+    const __m128i a1b0_2 = _mm_add_epi32(a1b0, a1b0);  // 2*(A1+B0)
+    const __m128i c0 = _mm_srai_epi32(_mm_add_epi32(a0b1_2, a0a1b0b1_8), 3);
+    const __m128i c1 = _mm_srai_epi32(_mm_add_epi32(a1b0_2, a0a1b0b1_8), 3);
+    const __m128i d0 = _mm_add_epi32(c1, a0);
+    const __m128i d1 = _mm_add_epi32(c0, a1);
+    const __m128i e0 = _mm_srai_epi32(d0, 1);
+    const __m128i e1 = _mm_srai_epi32(d1, 1);
+    const __m128i f0 = _mm_unpacklo_epi32(e0, e1);
+    const __m128i f1 = _mm_unpackhi_epi32(e0, e1);
+    const __m128i g = _mm_loadu_si128((const __m128i*)(best_y + 2 * i + 0));
+    const __m128i h_16 = _mm_add_epi16(g, _mm_packs_epi32(f0, f1));
+    const __m128i final = _mm_max_epi16(_mm_min_epi16(h_16, max), zero);
+    _mm_storeu_si128((__m128i*)(out + 2 * i + 0), final);
+  }
+  for (; i < len; ++i) {
+    //   (9 * A0 + 3 * A1 + 3 * B0 + B1 + 8) >> 4 =
+    // = (8 * A0 + 2 * (A1 + B0) + (A0 + A1 + B0 + B1 + 8)) >> 4
+    // We reuse the common sub-expressions.
+    const int a0b1 = A[i + 0] + B[i + 1];
+    const int a1b0 = A[i + 1] + B[i + 0];
+    const int a0a1b0b1 = a0b1 + a1b0 + 8;
+    const int v0 = (8 * A[i + 0] + 2 * a1b0 + a0a1b0b1) >> 4;
+    const int v1 = (8 * A[i + 1] + 2 * a0b1 + a0a1b0b1) >> 4;
+    out[2 * i + 0] = clip_SSE2(best_y[2 * i + 0] + v0, max_y);
+    out[2 * i + 1] = clip_SSE2(best_y[2 * i + 1] + v1, max_y);
+  }
+}
+
+static void SharpYuvFilterRow_SSE2(const int16_t* A, const int16_t* B, int len,
+                                   const uint16_t* best_y, uint16_t* out,
+                                   int bit_depth) {
+  if (bit_depth <= 10) {
+    SharpYuvFilterRow16_SSE2(A, B, len, best_y, out, bit_depth);
+  } else {
+    SharpYuvFilterRow32_SSE2(A, B, len, best_y, out, bit_depth);
+  }
+}
+
+//------------------------------------------------------------------------------
+
+extern void InitSharpYuvSSE2(void);
+
+WEBP_TSAN_IGNORE_FUNCTION void InitSharpYuvSSE2(void) {
+  SharpYuvUpdateY = SharpYuvUpdateY_SSE2;
+  SharpYuvUpdateRGB = SharpYuvUpdateRGB_SSE2;
+  SharpYuvFilterRow = SharpYuvFilterRow_SSE2;
+}
+#else  // !WEBP_USE_SSE2
+
+extern void InitSharpYuvSSE2(void);
+
+void InitSharpYuvSSE2(void) {}
+
+#endif  // WEBP_USE_SSE2
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..b2979fb
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,55 @@
+# The mux and demux libraries depend on libwebp, thus the '.' to force
+# the build order so it's available to them.
+SUBDIRS = dec enc dsp utils .
+if BUILD_MUX
+  SUBDIRS += mux
+endif
+if BUILD_DEMUX
+  SUBDIRS += demux
+endif
+
+lib_LTLIBRARIES = libwebp.la
+
+if BUILD_LIBWEBPDECODER
+  lib_LTLIBRARIES += libwebpdecoder.la
+endif
+
+common_HEADERS =
+common_HEADERS += webp/decode.h
+common_HEADERS += webp/types.h
+commondir = $(includedir)/webp
+
+libwebp_la_SOURCES =
+libwebpinclude_HEADERS =
+libwebpinclude_HEADERS += webp/encode.h
+
+noinst_HEADERS =
+noinst_HEADERS += webp/format_constants.h
+
+libwebp_la_LIBADD =
+libwebp_la_LIBADD += dec/libwebpdecode.la
+libwebp_la_LIBADD += dsp/libwebpdsp.la
+libwebp_la_LIBADD += enc/libwebpencode.la
+libwebp_la_LIBADD += utils/libwebputils.la
+
+# Use '-no-undefined' to declare that libwebp does not depend on any libraries
+# other than the ones listed on the command line, i.e., after linking, it will
+# not have unresolved symbols. Some platforms (Windows among them) require all
+# symbols in shared libraries to be resolved at library creation.
+libwebp_la_LDFLAGS = -no-undefined -version-info 8:6:1
+libwebpincludedir = $(includedir)/webp
+pkgconfig_DATA = libwebp.pc
+
+if BUILD_LIBWEBPDECODER
+  libwebpdecoder_la_SOURCES =
+
+  libwebpdecoder_la_LIBADD =
+  libwebpdecoder_la_LIBADD += dec/libwebpdecode.la
+  libwebpdecoder_la_LIBADD += dsp/libwebpdspdecode.la
+  libwebpdecoder_la_LIBADD += utils/libwebputilsdecode.la
+
+  libwebpdecoder_la_LDFLAGS = -no-undefined -version-info 4:6:1
+  pkgconfig_DATA += libwebpdecoder.pc
+endif
+
+${pkgconfig_DATA}: ${top_builddir}/config.status
diff --git a/src/dec/Makefile.am b/src/dec/Makefile.am
new file mode 100644
index 0000000..f8c6398
--- /dev/null
+++ b/src/dec/Makefile.am
@@ -0,0 +1,29 @@
+AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir)
+noinst_LTLIBRARIES = libwebpdecode.la
+
+libwebpdecode_la_SOURCES =
+libwebpdecode_la_SOURCES += alpha_dec.c
+libwebpdecode_la_SOURCES += alphai_dec.h
+libwebpdecode_la_SOURCES += buffer_dec.c
+libwebpdecode_la_SOURCES += common_dec.h
+libwebpdecode_la_SOURCES += vp8_dec.h
+libwebpdecode_la_SOURCES += frame_dec.c
+libwebpdecode_la_SOURCES += idec_dec.c
+libwebpdecode_la_SOURCES += io_dec.c
+libwebpdecode_la_SOURCES += quant_dec.c
+libwebpdecode_la_SOURCES += tree_dec.c
+libwebpdecode_la_SOURCES += vp8_dec.c
+libwebpdecode_la_SOURCES += vp8i_dec.h
+libwebpdecode_la_SOURCES += vp8l_dec.c
+libwebpdecode_la_SOURCES += vp8li_dec.h
+libwebpdecode_la_SOURCES += webp_dec.c
+libwebpdecode_la_SOURCES += webpi_dec.h
+
+libwebpdecodeinclude_HEADERS =
+libwebpdecodeinclude_HEADERS += ../webp/decode.h
+libwebpdecodeinclude_HEADERS += ../webp/types.h
+noinst_HEADERS =
+noinst_HEADERS += ../webp/format_constants.h
+
+libwebpdecode_la_CPPFLAGS = $(AM_CPPFLAGS)
+libwebpdecodeincludedir = $(includedir)/webp
diff --git a/src/dec/vp8i_dec.h b/src/dec/vp8i_dec.h
index 9af22f8..83791ec 100644
--- a/src/dec/vp8i_dec.h
+++ b/src/dec/vp8i_dec.h
@@ -31,8 +31,8 @@
 
 // version numbers
 #define DEC_MAJ_VERSION 1
-#define DEC_MIN_VERSION 2
-#define DEC_REV_VERSION 2
+#define DEC_MIN_VERSION 3
+#define DEC_REV_VERSION 0
 
 // YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
 // Constraints are: We need to store one 16x16 block of luma samples (y),
diff --git a/src/dec/vp8l_dec.c b/src/dec/vp8l_dec.c
index 78db014..c0ea018 100644
--- a/src/dec/vp8l_dec.c
+++ b/src/dec/vp8l_dec.c
@@ -178,7 +178,7 @@
 
 //------------------------------------------------------------------------------
 // Decodes the next Huffman code from bit-stream.
-// FillBitWindow(br) needs to be called at minimum every second call
+// VP8LFillBitWindow(br) needs to be called at minimum every second call
 // to ReadSymbol, in order to pre-fetch enough bits.
 static WEBP_INLINE int ReadSymbol(const HuffmanCode* table,
                                   VP8LBitReader* const br) {
@@ -321,7 +321,7 @@
     // The first code is either 1 bit or 8 bit code.
     int symbol = VP8LReadBits(br, (first_symbol_len_code == 0) ? 1 : 8);
     code_lengths[symbol] = 1;
-    // The second code (if present), is always 8 bit long.
+    // The second code (if present), is always 8 bits long.
     if (num_symbols == 2) {
       symbol = VP8LReadBits(br, 8);
       code_lengths[symbol] = 1;
@@ -1281,7 +1281,7 @@
     uint8_t* const new_data = (uint8_t*)new_color_map;
     new_color_map[0] = transform->data_[0];
     for (i = 4; i < 4 * num_colors; ++i) {
-      // Equivalent to AddPixelEq(), on a byte-basis.
+      // Equivalent to VP8LAddPixels(), on a byte-basis.
       new_data[i] = (data[i] + new_data[i - 4]) & 0xff;
     }
     for (; i < 4 * final_num_colors; ++i) {
@@ -1336,7 +1336,7 @@
        ok = ok && ExpandColorMap(num_colors, transform);
       break;
     }
-    case SUBTRACT_GREEN:
+    case SUBTRACT_GREEN_TRANSFORM:
       break;
     default:
       assert(0);    // can't happen
diff --git a/src/dec/webp_dec.c b/src/dec/webp_dec.c
index 77a54c5..3f4f7bb 100644
--- a/src/dec/webp_dec.c
+++ b/src/dec/webp_dec.c
@@ -179,7 +179,7 @@
       return VP8_STATUS_BITSTREAM_ERROR;          // Not a valid chunk size.
     }
     // For odd-sized chunk-payload, there's one byte padding at the end.
-    disk_chunk_size = (CHUNK_HEADER_SIZE + chunk_size + 1) & ~1;
+    disk_chunk_size = (CHUNK_HEADER_SIZE + chunk_size + 1) & ~1u;
     total_size += disk_chunk_size;
 
     // Check that total bytes skipped so far does not exceed riff_size.
diff --git a/src/demux/Makefile.am b/src/demux/Makefile.am
new file mode 100644
index 0000000..d7392b3
--- /dev/null
+++ b/src/demux/Makefile.am
@@ -0,0 +1,18 @@
+AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir)
+lib_LTLIBRARIES = libwebpdemux.la
+
+libwebpdemux_la_SOURCES =
+libwebpdemux_la_SOURCES += anim_decode.c demux.c
+
+libwebpdemuxinclude_HEADERS =
+libwebpdemuxinclude_HEADERS += ../webp/decode.h
+libwebpdemuxinclude_HEADERS += ../webp/demux.h
+libwebpdemuxinclude_HEADERS += ../webp/mux_types.h
+libwebpdemuxinclude_HEADERS += ../webp/types.h
+noinst_HEADERS =
+noinst_HEADERS += ../webp/format_constants.h
+
+libwebpdemux_la_LIBADD = ../libwebp.la
+libwebpdemux_la_LDFLAGS = -no-undefined -version-info 2:12:0
+libwebpdemuxincludedir = $(includedir)/webp
+pkgconfig_DATA = libwebpdemux.pc
diff --git a/src/demux/demux.c b/src/demux/demux.c
index f04a2b8..324e5eb 100644
--- a/src/demux/demux.c
+++ b/src/demux/demux.c
@@ -24,8 +24,8 @@
 #include "src/webp/format_constants.h"
 
 #define DMUX_MAJ_VERSION 1
-#define DMUX_MIN_VERSION 2
-#define DMUX_REV_VERSION 2
+#define DMUX_MIN_VERSION 3
+#define DMUX_REV_VERSION 0
 
 typedef struct {
   size_t start_;        // start location of the data
@@ -614,7 +614,6 @@
 
   while (f != NULL) {
     const int cur_frame_set = f->frame_num_;
-    int frame_count = 0;
 
     // Check frame properties.
     for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) {
@@ -649,8 +648,6 @@
                             dmux->canvas_width_, dmux->canvas_height_)) {
         return 0;
       }
-
-      ++frame_count;
     }
   }
   return 1;
diff --git a/src/demux/libwebpdemux.pc.in b/src/demux/libwebpdemux.pc.in
new file mode 100644
index 0000000..4da2e40
--- /dev/null
+++ b/src/demux/libwebpdemux.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libwebpdemux
+Description: Library for parsing the WebP graphics format container
+Version: @PACKAGE_VERSION@
+Requires.private: libwebp >= 0.2.0
+Cflags: -I${includedir}
+Libs: -L${libdir} -l@webp_libname_prefix@webpdemux
diff --git a/src/demux/libwebpdemux.rc b/src/demux/libwebpdemux.rc
new file mode 100644
index 0000000..18353e5
--- /dev/null
+++ b/src/demux/libwebpdemux.rc
@@ -0,0 +1,41 @@
+#define APSTUDIO_READONLY_SYMBOLS
+#include "winres.h"
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,3,0
+ PRODUCTVERSION 1,0,3,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", "Google, Inc."
+            VALUE "FileDescription", "libwebpdemux DLL"
+            VALUE "FileVersion", "1.3.0"
+            VALUE "InternalName", "libwebpdemux.dll"
+            VALUE "LegalCopyright", "Copyright (C) 2022"
+            VALUE "OriginalFilename", "libwebpdemux.dll"
+            VALUE "ProductName", "WebP Image Demuxer"
+            VALUE "ProductVersion", "1.3.0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+#endif    // English (United States) resources
diff --git a/src/dsp/Makefile.am b/src/dsp/Makefile.am
new file mode 100644
index 0000000..7db4ef0
--- /dev/null
+++ b/src/dsp/Makefile.am
@@ -0,0 +1,187 @@
+AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir)
+noinst_LTLIBRARIES =
+noinst_LTLIBRARIES += libwebpdsp.la
+noinst_LTLIBRARIES += libwebpdsp_sse2.la
+noinst_LTLIBRARIES += libwebpdspdecode_sse2.la
+noinst_LTLIBRARIES += libwebpdsp_sse41.la
+noinst_LTLIBRARIES += libwebpdspdecode_sse41.la
+noinst_LTLIBRARIES += libwebpdsp_neon.la
+noinst_LTLIBRARIES += libwebpdspdecode_neon.la
+noinst_LTLIBRARIES += libwebpdsp_msa.la
+noinst_LTLIBRARIES += libwebpdspdecode_msa.la
+noinst_LTLIBRARIES += libwebpdsp_mips32.la
+noinst_LTLIBRARIES += libwebpdspdecode_mips32.la
+noinst_LTLIBRARIES += libwebpdsp_mips_dsp_r2.la
+noinst_LTLIBRARIES += libwebpdspdecode_mips_dsp_r2.la
+
+if BUILD_LIBWEBPDECODER
+  noinst_LTLIBRARIES += libwebpdspdecode.la
+endif
+
+common_HEADERS = ../webp/types.h
+commondir = $(includedir)/webp
+
+COMMON_SOURCES =
+COMMON_SOURCES += alpha_processing.c
+COMMON_SOURCES += cpu.c
+COMMON_SOURCES += cpu.h
+COMMON_SOURCES += dec.c
+COMMON_SOURCES += dec_clip_tables.c
+COMMON_SOURCES += dsp.h
+COMMON_SOURCES += filters.c
+COMMON_SOURCES += lossless.c
+COMMON_SOURCES += lossless.h
+COMMON_SOURCES += lossless_common.h
+COMMON_SOURCES += rescaler.c
+COMMON_SOURCES += upsampling.c
+COMMON_SOURCES += yuv.c
+COMMON_SOURCES += yuv.h
+
+ENC_SOURCES =
+ENC_SOURCES += cost.c
+ENC_SOURCES += enc.c
+ENC_SOURCES += lossless_enc.c
+ENC_SOURCES += quant.h
+ENC_SOURCES += ssim.c
+
+libwebpdspdecode_sse41_la_SOURCES =
+libwebpdspdecode_sse41_la_SOURCES += alpha_processing_sse41.c
+libwebpdspdecode_sse41_la_SOURCES += dec_sse41.c
+libwebpdspdecode_sse41_la_SOURCES += lossless_sse41.c
+libwebpdspdecode_sse41_la_SOURCES += upsampling_sse41.c
+libwebpdspdecode_sse41_la_SOURCES += yuv_sse41.c
+libwebpdspdecode_sse41_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
+libwebpdspdecode_sse41_la_CFLAGS = $(AM_CFLAGS) $(SSE41_FLAGS)
+
+libwebpdspdecode_sse2_la_SOURCES =
+libwebpdspdecode_sse2_la_SOURCES += alpha_processing_sse2.c
+libwebpdspdecode_sse2_la_SOURCES += common_sse2.h
+libwebpdspdecode_sse2_la_SOURCES += dec_sse2.c
+libwebpdspdecode_sse2_la_SOURCES += filters_sse2.c
+libwebpdspdecode_sse2_la_SOURCES += lossless_sse2.c
+libwebpdspdecode_sse2_la_SOURCES += rescaler_sse2.c
+libwebpdspdecode_sse2_la_SOURCES += upsampling_sse2.c
+libwebpdspdecode_sse2_la_SOURCES += yuv_sse2.c
+libwebpdspdecode_sse2_la_CPPFLAGS = $(libwebpdsp_sse2_la_CPPFLAGS)
+libwebpdspdecode_sse2_la_CFLAGS = $(libwebpdsp_sse2_la_CFLAGS)
+
+libwebpdspdecode_neon_la_SOURCES =
+libwebpdspdecode_neon_la_SOURCES += alpha_processing_neon.c
+libwebpdspdecode_neon_la_SOURCES += dec_neon.c
+libwebpdspdecode_neon_la_SOURCES += filters_neon.c
+libwebpdspdecode_neon_la_SOURCES += lossless_neon.c
+libwebpdspdecode_neon_la_SOURCES += neon.h
+libwebpdspdecode_neon_la_SOURCES += rescaler_neon.c
+libwebpdspdecode_neon_la_SOURCES += upsampling_neon.c
+libwebpdspdecode_neon_la_SOURCES += yuv_neon.c
+libwebpdspdecode_neon_la_CPPFLAGS = $(libwebpdsp_neon_la_CPPFLAGS)
+libwebpdspdecode_neon_la_CFLAGS = $(libwebpdsp_neon_la_CFLAGS)
+
+libwebpdspdecode_msa_la_SOURCES =
+libwebpdspdecode_msa_la_SOURCES += dec_msa.c
+libwebpdspdecode_msa_la_SOURCES += filters_msa.c
+libwebpdspdecode_msa_la_SOURCES += lossless_msa.c
+libwebpdspdecode_msa_la_SOURCES += msa_macro.h
+libwebpdspdecode_msa_la_SOURCES += rescaler_msa.c
+libwebpdspdecode_msa_la_SOURCES += upsampling_msa.c
+libwebpdspdecode_msa_la_CPPFLAGS = $(libwebpdsp_msa_la_CPPFLAGS)
+libwebpdspdecode_msa_la_CFLAGS = $(libwebpdsp_msa_la_CFLAGS)
+
+libwebpdspdecode_mips32_la_SOURCES =
+libwebpdspdecode_mips32_la_SOURCES += dec_mips32.c
+libwebpdspdecode_mips32_la_SOURCES += mips_macro.h
+libwebpdspdecode_mips32_la_SOURCES += rescaler_mips32.c
+libwebpdspdecode_mips32_la_SOURCES += yuv_mips32.c
+libwebpdspdecode_mips32_la_CPPFLAGS = $(libwebpdsp_mips32_la_CPPFLAGS)
+libwebpdspdecode_mips32_la_CFLAGS = $(libwebpdsp_mips32_la_CFLAGS)
+
+libwebpdspdecode_mips_dsp_r2_la_SOURCES =
+libwebpdspdecode_mips_dsp_r2_la_SOURCES += alpha_processing_mips_dsp_r2.c
+libwebpdspdecode_mips_dsp_r2_la_SOURCES += dec_mips_dsp_r2.c
+libwebpdspdecode_mips_dsp_r2_la_SOURCES += filters_mips_dsp_r2.c
+libwebpdspdecode_mips_dsp_r2_la_SOURCES += lossless_mips_dsp_r2.c
+libwebpdspdecode_mips_dsp_r2_la_SOURCES += mips_macro.h
+libwebpdspdecode_mips_dsp_r2_la_SOURCES += rescaler_mips_dsp_r2.c
+libwebpdspdecode_mips_dsp_r2_la_SOURCES += upsampling_mips_dsp_r2.c
+libwebpdspdecode_mips_dsp_r2_la_SOURCES += yuv_mips_dsp_r2.c
+libwebpdspdecode_mips_dsp_r2_la_CPPFLAGS = $(libwebpdsp_mips_dsp_r2_la_CPPFLAGS)
+libwebpdspdecode_mips_dsp_r2_la_CFLAGS = $(libwebpdsp_mips_dsp_r2_la_CFLAGS)
+
+libwebpdsp_sse2_la_SOURCES =
+libwebpdsp_sse2_la_SOURCES += cost_sse2.c
+libwebpdsp_sse2_la_SOURCES += enc_sse2.c
+libwebpdsp_sse2_la_SOURCES += lossless_enc_sse2.c
+libwebpdsp_sse2_la_SOURCES += ssim_sse2.c
+libwebpdsp_sse2_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
+libwebpdsp_sse2_la_CFLAGS = $(AM_CFLAGS) $(SSE2_FLAGS)
+libwebpdsp_sse2_la_LIBADD = libwebpdspdecode_sse2.la
+
+libwebpdsp_sse41_la_SOURCES =
+libwebpdsp_sse41_la_SOURCES += enc_sse41.c
+libwebpdsp_sse41_la_SOURCES += lossless_enc_sse41.c
+libwebpdsp_sse41_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
+libwebpdsp_sse41_la_CFLAGS = $(AM_CFLAGS) $(SSE41_FLAGS)
+libwebpdsp_sse41_la_LIBADD = libwebpdspdecode_sse41.la
+
+libwebpdsp_neon_la_SOURCES =
+libwebpdsp_neon_la_SOURCES += cost_neon.c
+libwebpdsp_neon_la_SOURCES += enc_neon.c
+libwebpdsp_neon_la_SOURCES += lossless_enc_neon.c
+libwebpdsp_neon_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
+libwebpdsp_neon_la_CFLAGS = $(AM_CFLAGS) $(NEON_FLAGS)
+libwebpdsp_neon_la_LIBADD = libwebpdspdecode_neon.la
+
+libwebpdsp_msa_la_SOURCES =
+libwebpdsp_msa_la_SOURCES += enc_msa.c
+libwebpdsp_msa_la_SOURCES += lossless_enc_msa.c
+libwebpdsp_msa_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
+libwebpdsp_msa_la_CFLAGS = $(AM_CFLAGS)
+libwebpdsp_msa_la_LIBADD = libwebpdspdecode_msa.la
+
+libwebpdsp_mips32_la_SOURCES =
+libwebpdsp_mips32_la_SOURCES += cost_mips32.c
+libwebpdsp_mips32_la_SOURCES += enc_mips32.c
+libwebpdsp_mips32_la_SOURCES += lossless_enc_mips32.c
+libwebpdsp_mips32_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
+libwebpdsp_mips32_la_CFLAGS = $(AM_CFLAGS)
+libwebpdsp_mips32_la_LIBADD = libwebpdspdecode_mips32.la
+
+libwebpdsp_mips_dsp_r2_la_SOURCES =
+libwebpdsp_mips_dsp_r2_la_SOURCES += cost_mips_dsp_r2.c
+libwebpdsp_mips_dsp_r2_la_SOURCES += enc_mips_dsp_r2.c
+libwebpdsp_mips_dsp_r2_la_SOURCES += lossless_enc_mips_dsp_r2.c
+libwebpdsp_mips_dsp_r2_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
+libwebpdsp_mips_dsp_r2_la_CFLAGS = $(AM_CFLAGS)
+libwebpdsp_mips_dsp_r2_la_LIBADD = libwebpdspdecode_mips_dsp_r2.la
+
+libwebpdsp_la_SOURCES = $(COMMON_SOURCES) $(ENC_SOURCES)
+
+noinst_HEADERS =
+noinst_HEADERS += ../dec/vp8_dec.h
+noinst_HEADERS += ../webp/decode.h
+
+libwebpdsp_la_CPPFLAGS =
+libwebpdsp_la_CPPFLAGS += $(AM_CPPFLAGS)
+libwebpdsp_la_CPPFLAGS += $(USE_SWAP_16BIT_CSP)
+libwebpdsp_la_LDFLAGS = -lm
+libwebpdsp_la_LIBADD =
+libwebpdsp_la_LIBADD += libwebpdsp_sse2.la
+libwebpdsp_la_LIBADD += libwebpdsp_sse41.la
+libwebpdsp_la_LIBADD += libwebpdsp_neon.la
+libwebpdsp_la_LIBADD += libwebpdsp_msa.la
+libwebpdsp_la_LIBADD += libwebpdsp_mips32.la
+libwebpdsp_la_LIBADD += libwebpdsp_mips_dsp_r2.la
+
+if BUILD_LIBWEBPDECODER
+  libwebpdspdecode_la_SOURCES = $(COMMON_SOURCES)
+
+  libwebpdspdecode_la_CPPFLAGS = $(libwebpdsp_la_CPPFLAGS)
+  libwebpdspdecode_la_LDFLAGS = $(libwebpdsp_la_LDFLAGS)
+  libwebpdspdecode_la_LIBADD =
+  libwebpdspdecode_la_LIBADD += libwebpdspdecode_sse2.la
+  libwebpdspdecode_la_LIBADD += libwebpdspdecode_sse41.la
+  libwebpdspdecode_la_LIBADD += libwebpdspdecode_neon.la
+  libwebpdspdecode_la_LIBADD += libwebpdspdecode_msa.la
+  libwebpdspdecode_la_LIBADD += libwebpdspdecode_mips32.la
+  libwebpdspdecode_la_LIBADD += libwebpdspdecode_mips_dsp_r2.la
+endif
diff --git a/src/dsp/alpha_processing_sse2.c b/src/dsp/alpha_processing_sse2.c
index a5f8c9f..f0843d0 100644
--- a/src/dsp/alpha_processing_sse2.c
+++ b/src/dsp/alpha_processing_sse2.c
@@ -26,8 +26,8 @@
   uint32_t alpha_and = 0xff;
   int i, j;
   const __m128i zero = _mm_setzero_si128();
-  const __m128i rgb_mask = _mm_set1_epi32(0xffffff00u);  // to preserve RGB
-  const __m128i all_0xff = _mm_set_epi32(0, 0, ~0u, ~0u);
+  const __m128i rgb_mask = _mm_set1_epi32((int)0xffffff00);  // to preserve RGB
+  const __m128i all_0xff = _mm_set_epi32(0, 0, ~0, ~0);
   __m128i all_alphas = all_0xff;
 
   // We must be able to access 3 extra bytes after the last written byte
@@ -106,8 +106,8 @@
   // value is not 0xff if any of the alpha[] is not equal to 0xff.
   uint32_t alpha_and = 0xff;
   int i, j;
-  const __m128i a_mask = _mm_set1_epi32(0xffu);  // to preserve alpha
-  const __m128i all_0xff = _mm_set_epi32(0, 0, ~0u, ~0u);
+  const __m128i a_mask = _mm_set1_epi32(0xff);  // to preserve alpha
+  const __m128i all_0xff = _mm_set_epi32(0, 0, ~0, ~0);
   __m128i all_alphas = all_0xff;
 
   // We must be able to access 3 extra bytes after the last written byte
@@ -178,7 +178,7 @@
 static void ApplyAlphaMultiply_SSE2(uint8_t* rgba, int alpha_first,
                                     int w, int h, int stride) {
   const __m128i zero = _mm_setzero_si128();
-  const __m128i kMult = _mm_set1_epi16(0x8081u);
+  const __m128i kMult = _mm_set1_epi16((short)0x8081);
   const __m128i kMask = _mm_set_epi16(0, 0xff, 0xff, 0, 0, 0xff, 0xff, 0);
   const int kSpan = 4;
   while (h-- > 0) {
@@ -267,7 +267,7 @@
 }
 
 static void AlphaReplace_SSE2(uint32_t* src, int length, uint32_t color) {
-  const __m128i m_color = _mm_set1_epi32(color);
+  const __m128i m_color = _mm_set1_epi32((int)color);
   const __m128i zero = _mm_setzero_si128();
   int i = 0;
   for (; i + 8 <= length; i += 8) {
diff --git a/src/dsp/alpha_processing_sse41.c b/src/dsp/alpha_processing_sse41.c
index cdf877c..1156ac3 100644
--- a/src/dsp/alpha_processing_sse41.c
+++ b/src/dsp/alpha_processing_sse41.c
@@ -26,7 +26,7 @@
   // value is not 0xff if any of the alpha[] is not equal to 0xff.
   uint32_t alpha_and = 0xff;
   int i, j;
-  const __m128i all_0xff = _mm_set1_epi32(~0u);
+  const __m128i all_0xff = _mm_set1_epi32(~0);
   __m128i all_alphas = all_0xff;
 
   // We must be able to access 3 extra bytes after the last written byte
diff --git a/src/dsp/cpu.c b/src/dsp/cpu.c
index 3145e19..62de73f 100644
--- a/src/dsp/cpu.c
+++ b/src/dsp/cpu.c
@@ -11,7 +11,7 @@
 //
 // Author: Christian Duvivier (cduvivier@google.com)
 
-#include "src/dsp/dsp.h"
+#include "src/dsp/cpu.h"
 
 #if defined(WEBP_HAVE_NEON_RTCD)
 #include <stdio.h>
@@ -212,7 +212,7 @@
 #elif defined(WEBP_HAVE_NEON)
 // In most cases this function doesn't check for NEON support (it's assumed by
 // the configuration), but enables turning off NEON at runtime, for testing
-// purposes, by setting VP8DecGetCPUInfo = NULL.
+// purposes, by setting VP8GetCPUInfo = NULL.
 static int armCPUInfo(CPUFeature feature) {
   if (feature != kNEON) return 0;
 #if defined(__linux__) && defined(WEBP_HAVE_NEON_RTCD)
diff --git a/src/dsp/cpu.h b/src/dsp/cpu.h
new file mode 100644
index 0000000..be80727
--- /dev/null
+++ b/src/dsp/cpu.h
@@ -0,0 +1,256 @@
+// Copyright 2022 Google Inc. All Rights Reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+//   CPU detection functions and macros.
+//
+// Author: Skal (pascal.massimino@gmail.com)
+
+#ifndef WEBP_DSP_CPU_H_
+#define WEBP_DSP_CPU_H_
+
+#include <stddef.h>
+
+#ifdef HAVE_CONFIG_H
+#include "src/webp/config.h"
+#endif
+
+#include "src/webp/types.h"
+
+#if defined(__GNUC__)
+#define LOCAL_GCC_VERSION ((__GNUC__ << 8) | __GNUC_MINOR__)
+#define LOCAL_GCC_PREREQ(maj, min) (LOCAL_GCC_VERSION >= (((maj) << 8) | (min)))
+#else
+#define LOCAL_GCC_VERSION 0
+#define LOCAL_GCC_PREREQ(maj, min) 0
+#endif
+
+#if defined(__clang__)
+#define LOCAL_CLANG_VERSION ((__clang_major__ << 8) | __clang_minor__)
+#define LOCAL_CLANG_PREREQ(maj, min) \
+  (LOCAL_CLANG_VERSION >= (((maj) << 8) | (min)))
+#else
+#define LOCAL_CLANG_VERSION 0
+#define LOCAL_CLANG_PREREQ(maj, min) 0
+#endif
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#if !defined(HAVE_CONFIG_H)
+#if defined(_MSC_VER) && _MSC_VER > 1310 && \
+    (defined(_M_X64) || defined(_M_IX86))
+#define WEBP_MSC_SSE2  // Visual C++ SSE2 targets
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1500 && \
+    (defined(_M_X64) || defined(_M_IX86))
+#define WEBP_MSC_SSE41  // Visual C++ SSE4.1 targets
+#endif
+#endif
+
+// WEBP_HAVE_* are used to indicate the presence of the instruction set in dsp
+// files without intrinsics, allowing the corresponding Init() to be called.
+// Files containing intrinsics will need to be built targeting the instruction
+// set so should succeed on one of the earlier tests.
+#if (defined(__SSE2__) || defined(WEBP_MSC_SSE2)) && \
+    (!defined(HAVE_CONFIG_H) || defined(WEBP_HAVE_SSE2))
+#define WEBP_USE_SSE2
+#endif
+
+#if defined(WEBP_USE_SSE2) && !defined(WEBP_HAVE_SSE2)
+#define WEBP_HAVE_SSE2
+#endif
+
+#if (defined(__SSE4_1__) || defined(WEBP_MSC_SSE41)) && \
+    (!defined(HAVE_CONFIG_H) || defined(WEBP_HAVE_SSE41))
+#define WEBP_USE_SSE41
+#endif
+
+#if defined(WEBP_USE_SSE41) && !defined(WEBP_HAVE_SSE41)
+#define WEBP_HAVE_SSE41
+#endif
+
+#undef WEBP_MSC_SSE41
+#undef WEBP_MSC_SSE2
+
+// The intrinsics currently cause compiler errors with arm-nacl-gcc and the
+// inline assembly would need to be modified for use with Native Client.
+#if ((defined(__ARM_NEON__) || defined(__aarch64__)) &&       \
+     (!defined(HAVE_CONFIG_H) || defined(WEBP_HAVE_NEON))) && \
+    !defined(__native_client__)
+#define WEBP_USE_NEON
+#endif
+
+#if !defined(WEBP_USE_NEON) && defined(__ANDROID__) && \
+    defined(__ARM_ARCH_7A__) && defined(HAVE_CPU_FEATURES_H)
+#define WEBP_ANDROID_NEON  // Android targets that may have NEON
+#define WEBP_USE_NEON
+#endif
+
+// Note: ARM64 is supported in Visual Studio 2017, but requires the direct
+// inclusion of arm64_neon.h; Visual Studio 2019 includes this file in
+// arm_neon.h. Compile errors were seen with Visual Studio 2019 16.4 with
+// vtbl4_u8(); a fix was made in 16.6.
+#if defined(_MSC_VER) && ((_MSC_VER >= 1700 && defined(_M_ARM)) || \
+                          (_MSC_VER >= 1926 && defined(_M_ARM64)))
+#define WEBP_USE_NEON
+#define WEBP_USE_INTRINSICS
+#endif
+
+#if defined(WEBP_USE_NEON) && !defined(WEBP_HAVE_NEON)
+#define WEBP_HAVE_NEON
+#endif
+
+#if defined(__mips__) && !defined(__mips64) && defined(__mips_isa_rev) && \
+    (__mips_isa_rev >= 1) && (__mips_isa_rev < 6)
+#define WEBP_USE_MIPS32
+#if (__mips_isa_rev >= 2)
+#define WEBP_USE_MIPS32_R2
+#if defined(__mips_dspr2) || (defined(__mips_dsp_rev) && __mips_dsp_rev >= 2)
+#define WEBP_USE_MIPS_DSP_R2
+#endif
+#endif
+#endif
+
+#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5)
+#define WEBP_USE_MSA
+#endif
+
+#ifndef WEBP_DSP_OMIT_C_CODE
+#define WEBP_DSP_OMIT_C_CODE 1
+#endif
+
+#if defined(WEBP_USE_NEON) && WEBP_DSP_OMIT_C_CODE
+#define WEBP_NEON_OMIT_C_CODE 1
+#else
+#define WEBP_NEON_OMIT_C_CODE 0
+#endif
+
+#if !(LOCAL_CLANG_PREREQ(3, 8) || LOCAL_GCC_PREREQ(4, 8) || \
+      defined(__aarch64__))
+#define WEBP_NEON_WORK_AROUND_GCC 1
+#else
+#define WEBP_NEON_WORK_AROUND_GCC 0
+#endif
+
+// This macro prevents thread_sanitizer from reporting known concurrent writes.
+#define WEBP_TSAN_IGNORE_FUNCTION
+#if defined(__has_feature)
+#if __has_feature(thread_sanitizer)
+#undef WEBP_TSAN_IGNORE_FUNCTION
+#define WEBP_TSAN_IGNORE_FUNCTION __attribute__((no_sanitize_thread))
+#endif
+#endif
+
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer)
+#define WEBP_MSAN
+#endif
+#endif
+
+#if defined(WEBP_USE_THREAD) && !defined(_WIN32)
+#include <pthread.h>  // NOLINT
+
+#define WEBP_DSP_INIT(func)                                         \
+  do {                                                              \
+    static volatile VP8CPUInfo func##_last_cpuinfo_used =           \
+        (VP8CPUInfo)&func##_last_cpuinfo_used;                      \
+    static pthread_mutex_t func##_lock = PTHREAD_MUTEX_INITIALIZER; \
+    if (pthread_mutex_lock(&func##_lock)) break;                    \
+    if (func##_last_cpuinfo_used != VP8GetCPUInfo) func();          \
+    func##_last_cpuinfo_used = VP8GetCPUInfo;                       \
+    (void)pthread_mutex_unlock(&func##_lock);                       \
+  } while (0)
+#else  // !(defined(WEBP_USE_THREAD) && !defined(_WIN32))
+#define WEBP_DSP_INIT(func)                               \
+  do {                                                    \
+    static volatile VP8CPUInfo func##_last_cpuinfo_used = \
+        (VP8CPUInfo)&func##_last_cpuinfo_used;            \
+    if (func##_last_cpuinfo_used == VP8GetCPUInfo) break; \
+    func();                                               \
+    func##_last_cpuinfo_used = VP8GetCPUInfo;             \
+  } while (0)
+#endif  // defined(WEBP_USE_THREAD) && !defined(_WIN32)
+
+// Defines an Init + helper function that control multiple initialization of
+// function pointers / tables.
+/* Usage:
+   WEBP_DSP_INIT_FUNC(InitFunc) {
+     ...function body
+   }
+*/
+#define WEBP_DSP_INIT_FUNC(name)                                            \
+  static WEBP_TSAN_IGNORE_FUNCTION void name##_body(void);                  \
+  WEBP_TSAN_IGNORE_FUNCTION void name(void) { WEBP_DSP_INIT(name##_body); } \
+  static WEBP_TSAN_IGNORE_FUNCTION void name##_body(void)
+
+#define WEBP_UBSAN_IGNORE_UNDEF
+#define WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW
+#if defined(__clang__) && defined(__has_attribute)
+#if __has_attribute(no_sanitize)
+// This macro prevents the undefined behavior sanitizer from reporting
+// failures. This is only meant to silence unaligned loads on platforms that
+// are known to support them.
+#undef WEBP_UBSAN_IGNORE_UNDEF
+#define WEBP_UBSAN_IGNORE_UNDEF __attribute__((no_sanitize("undefined")))
+
+// This macro prevents the undefined behavior sanitizer from reporting
+// failures related to unsigned integer overflows. This is only meant to
+// silence cases where this well defined behavior is expected.
+#undef WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW
+#define WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW \
+  __attribute__((no_sanitize("unsigned-integer-overflow")))
+#endif
+#endif
+
+// If 'ptr' is NULL, returns NULL. Otherwise returns 'ptr + off'.
+// Prevents undefined behavior sanitizer nullptr-with-nonzero-offset warning.
+#if !defined(WEBP_OFFSET_PTR)
+#define WEBP_OFFSET_PTR(ptr, off) (((ptr) == NULL) ? NULL : ((ptr) + (off)))
+#endif
+
+// Regularize the definition of WEBP_SWAP_16BIT_CSP (backward compatibility)
+#if !defined(WEBP_SWAP_16BIT_CSP)
+#define WEBP_SWAP_16BIT_CSP 0
+#endif
+
+// some endian fix (e.g.: mips-gcc doesn't define __BIG_ENDIAN__)
+#if !defined(WORDS_BIGENDIAN) &&                   \
+    (defined(__BIG_ENDIAN__) || defined(_M_PPC) || \
+     (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
+#define WORDS_BIGENDIAN
+#endif
+
+typedef enum {
+  kSSE2,
+  kSSE3,
+  kSlowSSSE3,  // special feature for slow SSSE3 architectures
+  kSSE4_1,
+  kAVX,
+  kAVX2,
+  kNEON,
+  kMIPS32,
+  kMIPSdspR2,
+  kMSA
+} CPUFeature;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// returns true if the CPU supports the feature.
+typedef int (*VP8CPUInfo)(CPUFeature feature);
+WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
+
+#ifdef __cplusplus
+}    // extern "C"
+#endif
+
+#endif  // WEBP_DSP_CPU_H_
diff --git a/src/dsp/dec_sse2.c b/src/dsp/dec_sse2.c
index 873aa59..01e6bcb 100644
--- a/src/dsp/dec_sse2.c
+++ b/src/dsp/dec_sse2.c
@@ -158,10 +158,10 @@
       dst3 = _mm_loadl_epi64((__m128i*)(dst + 3 * BPS));
     } else {
       // Load four bytes/pixels per line.
-      dst0 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 0 * BPS));
-      dst1 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 1 * BPS));
-      dst2 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 2 * BPS));
-      dst3 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 3 * BPS));
+      dst0 = _mm_cvtsi32_si128(WebPMemToInt32(dst + 0 * BPS));
+      dst1 = _mm_cvtsi32_si128(WebPMemToInt32(dst + 1 * BPS));
+      dst2 = _mm_cvtsi32_si128(WebPMemToInt32(dst + 2 * BPS));
+      dst3 = _mm_cvtsi32_si128(WebPMemToInt32(dst + 3 * BPS));
     }
     // Convert to 16b.
     dst0 = _mm_unpacklo_epi8(dst0, zero);
@@ -187,10 +187,10 @@
       _mm_storel_epi64((__m128i*)(dst + 3 * BPS), dst3);
     } else {
       // Store four bytes/pixels per line.
-      WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(dst0));
-      WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(dst1));
-      WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(dst2));
-      WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(dst3));
+      WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(dst0));
+      WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(dst1));
+      WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(dst2));
+      WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(dst3));
     }
   }
 }
@@ -213,10 +213,10 @@
   const __m128i m3 = _mm_subs_epi16(B, d4);
   const __m128i zero = _mm_setzero_si128();
   // Load the source pixels.
-  __m128i dst0 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 0 * BPS));
-  __m128i dst1 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 1 * BPS));
-  __m128i dst2 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 2 * BPS));
-  __m128i dst3 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 3 * BPS));
+  __m128i dst0 = _mm_cvtsi32_si128(WebPMemToInt32(dst + 0 * BPS));
+  __m128i dst1 = _mm_cvtsi32_si128(WebPMemToInt32(dst + 1 * BPS));
+  __m128i dst2 = _mm_cvtsi32_si128(WebPMemToInt32(dst + 2 * BPS));
+  __m128i dst3 = _mm_cvtsi32_si128(WebPMemToInt32(dst + 3 * BPS));
   // Convert to 16b.
   dst0 = _mm_unpacklo_epi8(dst0, zero);
   dst1 = _mm_unpacklo_epi8(dst1, zero);
@@ -233,10 +233,10 @@
   dst2 = _mm_packus_epi16(dst2, dst2);
   dst3 = _mm_packus_epi16(dst3, dst3);
   // Store the results.
-  WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(dst0));
-  WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(dst1));
-  WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(dst2));
-  WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(dst3));
+  WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(dst0));
+  WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(dst1));
+  WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(dst2));
+  WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(dst3));
 }
 #undef MUL
 #endif   // USE_TRANSFORM_AC3
@@ -477,11 +477,11 @@
   // A0 = 63 62 61 60 23 22 21 20 43 42 41 40 03 02 01 00
   // A1 = 73 72 71 70 33 32 31 30 53 52 51 50 13 12 11 10
   const __m128i A0 = _mm_set_epi32(
-      WebPMemToUint32(&b[6 * stride]), WebPMemToUint32(&b[2 * stride]),
-      WebPMemToUint32(&b[4 * stride]), WebPMemToUint32(&b[0 * stride]));
+      WebPMemToInt32(&b[6 * stride]), WebPMemToInt32(&b[2 * stride]),
+      WebPMemToInt32(&b[4 * stride]), WebPMemToInt32(&b[0 * stride]));
   const __m128i A1 = _mm_set_epi32(
-      WebPMemToUint32(&b[7 * stride]), WebPMemToUint32(&b[3 * stride]),
-      WebPMemToUint32(&b[5 * stride]), WebPMemToUint32(&b[1 * stride]));
+      WebPMemToInt32(&b[7 * stride]), WebPMemToInt32(&b[3 * stride]),
+      WebPMemToInt32(&b[5 * stride]), WebPMemToInt32(&b[1 * stride]));
 
   // B0 = 53 43 52 42 51 41 50 40 13 03 12 02 11 01 10 00
   // B1 = 73 63 72 62 71 61 70 60 33 23 32 22 31 21 30 20
@@ -540,7 +540,7 @@
                                       uint8_t* dst, int stride) {
   int i;
   for (i = 0; i < 4; ++i, dst += stride) {
-    WebPUint32ToMem(dst, _mm_cvtsi128_si32(*x));
+    WebPInt32ToMem(dst, _mm_cvtsi128_si32(*x));
     *x = _mm_srli_si128(*x, 4);
   }
 }
@@ -908,10 +908,10 @@
   const __m128i lsb = _mm_and_si128(_mm_xor_si128(ABCDEFGH, CDEFGH00), one);
   const __m128i b = _mm_subs_epu8(a, lsb);
   const __m128i avg = _mm_avg_epu8(b, BCDEFGH0);
-  const uint32_t vals = _mm_cvtsi128_si32(avg);
+  const int vals = _mm_cvtsi128_si32(avg);
   int i;
   for (i = 0; i < 4; ++i) {
-    WebPUint32ToMem(dst + i * BPS, vals);
+    WebPInt32ToMem(dst + i * BPS, vals);
   }
 }
 
@@ -925,10 +925,10 @@
   const __m128i lsb = _mm_and_si128(_mm_xor_si128(ABCDEFGH, CDEFGHH0), one);
   const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
   const __m128i abcdefg = _mm_avg_epu8(avg2, BCDEFGH0);
-  WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(               abcdefg    ));
-  WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
-  WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
-  WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)));
+  WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(               abcdefg    ));
+  WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
+  WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
+  WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)));
 }
 
 static void VR4_SSE2(uint8_t* dst) {   // Vertical-Right
@@ -946,10 +946,10 @@
   const __m128i lsb = _mm_and_si128(_mm_xor_si128(IXABCD, ABCD0), one);
   const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
   const __m128i efgh = _mm_avg_epu8(avg2, XABCD);
-  WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(               abcd    ));
-  WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(               efgh    ));
-  WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(abcd, 1)));
-  WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(efgh, 1)));
+  WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(               abcd    ));
+  WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(               efgh    ));
+  WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(abcd, 1)));
+  WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(efgh, 1)));
 
   // these two are hard to implement in SSE2, so we keep the C-version:
   DST(0, 2) = AVG3(J, I, X);
@@ -970,11 +970,12 @@
   const __m128i abbc = _mm_or_si128(ab, bc);
   const __m128i lsb2 = _mm_and_si128(abbc, lsb1);
   const __m128i avg4 = _mm_subs_epu8(avg3, lsb2);
-  const uint32_t extra_out = _mm_cvtsi128_si32(_mm_srli_si128(avg4, 4));
-  WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(               avg1    ));
-  WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(               avg4    ));
-  WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg1, 1)));
-  WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg4, 1)));
+  const uint32_t extra_out =
+      (uint32_t)_mm_cvtsi128_si32(_mm_srli_si128(avg4, 4));
+  WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(               avg1    ));
+  WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(               avg4    ));
+  WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg1, 1)));
+  WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg4, 1)));
 
   // these two are hard to get and irregular
   DST(3, 2) = (extra_out >> 0) & 0xff;
@@ -990,7 +991,7 @@
   const uint32_t K = dst[-1 + 2 * BPS];
   const uint32_t L = dst[-1 + 3 * BPS];
   const __m128i LKJI_____ =
-      _mm_cvtsi32_si128(L | (K << 8) | (J << 16) | (I << 24));
+      _mm_cvtsi32_si128((int)(L | (K << 8) | (J << 16) | (I << 24)));
   const __m128i LKJIXABCD = _mm_or_si128(LKJI_____, ____XABCD);
   const __m128i KJIXABCD_ = _mm_srli_si128(LKJIXABCD, 1);
   const __m128i JIXABCD__ = _mm_srli_si128(LKJIXABCD, 2);
@@ -998,10 +999,10 @@
   const __m128i lsb = _mm_and_si128(_mm_xor_si128(JIXABCD__, LKJIXABCD), one);
   const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
   const __m128i abcdefg = _mm_avg_epu8(avg2, KJIXABCD_);
-  WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(               abcdefg    ));
-  WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
-  WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
-  WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)));
+  WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(               abcdefg    ));
+  WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
+  WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
+  WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)));
 }
 
 #undef DST
@@ -1015,13 +1016,13 @@
   const __m128i zero = _mm_setzero_si128();
   int y;
   if (size == 4) {
-    const __m128i top_values = _mm_cvtsi32_si128(WebPMemToUint32(top));
+    const __m128i top_values = _mm_cvtsi32_si128(WebPMemToInt32(top));
     const __m128i top_base = _mm_unpacklo_epi8(top_values, zero);
     for (y = 0; y < 4; ++y, dst += BPS) {
       const int val = dst[-1] - top[-1];
       const __m128i base = _mm_set1_epi16(val);
       const __m128i out = _mm_packus_epi16(_mm_add_epi16(base, top_base), zero);
-      WebPUint32ToMem(dst, _mm_cvtsi128_si32(out));
+      WebPInt32ToMem(dst, _mm_cvtsi128_si32(out));
     }
   } else if (size == 8) {
     const __m128i top_values = _mm_loadl_epi64((const __m128i*)top);
@@ -1062,7 +1063,7 @@
 static void HE16_SSE2(uint8_t* dst) {     // horizontal
   int j;
   for (j = 16; j > 0; --j) {
-    const __m128i values = _mm_set1_epi8(dst[-1]);
+    const __m128i values = _mm_set1_epi8((char)dst[-1]);
     _mm_storeu_si128((__m128i*)dst, values);
     dst += BPS;
   }
@@ -1070,7 +1071,7 @@
 
 static WEBP_INLINE void Put16_SSE2(uint8_t v, uint8_t* dst) {
   int j;
-  const __m128i values = _mm_set1_epi8(v);
+  const __m128i values = _mm_set1_epi8((char)v);
   for (j = 0; j < 16; ++j) {
     _mm_storeu_si128((__m128i*)(dst + j * BPS), values);
   }
@@ -1130,7 +1131,7 @@
 // helper for chroma-DC predictions
 static WEBP_INLINE void Put8x8uv_SSE2(uint8_t v, uint8_t* dst) {
   int j;
-  const __m128i values = _mm_set1_epi8(v);
+  const __m128i values = _mm_set1_epi8((char)v);
   for (j = 0; j < 8; ++j) {
     _mm_storel_epi64((__m128i*)(dst + j * BPS), values);
   }
diff --git a/src/dsp/dec_sse41.c b/src/dsp/dec_sse41.c
index 8f18506..08a3630 100644
--- a/src/dsp/dec_sse41.c
+++ b/src/dsp/dec_sse41.c
@@ -23,7 +23,7 @@
   int j;
   const __m128i kShuffle3 = _mm_set1_epi8(3);
   for (j = 16; j > 0; --j) {
-    const __m128i in = _mm_cvtsi32_si128(WebPMemToUint32(dst - 4));
+    const __m128i in = _mm_cvtsi32_si128(WebPMemToInt32(dst - 4));
     const __m128i values = _mm_shuffle_epi8(in, kShuffle3);
     _mm_storeu_si128((__m128i*)dst, values);
     dst += BPS;
diff --git a/src/dsp/dsp.h b/src/dsp/dsp.h
index 73a66be..d2000b8 100644
--- a/src/dsp/dsp.h
+++ b/src/dsp/dsp.h
@@ -18,6 +18,7 @@
 #include "src/webp/config.h"
 #endif
 
+#include "src/dsp/cpu.h"
 #include "src/webp/types.h"
 
 #ifdef __cplusplus
@@ -43,226 +44,6 @@
 #define WEBP_RESTRICT
 #endif
 
-//------------------------------------------------------------------------------
-// CPU detection
-
-#if defined(__GNUC__)
-# define LOCAL_GCC_VERSION ((__GNUC__ << 8) | __GNUC_MINOR__)
-# define LOCAL_GCC_PREREQ(maj, min) \
-    (LOCAL_GCC_VERSION >= (((maj) << 8) | (min)))
-#else
-# define LOCAL_GCC_VERSION 0
-# define LOCAL_GCC_PREREQ(maj, min) 0
-#endif
-
-#if defined(__clang__)
-# define LOCAL_CLANG_VERSION ((__clang_major__ << 8) | __clang_minor__)
-# define LOCAL_CLANG_PREREQ(maj, min) \
-    (LOCAL_CLANG_VERSION >= (((maj) << 8) | (min)))
-#else
-# define LOCAL_CLANG_VERSION 0
-# define LOCAL_CLANG_PREREQ(maj, min) 0
-#endif
-
-#ifndef __has_builtin
-# define __has_builtin(x) 0
-#endif
-
-#if !defined(HAVE_CONFIG_H)
-#if defined(_MSC_VER) && _MSC_VER > 1310 && \
-    (defined(_M_X64) || defined(_M_IX86))
-#define WEBP_MSC_SSE2  // Visual C++ SSE2 targets
-#endif
-
-#if defined(_MSC_VER) && _MSC_VER >= 1500 && \
-    (defined(_M_X64) || defined(_M_IX86))
-#define WEBP_MSC_SSE41  // Visual C++ SSE4.1 targets
-#endif
-#endif
-
-// WEBP_HAVE_* are used to indicate the presence of the instruction set in dsp
-// files without intrinsics, allowing the corresponding Init() to be called.
-// Files containing intrinsics will need to be built targeting the instruction
-// set so should succeed on one of the earlier tests.
-#if (defined(__SSE2__) || defined(WEBP_MSC_SSE2)) && \
-    (!defined(HAVE_CONFIG_H) || defined(WEBP_HAVE_SSE2))
-#define WEBP_USE_SSE2
-#endif
-
-#if defined(WEBP_USE_SSE2) && !defined(WEBP_HAVE_SSE2)
-#define WEBP_HAVE_SSE2
-#endif
-
-#if (defined(__SSE4_1__) || defined(WEBP_MSC_SSE41)) && \
-    (!defined(HAVE_CONFIG_H) || defined(WEBP_HAVE_SSE41))
-#define WEBP_USE_SSE41
-#endif
-
-#if defined(WEBP_USE_SSE41) && !defined(WEBP_HAVE_SSE41)
-#define WEBP_HAVE_SSE41
-#endif
-
-#undef WEBP_MSC_SSE41
-#undef WEBP_MSC_SSE2
-
-// The intrinsics currently cause compiler errors with arm-nacl-gcc and the
-// inline assembly would need to be modified for use with Native Client.
-#if ((defined(__ARM_NEON__) || defined(__aarch64__)) && \
-     (!defined(HAVE_CONFIG_H) || defined(WEBP_HAVE_NEON))) && \
-    !defined(__native_client__)
-#define WEBP_USE_NEON
-#endif
-
-#if !defined(WEBP_USE_NEON) && defined(__ANDROID__) && \
-    defined(__ARM_ARCH_7A__) && defined(HAVE_CPU_FEATURES_H)
-#define WEBP_ANDROID_NEON  // Android targets that may have NEON
-#define WEBP_USE_NEON
-#endif
-
-// Note: ARM64 is supported in Visual Studio 2017, but requires the direct
-// inclusion of arm64_neon.h; Visual Studio 2019 includes this file in
-// arm_neon.h. Compile errors were seen with Visual Studio 2019 16.4 with
-// vtbl4_u8(); a fix was made in 16.6.
-#if defined(_MSC_VER) && \
-  ((_MSC_VER >= 1700 && defined(_M_ARM)) || \
-   (_MSC_VER >= 1926 && defined(_M_ARM64)))
-#define WEBP_USE_NEON
-#define WEBP_USE_INTRINSICS
-#endif
-
-#if defined(WEBP_USE_NEON) && !defined(WEBP_HAVE_NEON)
-#define WEBP_HAVE_NEON
-#endif
-
-#if defined(__mips__) && !defined(__mips64) && \
-    defined(__mips_isa_rev) && (__mips_isa_rev >= 1) && (__mips_isa_rev < 6)
-#define WEBP_USE_MIPS32
-#if (__mips_isa_rev >= 2)
-#define WEBP_USE_MIPS32_R2
-#if defined(__mips_dspr2) || (defined(__mips_dsp_rev) && __mips_dsp_rev >= 2)
-#define WEBP_USE_MIPS_DSP_R2
-#endif
-#endif
-#endif
-
-#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5)
-#define WEBP_USE_MSA
-#endif
-
-#ifndef WEBP_DSP_OMIT_C_CODE
-#define WEBP_DSP_OMIT_C_CODE 1
-#endif
-
-#if defined(WEBP_USE_NEON) && WEBP_DSP_OMIT_C_CODE
-#define WEBP_NEON_OMIT_C_CODE 1
-#else
-#define WEBP_NEON_OMIT_C_CODE 0
-#endif
-
-#if !(LOCAL_CLANG_PREREQ(3,8) || LOCAL_GCC_PREREQ(4,8) || defined(__aarch64__))
-#define WEBP_NEON_WORK_AROUND_GCC 1
-#else
-#define WEBP_NEON_WORK_AROUND_GCC 0
-#endif
-
-// This macro prevents thread_sanitizer from reporting known concurrent writes.
-#define WEBP_TSAN_IGNORE_FUNCTION
-#if defined(__has_feature)
-#if __has_feature(thread_sanitizer)
-#undef WEBP_TSAN_IGNORE_FUNCTION
-#define WEBP_TSAN_IGNORE_FUNCTION __attribute__((no_sanitize_thread))
-#endif
-#endif
-
-#if defined(WEBP_USE_THREAD) && !defined(_WIN32)
-#include <pthread.h>  // NOLINT
-
-#define WEBP_DSP_INIT(func) do {                                    \
-  static volatile VP8CPUInfo func ## _last_cpuinfo_used =           \
-      (VP8CPUInfo)&func ## _last_cpuinfo_used;                      \
-  static pthread_mutex_t func ## _lock = PTHREAD_MUTEX_INITIALIZER; \
-  if (pthread_mutex_lock(&func ## _lock)) break;                    \
-  if (func ## _last_cpuinfo_used != VP8GetCPUInfo) func();          \
-  func ## _last_cpuinfo_used = VP8GetCPUInfo;                       \
-  (void)pthread_mutex_unlock(&func ## _lock);                       \
-} while (0)
-#else  // !(defined(WEBP_USE_THREAD) && !defined(_WIN32))
-#define WEBP_DSP_INIT(func) do {                                    \
-  static volatile VP8CPUInfo func ## _last_cpuinfo_used =           \
-      (VP8CPUInfo)&func ## _last_cpuinfo_used;                      \
-  if (func ## _last_cpuinfo_used == VP8GetCPUInfo) break;           \
-  func();                                                           \
-  func ## _last_cpuinfo_used = VP8GetCPUInfo;                       \
-} while (0)
-#endif  // defined(WEBP_USE_THREAD) && !defined(_WIN32)
-
-// Defines an Init + helper function that control multiple initialization of
-// function pointers / tables.
-/* Usage:
-   WEBP_DSP_INIT_FUNC(InitFunc) {
-     ...function body
-   }
-*/
-#define WEBP_DSP_INIT_FUNC(name)                             \
-  static WEBP_TSAN_IGNORE_FUNCTION void name ## _body(void); \
-  WEBP_TSAN_IGNORE_FUNCTION void name(void) {                \
-    WEBP_DSP_INIT(name ## _body);                            \
-  }                                                          \
-  static WEBP_TSAN_IGNORE_FUNCTION void name ## _body(void)
-
-#define WEBP_UBSAN_IGNORE_UNDEF
-#define WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW
-#if defined(__clang__) && defined(__has_attribute)
-#if __has_attribute(no_sanitize)
-// This macro prevents the undefined behavior sanitizer from reporting
-// failures. This is only meant to silence unaligned loads on platforms that
-// are known to support them.
-#undef WEBP_UBSAN_IGNORE_UNDEF
-#define WEBP_UBSAN_IGNORE_UNDEF \
-  __attribute__((no_sanitize("undefined")))
-
-// This macro prevents the undefined behavior sanitizer from reporting
-// failures related to unsigned integer overflows. This is only meant to
-// silence cases where this well defined behavior is expected.
-#undef WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW
-#define WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW \
-  __attribute__((no_sanitize("unsigned-integer-overflow")))
-#endif
-#endif
-
-// If 'ptr' is NULL, returns NULL. Otherwise returns 'ptr + off'.
-// Prevents undefined behavior sanitizer nullptr-with-nonzero-offset warning.
-#if !defined(WEBP_OFFSET_PTR)
-#define WEBP_OFFSET_PTR(ptr, off) (((ptr) == NULL) ? NULL : ((ptr) + (off)))
-#endif
-
-// Regularize the definition of WEBP_SWAP_16BIT_CSP (backward compatibility)
-#if !defined(WEBP_SWAP_16BIT_CSP)
-#define WEBP_SWAP_16BIT_CSP 0
-#endif
-
-// some endian fix (e.g.: mips-gcc doesn't define __BIG_ENDIAN__)
-#if !defined(WORDS_BIGENDIAN) && \
-    (defined(__BIG_ENDIAN__) || defined(_M_PPC) || \
-     (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)))
-#define WORDS_BIGENDIAN
-#endif
-
-typedef enum {
-  kSSE2,
-  kSSE3,
-  kSlowSSSE3,  // special feature for slow SSSE3 architectures
-  kSSE4_1,
-  kAVX,
-  kAVX2,
-  kNEON,
-  kMIPS32,
-  kMIPSdspR2,
-  kMSA
-} CPUFeature;
-// returns true if the CPU supports the feature.
-typedef int (*VP8CPUInfo)(CPUFeature feature);
-WEBP_EXTERN VP8CPUInfo VP8GetCPUInfo;
 
 //------------------------------------------------------------------------------
 // Init stub generator
@@ -551,15 +332,6 @@
 extern void WebPConvertRGBA32ToUV_C(const uint16_t* rgb,
                                     uint8_t* u, uint8_t* v, int width);
 
-// utilities for accurate RGB->YUV conversion
-extern uint64_t (*WebPSharpYUVUpdateY)(const uint16_t* src, const uint16_t* ref,
-                                       uint16_t* dst, int len);
-extern void (*WebPSharpYUVUpdateRGB)(const int16_t* src, const int16_t* ref,
-                                     int16_t* dst, int len);
-extern void (*WebPSharpYUVFilterRow)(const int16_t* A, const int16_t* B,
-                                     int len,
-                                     const uint16_t* best_y, uint16_t* out);
-
 // Must be called before using the above.
 void WebPInitConvertARGBToYUV(void);
 
diff --git a/src/dsp/enc_neon.c b/src/dsp/enc_neon.c
index 601962b..3a04111 100644
--- a/src/dsp/enc_neon.c
+++ b/src/dsp/enc_neon.c
@@ -764,9 +764,14 @@
 
 // Horizontal sum of all four uint32_t values in 'sum'.
 static int SumToInt_NEON(uint32x4_t sum) {
+#if defined(__aarch64__)
+  return (int)vaddvq_u32(sum);
+#else
   const uint64x2_t sum2 = vpaddlq_u32(sum);
-  const uint64_t sum3 = vgetq_lane_u64(sum2, 0) + vgetq_lane_u64(sum2, 1);
-  return (int)sum3;
+  const uint32x2_t sum3 = vadd_u32(vreinterpret_u32_u64(vget_low_u64(sum2)),
+                                   vreinterpret_u32_u64(vget_high_u64(sum2)));
+  return (int)vget_lane_u32(sum3, 0);
+#endif
 }
 
 static int SSE16x16_NEON(const uint8_t* a, const uint8_t* b) {
diff --git a/src/dsp/enc_sse2.c b/src/dsp/enc_sse2.c
index b2e78ed..1d10556 100644
--- a/src/dsp/enc_sse2.c
+++ b/src/dsp/enc_sse2.c
@@ -156,10 +156,10 @@
       ref3 = _mm_loadl_epi64((const __m128i*)&ref[3 * BPS]);
     } else {
       // Load four bytes/pixels per line.
-      ref0 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[0 * BPS]));
-      ref1 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[1 * BPS]));
-      ref2 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[2 * BPS]));
-      ref3 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[3 * BPS]));
+      ref0 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[0 * BPS]));
+      ref1 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[1 * BPS]));
+      ref2 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[2 * BPS]));
+      ref3 = _mm_cvtsi32_si128(WebPMemToInt32(&ref[3 * BPS]));
     }
     // Convert to 16b.
     ref0 = _mm_unpacklo_epi8(ref0, zero);
@@ -185,10 +185,10 @@
       _mm_storel_epi64((__m128i*)&dst[3 * BPS], ref3);
     } else {
       // Store four bytes/pixels per line.
-      WebPUint32ToMem(&dst[0 * BPS], _mm_cvtsi128_si32(ref0));
-      WebPUint32ToMem(&dst[1 * BPS], _mm_cvtsi128_si32(ref1));
-      WebPUint32ToMem(&dst[2 * BPS], _mm_cvtsi128_si32(ref2));
-      WebPUint32ToMem(&dst[3 * BPS], _mm_cvtsi128_si32(ref3));
+      WebPInt32ToMem(&dst[0 * BPS], _mm_cvtsi128_si32(ref0));
+      WebPInt32ToMem(&dst[1 * BPS], _mm_cvtsi128_si32(ref1));
+      WebPInt32ToMem(&dst[2 * BPS], _mm_cvtsi128_si32(ref2));
+      WebPInt32ToMem(&dst[3 * BPS], _mm_cvtsi128_si32(ref3));
     }
   }
 }
@@ -481,7 +481,7 @@
 // helper for chroma-DC predictions
 static WEBP_INLINE void Put8x8uv_SSE2(uint8_t v, uint8_t* dst) {
   int j;
-  const __m128i values = _mm_set1_epi8(v);
+  const __m128i values = _mm_set1_epi8((char)v);
   for (j = 0; j < 8; ++j) {
     _mm_storel_epi64((__m128i*)(dst + j * BPS), values);
   }
@@ -489,7 +489,7 @@
 
 static WEBP_INLINE void Put16_SSE2(uint8_t v, uint8_t* dst) {
   int j;
-  const __m128i values = _mm_set1_epi8(v);
+  const __m128i values = _mm_set1_epi8((char)v);
   for (j = 0; j < 16; ++j) {
     _mm_store_si128((__m128i*)(dst + j * BPS), values);
   }
@@ -540,7 +540,7 @@
 static WEBP_INLINE void HE8uv_SSE2(uint8_t* dst, const uint8_t* left) {
   int j;
   for (j = 0; j < 8; ++j) {
-    const __m128i values = _mm_set1_epi8(left[j]);
+    const __m128i values = _mm_set1_epi8((char)left[j]);
     _mm_storel_epi64((__m128i*)dst, values);
     dst += BPS;
   }
@@ -549,7 +549,7 @@
 static WEBP_INLINE void HE16_SSE2(uint8_t* dst, const uint8_t* left) {
   int j;
   for (j = 0; j < 16; ++j) {
-    const __m128i values = _mm_set1_epi8(left[j]);
+    const __m128i values = _mm_set1_epi8((char)left[j]);
     _mm_store_si128((__m128i*)dst, values);
     dst += BPS;
   }
@@ -722,10 +722,10 @@
   const __m128i lsb = _mm_and_si128(_mm_xor_si128(ABCDEFGH, CDEFGH00), one);
   const __m128i b = _mm_subs_epu8(a, lsb);
   const __m128i avg = _mm_avg_epu8(b, BCDEFGH0);
-  const uint32_t vals = _mm_cvtsi128_si32(avg);
+  const int vals = _mm_cvtsi128_si32(avg);
   int i;
   for (i = 0; i < 4; ++i) {
-    WebPUint32ToMem(dst + i * BPS, vals);
+    WebPInt32ToMem(dst + i * BPS, vals);
   }
 }
 
@@ -760,10 +760,10 @@
   const __m128i lsb = _mm_and_si128(_mm_xor_si128(ABCDEFGH, CDEFGHH0), one);
   const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
   const __m128i abcdefg = _mm_avg_epu8(avg2, BCDEFGH0);
-  WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(               abcdefg    ));
-  WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
-  WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
-  WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)));
+  WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(               abcdefg    ));
+  WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
+  WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
+  WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)));
 }
 
 static WEBP_INLINE void VR4_SSE2(uint8_t* dst,
@@ -782,10 +782,10 @@
   const __m128i lsb = _mm_and_si128(_mm_xor_si128(IXABCD, ABCD0), one);
   const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
   const __m128i efgh = _mm_avg_epu8(avg2, XABCD);
-  WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(               abcd    ));
-  WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(               efgh    ));
-  WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(abcd, 1)));
-  WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(efgh, 1)));
+  WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(               abcd    ));
+  WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(               efgh    ));
+  WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(abcd, 1)));
+  WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(efgh, 1)));
 
   // these two are hard to implement in SSE2, so we keep the C-version:
   DST(0, 2) = AVG3(J, I, X);
@@ -807,11 +807,12 @@
   const __m128i abbc = _mm_or_si128(ab, bc);
   const __m128i lsb2 = _mm_and_si128(abbc, lsb1);
   const __m128i avg4 = _mm_subs_epu8(avg3, lsb2);
-  const uint32_t extra_out = _mm_cvtsi128_si32(_mm_srli_si128(avg4, 4));
-  WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(               avg1    ));
-  WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(               avg4    ));
-  WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg1, 1)));
-  WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg4, 1)));
+  const uint32_t extra_out =
+      (uint32_t)_mm_cvtsi128_si32(_mm_srli_si128(avg4, 4));
+  WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(               avg1    ));
+  WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(               avg4    ));
+  WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg1, 1)));
+  WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg4, 1)));
 
   // these two are hard to get and irregular
   DST(3, 2) = (extra_out >> 0) & 0xff;
@@ -829,10 +830,10 @@
   const __m128i lsb = _mm_and_si128(_mm_xor_si128(JIXABCD__, LKJIXABCD), one);
   const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
   const __m128i abcdefg = _mm_avg_epu8(avg2, KJIXABCD_);
-  WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(               abcdefg    ));
-  WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
-  WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
-  WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)));
+  WebPInt32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(               abcdefg    ));
+  WebPInt32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)));
+  WebPInt32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)));
+  WebPInt32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)));
 }
 
 static WEBP_INLINE void HU4_SSE2(uint8_t* dst, const uint8_t* top) {
@@ -875,14 +876,14 @@
 
 static WEBP_INLINE void TM4_SSE2(uint8_t* dst, const uint8_t* top) {
   const __m128i zero = _mm_setzero_si128();
-  const __m128i top_values = _mm_cvtsi32_si128(WebPMemToUint32(top));
+  const __m128i top_values = _mm_cvtsi32_si128(WebPMemToInt32(top));
   const __m128i top_base = _mm_unpacklo_epi8(top_values, zero);
   int y;
   for (y = 0; y < 4; ++y, dst += BPS) {
     const int val = top[-2 - y] - top[-1];
     const __m128i base = _mm_set1_epi16(val);
     const __m128i out = _mm_packus_epi16(_mm_add_epi16(base, top_base), zero);
-    WebPUint32ToMem(dst, _mm_cvtsi128_si32(out));
+    WebPInt32ToMem(dst, _mm_cvtsi128_si32(out));
   }
 }
 
diff --git a/src/dsp/lossless.c b/src/dsp/lossless.c
index 84a5429..fb86e58 100644
--- a/src/dsp/lossless.c
+++ b/src/dsp/lossless.c
@@ -49,7 +49,7 @@
 }
 
 static WEBP_INLINE int AddSubtractComponentFull(int a, int b, int c) {
-  return Clip255(a + b - c);
+  return Clip255((uint32_t)(a + b - c));
 }
 
 static WEBP_INLINE uint32_t ClampedAddSubtractFull(uint32_t c0, uint32_t c1,
@@ -66,7 +66,7 @@
 }
 
 static WEBP_INLINE int AddSubtractComponentHalf(int a, int b) {
-  return Clip255(a + (a - b) / 2);
+  return Clip255((uint32_t)(a + (a - b) / 2));
 }
 
 static WEBP_INLINE uint32_t ClampedAddSubtractHalf(uint32_t c0, uint32_t c1,
@@ -293,10 +293,10 @@
     const uint32_t red = argb >> 16;
     int new_red = red & 0xff;
     int new_blue = argb & 0xff;
-    new_red += ColorTransformDelta(m->green_to_red_, green);
+    new_red += ColorTransformDelta((int8_t)m->green_to_red_, green);
     new_red &= 0xff;
-    new_blue += ColorTransformDelta(m->green_to_blue_, green);
-    new_blue += ColorTransformDelta(m->red_to_blue_, (int8_t)new_red);
+    new_blue += ColorTransformDelta((int8_t)m->green_to_blue_, green);
+    new_blue += ColorTransformDelta((int8_t)m->red_to_blue_, (int8_t)new_red);
     new_blue &= 0xff;
     dst[i] = (argb & 0xff00ff00u) | (new_red << 16) | (new_blue);
   }
@@ -395,7 +395,7 @@
   assert(row_start < row_end);
   assert(row_end <= transform->ysize_);
   switch (transform->type_) {
-    case SUBTRACT_GREEN:
+    case SUBTRACT_GREEN_TRANSFORM:
       VP8LAddGreenToBlueAndRed(in, (row_end - row_start) * width, out);
       break;
     case PREDICTOR_TRANSFORM:
diff --git a/src/dsp/lossless.h b/src/dsp/lossless.h
index c26c6bc..de60d95 100644
--- a/src/dsp/lossless.h
+++ b/src/dsp/lossless.h
@@ -182,9 +182,9 @@
 // -----------------------------------------------------------------------------
 // Huffman-cost related functions.
 
-typedef double (*VP8LCostFunc)(const uint32_t* population, int length);
-typedef double (*VP8LCostCombinedFunc)(const uint32_t* X, const uint32_t* Y,
-                                       int length);
+typedef float (*VP8LCostFunc)(const uint32_t* population, int length);
+typedef float (*VP8LCostCombinedFunc)(const uint32_t* X, const uint32_t* Y,
+                                      int length);
 typedef float (*VP8LCombinedShannonEntropyFunc)(const int X[256],
                                                 const int Y[256]);
 
@@ -198,7 +198,7 @@
 } VP8LStreaks;
 
 typedef struct {            // small struct to hold bit entropy results
-  double entropy;           // entropy
+  float entropy;            // entropy
   uint32_t sum;             // sum of the population
   int nonzeros;             // number of non-zero elements in the population
   uint32_t max_val;         // maximum value in the population
diff --git a/src/dsp/lossless_enc.c b/src/dsp/lossless_enc.c
index 1580631..b1f9f26 100644
--- a/src/dsp/lossless_enc.c
+++ b/src/dsp/lossless_enc.c
@@ -402,7 +402,7 @@
 // Compute the combined Shanon's entropy for distribution {X} and {X+Y}
 static float CombinedShannonEntropy_C(const int X[256], const int Y[256]) {
   int i;
-  double retval = 0.;
+  float retval = 0.f;
   int sumX = 0, sumXY = 0;
   for (i = 0; i < 256; ++i) {
     const int x = X[i];
@@ -418,7 +418,7 @@
     }
   }
   retval += VP8LFastSLog2(sumX) + VP8LFastSLog2(sumXY);
-  return (float)retval;
+  return retval;
 }
 
 void VP8LBitEntropyInit(VP8LBitEntropy* const entropy) {
@@ -522,11 +522,11 @@
 void VP8LSubtractGreenFromBlueAndRed_C(uint32_t* argb_data, int num_pixels) {
   int i;
   for (i = 0; i < num_pixels; ++i) {
-    const int argb = argb_data[i];
+    const int argb = (int)argb_data[i];
     const int green = (argb >> 8) & 0xff;
     const uint32_t new_r = (((argb >> 16) & 0xff) - green) & 0xff;
     const uint32_t new_b = (((argb >>  0) & 0xff) - green) & 0xff;
-    argb_data[i] = (argb & 0xff00ff00u) | (new_r << 16) | new_b;
+    argb_data[i] = ((uint32_t)argb & 0xff00ff00u) | (new_r << 16) | new_b;
   }
 }
 
@@ -547,10 +547,10 @@
     const int8_t red   = U32ToS8(argb >> 16);
     int new_red = red & 0xff;
     int new_blue = argb & 0xff;
-    new_red -= ColorTransformDelta(m->green_to_red_, green);
+    new_red -= ColorTransformDelta((int8_t)m->green_to_red_, green);
     new_red &= 0xff;
-    new_blue -= ColorTransformDelta(m->green_to_blue_, green);
-    new_blue -= ColorTransformDelta(m->red_to_blue_, red);
+    new_blue -= ColorTransformDelta((int8_t)m->green_to_blue_, green);
+    new_blue -= ColorTransformDelta((int8_t)m->red_to_blue_, red);
     new_blue &= 0xff;
     data[i] = (argb & 0xff00ff00u) | (new_red << 16) | (new_blue);
   }
@@ -560,7 +560,7 @@
                                              uint32_t argb) {
   const int8_t green = U32ToS8(argb >> 8);
   int new_red = argb >> 16;
-  new_red -= ColorTransformDelta(green_to_red, green);
+  new_red -= ColorTransformDelta((int8_t)green_to_red, green);
   return (new_red & 0xff);
 }
 
@@ -569,9 +569,9 @@
                                               uint32_t argb) {
   const int8_t green = U32ToS8(argb >>  8);
   const int8_t red   = U32ToS8(argb >> 16);
-  uint8_t new_blue = argb & 0xff;
-  new_blue -= ColorTransformDelta(green_to_blue, green);
-  new_blue -= ColorTransformDelta(red_to_blue, red);
+  int new_blue = argb & 0xff;
+  new_blue -= ColorTransformDelta((int8_t)green_to_blue, green);
+  new_blue -= ColorTransformDelta((int8_t)red_to_blue, red);
   return (new_blue & 0xff);
 }
 
@@ -636,17 +636,17 @@
 
 //------------------------------------------------------------------------------
 
-static double ExtraCost_C(const uint32_t* population, int length) {
+static float ExtraCost_C(const uint32_t* population, int length) {
   int i;
-  double cost = 0.;
+  float cost = 0.f;
   for (i = 2; i < length - 2; ++i) cost += (i >> 1) * population[i + 2];
   return cost;
 }
 
-static double ExtraCostCombined_C(const uint32_t* X, const uint32_t* Y,
+static float ExtraCostCombined_C(const uint32_t* X, const uint32_t* Y,
                                   int length) {
   int i;
-  double cost = 0.;
+  float cost = 0.f;
   for (i = 2; i < length - 2; ++i) {
     const int xy = X[i + 2] + Y[i + 2];
     cost += (i >> 1) * xy;
diff --git a/src/dsp/lossless_enc_mips32.c b/src/dsp/lossless_enc_mips32.c
index 9963051..639f786 100644
--- a/src/dsp/lossless_enc_mips32.c
+++ b/src/dsp/lossless_enc_mips32.c
@@ -103,8 +103,8 @@
 //     cost += i * *(pop + 1);
 //     pop += 2;
 //   }
-//   return (double)cost;
-static double ExtraCost_MIPS32(const uint32_t* const population, int length) {
+//   return (float)cost;
+static float ExtraCost_MIPS32(const uint32_t* const population, int length) {
   int i, temp0, temp1;
   const uint32_t* pop = &population[4];
   const uint32_t* const LoopEnd = &population[length];
@@ -130,7 +130,7 @@
     : "memory", "hi", "lo"
   );
 
-  return (double)((int64_t)temp0 << 32 | temp1);
+  return (float)((int64_t)temp0 << 32 | temp1);
 }
 
 // C version of this function:
@@ -148,9 +148,9 @@
 //     pX += 2;
 //     pY += 2;
 //   }
-//   return (double)cost;
-static double ExtraCostCombined_MIPS32(const uint32_t* const X,
-                                       const uint32_t* const Y, int length) {
+//   return (float)cost;
+static float ExtraCostCombined_MIPS32(const uint32_t* const X,
+                                      const uint32_t* const Y, int length) {
   int i, temp0, temp1, temp2, temp3;
   const uint32_t* pX = &X[4];
   const uint32_t* pY = &Y[4];
@@ -183,7 +183,7 @@
     : "memory", "hi", "lo"
   );
 
-  return (double)((int64_t)temp0 << 32 | temp1);
+  return (float)((int64_t)temp0 << 32 | temp1);
 }
 
 #define HUFFMAN_COST_PASS                                 \
diff --git a/src/dsp/lossless_enc_sse2.c b/src/dsp/lossless_enc_sse2.c
index b2f83b8..66cbaab 100644
--- a/src/dsp/lossless_enc_sse2.c
+++ b/src/dsp/lossless_enc_sse2.c
@@ -54,8 +54,8 @@
   const __m128i mults_rb = MK_CST_16(CST_5b(m->green_to_red_),
                                      CST_5b(m->green_to_blue_));
   const __m128i mults_b2 = MK_CST_16(CST_5b(m->red_to_blue_), 0);
-  const __m128i mask_ag = _mm_set1_epi32(0xff00ff00);  // alpha-green masks
-  const __m128i mask_rb = _mm_set1_epi32(0x00ff00ff);  // red-blue masks
+  const __m128i mask_ag = _mm_set1_epi32((int)0xff00ff00);  // alpha-green masks
+  const __m128i mask_rb = _mm_set1_epi32(0x00ff00ff);       // red-blue masks
   int i;
   for (i = 0; i + 4 <= num_pixels; i += 4) {
     const __m128i in = _mm_loadu_si128((__m128i*)&argb_data[i]); // argb
@@ -239,7 +239,7 @@
 
 static float CombinedShannonEntropy_SSE2(const int X[256], const int Y[256]) {
   int i;
-  double retval = 0.;
+  float retval = 0.f;
   int sumX = 0, sumXY = 0;
   const __m128i zero = _mm_setzero_si128();
 
@@ -273,7 +273,7 @@
     }
   }
   retval += VP8LFastSLog2(sumX) + VP8LFastSLog2(sumXY);
-  return (float)retval;
+  return retval;
 }
 
 #else
@@ -376,7 +376,7 @@
       break;
     }
     case 2: {
-      const __m128i mask_or = _mm_set1_epi32(0xff000000);
+      const __m128i mask_or = _mm_set1_epi32((int)0xff000000);
       const __m128i mul_cst = _mm_set1_epi16(0x0104);
       const __m128i mask_mul = _mm_set1_epi16(0x0f00);
       for (x = 0; x + 16 <= width; x += 16, dst += 4) {
@@ -427,7 +427,7 @@
 static void PredictorSub0_SSE2(const uint32_t* in, const uint32_t* upper,
                                int num_pixels, uint32_t* out) {
   int i;
-  const __m128i black = _mm_set1_epi32(ARGB_BLACK);
+  const __m128i black = _mm_set1_epi32((int)ARGB_BLACK);
   for (i = 0; i + 4 <= num_pixels; i += 4) {
     const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]);
     const __m128i res = _mm_sub_epi8(src, black);
diff --git a/src/dsp/lossless_sse2.c b/src/dsp/lossless_sse2.c
index 396cb0b..4b6a532 100644
--- a/src/dsp/lossless_sse2.c
+++ b/src/dsp/lossless_sse2.c
@@ -27,23 +27,22 @@
                                                         uint32_t c1,
                                                         uint32_t c2) {
   const __m128i zero = _mm_setzero_si128();
-  const __m128i C0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c0), zero);
-  const __m128i C1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c1), zero);
-  const __m128i C2 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c2), zero);
+  const __m128i C0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128((int)c0), zero);
+  const __m128i C1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128((int)c1), zero);
+  const __m128i C2 = _mm_unpacklo_epi8(_mm_cvtsi32_si128((int)c2), zero);
   const __m128i V1 = _mm_add_epi16(C0, C1);
   const __m128i V2 = _mm_sub_epi16(V1, C2);
   const __m128i b = _mm_packus_epi16(V2, V2);
-  const uint32_t output = _mm_cvtsi128_si32(b);
-  return output;
+  return (uint32_t)_mm_cvtsi128_si32(b);
 }
 
 static WEBP_INLINE uint32_t ClampedAddSubtractHalf_SSE2(uint32_t c0,
                                                         uint32_t c1,
                                                         uint32_t c2) {
   const __m128i zero = _mm_setzero_si128();
-  const __m128i C0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c0), zero);
-  const __m128i C1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c1), zero);
-  const __m128i B0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c2), zero);
+  const __m128i C0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128((int)c0), zero);
+  const __m128i C1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128((int)c1), zero);
+  const __m128i B0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128((int)c2), zero);
   const __m128i avg = _mm_add_epi16(C1, C0);
   const __m128i A0 = _mm_srli_epi16(avg, 1);
   const __m128i A1 = _mm_sub_epi16(A0, B0);
@@ -52,16 +51,15 @@
   const __m128i A3 = _mm_srai_epi16(A2, 1);
   const __m128i A4 = _mm_add_epi16(A0, A3);
   const __m128i A5 = _mm_packus_epi16(A4, A4);
-  const uint32_t output = _mm_cvtsi128_si32(A5);
-  return output;
+  return (uint32_t)_mm_cvtsi128_si32(A5);
 }
 
 static WEBP_INLINE uint32_t Select_SSE2(uint32_t a, uint32_t b, uint32_t c) {
   int pa_minus_pb;
   const __m128i zero = _mm_setzero_si128();
-  const __m128i A0 = _mm_cvtsi32_si128(a);
-  const __m128i B0 = _mm_cvtsi32_si128(b);
-  const __m128i C0 = _mm_cvtsi32_si128(c);
+  const __m128i A0 = _mm_cvtsi32_si128((int)a);
+  const __m128i B0 = _mm_cvtsi32_si128((int)b);
+  const __m128i C0 = _mm_cvtsi32_si128((int)c);
   const __m128i AC0 = _mm_subs_epu8(A0, C0);
   const __m128i CA0 = _mm_subs_epu8(C0, A0);
   const __m128i BC0 = _mm_subs_epu8(B0, C0);
@@ -94,8 +92,8 @@
                                              __m128i* const avg) {
   // (a + b) >> 1 = ((a + b + 1) >> 1) - ((a ^ b) & 1)
   const __m128i ones = _mm_set1_epi8(1);
-  const __m128i A0 = _mm_cvtsi32_si128(a0);
-  const __m128i A1 = _mm_cvtsi32_si128(a1);
+  const __m128i A0 = _mm_cvtsi32_si128((int)a0);
+  const __m128i A1 = _mm_cvtsi32_si128((int)a1);
   const __m128i avg1 = _mm_avg_epu8(A0, A1);
   const __m128i one = _mm_and_si128(_mm_xor_si128(A0, A1), ones);
   *avg = _mm_sub_epi8(avg1, one);
@@ -103,8 +101,8 @@
 
 static WEBP_INLINE __m128i Average2_uint32_16_SSE2(uint32_t a0, uint32_t a1) {
   const __m128i zero = _mm_setzero_si128();
-  const __m128i A0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(a0), zero);
-  const __m128i A1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(a1), zero);
+  const __m128i A0 = _mm_unpacklo_epi8(_mm_cvtsi32_si128((int)a0), zero);
+  const __m128i A1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128((int)a1), zero);
   const __m128i sum = _mm_add_epi16(A1, A0);
   return _mm_srli_epi16(sum, 1);
 }
@@ -112,19 +110,18 @@
 static WEBP_INLINE uint32_t Average2_SSE2(uint32_t a0, uint32_t a1) {
   __m128i output;
   Average2_uint32_SSE2(a0, a1, &output);
-  return _mm_cvtsi128_si32(output);
+  return (uint32_t)_mm_cvtsi128_si32(output);
 }
 
 static WEBP_INLINE uint32_t Average3_SSE2(uint32_t a0, uint32_t a1,
                                           uint32_t a2) {
   const __m128i zero = _mm_setzero_si128();
   const __m128i avg1 = Average2_uint32_16_SSE2(a0, a2);
-  const __m128i A1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128(a1), zero);
+  const __m128i A1 = _mm_unpacklo_epi8(_mm_cvtsi32_si128((int)a1), zero);
   const __m128i sum = _mm_add_epi16(avg1, A1);
   const __m128i avg2 = _mm_srli_epi16(sum, 1);
   const __m128i A2 = _mm_packus_epi16(avg2, avg2);
-  const uint32_t output = _mm_cvtsi128_si32(A2);
-  return output;
+  return (uint32_t)_mm_cvtsi128_si32(A2);
 }
 
 static WEBP_INLINE uint32_t Average4_SSE2(uint32_t a0, uint32_t a1,
@@ -134,8 +131,7 @@
   const __m128i sum = _mm_add_epi16(avg2, avg1);
   const __m128i avg3 = _mm_srli_epi16(sum, 1);
   const __m128i A0 = _mm_packus_epi16(avg3, avg3);
-  const uint32_t output = _mm_cvtsi128_si32(A0);
-  return output;
+  return (uint32_t)_mm_cvtsi128_si32(A0);
 }
 
 static uint32_t Predictor5_SSE2(const uint32_t* const left,
@@ -192,7 +188,7 @@
 static void PredictorAdd0_SSE2(const uint32_t* in, const uint32_t* upper,
                                int num_pixels, uint32_t* out) {
   int i;
-  const __m128i black = _mm_set1_epi32(ARGB_BLACK);
+  const __m128i black = _mm_set1_epi32((int)ARGB_BLACK);
   for (i = 0; i + 4 <= num_pixels; i += 4) {
     const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]);
     const __m128i res = _mm_add_epi8(src, black);
@@ -208,7 +204,7 @@
 static void PredictorAdd1_SSE2(const uint32_t* in, const uint32_t* upper,
                                int num_pixels, uint32_t* out) {
   int i;
-  __m128i prev = _mm_set1_epi32(out[-1]);
+  __m128i prev = _mm_set1_epi32((int)out[-1]);
   for (i = 0; i + 4 <= num_pixels; i += 4) {
     // a | b | c | d
     const __m128i src = _mm_loadu_si128((const __m128i*)&in[i]);
@@ -285,12 +281,12 @@
 #undef GENERATE_PREDICTOR_2
 
 // Predictor10: average of (average of (L,TL), average of (T, TR)).
-#define DO_PRED10(OUT) do {               \
-  __m128i avgLTL, avg;                    \
-  Average2_m128i(&L, &TL, &avgLTL);       \
-  Average2_m128i(&avgTTR, &avgLTL, &avg); \
-  L = _mm_add_epi8(avg, src);             \
-  out[i + (OUT)] = _mm_cvtsi128_si32(L);  \
+#define DO_PRED10(OUT) do {                         \
+  __m128i avgLTL, avg;                              \
+  Average2_m128i(&L, &TL, &avgLTL);                 \
+  Average2_m128i(&avgTTR, &avgLTL, &avg);           \
+  L = _mm_add_epi8(avg, src);                       \
+  out[i + (OUT)] = (uint32_t)_mm_cvtsi128_si32(L);  \
 } while (0)
 
 #define DO_PRED10_SHIFT do {                                  \
@@ -303,7 +299,7 @@
 static void PredictorAdd10_SSE2(const uint32_t* in, const uint32_t* upper,
                                 int num_pixels, uint32_t* out) {
   int i;
-  __m128i L = _mm_cvtsi32_si128(out[-1]);
+  __m128i L = _mm_cvtsi32_si128((int)out[-1]);
   for (i = 0; i + 4 <= num_pixels; i += 4) {
     __m128i src = _mm_loadu_si128((const __m128i*)&in[i]);
     __m128i TL = _mm_loadu_si128((const __m128i*)&upper[i - 1]);
@@ -336,7 +332,7 @@
   const __m128i B = _mm_andnot_si128(mask, T);                         \
   const __m128i pred = _mm_or_si128(A, B); /* pred = (pa > b)? L : T*/ \
   L = _mm_add_epi8(src, pred);                                         \
-  out[i + (OUT)] = _mm_cvtsi128_si32(L);                               \
+  out[i + (OUT)] = (uint32_t)_mm_cvtsi128_si32(L);                     \
 } while (0)
 
 #define DO_PRED11_SHIFT do {                                \
@@ -351,7 +347,7 @@
                                 int num_pixels, uint32_t* out) {
   int i;
   __m128i pa;
-  __m128i L = _mm_cvtsi32_si128(out[-1]);
+  __m128i L = _mm_cvtsi32_si128((int)out[-1]);
   for (i = 0; i + 4 <= num_pixels; i += 4) {
     __m128i T = _mm_loadu_si128((const __m128i*)&upper[i]);
     __m128i TL = _mm_loadu_si128((const __m128i*)&upper[i - 1]);
@@ -384,12 +380,12 @@
 #undef DO_PRED11_SHIFT
 
 // Predictor12: ClampedAddSubtractFull.
-#define DO_PRED12(DIFF, LANE, OUT) do {            \
-  const __m128i all = _mm_add_epi16(L, (DIFF));    \
-  const __m128i alls = _mm_packus_epi16(all, all); \
-  const __m128i res = _mm_add_epi8(src, alls);     \
-  out[i + (OUT)] = _mm_cvtsi128_si32(res);         \
-  L = _mm_unpacklo_epi8(res, zero);                \
+#define DO_PRED12(DIFF, LANE, OUT) do {              \
+  const __m128i all = _mm_add_epi16(L, (DIFF));      \
+  const __m128i alls = _mm_packus_epi16(all, all);   \
+  const __m128i res = _mm_add_epi8(src, alls);       \
+  out[i + (OUT)] = (uint32_t)_mm_cvtsi128_si32(res); \
+  L = _mm_unpacklo_epi8(res, zero);                  \
 } while (0)
 
 #define DO_PRED12_SHIFT(DIFF, LANE) do {                    \
@@ -402,7 +398,7 @@
                                 int num_pixels, uint32_t* out) {
   int i;
   const __m128i zero = _mm_setzero_si128();
-  const __m128i L8 = _mm_cvtsi32_si128(out[-1]);
+  const __m128i L8 = _mm_cvtsi32_si128((int)out[-1]);
   __m128i L = _mm_unpacklo_epi8(L8, zero);
   for (i = 0; i + 4 <= num_pixels; i += 4) {
     // Load 4 pixels at a time.
@@ -468,7 +464,7 @@
   const __m128i mults_b2 = MK_CST_16(CST(red_to_blue_), 0);
 #undef MK_CST_16
 #undef CST
-  const __m128i mask_ag = _mm_set1_epi32(0xff00ff00);  // alpha-green masks
+  const __m128i mask_ag = _mm_set1_epi32((int)0xff00ff00);  // alpha-green masks
   int i;
   for (i = 0; i + 4 <= num_pixels; i += 4) {
     const __m128i in = _mm_loadu_si128((const __m128i*)&src[i]); // argb
@@ -532,7 +528,7 @@
 
 static void ConvertBGRAToRGBA_SSE2(const uint32_t* src,
                                    int num_pixels, uint8_t* dst) {
-  const __m128i red_blue_mask = _mm_set1_epi32(0x00ff00ffu);
+  const __m128i red_blue_mask = _mm_set1_epi32(0x00ff00ff);
   const __m128i* in = (const __m128i*)src;
   __m128i* out = (__m128i*)dst;
   while (num_pixels >= 8) {
@@ -561,7 +557,7 @@
 static void ConvertBGRAToRGBA4444_SSE2(const uint32_t* src,
                                        int num_pixels, uint8_t* dst) {
   const __m128i mask_0x0f = _mm_set1_epi8(0x0f);
-  const __m128i mask_0xf0 = _mm_set1_epi8(0xf0);
+  const __m128i mask_0xf0 = _mm_set1_epi8((char)0xf0);
   const __m128i* in = (const __m128i*)src;
   __m128i* out = (__m128i*)dst;
   while (num_pixels >= 8) {
@@ -596,8 +592,8 @@
 
 static void ConvertBGRAToRGB565_SSE2(const uint32_t* src,
                                      int num_pixels, uint8_t* dst) {
-  const __m128i mask_0xe0 = _mm_set1_epi8(0xe0);
-  const __m128i mask_0xf8 = _mm_set1_epi8(0xf8);
+  const __m128i mask_0xe0 = _mm_set1_epi8((char)0xe0);
+  const __m128i mask_0xf8 = _mm_set1_epi8((char)0xf8);
   const __m128i mask_0x07 = _mm_set1_epi8(0x07);
   const __m128i* in = (const __m128i*)src;
   __m128i* out = (__m128i*)dst;
diff --git a/src/dsp/lossless_sse41.c b/src/dsp/lossless_sse41.c
index b0d6daa..bb7ce76 100644
--- a/src/dsp/lossless_sse41.c
+++ b/src/dsp/lossless_sse41.c
@@ -25,11 +25,12 @@
                                         int num_pixels, uint32_t* dst) {
 // sign-extended multiplying constants, pre-shifted by 5.
 #define CST(X)  (((int16_t)(m->X << 8)) >> 5)   // sign-extend
-  const __m128i mults_rb = _mm_set1_epi32((uint32_t)CST(green_to_red_) << 16 |
-                                          (CST(green_to_blue_) & 0xffff));
+  const __m128i mults_rb =
+      _mm_set1_epi32((int)((uint32_t)CST(green_to_red_) << 16 |
+                           (CST(green_to_blue_) & 0xffff)));
   const __m128i mults_b2 = _mm_set1_epi32(CST(red_to_blue_));
 #undef CST
-  const __m128i mask_ag = _mm_set1_epi32(0xff00ff00);
+  const __m128i mask_ag = _mm_set1_epi32((int)0xff00ff00);
   const __m128i perm1 = _mm_setr_epi8(-1, 1, -1, 1, -1, 5, -1, 5,
                                       -1, 9, -1, 9, -1, 13, -1, 13);
   const __m128i perm2 = _mm_setr_epi8(-1, 2, -1, -1, -1, 6, -1, -1,
diff --git a/src/dsp/quant.h b/src/dsp/quant.h
index 5e8dba8..fc099bf 100644
--- a/src/dsp/quant.h
+++ b/src/dsp/quant.h
@@ -21,10 +21,15 @@
 
 #define IsFlat IsFlat_NEON
 
-static uint32x2_t horizontal_add_uint32x4(const uint32x4_t a) {
+static uint32_t horizontal_add_uint32x4(const uint32x4_t a) {
+#if defined(__aarch64__)
+  return vaddvq_u32(a);
+#else
   const uint64x2_t b = vpaddlq_u32(a);
-  return vadd_u32(vreinterpret_u32_u64(vget_low_u64(b)),
-                  vreinterpret_u32_u64(vget_high_u64(b)));
+  const uint32x2_t c = vadd_u32(vreinterpret_u32_u64(vget_low_u64(b)),
+                                vreinterpret_u32_u64(vget_high_u64(b)));
+  return vget_lane_u32(c, 0);
+#endif
 }
 
 static WEBP_INLINE int IsFlat(const int16_t* levels, int num_blocks,
@@ -45,7 +50,7 @@
 
     levels += 16;
   }
-  return thresh >= (int32_t)vget_lane_u32(horizontal_add_uint32x4(sum), 0);
+  return thresh >= (int)horizontal_add_uint32x4(sum);
 }
 
 #else
diff --git a/src/dsp/rescaler_sse2.c b/src/dsp/rescaler_sse2.c
index d7effea..3f18e94 100644
--- a/src/dsp/rescaler_sse2.c
+++ b/src/dsp/rescaler_sse2.c
@@ -85,7 +85,7 @@
       const __m128i mult = _mm_cvtsi32_si128(((x_add - accum) << 16) | accum);
       const __m128i out = _mm_madd_epi16(cur_pixels, mult);
       assert(sizeof(*frow) == sizeof(uint32_t));
-      WebPUint32ToMem((uint8_t*)frow, _mm_cvtsi128_si32(out));
+      WebPInt32ToMem((uint8_t*)frow, _mm_cvtsi128_si32(out));
       frow += 1;
       if (frow >= frow_end) break;
       accum -= wrk->x_sub;
@@ -132,7 +132,7 @@
     __m128i base = zero;
     accum += wrk->x_add;
     while (accum > 0) {
-      const __m128i A = _mm_cvtsi32_si128(WebPMemToUint32(src));
+      const __m128i A = _mm_cvtsi32_si128(WebPMemToInt32(src));
       src += 4;
       base = _mm_unpacklo_epi8(A, zero);
       // To avoid overflow, we need: base * x_add / x_sub < 32768
@@ -198,7 +198,7 @@
                                         const __m128i* const mult,
                                         uint8_t* const dst) {
   const __m128i rounder = _mm_set_epi32(0, ROUNDER, 0, ROUNDER);
-  const __m128i mask = _mm_set_epi32(0xffffffffu, 0, 0xffffffffu, 0);
+  const __m128i mask = _mm_set_epi32(~0, 0, ~0, 0);
   const __m128i B0 = _mm_mul_epu32(*A0, *mult);
   const __m128i B1 = _mm_mul_epu32(*A1, *mult);
   const __m128i B2 = _mm_mul_epu32(*A2, *mult);
diff --git a/src/dsp/upsampling_sse2.c b/src/dsp/upsampling_sse2.c
index 340f1e2..08b6d0b 100644
--- a/src/dsp/upsampling_sse2.c
+++ b/src/dsp/upsampling_sse2.c
@@ -121,7 +121,7 @@
   int uv_pos, pos;                                                             \
   /* 16byte-aligned array to cache reconstructed u and v */                    \
   uint8_t uv_buf[14 * 32 + 15] = { 0 };                                        \
-  uint8_t* const r_u = (uint8_t*)((uintptr_t)(uv_buf + 15) & ~15);             \
+  uint8_t* const r_u = (uint8_t*)((uintptr_t)(uv_buf + 15) & ~(uintptr_t)15);  \
   uint8_t* const r_v = r_u + 32;                                               \
                                                                                \
   assert(top_y != NULL);                                                       \
diff --git a/src/dsp/yuv.c b/src/dsp/yuv.c
index 48466f8..d16c13d 100644
--- a/src/dsp/yuv.c
+++ b/src/dsp/yuv.c
@@ -194,50 +194,6 @@
 
 //-----------------------------------------------------------------------------
 
-#if !WEBP_NEON_OMIT_C_CODE
-#define MAX_Y ((1 << 10) - 1)    // 10b precision over 16b-arithmetic
-static uint16_t clip_y(int v) {
-  return (v < 0) ? 0 : (v > MAX_Y) ? MAX_Y : (uint16_t)v;
-}
-
-static uint64_t SharpYUVUpdateY_C(const uint16_t* ref, const uint16_t* src,
-                                  uint16_t* dst, int len) {
-  uint64_t diff = 0;
-  int i;
-  for (i = 0; i < len; ++i) {
-    const int diff_y = ref[i] - src[i];
-    const int new_y = (int)dst[i] + diff_y;
-    dst[i] = clip_y(new_y);
-    diff += (uint64_t)abs(diff_y);
-  }
-  return diff;
-}
-
-static void SharpYUVUpdateRGB_C(const int16_t* ref, const int16_t* src,
-                                int16_t* dst, int len) {
-  int i;
-  for (i = 0; i < len; ++i) {
-    const int diff_uv = ref[i] - src[i];
-    dst[i] += diff_uv;
-  }
-}
-
-static void SharpYUVFilterRow_C(const int16_t* A, const int16_t* B, int len,
-                                const uint16_t* best_y, uint16_t* out) {
-  int i;
-  for (i = 0; i < len; ++i, ++A, ++B) {
-    const int v0 = (A[0] * 9 + A[1] * 3 + B[0] * 3 + B[1] + 8) >> 4;
-    const int v1 = (A[1] * 9 + A[0] * 3 + B[1] * 3 + B[0] + 8) >> 4;
-    out[2 * i + 0] = clip_y(best_y[2 * i + 0] + v0);
-    out[2 * i + 1] = clip_y(best_y[2 * i + 1] + v1);
-  }
-}
-#endif  // !WEBP_NEON_OMIT_C_CODE
-
-#undef MAX_Y
-
-//-----------------------------------------------------------------------------
-
 void (*WebPConvertRGB24ToY)(const uint8_t* rgb, uint8_t* y, int width);
 void (*WebPConvertBGR24ToY)(const uint8_t* bgr, uint8_t* y, int width);
 void (*WebPConvertRGBA32ToUV)(const uint16_t* rgb,
@@ -247,18 +203,9 @@
 void (*WebPConvertARGBToUV)(const uint32_t* argb, uint8_t* u, uint8_t* v,
                             int src_width, int do_store);
 
-uint64_t (*WebPSharpYUVUpdateY)(const uint16_t* ref, const uint16_t* src,
-                                uint16_t* dst, int len);
-void (*WebPSharpYUVUpdateRGB)(const int16_t* ref, const int16_t* src,
-                              int16_t* dst, int len);
-void (*WebPSharpYUVFilterRow)(const int16_t* A, const int16_t* B, int len,
-                              const uint16_t* best_y, uint16_t* out);
-
 extern void WebPInitConvertARGBToYUVSSE2(void);
 extern void WebPInitConvertARGBToYUVSSE41(void);
 extern void WebPInitConvertARGBToYUVNEON(void);
-extern void WebPInitSharpYUVSSE2(void);
-extern void WebPInitSharpYUVNEON(void);
 
 WEBP_DSP_INIT_FUNC(WebPInitConvertARGBToYUV) {
   WebPConvertARGBToY = ConvertARGBToY_C;
@@ -269,17 +216,10 @@
 
   WebPConvertRGBA32ToUV = WebPConvertRGBA32ToUV_C;
 
-#if !WEBP_NEON_OMIT_C_CODE
-  WebPSharpYUVUpdateY = SharpYUVUpdateY_C;
-  WebPSharpYUVUpdateRGB = SharpYUVUpdateRGB_C;
-  WebPSharpYUVFilterRow = SharpYUVFilterRow_C;
-#endif
-
   if (VP8GetCPUInfo != NULL) {
 #if defined(WEBP_HAVE_SSE2)
     if (VP8GetCPUInfo(kSSE2)) {
       WebPInitConvertARGBToYUVSSE2();
-      WebPInitSharpYUVSSE2();
     }
 #endif  // WEBP_HAVE_SSE2
 #if defined(WEBP_HAVE_SSE41)
@@ -293,7 +233,6 @@
   if (WEBP_NEON_OMIT_C_CODE ||
       (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
     WebPInitConvertARGBToYUVNEON();
-    WebPInitSharpYUVNEON();
   }
 #endif  // WEBP_HAVE_NEON
 
@@ -302,7 +241,4 @@
   assert(WebPConvertRGB24ToY != NULL);
   assert(WebPConvertBGR24ToY != NULL);
   assert(WebPConvertRGBA32ToUV != NULL);
-  assert(WebPSharpYUVUpdateY != NULL);
-  assert(WebPSharpYUVUpdateRGB != NULL);
-  assert(WebPSharpYUVFilterRow != NULL);
 }
diff --git a/src/dsp/yuv_neon.c b/src/dsp/yuv_neon.c
index a34d602..ff77b00 100644
--- a/src/dsp/yuv_neon.c
+++ b/src/dsp/yuv_neon.c
@@ -173,116 +173,8 @@
   WebPConvertRGBA32ToUV = ConvertRGBA32ToUV_NEON;
 }
 
-//------------------------------------------------------------------------------
-
-#define MAX_Y ((1 << 10) - 1)    // 10b precision over 16b-arithmetic
-static uint16_t clip_y_NEON(int v) {
-  return (v < 0) ? 0 : (v > MAX_Y) ? MAX_Y : (uint16_t)v;
-}
-
-static uint64_t SharpYUVUpdateY_NEON(const uint16_t* ref, const uint16_t* src,
-                                     uint16_t* dst, int len) {
-  int i;
-  const int16x8_t zero = vdupq_n_s16(0);
-  const int16x8_t max = vdupq_n_s16(MAX_Y);
-  uint64x2_t sum = vdupq_n_u64(0);
-  uint64_t diff;
-
-  for (i = 0; i + 8 <= len; i += 8) {
-    const int16x8_t A = vreinterpretq_s16_u16(vld1q_u16(ref + i));
-    const int16x8_t B = vreinterpretq_s16_u16(vld1q_u16(src + i));
-    const int16x8_t C = vreinterpretq_s16_u16(vld1q_u16(dst + i));
-    const int16x8_t D = vsubq_s16(A, B);       // diff_y
-    const int16x8_t F = vaddq_s16(C, D);       // new_y
-    const uint16x8_t H =
-        vreinterpretq_u16_s16(vmaxq_s16(vminq_s16(F, max), zero));
-    const int16x8_t I = vabsq_s16(D);          // abs(diff_y)
-    vst1q_u16(dst + i, H);
-    sum = vpadalq_u32(sum, vpaddlq_u16(vreinterpretq_u16_s16(I)));
-  }
-  diff = vgetq_lane_u64(sum, 0) + vgetq_lane_u64(sum, 1);
-  for (; i < len; ++i) {
-    const int diff_y = ref[i] - src[i];
-    const int new_y = (int)(dst[i]) + diff_y;
-    dst[i] = clip_y_NEON(new_y);
-    diff += (uint64_t)(abs(diff_y));
-  }
-  return diff;
-}
-
-static void SharpYUVUpdateRGB_NEON(const int16_t* ref, const int16_t* src,
-                                   int16_t* dst, int len) {
-  int i;
-  for (i = 0; i + 8 <= len; i += 8) {
-    const int16x8_t A = vld1q_s16(ref + i);
-    const int16x8_t B = vld1q_s16(src + i);
-    const int16x8_t C = vld1q_s16(dst + i);
-    const int16x8_t D = vsubq_s16(A, B);   // diff_uv
-    const int16x8_t E = vaddq_s16(C, D);   // new_uv
-    vst1q_s16(dst + i, E);
-  }
-  for (; i < len; ++i) {
-    const int diff_uv = ref[i] - src[i];
-    dst[i] += diff_uv;
-  }
-}
-
-static void SharpYUVFilterRow_NEON(const int16_t* A, const int16_t* B, int len,
-                                   const uint16_t* best_y, uint16_t* out) {
-  int i;
-  const int16x8_t max = vdupq_n_s16(MAX_Y);
-  const int16x8_t zero = vdupq_n_s16(0);
-  for (i = 0; i + 8 <= len; i += 8) {
-    const int16x8_t a0 = vld1q_s16(A + i + 0);
-    const int16x8_t a1 = vld1q_s16(A + i + 1);
-    const int16x8_t b0 = vld1q_s16(B + i + 0);
-    const int16x8_t b1 = vld1q_s16(B + i + 1);
-    const int16x8_t a0b1 = vaddq_s16(a0, b1);
-    const int16x8_t a1b0 = vaddq_s16(a1, b0);
-    const int16x8_t a0a1b0b1 = vaddq_s16(a0b1, a1b0);  // A0+A1+B0+B1
-    const int16x8_t a0b1_2 = vaddq_s16(a0b1, a0b1);    // 2*(A0+B1)
-    const int16x8_t a1b0_2 = vaddq_s16(a1b0, a1b0);    // 2*(A1+B0)
-    const int16x8_t c0 = vshrq_n_s16(vaddq_s16(a0b1_2, a0a1b0b1), 3);
-    const int16x8_t c1 = vshrq_n_s16(vaddq_s16(a1b0_2, a0a1b0b1), 3);
-    const int16x8_t d0 = vaddq_s16(c1, a0);
-    const int16x8_t d1 = vaddq_s16(c0, a1);
-    const int16x8_t e0 = vrshrq_n_s16(d0, 1);
-    const int16x8_t e1 = vrshrq_n_s16(d1, 1);
-    const int16x8x2_t f = vzipq_s16(e0, e1);
-    const int16x8_t g0 = vreinterpretq_s16_u16(vld1q_u16(best_y + 2 * i + 0));
-    const int16x8_t g1 = vreinterpretq_s16_u16(vld1q_u16(best_y + 2 * i + 8));
-    const int16x8_t h0 = vaddq_s16(g0, f.val[0]);
-    const int16x8_t h1 = vaddq_s16(g1, f.val[1]);
-    const int16x8_t i0 = vmaxq_s16(vminq_s16(h0, max), zero);
-    const int16x8_t i1 = vmaxq_s16(vminq_s16(h1, max), zero);
-    vst1q_u16(out + 2 * i + 0, vreinterpretq_u16_s16(i0));
-    vst1q_u16(out + 2 * i + 8, vreinterpretq_u16_s16(i1));
-  }
-  for (; i < len; ++i) {
-    const int a0b1 = A[i + 0] + B[i + 1];
-    const int a1b0 = A[i + 1] + B[i + 0];
-    const int a0a1b0b1 = a0b1 + a1b0 + 8;
-    const int v0 = (8 * A[i + 0] + 2 * a1b0 + a0a1b0b1) >> 4;
-    const int v1 = (8 * A[i + 1] + 2 * a0b1 + a0a1b0b1) >> 4;
-    out[2 * i + 0] = clip_y_NEON(best_y[2 * i + 0] + v0);
-    out[2 * i + 1] = clip_y_NEON(best_y[2 * i + 1] + v1);
-  }
-}
-#undef MAX_Y
-
-//------------------------------------------------------------------------------
-
-extern void WebPInitSharpYUVNEON(void);
-
-WEBP_TSAN_IGNORE_FUNCTION void WebPInitSharpYUVNEON(void) {
-  WebPSharpYUVUpdateY = SharpYUVUpdateY_NEON;
-  WebPSharpYUVUpdateRGB = SharpYUVUpdateRGB_NEON;
-  WebPSharpYUVFilterRow = SharpYUVFilterRow_NEON;
-}
-
 #else  // !WEBP_USE_NEON
 
 WEBP_DSP_INIT_STUB(WebPInitConvertARGBToYUVNEON)
-WEBP_DSP_INIT_STUB(WebPInitSharpYUVNEON)
 
 #endif  // WEBP_USE_NEON
diff --git a/src/dsp/yuv_sse2.c b/src/dsp/yuv_sse2.c
index baa48d5..01a48f9 100644
--- a/src/dsp/yuv_sse2.c
+++ b/src/dsp/yuv_sse2.c
@@ -15,10 +15,12 @@
 
 #if defined(WEBP_USE_SSE2)
 
-#include "src/dsp/common_sse2.h"
 #include <stdlib.h>
 #include <emmintrin.h>
 
+#include "src/dsp/common_sse2.h"
+#include "src/utils/utils.h"
+
 //-----------------------------------------------------------------------------
 // Convert spans of 32 pixels to various RGB formats for the fancy upsampler.
 
@@ -74,7 +76,7 @@
 // Load and replicate the U/V samples
 static WEBP_INLINE __m128i Load_UV_HI_8_SSE2(const uint8_t* src) {
   const __m128i zero = _mm_setzero_si128();
-  const __m128i tmp0 = _mm_cvtsi32_si128(*(const uint32_t*)src);
+  const __m128i tmp0 = _mm_cvtsi32_si128(WebPMemToInt32(src));
   const __m128i tmp1 = _mm_unpacklo_epi8(zero, tmp0);
   return _mm_unpacklo_epi16(tmp1, tmp1);   // replicate samples
 }
@@ -130,7 +132,7 @@
   const __m128i rg0 = _mm_packus_epi16(*B, *A);
   const __m128i ba0 = _mm_packus_epi16(*R, *G);
 #endif
-  const __m128i mask_0xf0 = _mm_set1_epi8(0xf0);
+  const __m128i mask_0xf0 = _mm_set1_epi8((char)0xf0);
   const __m128i rb1 = _mm_unpacklo_epi8(rg0, ba0);  // rbrbrbrbrb...
   const __m128i ga1 = _mm_unpackhi_epi8(rg0, ba0);  // gagagagaga...
   const __m128i rb2 = _mm_and_si128(rb1, mask_0xf0);
@@ -147,9 +149,10 @@
   const __m128i r0 = _mm_packus_epi16(*R, *R);
   const __m128i g0 = _mm_packus_epi16(*G, *G);
   const __m128i b0 = _mm_packus_epi16(*B, *B);
-  const __m128i r1 = _mm_and_si128(r0, _mm_set1_epi8(0xf8));
+  const __m128i r1 = _mm_and_si128(r0, _mm_set1_epi8((char)0xf8));
   const __m128i b1 = _mm_and_si128(_mm_srli_epi16(b0, 3), _mm_set1_epi8(0x1f));
-  const __m128i g1 = _mm_srli_epi16(_mm_and_si128(g0, _mm_set1_epi8(0xe0)), 5);
+  const __m128i g1 =
+      _mm_srli_epi16(_mm_and_si128(g0, _mm_set1_epi8((char)0xe0)), 5);
   const __m128i g2 = _mm_slli_epi16(_mm_and_si128(g0, _mm_set1_epi8(0x1c)), 3);
   const __m128i rg = _mm_or_si128(r1, g1);
   const __m128i gb = _mm_or_si128(g2, b1);
@@ -747,128 +750,9 @@
   WebPConvertRGBA32ToUV = ConvertRGBA32ToUV_SSE2;
 }
 
-//------------------------------------------------------------------------------
-
-#define MAX_Y ((1 << 10) - 1)    // 10b precision over 16b-arithmetic
-static uint16_t clip_y(int v) {
-  return (v < 0) ? 0 : (v > MAX_Y) ? MAX_Y : (uint16_t)v;
-}
-
-static uint64_t SharpYUVUpdateY_SSE2(const uint16_t* ref, const uint16_t* src,
-                                     uint16_t* dst, int len) {
-  uint64_t diff = 0;
-  uint32_t tmp[4];
-  int i;
-  const __m128i zero = _mm_setzero_si128();
-  const __m128i max = _mm_set1_epi16(MAX_Y);
-  const __m128i one = _mm_set1_epi16(1);
-  __m128i sum = zero;
-
-  for (i = 0; i + 8 <= len; i += 8) {
-    const __m128i A = _mm_loadu_si128((const __m128i*)(ref + i));
-    const __m128i B = _mm_loadu_si128((const __m128i*)(src + i));
-    const __m128i C = _mm_loadu_si128((const __m128i*)(dst + i));
-    const __m128i D = _mm_sub_epi16(A, B);       // diff_y
-    const __m128i E = _mm_cmpgt_epi16(zero, D);  // sign (-1 or 0)
-    const __m128i F = _mm_add_epi16(C, D);       // new_y
-    const __m128i G = _mm_or_si128(E, one);      // -1 or 1
-    const __m128i H = _mm_max_epi16(_mm_min_epi16(F, max), zero);
-    const __m128i I = _mm_madd_epi16(D, G);      // sum(abs(...))
-    _mm_storeu_si128((__m128i*)(dst + i), H);
-    sum = _mm_add_epi32(sum, I);
-  }
-  _mm_storeu_si128((__m128i*)tmp, sum);
-  diff = tmp[3] + tmp[2] + tmp[1] + tmp[0];
-  for (; i < len; ++i) {
-    const int diff_y = ref[i] - src[i];
-    const int new_y = (int)dst[i] + diff_y;
-    dst[i] = clip_y(new_y);
-    diff += (uint64_t)abs(diff_y);
-  }
-  return diff;
-}
-
-static void SharpYUVUpdateRGB_SSE2(const int16_t* ref, const int16_t* src,
-                                   int16_t* dst, int len) {
-  int i = 0;
-  for (i = 0; i + 8 <= len; i += 8) {
-    const __m128i A = _mm_loadu_si128((const __m128i*)(ref + i));
-    const __m128i B = _mm_loadu_si128((const __m128i*)(src + i));
-    const __m128i C = _mm_loadu_si128((const __m128i*)(dst + i));
-    const __m128i D = _mm_sub_epi16(A, B);   // diff_uv
-    const __m128i E = _mm_add_epi16(C, D);   // new_uv
-    _mm_storeu_si128((__m128i*)(dst + i), E);
-  }
-  for (; i < len; ++i) {
-    const int diff_uv = ref[i] - src[i];
-    dst[i] += diff_uv;
-  }
-}
-
-static void SharpYUVFilterRow_SSE2(const int16_t* A, const int16_t* B, int len,
-                                   const uint16_t* best_y, uint16_t* out) {
-  int i;
-  const __m128i kCst8 = _mm_set1_epi16(8);
-  const __m128i max = _mm_set1_epi16(MAX_Y);
-  const __m128i zero = _mm_setzero_si128();
-  for (i = 0; i + 8 <= len; i += 8) {
-    const __m128i a0 = _mm_loadu_si128((const __m128i*)(A + i + 0));
-    const __m128i a1 = _mm_loadu_si128((const __m128i*)(A + i + 1));
-    const __m128i b0 = _mm_loadu_si128((const __m128i*)(B + i + 0));
-    const __m128i b1 = _mm_loadu_si128((const __m128i*)(B + i + 1));
-    const __m128i a0b1 = _mm_add_epi16(a0, b1);
-    const __m128i a1b0 = _mm_add_epi16(a1, b0);
-    const __m128i a0a1b0b1 = _mm_add_epi16(a0b1, a1b0);  // A0+A1+B0+B1
-    const __m128i a0a1b0b1_8 = _mm_add_epi16(a0a1b0b1, kCst8);
-    const __m128i a0b1_2 = _mm_add_epi16(a0b1, a0b1);    // 2*(A0+B1)
-    const __m128i a1b0_2 = _mm_add_epi16(a1b0, a1b0);    // 2*(A1+B0)
-    const __m128i c0 = _mm_srai_epi16(_mm_add_epi16(a0b1_2, a0a1b0b1_8), 3);
-    const __m128i c1 = _mm_srai_epi16(_mm_add_epi16(a1b0_2, a0a1b0b1_8), 3);
-    const __m128i d0 = _mm_add_epi16(c1, a0);
-    const __m128i d1 = _mm_add_epi16(c0, a1);
-    const __m128i e0 = _mm_srai_epi16(d0, 1);
-    const __m128i e1 = _mm_srai_epi16(d1, 1);
-    const __m128i f0 = _mm_unpacklo_epi16(e0, e1);
-    const __m128i f1 = _mm_unpackhi_epi16(e0, e1);
-    const __m128i g0 = _mm_loadu_si128((const __m128i*)(best_y + 2 * i + 0));
-    const __m128i g1 = _mm_loadu_si128((const __m128i*)(best_y + 2 * i + 8));
-    const __m128i h0 = _mm_add_epi16(g0, f0);
-    const __m128i h1 = _mm_add_epi16(g1, f1);
-    const __m128i i0 = _mm_max_epi16(_mm_min_epi16(h0, max), zero);
-    const __m128i i1 = _mm_max_epi16(_mm_min_epi16(h1, max), zero);
-    _mm_storeu_si128((__m128i*)(out + 2 * i + 0), i0);
-    _mm_storeu_si128((__m128i*)(out + 2 * i + 8), i1);
-  }
-  for (; i < len; ++i) {
-    //   (9 * A0 + 3 * A1 + 3 * B0 + B1 + 8) >> 4 =
-    // = (8 * A0 + 2 * (A1 + B0) + (A0 + A1 + B0 + B1 + 8)) >> 4
-    // We reuse the common sub-expressions.
-    const int a0b1 = A[i + 0] + B[i + 1];
-    const int a1b0 = A[i + 1] + B[i + 0];
-    const int a0a1b0b1 = a0b1 + a1b0 + 8;
-    const int v0 = (8 * A[i + 0] + 2 * a1b0 + a0a1b0b1) >> 4;
-    const int v1 = (8 * A[i + 1] + 2 * a0b1 + a0a1b0b1) >> 4;
-    out[2 * i + 0] = clip_y(best_y[2 * i + 0] + v0);
-    out[2 * i + 1] = clip_y(best_y[2 * i + 1] + v1);
-  }
-}
-
-#undef MAX_Y
-
-//------------------------------------------------------------------------------
-
-extern void WebPInitSharpYUVSSE2(void);
-
-WEBP_TSAN_IGNORE_FUNCTION void WebPInitSharpYUVSSE2(void) {
-  WebPSharpYUVUpdateY = SharpYUVUpdateY_SSE2;
-  WebPSharpYUVUpdateRGB = SharpYUVUpdateRGB_SSE2;
-  WebPSharpYUVFilterRow = SharpYUVFilterRow_SSE2;
-}
-
 #else  // !WEBP_USE_SSE2
 
 WEBP_DSP_INIT_STUB(WebPInitSamplersSSE2)
 WEBP_DSP_INIT_STUB(WebPInitConvertARGBToYUVSSE2)
-WEBP_DSP_INIT_STUB(WebPInitSharpYUVSSE2)
 
 #endif  // WEBP_USE_SSE2
diff --git a/src/dsp/yuv_sse41.c b/src/dsp/yuv_sse41.c
index 579d1f7..f79b802 100644
--- a/src/dsp/yuv_sse41.c
+++ b/src/dsp/yuv_sse41.c
@@ -15,10 +15,12 @@
 
 #if defined(WEBP_USE_SSE41)
 
-#include "src/dsp/common_sse41.h"
 #include <stdlib.h>
 #include <smmintrin.h>
 
+#include "src/dsp/common_sse41.h"
+#include "src/utils/utils.h"
+
 //-----------------------------------------------------------------------------
 // Convert spans of 32 pixels to various RGB formats for the fancy upsampler.
 
@@ -74,7 +76,7 @@
 // Load and replicate the U/V samples
 static WEBP_INLINE __m128i Load_UV_HI_8_SSE41(const uint8_t* src) {
   const __m128i zero = _mm_setzero_si128();
-  const __m128i tmp0 = _mm_cvtsi32_si128(*(const uint32_t*)src);
+  const __m128i tmp0 = _mm_cvtsi32_si128(WebPMemToInt32(src));
   const __m128i tmp1 = _mm_unpacklo_epi8(zero, tmp0);
   return _mm_unpacklo_epi16(tmp1, tmp1);   // replicate samples
 }
diff --git a/src/enc/Makefile.am b/src/enc/Makefile.am
new file mode 100644
index 0000000..2fec804
--- /dev/null
+++ b/src/enc/Makefile.am
@@ -0,0 +1,43 @@
+AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir)
+noinst_LTLIBRARIES = libwebpencode.la
+
+libwebpencode_la_SOURCES =
+libwebpencode_la_SOURCES += alpha_enc.c
+libwebpencode_la_SOURCES += analysis_enc.c
+libwebpencode_la_SOURCES += backward_references_cost_enc.c
+libwebpencode_la_SOURCES += backward_references_enc.c
+libwebpencode_la_SOURCES += backward_references_enc.h
+libwebpencode_la_SOURCES += config_enc.c
+libwebpencode_la_SOURCES += cost_enc.c
+libwebpencode_la_SOURCES += cost_enc.h
+libwebpencode_la_SOURCES += filter_enc.c
+libwebpencode_la_SOURCES += frame_enc.c
+libwebpencode_la_SOURCES += histogram_enc.c
+libwebpencode_la_SOURCES += histogram_enc.h
+libwebpencode_la_SOURCES += iterator_enc.c
+libwebpencode_la_SOURCES += near_lossless_enc.c
+libwebpencode_la_SOURCES += picture_enc.c
+libwebpencode_la_SOURCES += picture_csp_enc.c
+libwebpencode_la_SOURCES += picture_psnr_enc.c
+libwebpencode_la_SOURCES += picture_rescale_enc.c
+libwebpencode_la_SOURCES += picture_tools_enc.c
+libwebpencode_la_SOURCES += predictor_enc.c
+libwebpencode_la_SOURCES += quant_enc.c
+libwebpencode_la_SOURCES += syntax_enc.c
+libwebpencode_la_SOURCES += token_enc.c
+libwebpencode_la_SOURCES += tree_enc.c
+libwebpencode_la_SOURCES += vp8i_enc.h
+libwebpencode_la_SOURCES += vp8l_enc.c
+libwebpencode_la_SOURCES += vp8li_enc.h
+libwebpencode_la_SOURCES += webp_enc.c
+
+libwebpencodeinclude_HEADERS =
+libwebpencodeinclude_HEADERS += ../webp/encode.h
+libwebpencodeinclude_HEADERS += ../webp/types.h
+noinst_HEADERS =
+noinst_HEADERS += ../webp/format_constants.h
+
+libwebpencode_la_LIBADD = ../../sharpyuv/libsharpyuv.la
+libwebpencode_la_LDFLAGS = -lm
+libwebpencode_la_CPPFLAGS = $(AM_CPPFLAGS)
+libwebpencodeincludedir = $(includedir)/webp
diff --git a/src/enc/alpha_enc.c b/src/enc/alpha_enc.c
index 0b54f3e..7d20558 100644
--- a/src/enc/alpha_enc.c
+++ b/src/enc/alpha_enc.c
@@ -13,6 +13,7 @@
 
 #include <assert.h>
 #include <stdlib.h>
+#include <string.h>
 
 #include "src/enc/vp8i_enc.h"
 #include "src/dsp/dsp.h"
@@ -86,7 +87,7 @@
   // a decoder bug related to alpha with color cache.
   // See: https://code.google.com/p/webp/issues/detail?id=239
   // Need to re-enable this later.
-  ok = (VP8LEncodeStream(&config, &picture, bw, 0 /*use_cache*/) == VP8_ENC_OK);
+  ok = VP8LEncodeStream(&config, &picture, bw, /*use_cache=*/0);
   WebPPictureFree(&picture);
   ok = ok && !bw->error_;
   if (!ok) {
@@ -148,6 +149,7 @@
       }
     } else {
       VP8LBitWriterWipeOut(&tmp_bw);
+      memset(&result->bw, 0, sizeof(result->bw));
       return 0;
     }
   }
@@ -162,7 +164,7 @@
   header = method | (filter << 2);
   if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4;
 
-  VP8BitWriterInit(&result->bw, ALPHA_HEADER_LEN + output_size);
+  if (!VP8BitWriterInit(&result->bw, ALPHA_HEADER_LEN + output_size)) ok = 0;
   ok = ok && VP8BitWriterAppend(&result->bw, &header, ALPHA_HEADER_LEN);
   ok = ok && VP8BitWriterAppend(&result->bw, output, output_size);
 
diff --git a/src/enc/analysis_enc.c b/src/enc/analysis_enc.c
index ebb7842..a0001ac 100644
--- a/src/enc/analysis_enc.c
+++ b/src/enc/analysis_enc.c
@@ -391,12 +391,14 @@
   return ok;
 }
 
+#ifdef WEBP_USE_THREAD
 static void MergeJobs(const SegmentJob* const src, SegmentJob* const dst) {
   int i;
   for (i = 0; i <= MAX_ALPHA; ++i) dst->alphas[i] += src->alphas[i];
   dst->alpha += src->alpha;
   dst->uv_alpha += src->uv_alpha;
 }
+#endif
 
 // initialize the job struct with some tasks to perform
 static void InitSegmentJob(VP8Encoder* const enc, SegmentJob* const job,
@@ -425,10 +427,10 @@
       (enc->method_ <= 1);  // for method 0 - 1, we need preds_[] to be filled.
   if (do_segments) {
     const int last_row = enc->mb_h_;
-    // We give a little more than a half work to the main thread.
-    const int split_row = (9 * last_row + 15) >> 4;
     const int total_mb = last_row * enc->mb_w_;
 #ifdef WEBP_USE_THREAD
+    // We give a little more than a half work to the main thread.
+    const int split_row = (9 * last_row + 15) >> 4;
     const int kMinSplitRow = 2;  // minimal rows needed for mt to be worth it
     const int do_mt = (enc->thread_level_ > 0) && (split_row >= kMinSplitRow);
 #else
@@ -438,6 +440,7 @@
         WebPGetWorkerInterface();
     SegmentJob main_job;
     if (do_mt) {
+#ifdef WEBP_USE_THREAD
       SegmentJob side_job;
       // Note the use of '&' instead of '&&' because we must call the functions
       // no matter what.
@@ -455,6 +458,7 @@
       }
       worker_interface->End(&side_job.worker);
       if (ok) MergeJobs(&side_job, &main_job);  // merge results together
+#endif  // WEBP_USE_THREAD
     } else {
       // Even for single-thread case, we use the generic Worker tools.
       InitSegmentJob(enc, &main_job, 0, last_row);
diff --git a/src/enc/backward_references_cost_enc.c b/src/enc/backward_references_cost_enc.c
index 5eb24d4..6968ef3 100644
--- a/src/enc/backward_references_cost_enc.c
+++ b/src/enc/backward_references_cost_enc.c
@@ -15,10 +15,11 @@
 //
 
 #include <assert.h>
+#include <float.h>
 
+#include "src/dsp/lossless_common.h"
 #include "src/enc/backward_references_enc.h"
 #include "src/enc/histogram_enc.h"
-#include "src/dsp/lossless_common.h"
 #include "src/utils/color_cache_utils.h"
 #include "src/utils/utils.h"
 
@@ -30,15 +31,15 @@
                                       const PixOrCopy v);
 
 typedef struct {
-  double alpha_[VALUES_IN_BYTE];
-  double red_[VALUES_IN_BYTE];
-  double blue_[VALUES_IN_BYTE];
-  double distance_[NUM_DISTANCE_CODES];
-  double* literal_;
+  float alpha_[VALUES_IN_BYTE];
+  float red_[VALUES_IN_BYTE];
+  float blue_[VALUES_IN_BYTE];
+  float distance_[NUM_DISTANCE_CODES];
+  float* literal_;
 } CostModel;
 
 static void ConvertPopulationCountTableToBitEstimates(
-    int num_symbols, const uint32_t population_counts[], double output[]) {
+    int num_symbols, const uint32_t population_counts[], float output[]) {
   uint32_t sum = 0;
   int nonzeros = 0;
   int i;
@@ -51,7 +52,7 @@
   if (nonzeros <= 1) {
     memset(output, 0, num_symbols * sizeof(*output));
   } else {
-    const double logsum = VP8LFastLog2(sum);
+    const float logsum = VP8LFastLog2(sum);
     for (i = 0; i < num_symbols; ++i) {
       output[i] = logsum - VP8LFastLog2(population_counts[i]);
     }
@@ -75,8 +76,8 @@
   }
 
   ConvertPopulationCountTableToBitEstimates(
-      VP8LHistogramNumCodes(histo->palette_code_bits_),
-      histo->literal_, m->literal_);
+      VP8LHistogramNumCodes(histo->palette_code_bits_), histo->literal_,
+      m->literal_);
   ConvertPopulationCountTableToBitEstimates(
       VALUES_IN_BYTE, histo->red_, m->red_);
   ConvertPopulationCountTableToBitEstimates(
@@ -92,27 +93,27 @@
   return ok;
 }
 
-static WEBP_INLINE double GetLiteralCost(const CostModel* const m, uint32_t v) {
+static WEBP_INLINE float GetLiteralCost(const CostModel* const m, uint32_t v) {
   return m->alpha_[v >> 24] +
          m->red_[(v >> 16) & 0xff] +
          m->literal_[(v >> 8) & 0xff] +
          m->blue_[v & 0xff];
 }
 
-static WEBP_INLINE double GetCacheCost(const CostModel* const m, uint32_t idx) {
+static WEBP_INLINE float GetCacheCost(const CostModel* const m, uint32_t idx) {
   const int literal_idx = VALUES_IN_BYTE + NUM_LENGTH_CODES + idx;
   return m->literal_[literal_idx];
 }
 
-static WEBP_INLINE double GetLengthCost(const CostModel* const m,
-                                        uint32_t length) {
+static WEBP_INLINE float GetLengthCost(const CostModel* const m,
+                                       uint32_t length) {
   int code, extra_bits;
   VP8LPrefixEncodeBits(length, &code, &extra_bits);
   return m->literal_[VALUES_IN_BYTE + code] + extra_bits;
 }
 
-static WEBP_INLINE double GetDistanceCost(const CostModel* const m,
-                                          uint32_t distance) {
+static WEBP_INLINE float GetDistanceCost(const CostModel* const m,
+                                         uint32_t distance) {
   int code, extra_bits;
   VP8LPrefixEncodeBits(distance, &code, &extra_bits);
   return m->distance_[code] + extra_bits;
@@ -122,20 +123,20 @@
     const uint32_t* const argb, VP8LColorCache* const hashers,
     const CostModel* const cost_model, int idx, int use_color_cache,
     float prev_cost, float* const cost, uint16_t* const dist_array) {
-  double cost_val = prev_cost;
+  float cost_val = prev_cost;
   const uint32_t color = argb[idx];
   const int ix = use_color_cache ? VP8LColorCacheContains(hashers, color) : -1;
   if (ix >= 0) {
     // use_color_cache is true and hashers contains color
-    const double mul0 = 0.68;
+    const float mul0 = 0.68f;
     cost_val += GetCacheCost(cost_model, ix) * mul0;
   } else {
-    const double mul1 = 0.82;
+    const float mul1 = 0.82f;
     if (use_color_cache) VP8LColorCacheInsert(hashers, color);
     cost_val += GetLiteralCost(cost_model, color) * mul1;
   }
   if (cost[idx] > cost_val) {
-    cost[idx] = (float)cost_val;
+    cost[idx] = cost_val;
     dist_array[idx] = 1;  // only one is inserted.
   }
 }
@@ -172,7 +173,7 @@
 
 // The GetLengthCost(cost_model, k) are cached in a CostCacheInterval.
 typedef struct {
-  double cost_;
+  float cost_;
   int start_;
   int end_;       // Exclusive.
 } CostCacheInterval;
@@ -187,7 +188,7 @@
   int count_;  // The number of stored intervals.
   CostCacheInterval* cache_intervals_;
   size_t cache_intervals_size_;
-  double cost_cache_[MAX_LENGTH];  // Contains the GetLengthCost(cost_model, k).
+  float cost_cache_[MAX_LENGTH];  // Contains the GetLengthCost(cost_model, k).
   float* costs_;
   uint16_t* dist_array_;
   // Most of the time, we only need few intervals -> use a free-list, to avoid
@@ -262,10 +263,13 @@
   CostManagerInitFreeList(manager);
 
   // Fill in the cost_cache_.
-  manager->cache_intervals_size_ = 1;
-  manager->cost_cache_[0] = GetLengthCost(cost_model, 0);
-  for (i = 1; i < cost_cache_size; ++i) {
+  // Has to be done in two passes due to a GCC bug on i686
+  // related to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=323
+  for (i = 0; i < cost_cache_size; ++i) {
     manager->cost_cache_[i] = GetLengthCost(cost_model, i);
+  }
+  manager->cache_intervals_size_ = 1;
+  for (i = 1; i < cost_cache_size; ++i) {
     // Get the number of bound intervals.
     if (manager->cost_cache_[i] != manager->cost_cache_[i - 1]) {
       ++manager->cache_intervals_size_;
@@ -294,7 +298,7 @@
     cur->end_ = 1;
     cur->cost_ = manager->cost_cache_[0];
     for (i = 1; i < cost_cache_size; ++i) {
-      const double cost_val = manager->cost_cache_[i];
+      const float cost_val = manager->cost_cache_[i];
       if (cost_val != cur->cost_) {
         ++cur;
         // Initialize an interval.
@@ -303,6 +307,8 @@
       }
       cur->end_ = i + 1;
     }
+    assert((size_t)(cur - manager->cache_intervals_) + 1 ==
+           manager->cache_intervals_size_);
   }
 
   manager->costs_ = (float*)WebPSafeMalloc(pix_count, sizeof(*manager->costs_));
@@ -311,7 +317,7 @@
     return 0;
   }
   // Set the initial costs_ high for every pixel as we will keep the minimum.
-  for (i = 0; i < pix_count; ++i) manager->costs_[i] = 1e38f;
+  for (i = 0; i < pix_count; ++i) manager->costs_[i] = FLT_MAX;
 
   return 1;
 }
@@ -457,7 +463,7 @@
 // If handling the interval or one of its subintervals becomes to heavy, its
 // contribution is added to the costs right away.
 static WEBP_INLINE void PushInterval(CostManager* const manager,
-                                     double distance_cost, int position,
+                                     float distance_cost, int position,
                                      int len) {
   size_t i;
   CostInterval* interval = manager->head_;
@@ -474,7 +480,7 @@
       const int k = j - position;
       float cost_tmp;
       assert(k >= 0 && k < MAX_LENGTH);
-      cost_tmp = (float)(distance_cost + manager->cost_cache_[k]);
+      cost_tmp = distance_cost + manager->cost_cache_[k];
 
       if (manager->costs_[j] > cost_tmp) {
         manager->costs_[j] = cost_tmp;
@@ -492,7 +498,7 @@
     const int end = position + (cost_cache_intervals[i].end_ > len
                                  ? len
                                  : cost_cache_intervals[i].end_);
-    const float cost = (float)(distance_cost + cost_cache_intervals[i].cost_);
+    const float cost = distance_cost + cost_cache_intervals[i].cost_;
 
     for (; interval != NULL && interval->start_ < end;
          interval = interval_next) {
@@ -570,8 +576,7 @@
   const int pix_count = xsize * ysize;
   const int use_color_cache = (cache_bits > 0);
   const size_t literal_array_size =
-      sizeof(double) * (NUM_LITERAL_CODES + NUM_LENGTH_CODES +
-                        ((cache_bits > 0) ? (1 << cache_bits) : 0));
+      sizeof(float) * (VP8LHistogramNumCodes(cache_bits));
   const size_t cost_model_size = sizeof(CostModel) + literal_array_size;
   CostModel* const cost_model =
       (CostModel*)WebPSafeCalloc(1ULL, cost_model_size);
@@ -579,13 +584,13 @@
   CostManager* cost_manager =
       (CostManager*)WebPSafeCalloc(1ULL, sizeof(*cost_manager));
   int offset_prev = -1, len_prev = -1;
-  double offset_cost = -1;
+  float offset_cost = -1.f;
   int first_offset_is_constant = -1;  // initialized with 'impossible' value
   int reach = 0;
 
   if (cost_model == NULL || cost_manager == NULL) goto Error;
 
-  cost_model->literal_ = (double*)(cost_model + 1);
+  cost_model->literal_ = (float*)(cost_model + 1);
   if (use_color_cache) {
     cc_init = VP8LColorCacheInit(&hashers, cache_bits);
     if (!cc_init) goto Error;
@@ -675,7 +680,7 @@
   }
 
   ok = !refs->error_;
-Error:
+ Error:
   if (cc_init) VP8LColorCacheClear(&hashers);
   CostManagerClear(cost_manager);
   WebPSafeFree(cost_model);
diff --git a/src/enc/backward_references_enc.c b/src/enc/backward_references_enc.c
index d5e931e..49a0fac 100644
--- a/src/enc/backward_references_enc.c
+++ b/src/enc/backward_references_enc.c
@@ -10,6 +10,8 @@
 // Author: Jyrki Alakuijala (jyrki@google.com)
 //
 
+#include "src/enc/backward_references_enc.h"
+
 #include <assert.h>
 #include <float.h>
 #include <math.h>
@@ -17,10 +19,11 @@
 #include "src/dsp/dsp.h"
 #include "src/dsp/lossless.h"
 #include "src/dsp/lossless_common.h"
-#include "src/enc/backward_references_enc.h"
 #include "src/enc/histogram_enc.h"
+#include "src/enc/vp8i_enc.h"
 #include "src/utils/color_cache_utils.h"
 #include "src/utils/utils.h"
+#include "src/webp/encode.h"
 
 #define MIN_BLOCK_SIZE 256  // minimum block size for backward references
 
@@ -255,10 +258,13 @@
 
 int VP8LHashChainFill(VP8LHashChain* const p, int quality,
                       const uint32_t* const argb, int xsize, int ysize,
-                      int low_effort) {
+                      int low_effort, const WebPPicture* const pic,
+                      int percent_range, int* const percent) {
   const int size = xsize * ysize;
   const int iter_max = GetMaxItersForQuality(quality);
   const uint32_t window_size = GetWindowSizeForHashChain(quality, xsize);
+  int remaining_percent = percent_range;
+  int percent_start = *percent;
   int pos;
   int argb_comp;
   uint32_t base_position;
@@ -276,7 +282,13 @@
 
   hash_to_first_index =
       (int32_t*)WebPSafeMalloc(HASH_SIZE, sizeof(*hash_to_first_index));
-  if (hash_to_first_index == NULL) return 0;
+  if (hash_to_first_index == NULL) {
+    WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+    return 0;
+  }
+
+  percent_range = remaining_percent / 2;
+  remaining_percent -= percent_range;
 
   // Set the int32_t array to -1.
   memset(hash_to_first_index, 0xff, HASH_SIZE * sizeof(*hash_to_first_index));
@@ -323,12 +335,22 @@
       hash_to_first_index[hash_code] = pos++;
       argb_comp = argb_comp_next;
     }
+
+    if (!WebPReportProgress(
+            pic, percent_start + percent_range * pos / (size - 2), percent)) {
+      WebPSafeFree(hash_to_first_index);
+      return 0;
+    }
   }
   // Process the penultimate pixel.
   chain[pos] = hash_to_first_index[GetPixPairHash64(argb + pos)];
 
   WebPSafeFree(hash_to_first_index);
 
+  percent_start += percent_range;
+  if (!WebPReportProgress(pic, percent_start, percent)) return 0;
+  percent_range = remaining_percent;
+
   // Find the best match interval at each pixel, defined by an offset to the
   // pixel and a length. The right-most pixel cannot match anything to the right
   // (hence a best length of 0) and the left-most pixel nothing to the left
@@ -417,8 +439,17 @@
         max_base_position = base_position;
       }
     }
+
+    if (!WebPReportProgress(pic,
+                            percent_start + percent_range *
+                                                (size - 2 - base_position) /
+                                                (size - 2),
+                            percent)) {
+      return 0;
+    }
   }
-  return 1;
+
+  return WebPReportProgress(pic, percent_start + percent_range, percent);
 }
 
 static WEBP_INLINE void AddSingleLiteral(uint32_t pixel, int use_color_cache,
@@ -728,7 +759,7 @@
                                   int* const best_cache_bits) {
   int i;
   const int cache_bits_max = (quality <= 25) ? 0 : *best_cache_bits;
-  double entropy_min = MAX_ENTROPY;
+  float entropy_min = MAX_ENTROPY;
   int cc_init[MAX_COLOR_CACHE_BITS + 1] = { 0 };
   VP8LColorCache hashers[MAX_COLOR_CACHE_BITS + 1];
   VP8LRefsCursor c = VP8LRefsCursorInit(refs);
@@ -813,14 +844,14 @@
   }
 
   for (i = 0; i <= cache_bits_max; ++i) {
-    const double entropy = VP8LHistogramEstimateBits(histos[i]);
+    const float entropy = VP8LHistogramEstimateBits(histos[i]);
     if (i == 0 || entropy < entropy_min) {
       entropy_min = entropy;
       *best_cache_bits = i;
     }
   }
   ok = 1;
-Error:
+ Error:
   for (i = 0; i <= cache_bits_max; ++i) {
     if (cc_init[i]) VP8LColorCacheClear(&hashers[i]);
     VP8LFreeHistogram(histos[i]);
@@ -890,7 +921,7 @@
   int i, lz77_type;
   // Index 0 is for a color cache, index 1 for no cache (if needed).
   int lz77_types_best[2] = {0, 0};
-  double bit_costs_best[2] = {DBL_MAX, DBL_MAX};
+  float bit_costs_best[2] = {FLT_MAX, FLT_MAX};
   VP8LHashChain hash_chain_box;
   VP8LBackwardRefs* const refs_tmp = &refs[do_no_cache ? 2 : 1];
   int status = 0;
@@ -902,7 +933,7 @@
   for (lz77_type = 1; lz77_types_to_try;
        lz77_types_to_try &= ~lz77_type, lz77_type <<= 1) {
     int res = 0;
-    double bit_cost = 0.;
+    float bit_cost = 0.f;
     if ((lz77_types_to_try & lz77_type) == 0) continue;
     switch (lz77_type) {
       case kLZ77RLE:
@@ -976,7 +1007,7 @@
       const VP8LHashChain* const hash_chain_tmp =
           (lz77_types_best[i] == kLZ77Standard) ? hash_chain : &hash_chain_box;
       const int cache_bits = (i == 1) ? 0 : *cache_bits_best;
-      double bit_cost_trace;
+      float bit_cost_trace;
       if (!VP8LBackwardReferencesTraceBackwards(width, height, argb, cache_bits,
                                                 hash_chain_tmp, &refs[i],
                                                 refs_tmp)) {
@@ -1001,31 +1032,37 @@
   }
   status = 1;
 
-Error:
+ Error:
   VP8LHashChainClear(&hash_chain_box);
   VP8LFreeHistogram(histo);
   return status;
 }
 
-WebPEncodingError VP8LGetBackwardReferences(
+int VP8LGetBackwardReferences(
     int width, int height, const uint32_t* const argb, int quality,
     int low_effort, int lz77_types_to_try, int cache_bits_max, int do_no_cache,
     const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs,
-    int* const cache_bits_best) {
+    int* const cache_bits_best, const WebPPicture* const pic, int percent_range,
+    int* const percent) {
   if (low_effort) {
     VP8LBackwardRefs* refs_best;
     *cache_bits_best = cache_bits_max;
     refs_best = GetBackwardReferencesLowEffort(
         width, height, argb, cache_bits_best, hash_chain, refs);
-    if (refs_best == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
+    if (refs_best == NULL) {
+      WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+      return 0;
+    }
     // Set it in first position.
     BackwardRefsSwap(refs_best, &refs[0]);
   } else {
     if (!GetBackwardReferences(width, height, argb, quality, lz77_types_to_try,
                                cache_bits_max, do_no_cache, hash_chain, refs,
                                cache_bits_best)) {
-      return VP8_ENC_ERROR_OUT_OF_MEMORY;
+      WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+      return 0;
     }
   }
-  return VP8_ENC_OK;
+
+  return WebPReportProgress(pic, *percent + percent_range, percent);
 }
diff --git a/src/enc/backward_references_enc.h b/src/enc/backward_references_enc.h
index 4c0267b..4dff1c2 100644
--- a/src/enc/backward_references_enc.h
+++ b/src/enc/backward_references_enc.h
@@ -134,10 +134,11 @@
 
 // Must be called first, to set size.
 int VP8LHashChainInit(VP8LHashChain* const p, int size);
-// Pre-compute the best matches for argb.
+// Pre-compute the best matches for argb. pic and percent are for progress.
 int VP8LHashChainFill(VP8LHashChain* const p, int quality,
                       const uint32_t* const argb, int xsize, int ysize,
-                      int low_effort);
+                      int low_effort, const WebPPicture* const pic,
+                      int percent_range, int* const percent);
 void VP8LHashChainClear(VP8LHashChain* const p);  // release memory
 
 static WEBP_INLINE int VP8LHashChainFindOffset(const VP8LHashChain* const p,
@@ -227,11 +228,14 @@
 // VP8LBackwardRefs is put in the first element, the best value with no-cache in
 // the second element.
 // In both cases, the last element is used as temporary internally.
-WebPEncodingError VP8LGetBackwardReferences(
+// pic and percent are for progress.
+// Returns false in case of error (stored in pic->error_code).
+int VP8LGetBackwardReferences(
     int width, int height, const uint32_t* const argb, int quality,
     int low_effort, int lz77_types_to_try, int cache_bits_max, int do_no_cache,
     const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs,
-    int* const cache_bits_best);
+    int* const cache_bits_best, const WebPPicture* const pic, int percent_range,
+    int* const percent);
 
 #ifdef __cplusplus
 }
diff --git a/src/enc/histogram_enc.c b/src/enc/histogram_enc.c
index 38a0ceb..8418def 100644
--- a/src/enc/histogram_enc.c
+++ b/src/enc/histogram_enc.c
@@ -13,15 +13,17 @@
 #include "src/webp/config.h"
 #endif
 
+#include <float.h>
 #include <math.h>
 
-#include "src/enc/backward_references_enc.h"
-#include "src/enc/histogram_enc.h"
 #include "src/dsp/lossless.h"
 #include "src/dsp/lossless_common.h"
+#include "src/enc/backward_references_enc.h"
+#include "src/enc/histogram_enc.h"
+#include "src/enc/vp8i_enc.h"
 #include "src/utils/utils.h"
 
-#define MAX_COST 1.e38
+#define MAX_BIT_COST FLT_MAX
 
 // Number of partitions for the three dominant (literal, red and blue) symbol
 // costs.
@@ -228,8 +230,8 @@
 // -----------------------------------------------------------------------------
 // Entropy-related functions.
 
-static WEBP_INLINE double BitsEntropyRefine(const VP8LBitEntropy* entropy) {
-  double mix;
+static WEBP_INLINE float BitsEntropyRefine(const VP8LBitEntropy* entropy) {
+  float mix;
   if (entropy->nonzeros < 5) {
     if (entropy->nonzeros <= 1) {
       return 0;
@@ -238,67 +240,67 @@
     // Let's mix in a bit of entropy to favor good clustering when
     // distributions of these are combined.
     if (entropy->nonzeros == 2) {
-      return 0.99 * entropy->sum + 0.01 * entropy->entropy;
+      return 0.99f * entropy->sum + 0.01f * entropy->entropy;
     }
     // No matter what the entropy says, we cannot be better than min_limit
     // with Huffman coding. I am mixing a bit of entropy into the
     // min_limit since it produces much better (~0.5 %) compression results
     // perhaps because of better entropy clustering.
     if (entropy->nonzeros == 3) {
-      mix = 0.95;
+      mix = 0.95f;
     } else {
-      mix = 0.7;  // nonzeros == 4.
+      mix = 0.7f;  // nonzeros == 4.
     }
   } else {
-    mix = 0.627;
+    mix = 0.627f;
   }
 
   {
-    double min_limit = 2 * entropy->sum - entropy->max_val;
-    min_limit = mix * min_limit + (1.0 - mix) * entropy->entropy;
+    float min_limit = 2.f * entropy->sum - entropy->max_val;
+    min_limit = mix * min_limit + (1.f - mix) * entropy->entropy;
     return (entropy->entropy < min_limit) ? min_limit : entropy->entropy;
   }
 }
 
-double VP8LBitsEntropy(const uint32_t* const array, int n) {
+float VP8LBitsEntropy(const uint32_t* const array, int n) {
   VP8LBitEntropy entropy;
   VP8LBitsEntropyUnrefined(array, n, &entropy);
 
   return BitsEntropyRefine(&entropy);
 }
 
-static double InitialHuffmanCost(void) {
+static float InitialHuffmanCost(void) {
   // Small bias because Huffman code length is typically not stored in
   // full length.
   static const int kHuffmanCodeOfHuffmanCodeSize = CODE_LENGTH_CODES * 3;
-  static const double kSmallBias = 9.1;
+  static const float kSmallBias = 9.1f;
   return kHuffmanCodeOfHuffmanCodeSize - kSmallBias;
 }
 
 // Finalize the Huffman cost based on streak numbers and length type (<3 or >=3)
-static double FinalHuffmanCost(const VP8LStreaks* const stats) {
+static float FinalHuffmanCost(const VP8LStreaks* const stats) {
   // The constants in this function are experimental and got rounded from
   // their original values in 1/8 when switched to 1/1024.
-  double retval = InitialHuffmanCost();
+  float retval = InitialHuffmanCost();
   // Second coefficient: Many zeros in the histogram are covered efficiently
   // by a run-length encode. Originally 2/8.
-  retval += stats->counts[0] * 1.5625 + 0.234375 * stats->streaks[0][1];
+  retval += stats->counts[0] * 1.5625f + 0.234375f * stats->streaks[0][1];
   // Second coefficient: Constant values are encoded less efficiently, but still
   // RLE'ed. Originally 6/8.
-  retval += stats->counts[1] * 2.578125 + 0.703125 * stats->streaks[1][1];
+  retval += stats->counts[1] * 2.578125f + 0.703125f * stats->streaks[1][1];
   // 0s are usually encoded more efficiently than non-0s.
   // Originally 15/8.
-  retval += 1.796875 * stats->streaks[0][0];
+  retval += 1.796875f * stats->streaks[0][0];
   // Originally 26/8.
-  retval += 3.28125 * stats->streaks[1][0];
+  retval += 3.28125f * stats->streaks[1][0];
   return retval;
 }
 
 // Get the symbol entropy for the distribution 'population'.
 // Set 'trivial_sym', if there's only one symbol present in the distribution.
-static double PopulationCost(const uint32_t* const population, int length,
-                             uint32_t* const trivial_sym,
-                             uint8_t* const is_used) {
+static float PopulationCost(const uint32_t* const population, int length,
+                            uint32_t* const trivial_sym,
+                            uint8_t* const is_used) {
   VP8LBitEntropy bit_entropy;
   VP8LStreaks stats;
   VP8LGetEntropyUnrefined(population, length, &bit_entropy, &stats);
@@ -314,11 +316,10 @@
 
 // trivial_at_end is 1 if the two histograms only have one element that is
 // non-zero: both the zero-th one, or both the last one.
-static WEBP_INLINE double GetCombinedEntropy(const uint32_t* const X,
-                                             const uint32_t* const Y,
-                                             int length, int is_X_used,
-                                             int is_Y_used,
-                                             int trivial_at_end) {
+static WEBP_INLINE float GetCombinedEntropy(const uint32_t* const X,
+                                            const uint32_t* const Y, int length,
+                                            int is_X_used, int is_Y_used,
+                                            int trivial_at_end) {
   VP8LStreaks stats;
   if (trivial_at_end) {
     // This configuration is due to palettization that transforms an indexed
@@ -356,7 +357,7 @@
 }
 
 // Estimates the Entropy + Huffman + other block overhead size cost.
-double VP8LHistogramEstimateBits(VP8LHistogram* const p) {
+float VP8LHistogramEstimateBits(VP8LHistogram* const p) {
   return
       PopulationCost(p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_),
                      NULL, &p->is_used_[0])
@@ -373,8 +374,7 @@
 
 static int GetCombinedHistogramEntropy(const VP8LHistogram* const a,
                                        const VP8LHistogram* const b,
-                                       double cost_threshold,
-                                       double* cost) {
+                                       float cost_threshold, float* cost) {
   const int palette_code_bits = a->palette_code_bits_;
   int trivial_at_end = 0;
   assert(a->palette_code_bits_ == b->palette_code_bits_);
@@ -439,12 +439,11 @@
 // Since the previous score passed is 'cost_threshold', we only need to compare
 // the partial cost against 'cost_threshold + C(a) + C(b)' to possibly bail-out
 // early.
-static double HistogramAddEval(const VP8LHistogram* const a,
-                               const VP8LHistogram* const b,
-                               VP8LHistogram* const out,
-                               double cost_threshold) {
-  double cost = 0;
-  const double sum_cost = a->bit_cost_ + b->bit_cost_;
+static float HistogramAddEval(const VP8LHistogram* const a,
+                              const VP8LHistogram* const b,
+                              VP8LHistogram* const out, float cost_threshold) {
+  float cost = 0;
+  const float sum_cost = a->bit_cost_ + b->bit_cost_;
   cost_threshold += sum_cost;
 
   if (GetCombinedHistogramEntropy(a, b, cost_threshold, &cost)) {
@@ -459,10 +458,10 @@
 // Same as HistogramAddEval(), except that the resulting histogram
 // is not stored. Only the cost C(a+b) - C(a) is evaluated. We omit
 // the term C(b) which is constant over all the evaluations.
-static double HistogramAddThresh(const VP8LHistogram* const a,
-                                 const VP8LHistogram* const b,
-                                 double cost_threshold) {
-  double cost;
+static float HistogramAddThresh(const VP8LHistogram* const a,
+                                const VP8LHistogram* const b,
+                                float cost_threshold) {
+  float cost;
   assert(a != NULL && b != NULL);
   cost = -a->bit_cost_;
   GetCombinedHistogramEntropy(a, b, cost_threshold, &cost);
@@ -473,24 +472,22 @@
 
 // The structure to keep track of cost range for the three dominant entropy
 // symbols.
-// TODO(skal): Evaluate if float can be used here instead of double for
-// representing the entropy costs.
 typedef struct {
-  double literal_max_;
-  double literal_min_;
-  double red_max_;
-  double red_min_;
-  double blue_max_;
-  double blue_min_;
+  float literal_max_;
+  float literal_min_;
+  float red_max_;
+  float red_min_;
+  float blue_max_;
+  float blue_min_;
 } DominantCostRange;
 
 static void DominantCostRangeInit(DominantCostRange* const c) {
   c->literal_max_ = 0.;
-  c->literal_min_ = MAX_COST;
+  c->literal_min_ = MAX_BIT_COST;
   c->red_max_ = 0.;
-  c->red_min_ = MAX_COST;
+  c->red_min_ = MAX_BIT_COST;
   c->blue_max_ = 0.;
-  c->blue_min_ = MAX_COST;
+  c->blue_min_ = MAX_BIT_COST;
 }
 
 static void UpdateDominantCostRange(
@@ -505,10 +502,9 @@
 
 static void UpdateHistogramCost(VP8LHistogram* const h) {
   uint32_t alpha_sym, red_sym, blue_sym;
-  const double alpha_cost =
-      PopulationCost(h->alpha_, NUM_LITERAL_CODES, &alpha_sym,
-                     &h->is_used_[3]);
-  const double distance_cost =
+  const float alpha_cost =
+      PopulationCost(h->alpha_, NUM_LITERAL_CODES, &alpha_sym, &h->is_used_[3]);
+  const float distance_cost =
       PopulationCost(h->distance_, NUM_DISTANCE_CODES, NULL, &h->is_used_[4]) +
       VP8LExtraCost(h->distance_, NUM_DISTANCE_CODES);
   const int num_codes = VP8LHistogramNumCodes(h->palette_code_bits_);
@@ -529,10 +525,10 @@
   }
 }
 
-static int GetBinIdForEntropy(double min, double max, double val) {
-  const double range = max - min;
+static int GetBinIdForEntropy(float min, float max, float val) {
+  const float range = max - min;
   if (range > 0.) {
-    const double delta = val - min;
+    const float delta = val - min;
     return (int)((NUM_PARTITIONS - 1e-6) * delta / range);
   } else {
     return 0;
@@ -641,15 +637,11 @@
 
 // Merges some histograms with same bin_id together if it's advantageous.
 // Sets the remaining histograms to NULL.
-static void HistogramCombineEntropyBin(VP8LHistogramSet* const image_histo,
-                                       int* num_used,
-                                       const uint16_t* const clusters,
-                                       uint16_t* const cluster_mappings,
-                                       VP8LHistogram* cur_combo,
-                                       const uint16_t* const bin_map,
-                                       int num_bins,
-                                       double combine_cost_factor,
-                                       int low_effort) {
+static void HistogramCombineEntropyBin(
+    VP8LHistogramSet* const image_histo, int* num_used,
+    const uint16_t* const clusters, uint16_t* const cluster_mappings,
+    VP8LHistogram* cur_combo, const uint16_t* const bin_map, int num_bins,
+    float combine_cost_factor, int low_effort) {
   VP8LHistogram** const histograms = image_histo->histograms;
   int idx;
   struct {
@@ -679,11 +671,10 @@
       cluster_mappings[clusters[idx]] = clusters[first];
     } else {
       // try to merge #idx into #first (both share the same bin_id)
-      const double bit_cost = histograms[idx]->bit_cost_;
-      const double bit_cost_thresh = -bit_cost * combine_cost_factor;
-      const double curr_cost_diff =
-          HistogramAddEval(histograms[first], histograms[idx],
-                           cur_combo, bit_cost_thresh);
+      const float bit_cost = histograms[idx]->bit_cost_;
+      const float bit_cost_thresh = -bit_cost * combine_cost_factor;
+      const float curr_cost_diff = HistogramAddEval(
+          histograms[first], histograms[idx], cur_combo, bit_cost_thresh);
       if (curr_cost_diff < bit_cost_thresh) {
         // Try to merge two histograms only if the combo is a trivial one or
         // the two candidate histograms are already non-trivial.
@@ -731,8 +722,8 @@
 typedef struct {
   int idx1;
   int idx2;
-  double cost_diff;
-  double cost_combo;
+  float cost_diff;
+  float cost_combo;
 } HistogramPair;
 
 typedef struct {
@@ -787,10 +778,9 @@
 // Update the cost diff and combo of a pair of histograms. This needs to be
 // called when the the histograms have been merged with a third one.
 static void HistoQueueUpdatePair(const VP8LHistogram* const h1,
-                                 const VP8LHistogram* const h2,
-                                 double threshold,
+                                 const VP8LHistogram* const h2, float threshold,
                                  HistogramPair* const pair) {
-  const double sum_cost = h1->bit_cost_ + h2->bit_cost_;
+  const float sum_cost = h1->bit_cost_ + h2->bit_cost_;
   pair->cost_combo = 0.;
   GetCombinedHistogramEntropy(h1, h2, sum_cost + threshold, &pair->cost_combo);
   pair->cost_diff = pair->cost_combo - sum_cost;
@@ -799,9 +789,9 @@
 // Create a pair from indices "idx1" and "idx2" provided its cost
 // is inferior to "threshold", a negative entropy.
 // It returns the cost of the pair, or 0. if it superior to threshold.
-static double HistoQueuePush(HistoQueue* const histo_queue,
-                             VP8LHistogram** const histograms, int idx1,
-                             int idx2, double threshold) {
+static float HistoQueuePush(HistoQueue* const histo_queue,
+                            VP8LHistogram** const histograms, int idx1,
+                            int idx2, float threshold) {
   const VP8LHistogram* h1;
   const VP8LHistogram* h2;
   HistogramPair pair;
@@ -945,8 +935,8 @@
            ++tries_with_no_success < num_tries_no_success;
        ++iter) {
     int* mapping_index;
-    double best_cost =
-        (histo_queue.size == 0) ? 0. : histo_queue.queue[0].cost_diff;
+    float best_cost =
+        (histo_queue.size == 0) ? 0.f : histo_queue.queue[0].cost_diff;
     int best_idx1 = -1, best_idx2 = 1;
     const uint32_t rand_range = (*num_used - 1) * (*num_used);
     // (*num_used) / 2 was chosen empirically. Less means faster but worse
@@ -955,7 +945,7 @@
 
     // Pick random samples.
     for (j = 0; *num_used >= 2 && j < num_tries; ++j) {
-      double curr_cost;
+      float curr_cost;
       // Choose two different histograms at random and try to combine them.
       const uint32_t tmp = MyRand(&seed) % rand_range;
       uint32_t idx1 = tmp / (*num_used - 1);
@@ -1034,7 +1024,7 @@
   *do_greedy = (*num_used <= min_cluster_size);
   ok = 1;
 
-End:
+ End:
   HistoQueueClear(&histo_queue);
   WebPSafeFree(mappings);
   return ok;
@@ -1057,7 +1047,7 @@
   if (out_size > 1) {
     for (i = 0; i < in_size; ++i) {
       int best_out = 0;
-      double best_bits = MAX_COST;
+      float best_bits = MAX_BIT_COST;
       int k;
       if (in_histo[i] == NULL) {
         // Arbitrarily set to the previous value if unused to help future LZ77.
@@ -1065,7 +1055,7 @@
         continue;
       }
       for (k = 0; k < out_size; ++k) {
-        double cur_bits;
+        float cur_bits;
         cur_bits = HistogramAddThresh(out_histo[k], in_histo[i], best_bits);
         if (k == 0 || cur_bits < best_bits) {
           best_bits = cur_bits;
@@ -1093,13 +1083,13 @@
   }
 }
 
-static double GetCombineCostFactor(int histo_size, int quality) {
-  double combine_cost_factor = 0.16;
+static float GetCombineCostFactor(int histo_size, int quality) {
+  float combine_cost_factor = 0.16f;
   if (quality < 90) {
-    if (histo_size > 256) combine_cost_factor /= 2.;
-    if (histo_size > 512) combine_cost_factor /= 2.;
-    if (histo_size > 1024) combine_cost_factor /= 2.;
-    if (quality <= 50) combine_cost_factor /= 2.;
+    if (histo_size > 256) combine_cost_factor /= 2.f;
+    if (histo_size > 512) combine_cost_factor /= 2.f;
+    if (histo_size > 1024) combine_cost_factor /= 2.f;
+    if (quality <= 50) combine_cost_factor /= 2.f;
   }
   return combine_cost_factor;
 }
@@ -1169,13 +1159,13 @@
 }
 
 int VP8LGetHistoImageSymbols(int xsize, int ysize,
-                             const VP8LBackwardRefs* const refs,
-                             int quality, int low_effort,
-                             int histogram_bits, int cache_bits,
+                             const VP8LBackwardRefs* const refs, int quality,
+                             int low_effort, int histogram_bits, int cache_bits,
                              VP8LHistogramSet* const image_histo,
                              VP8LHistogram* const tmp_histo,
-                             uint16_t* const histogram_symbols) {
-  int ok = 0;
+                             uint16_t* const histogram_symbols,
+                             const WebPPicture* const pic, int percent_range,
+                             int* const percent) {
   const int histo_xsize =
       histogram_bits ? VP8LSubSampleSize(xsize, histogram_bits) : 1;
   const int histo_ysize =
@@ -1192,7 +1182,10 @@
       WebPSafeMalloc(2 * image_histo_raw_size, sizeof(map_tmp));
   uint16_t* const cluster_mappings = map_tmp + image_histo_raw_size;
   int num_used = image_histo_raw_size;
-  if (orig_histo == NULL || map_tmp == NULL) goto Error;
+  if (orig_histo == NULL || map_tmp == NULL) {
+    WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+    goto Error;
+  }
 
   // Construct the histograms from backward references.
   HistogramBuild(xsize, histogram_bits, refs, orig_histo);
@@ -1206,16 +1199,15 @@
 
   if (entropy_combine) {
     uint16_t* const bin_map = map_tmp;
-    const double combine_cost_factor =
+    const float combine_cost_factor =
         GetCombineCostFactor(image_histo_raw_size, quality);
     const uint32_t num_clusters = num_used;
 
     HistogramAnalyzeEntropyBin(image_histo, bin_map, low_effort);
     // Collapse histograms with similar entropy.
-    HistogramCombineEntropyBin(image_histo, &num_used, histogram_symbols,
-                               cluster_mappings, tmp_histo, bin_map,
-                               entropy_combine_num_bins, combine_cost_factor,
-                               low_effort);
+    HistogramCombineEntropyBin(
+        image_histo, &num_used, histogram_symbols, cluster_mappings, tmp_histo,
+        bin_map, entropy_combine_num_bins, combine_cost_factor, low_effort);
     OptimizeHistogramSymbols(image_histo, cluster_mappings, num_clusters,
                              map_tmp, histogram_symbols);
   }
@@ -1229,11 +1221,13 @@
     int do_greedy;
     if (!HistogramCombineStochastic(image_histo, &num_used, threshold_size,
                                     &do_greedy)) {
+      WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
       goto Error;
     }
     if (do_greedy) {
       RemoveEmptyHistograms(image_histo);
       if (!HistogramCombineGreedy(image_histo, &num_used)) {
+        WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
         goto Error;
       }
     }
@@ -1243,10 +1237,12 @@
   RemoveEmptyHistograms(image_histo);
   HistogramRemap(orig_histo, image_histo, histogram_symbols);
 
-  ok = 1;
+  if (!WebPReportProgress(pic, *percent + percent_range, percent)) {
+    goto Error;
+  }
 
  Error:
   VP8LFreeHistogramSet(orig_histo);
   WebPSafeFree(map_tmp);
-  return ok;
+  return (pic->error_code == VP8_ENC_OK);
 }
diff --git a/src/enc/histogram_enc.h b/src/enc/histogram_enc.h
index c3428b5..4c0bb97 100644
--- a/src/enc/histogram_enc.h
+++ b/src/enc/histogram_enc.h
@@ -40,10 +40,10 @@
   int palette_code_bits_;
   uint32_t trivial_symbol_;  // True, if histograms for Red, Blue & Alpha
                              // literal symbols are single valued.
-  double bit_cost_;          // cached value of bit cost.
-  double literal_cost_;      // Cached values of dominant entropy costs:
-  double red_cost_;          // literal, red & blue.
-  double blue_cost_;
+  float bit_cost_;           // cached value of bit cost.
+  float literal_cost_;       // Cached values of dominant entropy costs:
+  float red_cost_;           // literal, red & blue.
+  float blue_cost_;
   uint8_t is_used_[5];       // 5 for literal, red, blue, alpha, distance
 } VP8LHistogram;
 
@@ -105,21 +105,23 @@
       ((palette_code_bits > 0) ? (1 << palette_code_bits) : 0);
 }
 
-// Builds the histogram image.
+// Builds the histogram image. pic and percent are for progress.
+// Returns false in case of error (stored in pic->error_code).
 int VP8LGetHistoImageSymbols(int xsize, int ysize,
-                             const VP8LBackwardRefs* const refs,
-                             int quality, int low_effort,
-                             int histogram_bits, int cache_bits,
+                             const VP8LBackwardRefs* const refs, int quality,
+                             int low_effort, int histogram_bits, int cache_bits,
                              VP8LHistogramSet* const image_histo,
                              VP8LHistogram* const tmp_histo,
-                             uint16_t* const histogram_symbols);
+                             uint16_t* const histogram_symbols,
+                             const WebPPicture* const pic, int percent_range,
+                             int* const percent);
 
 // Returns the entropy for the symbols in the input array.
-double VP8LBitsEntropy(const uint32_t* const array, int n);
+float VP8LBitsEntropy(const uint32_t* const array, int n);
 
 // Estimate how many bits the combined entropy of literals and distance
 // approximately maps to.
-double VP8LHistogramEstimateBits(VP8LHistogram* const p);
+float VP8LHistogramEstimateBits(VP8LHistogram* const p);
 
 #ifdef __cplusplus
 }
diff --git a/src/enc/picture_csp_enc.c b/src/enc/picture_csp_enc.c
index 35eede9..78c8ca4 100644
--- a/src/enc/picture_csp_enc.c
+++ b/src/enc/picture_csp_enc.c
@@ -15,12 +15,19 @@
 #include <stdlib.h>
 #include <math.h>
 
+#include "sharpyuv/sharpyuv.h"
+#include "sharpyuv/sharpyuv_csp.h"
 #include "src/enc/vp8i_enc.h"
 #include "src/utils/random_utils.h"
 #include "src/utils/utils.h"
 #include "src/dsp/dsp.h"
 #include "src/dsp/lossless.h"
 #include "src/dsp/yuv.h"
+#include "src/dsp/cpu.h"
+
+#if defined(WEBP_USE_THREAD) && !defined(_WIN32)
+#include <pthread.h>
+#endif
 
 // Uncomment to disable gamma-compression during RGB->U/V averaging
 #define USE_GAMMA_COMPRESSION
@@ -62,10 +69,12 @@
 int WebPPictureHasTransparency(const WebPPicture* picture) {
   if (picture == NULL) return 0;
   if (picture->use_argb) {
-    const int alpha_offset = ALPHA_OFFSET;
-    return CheckNonOpaque((const uint8_t*)picture->argb + alpha_offset,
-                          picture->width, picture->height,
-                          4, picture->argb_stride * sizeof(*picture->argb));
+    if (picture->argb != NULL) {
+      return CheckNonOpaque((const uint8_t*)picture->argb + ALPHA_OFFSET,
+                            picture->width, picture->height,
+                            4, picture->argb_stride * sizeof(*picture->argb));
+    }
+    return 0;
   }
   return CheckNonOpaque(picture->a, picture->width, picture->height,
                         1, picture->a_stride);
@@ -76,16 +85,16 @@
 
 #if defined(USE_GAMMA_COMPRESSION)
 
-// gamma-compensates loss of resolution during chroma subsampling
-#define kGamma 0.80      // for now we use a different gamma value than kGammaF
-#define kGammaFix 12     // fixed-point precision for linear values
-#define kGammaScale ((1 << kGammaFix) - 1)
-#define kGammaTabFix 7   // fixed-point fractional bits precision
-#define kGammaTabScale (1 << kGammaTabFix)
-#define kGammaTabRounder (kGammaTabScale >> 1)
-#define kGammaTabSize (1 << (kGammaFix - kGammaTabFix))
+// Gamma correction compensates loss of resolution during chroma subsampling.
+#define GAMMA_FIX 12      // fixed-point precision for linear values
+#define GAMMA_TAB_FIX 7   // fixed-point fractional bits precision
+#define GAMMA_TAB_SIZE (1 << (GAMMA_FIX - GAMMA_TAB_FIX))
+static const double kGamma = 0.80;
+static const int kGammaScale = ((1 << GAMMA_FIX) - 1);
+static const int kGammaTabScale = (1 << GAMMA_TAB_FIX);
+static const int kGammaTabRounder = (1 << GAMMA_TAB_FIX >> 1);
 
-static int kLinearToGammaTab[kGammaTabSize + 1];
+static int kLinearToGammaTab[GAMMA_TAB_SIZE + 1];
 static uint16_t kGammaToLinearTab[256];
 static volatile int kGammaTablesOk = 0;
 static void InitGammaTables(void);
@@ -93,13 +102,13 @@
 WEBP_DSP_INIT_FUNC(InitGammaTables) {
   if (!kGammaTablesOk) {
     int v;
-    const double scale = (double)(1 << kGammaTabFix) / kGammaScale;
+    const double scale = (double)(1 << GAMMA_TAB_FIX) / kGammaScale;
     const double norm = 1. / 255.;
     for (v = 0; v <= 255; ++v) {
       kGammaToLinearTab[v] =
           (uint16_t)(pow(norm * v, kGamma) * kGammaScale + .5);
     }
-    for (v = 0; v <= kGammaTabSize; ++v) {
+    for (v = 0; v <= GAMMA_TAB_SIZE; ++v) {
       kLinearToGammaTab[v] = (int)(255. * pow(scale * v, 1. / kGamma) + .5);
     }
     kGammaTablesOk = 1;
@@ -111,12 +120,12 @@
 }
 
 static WEBP_INLINE int Interpolate(int v) {
-  const int tab_pos = v >> (kGammaTabFix + 2);    // integer part
+  const int tab_pos = v >> (GAMMA_TAB_FIX + 2);    // integer part
   const int x = v & ((kGammaTabScale << 2) - 1);  // fractional part
   const int v0 = kLinearToGammaTab[tab_pos];
   const int v1 = kLinearToGammaTab[tab_pos + 1];
   const int y = v1 * x + v0 * ((kGammaTabScale << 2) - x);   // interpolate
-  assert(tab_pos + 1 < kGammaTabSize + 1);
+  assert(tab_pos + 1 < GAMMA_TAB_SIZE + 1);
   return y;
 }
 
@@ -124,7 +133,7 @@
 // U/V value, suitable for RGBToU/V calls.
 static WEBP_INLINE int LinearToGamma(uint32_t base_value, int shift) {
   const int y = Interpolate(base_value << shift);   // final uplifted value
-  return (y + kGammaTabRounder) >> kGammaTabFix;    // descale
+  return (y + kGammaTabRounder) >> GAMMA_TAB_FIX;    // descale
 }
 
 #else
@@ -158,415 +167,26 @@
 //------------------------------------------------------------------------------
 // Sharp RGB->YUV conversion
 
-static const int kNumIterations = 4;
 static const int kMinDimensionIterativeConversion = 4;
 
-// We could use SFIX=0 and only uint8_t for fixed_y_t, but it produces some
-// banding sometimes. Better use extra precision.
-#define SFIX 2                // fixed-point precision of RGB and Y/W
-typedef int16_t fixed_t;      // signed type with extra SFIX precision for UV
-typedef uint16_t fixed_y_t;   // unsigned type with extra SFIX precision for W
-
-#define SHALF (1 << SFIX >> 1)
-#define MAX_Y_T ((256 << SFIX) - 1)
-#define SROUNDER (1 << (YUV_FIX + SFIX - 1))
-
-#if defined(USE_GAMMA_COMPRESSION)
-
-// We use tables of different size and precision for the Rec709 / BT2020
-// transfer function.
-#define kGammaF (1./0.45)
-static uint32_t kLinearToGammaTabS[kGammaTabSize + 2];
-#define GAMMA_TO_LINEAR_BITS 14
-static uint32_t kGammaToLinearTabS[MAX_Y_T + 1];   // size scales with Y_FIX
-static volatile int kGammaTablesSOk = 0;
-static void InitGammaTablesS(void);
-
-WEBP_DSP_INIT_FUNC(InitGammaTablesS) {
-  assert(2 * GAMMA_TO_LINEAR_BITS < 32);  // we use uint32_t intermediate values
-  if (!kGammaTablesSOk) {
-    int v;
-    const double norm = 1. / MAX_Y_T;
-    const double scale = 1. / kGammaTabSize;
-    const double a = 0.09929682680944;
-    const double thresh = 0.018053968510807;
-    const double final_scale = 1 << GAMMA_TO_LINEAR_BITS;
-    for (v = 0; v <= MAX_Y_T; ++v) {
-      const double g = norm * v;
-      double value;
-      if (g <= thresh * 4.5) {
-        value = g / 4.5;
-      } else {
-        const double a_rec = 1. / (1. + a);
-        value = pow(a_rec * (g + a), kGammaF);
-      }
-      kGammaToLinearTabS[v] = (uint32_t)(value * final_scale + .5);
-    }
-    for (v = 0; v <= kGammaTabSize; ++v) {
-      const double g = scale * v;
-      double value;
-      if (g <= thresh) {
-        value = 4.5 * g;
-      } else {
-        value = (1. + a) * pow(g, 1. / kGammaF) - a;
-      }
-      // we already incorporate the 1/2 rounding constant here
-      kLinearToGammaTabS[v] =
-          (uint32_t)(MAX_Y_T * value) + (1 << GAMMA_TO_LINEAR_BITS >> 1);
-    }
-    // to prevent small rounding errors to cause read-overflow:
-    kLinearToGammaTabS[kGammaTabSize + 1] = kLinearToGammaTabS[kGammaTabSize];
-    kGammaTablesSOk = 1;
-  }
-}
-
-// return value has a fixed-point precision of GAMMA_TO_LINEAR_BITS
-static WEBP_INLINE uint32_t GammaToLinearS(int v) {
-  return kGammaToLinearTabS[v];
-}
-
-static WEBP_INLINE uint32_t LinearToGammaS(uint32_t value) {
-  // 'value' is in GAMMA_TO_LINEAR_BITS fractional precision
-  const uint32_t v = value * kGammaTabSize;
-  const uint32_t tab_pos = v >> GAMMA_TO_LINEAR_BITS;
-  // fractional part, in GAMMA_TO_LINEAR_BITS fixed-point precision
-  const uint32_t x = v - (tab_pos << GAMMA_TO_LINEAR_BITS);  // fractional part
-  // v0 / v1 are in GAMMA_TO_LINEAR_BITS fixed-point precision (range [0..1])
-  const uint32_t v0 = kLinearToGammaTabS[tab_pos + 0];
-  const uint32_t v1 = kLinearToGammaTabS[tab_pos + 1];
-  // Final interpolation. Note that rounding is already included.
-  const uint32_t v2 = (v1 - v0) * x;    // note: v1 >= v0.
-  const uint32_t result = v0 + (v2 >> GAMMA_TO_LINEAR_BITS);
-  return result;
-}
-
-#else
-
-static void InitGammaTablesS(void) {}
-static WEBP_INLINE uint32_t GammaToLinearS(int v) {
-  return (v << GAMMA_TO_LINEAR_BITS) / MAX_Y_T;
-}
-static WEBP_INLINE uint32_t LinearToGammaS(uint32_t value) {
-  return (MAX_Y_T * value) >> GAMMA_TO_LINEAR_BITS;
-}
-
-#endif    // USE_GAMMA_COMPRESSION
-
-//------------------------------------------------------------------------------
-
-static uint8_t clip_8b(fixed_t v) {
-  return (!(v & ~0xff)) ? (uint8_t)v : (v < 0) ? 0u : 255u;
-}
-
-static fixed_y_t clip_y(int y) {
-  return (!(y & ~MAX_Y_T)) ? (fixed_y_t)y : (y < 0) ? 0 : MAX_Y_T;
-}
-
-//------------------------------------------------------------------------------
-
-static int RGBToGray(int r, int g, int b) {
-  const int luma = 13933 * r + 46871 * g + 4732 * b + YUV_HALF;
-  return (luma >> YUV_FIX);
-}
-
-static uint32_t ScaleDown(int a, int b, int c, int d) {
-  const uint32_t A = GammaToLinearS(a);
-  const uint32_t B = GammaToLinearS(b);
-  const uint32_t C = GammaToLinearS(c);
-  const uint32_t D = GammaToLinearS(d);
-  return LinearToGammaS((A + B + C + D + 2) >> 2);
-}
-
-static WEBP_INLINE void UpdateW(const fixed_y_t* src, fixed_y_t* dst, int w) {
-  int i;
-  for (i = 0; i < w; ++i) {
-    const uint32_t R = GammaToLinearS(src[0 * w + i]);
-    const uint32_t G = GammaToLinearS(src[1 * w + i]);
-    const uint32_t B = GammaToLinearS(src[2 * w + i]);
-    const uint32_t Y = RGBToGray(R, G, B);
-    dst[i] = (fixed_y_t)LinearToGammaS(Y);
-  }
-}
-
-static void UpdateChroma(const fixed_y_t* src1, const fixed_y_t* src2,
-                         fixed_t* dst, int uv_w) {
-  int i;
-  for (i = 0; i < uv_w; ++i) {
-    const int r = ScaleDown(src1[0 * uv_w + 0], src1[0 * uv_w + 1],
-                            src2[0 * uv_w + 0], src2[0 * uv_w + 1]);
-    const int g = ScaleDown(src1[2 * uv_w + 0], src1[2 * uv_w + 1],
-                            src2[2 * uv_w + 0], src2[2 * uv_w + 1]);
-    const int b = ScaleDown(src1[4 * uv_w + 0], src1[4 * uv_w + 1],
-                            src2[4 * uv_w + 0], src2[4 * uv_w + 1]);
-    const int W = RGBToGray(r, g, b);
-    dst[0 * uv_w] = (fixed_t)(r - W);
-    dst[1 * uv_w] = (fixed_t)(g - W);
-    dst[2 * uv_w] = (fixed_t)(b - W);
-    dst  += 1;
-    src1 += 2;
-    src2 += 2;
-  }
-}
-
-static void StoreGray(const fixed_y_t* rgb, fixed_y_t* y, int w) {
-  int i;
-  for (i = 0; i < w; ++i) {
-    y[i] = RGBToGray(rgb[0 * w + i], rgb[1 * w + i], rgb[2 * w + i]);
-  }
-}
-
-//------------------------------------------------------------------------------
-
-static WEBP_INLINE fixed_y_t Filter2(int A, int B, int W0) {
-  const int v0 = (A * 3 + B + 2) >> 2;
-  return clip_y(v0 + W0);
-}
-
-//------------------------------------------------------------------------------
-
-static WEBP_INLINE fixed_y_t UpLift(uint8_t a) {  // 8bit -> SFIX
-  return ((fixed_y_t)a << SFIX) | SHALF;
-}
-
-static void ImportOneRow(const uint8_t* const r_ptr,
-                         const uint8_t* const g_ptr,
-                         const uint8_t* const b_ptr,
-                         int step,
-                         int pic_width,
-                         fixed_y_t* const dst) {
-  int i;
-  const int w = (pic_width + 1) & ~1;
-  for (i = 0; i < pic_width; ++i) {
-    const int off = i * step;
-    dst[i + 0 * w] = UpLift(r_ptr[off]);
-    dst[i + 1 * w] = UpLift(g_ptr[off]);
-    dst[i + 2 * w] = UpLift(b_ptr[off]);
-  }
-  if (pic_width & 1) {  // replicate rightmost pixel
-    dst[pic_width + 0 * w] = dst[pic_width + 0 * w - 1];
-    dst[pic_width + 1 * w] = dst[pic_width + 1 * w - 1];
-    dst[pic_width + 2 * w] = dst[pic_width + 2 * w - 1];
-  }
-}
-
-static void InterpolateTwoRows(const fixed_y_t* const best_y,
-                               const fixed_t* prev_uv,
-                               const fixed_t* cur_uv,
-                               const fixed_t* next_uv,
-                               int w,
-                               fixed_y_t* out1,
-                               fixed_y_t* out2) {
-  const int uv_w = w >> 1;
-  const int len = (w - 1) >> 1;   // length to filter
-  int k = 3;
-  while (k-- > 0) {   // process each R/G/B segments in turn
-    // special boundary case for i==0
-    out1[0] = Filter2(cur_uv[0], prev_uv[0], best_y[0]);
-    out2[0] = Filter2(cur_uv[0], next_uv[0], best_y[w]);
-
-    WebPSharpYUVFilterRow(cur_uv, prev_uv, len, best_y + 0 + 1, out1 + 1);
-    WebPSharpYUVFilterRow(cur_uv, next_uv, len, best_y + w + 1, out2 + 1);
-
-    // special boundary case for i == w - 1 when w is even
-    if (!(w & 1)) {
-      out1[w - 1] = Filter2(cur_uv[uv_w - 1], prev_uv[uv_w - 1],
-                            best_y[w - 1 + 0]);
-      out2[w - 1] = Filter2(cur_uv[uv_w - 1], next_uv[uv_w - 1],
-                            best_y[w - 1 + w]);
-    }
-    out1 += w;
-    out2 += w;
-    prev_uv += uv_w;
-    cur_uv  += uv_w;
-    next_uv += uv_w;
-  }
-}
-
-static WEBP_INLINE uint8_t ConvertRGBToY(int r, int g, int b) {
-  const int luma = 16839 * r + 33059 * g + 6420 * b + SROUNDER;
-  return clip_8b(16 + (luma >> (YUV_FIX + SFIX)));
-}
-
-static WEBP_INLINE uint8_t ConvertRGBToU(int r, int g, int b) {
-  const int u =  -9719 * r - 19081 * g + 28800 * b + SROUNDER;
-  return clip_8b(128 + (u >> (YUV_FIX + SFIX)));
-}
-
-static WEBP_INLINE uint8_t ConvertRGBToV(int r, int g, int b) {
-  const int v = +28800 * r - 24116 * g -  4684 * b + SROUNDER;
-  return clip_8b(128 + (v >> (YUV_FIX + SFIX)));
-}
-
-static int ConvertWRGBToYUV(const fixed_y_t* best_y, const fixed_t* best_uv,
-                            WebPPicture* const picture) {
-  int i, j;
-  uint8_t* dst_y = picture->y;
-  uint8_t* dst_u = picture->u;
-  uint8_t* dst_v = picture->v;
-  const fixed_t* const best_uv_base = best_uv;
-  const int w = (picture->width + 1) & ~1;
-  const int h = (picture->height + 1) & ~1;
-  const int uv_w = w >> 1;
-  const int uv_h = h >> 1;
-  for (best_uv = best_uv_base, j = 0; j < picture->height; ++j) {
-    for (i = 0; i < picture->width; ++i) {
-      const int off = (i >> 1);
-      const int W = best_y[i];
-      const int r = best_uv[off + 0 * uv_w] + W;
-      const int g = best_uv[off + 1 * uv_w] + W;
-      const int b = best_uv[off + 2 * uv_w] + W;
-      dst_y[i] = ConvertRGBToY(r, g, b);
-    }
-    best_y += w;
-    best_uv += (j & 1) * 3 * uv_w;
-    dst_y += picture->y_stride;
-  }
-  for (best_uv = best_uv_base, j = 0; j < uv_h; ++j) {
-    for (i = 0; i < uv_w; ++i) {
-      const int off = i;
-      const int r = best_uv[off + 0 * uv_w];
-      const int g = best_uv[off + 1 * uv_w];
-      const int b = best_uv[off + 2 * uv_w];
-      dst_u[i] = ConvertRGBToU(r, g, b);
-      dst_v[i] = ConvertRGBToV(r, g, b);
-    }
-    best_uv += 3 * uv_w;
-    dst_u += picture->uv_stride;
-    dst_v += picture->uv_stride;
-  }
-  return 1;
-}
-
 //------------------------------------------------------------------------------
 // Main function
 
-#define SAFE_ALLOC(W, H, T) ((T*)WebPSafeMalloc((W) * (H), sizeof(T)))
-
 static int PreprocessARGB(const uint8_t* r_ptr,
                           const uint8_t* g_ptr,
                           const uint8_t* b_ptr,
                           int step, int rgb_stride,
                           WebPPicture* const picture) {
-  // we expand the right/bottom border if needed
-  const int w = (picture->width + 1) & ~1;
-  const int h = (picture->height + 1) & ~1;
-  const int uv_w = w >> 1;
-  const int uv_h = h >> 1;
-  uint64_t prev_diff_y_sum = ~0;
-  int j, iter;
-
-  // TODO(skal): allocate one big memory chunk. But for now, it's easier
-  // for valgrind debugging to have several chunks.
-  fixed_y_t* const tmp_buffer = SAFE_ALLOC(w * 3, 2, fixed_y_t);   // scratch
-  fixed_y_t* const best_y_base = SAFE_ALLOC(w, h, fixed_y_t);
-  fixed_y_t* const target_y_base = SAFE_ALLOC(w, h, fixed_y_t);
-  fixed_y_t* const best_rgb_y = SAFE_ALLOC(w, 2, fixed_y_t);
-  fixed_t* const best_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);
-  fixed_t* const target_uv_base = SAFE_ALLOC(uv_w * 3, uv_h, fixed_t);
-  fixed_t* const best_rgb_uv = SAFE_ALLOC(uv_w * 3, 1, fixed_t);
-  fixed_y_t* best_y = best_y_base;
-  fixed_y_t* target_y = target_y_base;
-  fixed_t* best_uv = best_uv_base;
-  fixed_t* target_uv = target_uv_base;
-  const uint64_t diff_y_threshold = (uint64_t)(3.0 * w * h);
-  int ok;
-
-  if (best_y_base == NULL || best_uv_base == NULL ||
-      target_y_base == NULL || target_uv_base == NULL ||
-      best_rgb_y == NULL || best_rgb_uv == NULL ||
-      tmp_buffer == NULL) {
-    ok = WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
-    goto End;
+  const int ok = SharpYuvConvert(
+      r_ptr, g_ptr, b_ptr, step, rgb_stride, /*rgb_bit_depth=*/8,
+      picture->y, picture->y_stride, picture->u, picture->uv_stride, picture->v,
+      picture->uv_stride, /*yuv_bit_depth=*/8, picture->width,
+      picture->height, SharpYuvGetConversionMatrix(kSharpYuvMatrixWebp));
+  if (!ok) {
+    return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
   }
-  assert(picture->width >= kMinDimensionIterativeConversion);
-  assert(picture->height >= kMinDimensionIterativeConversion);
-
-  WebPInitConvertARGBToYUV();
-
-  // Import RGB samples to W/RGB representation.
-  for (j = 0; j < picture->height; j += 2) {
-    const int is_last_row = (j == picture->height - 1);
-    fixed_y_t* const src1 = tmp_buffer + 0 * w;
-    fixed_y_t* const src2 = tmp_buffer + 3 * w;
-
-    // prepare two rows of input
-    ImportOneRow(r_ptr, g_ptr, b_ptr, step, picture->width, src1);
-    if (!is_last_row) {
-      ImportOneRow(r_ptr + rgb_stride, g_ptr + rgb_stride, b_ptr + rgb_stride,
-                   step, picture->width, src2);
-    } else {
-      memcpy(src2, src1, 3 * w * sizeof(*src2));
-    }
-    StoreGray(src1, best_y + 0, w);
-    StoreGray(src2, best_y + w, w);
-
-    UpdateW(src1, target_y, w);
-    UpdateW(src2, target_y + w, w);
-    UpdateChroma(src1, src2, target_uv, uv_w);
-    memcpy(best_uv, target_uv, 3 * uv_w * sizeof(*best_uv));
-    best_y += 2 * w;
-    best_uv += 3 * uv_w;
-    target_y += 2 * w;
-    target_uv += 3 * uv_w;
-    r_ptr += 2 * rgb_stride;
-    g_ptr += 2 * rgb_stride;
-    b_ptr += 2 * rgb_stride;
-  }
-
-  // Iterate and resolve clipping conflicts.
-  for (iter = 0; iter < kNumIterations; ++iter) {
-    const fixed_t* cur_uv = best_uv_base;
-    const fixed_t* prev_uv = best_uv_base;
-    uint64_t diff_y_sum = 0;
-
-    best_y = best_y_base;
-    best_uv = best_uv_base;
-    target_y = target_y_base;
-    target_uv = target_uv_base;
-    for (j = 0; j < h; j += 2) {
-      fixed_y_t* const src1 = tmp_buffer + 0 * w;
-      fixed_y_t* const src2 = tmp_buffer + 3 * w;
-      {
-        const fixed_t* const next_uv = cur_uv + ((j < h - 2) ? 3 * uv_w : 0);
-        InterpolateTwoRows(best_y, prev_uv, cur_uv, next_uv, w, src1, src2);
-        prev_uv = cur_uv;
-        cur_uv = next_uv;
-      }
-
-      UpdateW(src1, best_rgb_y + 0 * w, w);
-      UpdateW(src2, best_rgb_y + 1 * w, w);
-      UpdateChroma(src1, src2, best_rgb_uv, uv_w);
-
-      // update two rows of Y and one row of RGB
-      diff_y_sum += WebPSharpYUVUpdateY(target_y, best_rgb_y, best_y, 2 * w);
-      WebPSharpYUVUpdateRGB(target_uv, best_rgb_uv, best_uv, 3 * uv_w);
-
-      best_y += 2 * w;
-      best_uv += 3 * uv_w;
-      target_y += 2 * w;
-      target_uv += 3 * uv_w;
-    }
-    // test exit condition
-    if (iter > 0) {
-      if (diff_y_sum < diff_y_threshold) break;
-      if (diff_y_sum > prev_diff_y_sum) break;
-    }
-    prev_diff_y_sum = diff_y_sum;
-  }
-  // final reconstruction
-  ok = ConvertWRGBToYUV(best_y_base, best_uv_base, picture);
-
- End:
-  WebPSafeFree(best_y_base);
-  WebPSafeFree(best_uv_base);
-  WebPSafeFree(target_y_base);
-  WebPSafeFree(target_uv_base);
-  WebPSafeFree(best_rgb_y);
-  WebPSafeFree(best_rgb_uv);
-  WebPSafeFree(tmp_buffer);
   return ok;
 }
-#undef SAFE_ALLOC
 
 //------------------------------------------------------------------------------
 // "Fast" regular RGB->YUV
@@ -591,8 +211,8 @@
 // and constant are adjusted very tightly to fit 32b arithmetic.
 // In particular, they use the fact that the operands for 'v / a' are actually
 // derived as v = (a0.p0 + a1.p1 + a2.p2 + a3.p3) and a = a0 + a1 + a2 + a3
-// with ai in [0..255] and pi in [0..1<<kGammaFix). The constraint to avoid
-// overflow is: kGammaFix + kAlphaFix <= 31.
+// with ai in [0..255] and pi in [0..1<<GAMMA_FIX). The constraint to avoid
+// overflow is: GAMMA_FIX + kAlphaFix <= 31.
 static const uint32_t kInvAlpha[4 * 0xff + 1] = {
   0,  /* alpha = 0 */
   524288, 262144, 174762, 131072, 104857, 87381, 74898, 65536,
@@ -818,11 +438,20 @@
     dst[0] = SUM4(r_ptr + j, step);
     dst[1] = SUM4(g_ptr + j, step);
     dst[2] = SUM4(b_ptr + j, step);
+    // MemorySanitizer may raise false positives with data that passes through
+    // RGBA32PackedToPlanar_16b_SSE41() due to incorrect modeling of shuffles.
+    // See https://crbug.com/webp/573.
+#ifdef WEBP_MSAN
+    dst[3] = 0;
+#endif
   }
   if (width & 1) {
     dst[0] = SUM2(r_ptr + j);
     dst[1] = SUM2(g_ptr + j);
     dst[2] = SUM2(b_ptr + j);
+#ifdef WEBP_MSAN
+    dst[3] = 0;
+#endif
   }
 }
 
@@ -839,6 +468,8 @@
   }
 }
 
+extern void SharpYuvInit(VP8CPUInfo cpu_info_func);
+
 static int ImportYUVAFromRGBA(const uint8_t* r_ptr,
                               const uint8_t* g_ptr,
                               const uint8_t* b_ptr,
@@ -863,18 +494,18 @@
     use_iterative_conversion = 0;
   }
 
-  if (!WebPPictureAllocYUVA(picture, width, height)) {
+  if (!WebPPictureAllocYUVA(picture)) {
     return 0;
   }
   if (has_alpha) {
     assert(step == 4);
 #if defined(USE_GAMMA_COMPRESSION) && defined(USE_INVERSE_ALPHA_TABLE)
-    assert(kAlphaFix + kGammaFix <= 31);
+    assert(kAlphaFix + GAMMA_FIX <= 31);
 #endif
   }
 
   if (use_iterative_conversion) {
-    InitGammaTablesS();
+    SharpYuvInit(VP8GetCPUInfo);
     if (!PreprocessARGB(r_ptr, g_ptr, b_ptr, step, rgb_stride, picture)) {
       return 0;
     }
@@ -1044,7 +675,7 @@
     return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
   }
   // Allocate a new argb buffer (discarding the previous one).
-  if (!WebPPictureAllocARGB(picture, picture->width, picture->height)) return 0;
+  if (!WebPPictureAllocARGB(picture)) return 0;
   picture->use_argb = 1;
 
   // Convert
@@ -1106,6 +737,8 @@
   const int width = picture->width;
   const int height = picture->height;
 
+  if (abs(rgb_stride) < (import_alpha ? 4 : 3) * width) return 0;
+
   if (!picture->use_argb) {
     const uint8_t* a_ptr = import_alpha ? rgb + 3 : NULL;
     return ImportYUVAFromRGBA(r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride,
@@ -1163,24 +796,24 @@
 #if !defined(WEBP_REDUCE_CSP)
 
 int WebPPictureImportBGR(WebPPicture* picture,
-                         const uint8_t* rgb, int rgb_stride) {
-  return (picture != NULL && rgb != NULL)
-             ? Import(picture, rgb, rgb_stride, 3, 1, 0)
+                         const uint8_t* bgr, int bgr_stride) {
+  return (picture != NULL && bgr != NULL)
+             ? Import(picture, bgr, bgr_stride, 3, 1, 0)
              : 0;
 }
 
 int WebPPictureImportBGRA(WebPPicture* picture,
-                          const uint8_t* rgba, int rgba_stride) {
-  return (picture != NULL && rgba != NULL)
-             ? Import(picture, rgba, rgba_stride, 4, 1, 1)
+                          const uint8_t* bgra, int bgra_stride) {
+  return (picture != NULL && bgra != NULL)
+             ? Import(picture, bgra, bgra_stride, 4, 1, 1)
              : 0;
 }
 
 
 int WebPPictureImportBGRX(WebPPicture* picture,
-                          const uint8_t* rgba, int rgba_stride) {
-  return (picture != NULL && rgba != NULL)
-             ? Import(picture, rgba, rgba_stride, 4, 1, 0)
+                          const uint8_t* bgrx, int bgrx_stride) {
+  return (picture != NULL && bgrx != NULL)
+             ? Import(picture, bgrx, bgrx_stride, 4, 1, 0)
              : 0;
 }
 
@@ -1201,9 +834,9 @@
 }
 
 int WebPPictureImportRGBX(WebPPicture* picture,
-                          const uint8_t* rgba, int rgba_stride) {
-  return (picture != NULL && rgba != NULL)
-             ? Import(picture, rgba, rgba_stride, 4, 0, 0)
+                          const uint8_t* rgbx, int rgbx_stride) {
+  return (picture != NULL && rgbx != NULL)
+             ? Import(picture, rgbx, rgbx_stride, 4, 0, 0)
              : 0;
 }
 
diff --git a/src/enc/picture_enc.c b/src/enc/picture_enc.c
index c691622..3af6383 100644
--- a/src/enc/picture_enc.c
+++ b/src/enc/picture_enc.c
@@ -45,6 +45,22 @@
 
 //------------------------------------------------------------------------------
 
+int WebPValidatePicture(const WebPPicture* const picture) {
+  if (picture == NULL) return 0;
+  if (picture->width <= 0 || picture->height <= 0) {
+    return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
+  }
+  if (picture->width <= 0 || picture->width / 4 > INT_MAX / 4 ||
+      picture->height <= 0 || picture->height / 4 > INT_MAX / 4) {
+    return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
+  }
+  if (picture->colorspace != WEBP_YUV420 &&
+      picture->colorspace != WEBP_YUV420A) {
+    return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
+  }
+  return 1;
+}
+
 static void WebPPictureResetBufferARGB(WebPPicture* const picture) {
   picture->memory_argb_ = NULL;
   picture->argb = NULL;
@@ -63,18 +79,17 @@
   WebPPictureResetBufferYUVA(picture);
 }
 
-int WebPPictureAllocARGB(WebPPicture* const picture, int width, int height) {
+int WebPPictureAllocARGB(WebPPicture* const picture) {
   void* memory;
+  const int width = picture->width;
+  const int height = picture->height;
   const uint64_t argb_size = (uint64_t)width * height;
 
-  assert(picture != NULL);
+  if (!WebPValidatePicture(picture)) return 0;
 
   WebPSafeFree(picture->memory_argb_);
   WebPPictureResetBufferARGB(picture);
 
-  if (width <= 0 || height <= 0) {
-    return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
-  }
   // allocate a new buffer.
   memory = WebPSafeMalloc(argb_size + WEBP_ALIGN_CST, sizeof(*picture->argb));
   if (memory == NULL) {
@@ -86,10 +101,10 @@
   return 1;
 }
 
-int WebPPictureAllocYUVA(WebPPicture* const picture, int width, int height) {
-  const WebPEncCSP uv_csp =
-      (WebPEncCSP)((int)picture->colorspace & WEBP_CSP_UV_MASK);
+int WebPPictureAllocYUVA(WebPPicture* const picture) {
   const int has_alpha = (int)picture->colorspace & WEBP_CSP_ALPHA_BIT;
+  const int width = picture->width;
+  const int height = picture->height;
   const int y_stride = width;
   const int uv_width = (int)(((int64_t)width + 1) >> 1);
   const int uv_height = (int)(((int64_t)height + 1) >> 1);
@@ -98,15 +113,11 @@
   uint64_t y_size, uv_size, a_size, total_size;
   uint8_t* mem;
 
-  assert(picture != NULL);
+  if (!WebPValidatePicture(picture)) return 0;
 
   WebPSafeFree(picture->memory_);
   WebPPictureResetBufferYUVA(picture);
 
-  if (uv_csp != WEBP_YUV420) {
-    return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
-  }
-
   // alpha
   a_width = has_alpha ? width : 0;
   a_stride = a_width;
@@ -152,15 +163,12 @@
 
 int WebPPictureAlloc(WebPPicture* picture) {
   if (picture != NULL) {
-    const int width = picture->width;
-    const int height = picture->height;
-
     WebPPictureFree(picture);   // erase previous buffer
 
     if (!picture->use_argb) {
-      return WebPPictureAllocYUVA(picture, width, height);
+      return WebPPictureAllocYUVA(picture);
     } else {
-      return WebPPictureAllocARGB(picture, width, height);
+      return WebPPictureAllocARGB(picture);
     }
   }
   return 1;
diff --git a/src/enc/picture_rescale_enc.c b/src/enc/picture_rescale_enc.c
index a75f5d9..839f91c 100644
--- a/src/enc/picture_rescale_enc.c
+++ b/src/enc/picture_rescale_enc.c
@@ -13,14 +13,15 @@
 
 #include "src/webp/encode.h"
 
-#if !defined(WEBP_REDUCE_SIZE)
-
 #include <assert.h>
 #include <stdlib.h>
 
 #include "src/enc/vp8i_enc.h"
+
+#if !defined(WEBP_REDUCE_SIZE)
 #include "src/utils/rescaler_utils.h"
 #include "src/utils/utils.h"
+#endif  // !defined(WEBP_REDUCE_SIZE)
 
 #define HALVE(x) (((x) + 1) >> 1)
 
@@ -56,6 +57,7 @@
   return 1;
 }
 
+#if !defined(WEBP_REDUCE_SIZE)
 int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) {
   if (src == NULL || dst == NULL) return 0;
   if (src == dst) return 1;
@@ -81,6 +83,7 @@
   }
   return 1;
 }
+#endif  // !defined(WEBP_REDUCE_SIZE)
 
 int WebPPictureIsView(const WebPPicture* picture) {
   if (picture == NULL) return 0;
@@ -120,6 +123,7 @@
   return 1;
 }
 
+#if !defined(WEBP_REDUCE_SIZE)
 //------------------------------------------------------------------------------
 // Picture cropping
 
@@ -198,34 +202,34 @@
   }
 }
 
-int WebPPictureRescale(WebPPicture* pic, int width, int height) {
+int WebPPictureRescale(WebPPicture* picture, int width, int height) {
   WebPPicture tmp;
   int prev_width, prev_height;
   rescaler_t* work;
 
-  if (pic == NULL) return 0;
-  prev_width = pic->width;
-  prev_height = pic->height;
+  if (picture == NULL) return 0;
+  prev_width = picture->width;
+  prev_height = picture->height;
   if (!WebPRescalerGetScaledDimensions(
           prev_width, prev_height, &width, &height)) {
     return 0;
   }
 
-  PictureGrabSpecs(pic, &tmp);
+  PictureGrabSpecs(picture, &tmp);
   tmp.width = width;
   tmp.height = height;
   if (!WebPPictureAlloc(&tmp)) return 0;
 
-  if (!pic->use_argb) {
+  if (!picture->use_argb) {
     work = (rescaler_t*)WebPSafeMalloc(2ULL * width, sizeof(*work));
     if (work == NULL) {
       WebPPictureFree(&tmp);
       return 0;
     }
     // If present, we need to rescale alpha first (for AlphaMultiplyY).
-    if (pic->a != NULL) {
+    if (picture->a != NULL) {
       WebPInitAlphaProcessing();
-      if (!RescalePlane(pic->a, prev_width, prev_height, pic->a_stride,
+      if (!RescalePlane(picture->a, prev_width, prev_height, picture->a_stride,
                         tmp.a, width, height, tmp.a_stride, work, 1)) {
         return 0;
       }
@@ -233,17 +237,15 @@
 
     // We take transparency into account on the luma plane only. That's not
     // totally exact blending, but still is a good approximation.
-    AlphaMultiplyY(pic, 0);
-    if (!RescalePlane(pic->y, prev_width, prev_height, pic->y_stride,
+    AlphaMultiplyY(picture, 0);
+    if (!RescalePlane(picture->y, prev_width, prev_height, picture->y_stride,
                       tmp.y, width, height, tmp.y_stride, work, 1) ||
-        !RescalePlane(pic->u,
-                      HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
-                      tmp.u,
-                      HALVE(width), HALVE(height), tmp.uv_stride, work, 1) ||
-        !RescalePlane(pic->v,
-                      HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
-                      tmp.v,
-                      HALVE(width), HALVE(height), tmp.uv_stride, work, 1)) {
+        !RescalePlane(picture->u, HALVE(prev_width), HALVE(prev_height),
+                      picture->uv_stride, tmp.u, HALVE(width), HALVE(height),
+                      tmp.uv_stride, work, 1) ||
+        !RescalePlane(picture->v, HALVE(prev_width), HALVE(prev_height),
+                      picture->uv_stride, tmp.v, HALVE(width), HALVE(height),
+                      tmp.uv_stride, work, 1)) {
       return 0;
     }
     AlphaMultiplyY(&tmp, 1);
@@ -257,18 +259,17 @@
     // weighting first (black-matting), scale the RGB values, and remove
     // the premultiplication afterward (while preserving the alpha channel).
     WebPInitAlphaProcessing();
-    AlphaMultiplyARGB(pic, 0);
-    if (!RescalePlane((const uint8_t*)pic->argb, prev_width, prev_height,
-                      pic->argb_stride * 4,
-                      (uint8_t*)tmp.argb, width, height,
-                      tmp.argb_stride * 4, work, 4)) {
+    AlphaMultiplyARGB(picture, 0);
+    if (!RescalePlane((const uint8_t*)picture->argb, prev_width, prev_height,
+                      picture->argb_stride * 4, (uint8_t*)tmp.argb, width,
+                      height, tmp.argb_stride * 4, work, 4)) {
       return 0;
     }
     AlphaMultiplyARGB(&tmp, 1);
   }
-  WebPPictureFree(pic);
+  WebPPictureFree(picture);
   WebPSafeFree(work);
-  *pic = tmp;
+  *picture = tmp;
   return 1;
 }
 
@@ -280,23 +281,6 @@
   return 0;
 }
 
-int WebPPictureIsView(const WebPPicture* picture) {
-  (void)picture;
-  return 0;
-}
-
-int WebPPictureView(const WebPPicture* src,
-                    int left, int top, int width, int height,
-                    WebPPicture* dst) {
-  (void)src;
-  (void)left;
-  (void)top;
-  (void)width;
-  (void)height;
-  (void)dst;
-  return 0;
-}
-
 int WebPPictureCrop(WebPPicture* pic,
                     int left, int top, int width, int height) {
   (void)pic;
diff --git a/src/enc/picture_tools_enc.c b/src/enc/picture_tools_enc.c
index 38cb015..147cc18 100644
--- a/src/enc/picture_tools_enc.c
+++ b/src/enc/picture_tools_enc.c
@@ -190,27 +190,28 @@
   return (0xff000000u | (r << 16) | (g << 8) | b);
 }
 
-void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb) {
+void WebPBlendAlpha(WebPPicture* picture, uint32_t background_rgb) {
   const int red = (background_rgb >> 16) & 0xff;
   const int green = (background_rgb >> 8) & 0xff;
   const int blue = (background_rgb >> 0) & 0xff;
   int x, y;
-  if (pic == NULL) return;
-  if (!pic->use_argb) {
-    const int uv_width = (pic->width >> 1);  // omit last pixel during u/v loop
+  if (picture == NULL) return;
+  if (!picture->use_argb) {
+    // omit last pixel during u/v loop
+    const int uv_width = (picture->width >> 1);
     const int Y0 = VP8RGBToY(red, green, blue, YUV_HALF);
     // VP8RGBToU/V expects the u/v values summed over four pixels
     const int U0 = VP8RGBToU(4 * red, 4 * green, 4 * blue, 4 * YUV_HALF);
     const int V0 = VP8RGBToV(4 * red, 4 * green, 4 * blue, 4 * YUV_HALF);
-    const int has_alpha = pic->colorspace & WEBP_CSP_ALPHA_BIT;
-    uint8_t* y_ptr = pic->y;
-    uint8_t* u_ptr = pic->u;
-    uint8_t* v_ptr = pic->v;
-    uint8_t* a_ptr = pic->a;
+    const int has_alpha = picture->colorspace & WEBP_CSP_ALPHA_BIT;
+    uint8_t* y_ptr = picture->y;
+    uint8_t* u_ptr = picture->u;
+    uint8_t* v_ptr = picture->v;
+    uint8_t* a_ptr = picture->a;
     if (!has_alpha || a_ptr == NULL) return;    // nothing to do
-    for (y = 0; y < pic->height; ++y) {
+    for (y = 0; y < picture->height; ++y) {
       // Luma blending
-      for (x = 0; x < pic->width; ++x) {
+      for (x = 0; x < picture->width; ++x) {
         const uint8_t alpha = a_ptr[x];
         if (alpha < 0xff) {
           y_ptr[x] = BLEND(Y0, y_ptr[x], alpha);
@@ -219,7 +220,7 @@
       // Chroma blending every even line
       if ((y & 1) == 0) {
         uint8_t* const a_ptr2 =
-            (y + 1 == pic->height) ? a_ptr : a_ptr + pic->a_stride;
+            (y + 1 == picture->height) ? a_ptr : a_ptr + picture->a_stride;
         for (x = 0; x < uv_width; ++x) {
           // Average four alpha values into a single blending weight.
           // TODO(skal): might lead to visible contouring. Can we do better?
@@ -229,24 +230,24 @@
           u_ptr[x] = BLEND_10BIT(U0, u_ptr[x], alpha);
           v_ptr[x] = BLEND_10BIT(V0, v_ptr[x], alpha);
         }
-        if (pic->width & 1) {   // rightmost pixel
+        if (picture->width & 1) {  // rightmost pixel
           const uint32_t alpha = 2 * (a_ptr[2 * x + 0] + a_ptr2[2 * x + 0]);
           u_ptr[x] = BLEND_10BIT(U0, u_ptr[x], alpha);
           v_ptr[x] = BLEND_10BIT(V0, v_ptr[x], alpha);
         }
       } else {
-        u_ptr += pic->uv_stride;
-        v_ptr += pic->uv_stride;
+        u_ptr += picture->uv_stride;
+        v_ptr += picture->uv_stride;
       }
-      memset(a_ptr, 0xff, pic->width);  // reset alpha value to opaque
-      a_ptr += pic->a_stride;
-      y_ptr += pic->y_stride;
+      memset(a_ptr, 0xff, picture->width);  // reset alpha value to opaque
+      a_ptr += picture->a_stride;
+      y_ptr += picture->y_stride;
     }
   } else {
-    uint32_t* argb = pic->argb;
+    uint32_t* argb = picture->argb;
     const uint32_t background = MakeARGB32(red, green, blue);
-    for (y = 0; y < pic->height; ++y) {
-      for (x = 0; x < pic->width; ++x) {
+    for (y = 0; y < picture->height; ++y) {
+      for (x = 0; x < picture->width; ++x) {
         const int alpha = (argb[x] >> 24) & 0xff;
         if (alpha != 0xff) {
           if (alpha > 0) {
@@ -262,7 +263,7 @@
           }
         }
       }
-      argb += pic->argb_stride;
+      argb += picture->argb_stride;
     }
   }
 }
diff --git a/src/enc/predictor_enc.c b/src/enc/predictor_enc.c
index 2b5c767..b3d44b5 100644
--- a/src/enc/predictor_enc.c
+++ b/src/enc/predictor_enc.c
@@ -16,6 +16,7 @@
 
 #include "src/dsp/lossless.h"
 #include "src/dsp/lossless_common.h"
+#include "src/enc/vp8i_enc.h"
 #include "src/enc/vp8li_enc.h"
 
 #define MAX_DIFF_COST (1e30f)
@@ -31,10 +32,10 @@
 // Methods to calculate Entropy (Shannon).
 
 static float PredictionCostSpatial(const int counts[256], int weight_0,
-                                   double exp_val) {
+                                   float exp_val) {
   const int significant_symbols = 256 >> 4;
-  const double exp_decay_factor = 0.6;
-  double bits = weight_0 * counts[0];
+  const float exp_decay_factor = 0.6f;
+  float bits = (float)weight_0 * counts[0];
   int i;
   for (i = 1; i < significant_symbols; ++i) {
     bits += exp_val * (counts[i] + counts[256 - i]);
@@ -46,9 +47,9 @@
 static float PredictionCostSpatialHistogram(const int accumulated[4][256],
                                             const int tile[4][256]) {
   int i;
-  double retval = 0;
+  float retval = 0.f;
   for (i = 0; i < 4; ++i) {
-    const double kExpValue = 0.94;
+    const float kExpValue = 0.94f;
     retval += PredictionCostSpatial(tile[i], 1, kExpValue);
     retval += VP8LCombinedShannonEntropy(tile[i], accumulated[i]);
   }
@@ -472,12 +473,15 @@
 // with respect to predictions. If near_lossless_quality < 100, applies
 // near lossless processing, shaving off more bits of residuals for lower
 // qualities.
-void VP8LResidualImage(int width, int height, int bits, int low_effort,
-                       uint32_t* const argb, uint32_t* const argb_scratch,
-                       uint32_t* const image, int near_lossless_quality,
-                       int exact, int used_subtract_green) {
+int VP8LResidualImage(int width, int height, int bits, int low_effort,
+                      uint32_t* const argb, uint32_t* const argb_scratch,
+                      uint32_t* const image, int near_lossless_quality,
+                      int exact, int used_subtract_green,
+                      const WebPPicture* const pic, int percent_range,
+                      int* const percent) {
   const int tiles_per_row = VP8LSubSampleSize(width, bits);
   const int tiles_per_col = VP8LSubSampleSize(height, bits);
+  int percent_start = *percent;
   int tile_y;
   int histo[4][256];
   const int max_quantization = 1 << VP8LNearLosslessBits(near_lossless_quality);
@@ -491,17 +495,24 @@
     for (tile_y = 0; tile_y < tiles_per_col; ++tile_y) {
       int tile_x;
       for (tile_x = 0; tile_x < tiles_per_row; ++tile_x) {
-        const int pred = GetBestPredictorForTile(width, height, tile_x, tile_y,
-            bits, histo, argb_scratch, argb, max_quantization, exact,
-            used_subtract_green, image);
+        const int pred = GetBestPredictorForTile(
+            width, height, tile_x, tile_y, bits, histo, argb_scratch, argb,
+            max_quantization, exact, used_subtract_green, image);
         image[tile_y * tiles_per_row + tile_x] = ARGB_BLACK | (pred << 8);
       }
+
+      if (!WebPReportProgress(
+              pic, percent_start + percent_range * tile_y / tiles_per_col,
+              percent)) {
+        return 0;
+      }
     }
   }
 
   CopyImageWithPrediction(width, height, bits, image, argb_scratch, argb,
                           low_effort, max_quantization, exact,
                           used_subtract_green);
+  return WebPReportProgress(pic, percent_start + percent_range, percent);
 }
 
 //------------------------------------------------------------------------------
@@ -532,7 +543,7 @@
                                       const int counts[256]) {
   // Favor low entropy, locally and globally.
   // Favor small absolute values for PredictionCostSpatial
-  static const double kExpValue = 2.4;
+  static const float kExpValue = 2.4f;
   return VP8LCombinedShannonEntropy(counts, accumulated) +
          PredictionCostSpatial(counts, 3, kExpValue);
 }
@@ -714,11 +725,14 @@
   }
 }
 
-void VP8LColorSpaceTransform(int width, int height, int bits, int quality,
-                             uint32_t* const argb, uint32_t* image) {
+int VP8LColorSpaceTransform(int width, int height, int bits, int quality,
+                            uint32_t* const argb, uint32_t* image,
+                            const WebPPicture* const pic, int percent_range,
+                            int* const percent) {
   const int max_tile_size = 1 << bits;
   const int tile_xsize = VP8LSubSampleSize(width, bits);
   const int tile_ysize = VP8LSubSampleSize(height, bits);
+  int percent_start = *percent;
   int accumulated_red_histo[256] = { 0 };
   int accumulated_blue_histo[256] = { 0 };
   int tile_x, tile_y;
@@ -768,5 +782,11 @@
         }
       }
     }
+    if (!WebPReportProgress(
+            pic, percent_start + percent_range * tile_y / tile_ysize,
+            percent)) {
+      return 0;
+    }
   }
+  return 1;
 }
diff --git a/src/enc/quant_enc.c b/src/enc/quant_enc.c
index 6cede28..6d8202d 100644
--- a/src/enc/quant_enc.c
+++ b/src/enc/quant_enc.c
@@ -533,7 +533,8 @@
   rd->score = MAX_COST;
 }
 
-static void CopyScore(VP8ModeScore* const dst, const VP8ModeScore* const src) {
+static void CopyScore(VP8ModeScore* WEBP_RESTRICT const dst,
+                      const VP8ModeScore* WEBP_RESTRICT const src) {
   dst->D  = src->D;
   dst->SD = src->SD;
   dst->R  = src->R;
@@ -542,7 +543,8 @@
   dst->score = src->score;
 }
 
-static void AddScore(VP8ModeScore* const dst, const VP8ModeScore* const src) {
+static void AddScore(VP8ModeScore* WEBP_RESTRICT const dst,
+                     const VP8ModeScore* WEBP_RESTRICT const src) {
   dst->D  += src->D;
   dst->SD += src->SD;
   dst->R  += src->R;
@@ -588,10 +590,10 @@
 // Coefficient type.
 enum { TYPE_I16_AC = 0, TYPE_I16_DC = 1, TYPE_CHROMA_A = 2, TYPE_I4_AC = 3 };
 
-static int TrellisQuantizeBlock(const VP8Encoder* const enc,
+static int TrellisQuantizeBlock(const VP8Encoder* WEBP_RESTRICT const enc,
                                 int16_t in[16], int16_t out[16],
                                 int ctx0, int coeff_type,
-                                const VP8Matrix* const mtx,
+                                const VP8Matrix* WEBP_RESTRICT const mtx,
                                 int lambda) {
   const ProbaArray* const probas = enc->proba_.coeffs_[coeff_type];
   CostArrayPtr const costs =
@@ -767,9 +769,9 @@
 // all at once. Output is the reconstructed block in *yuv_out, and the
 // quantized levels in *levels.
 
-static int ReconstructIntra16(VP8EncIterator* const it,
-                              VP8ModeScore* const rd,
-                              uint8_t* const yuv_out,
+static int ReconstructIntra16(VP8EncIterator* WEBP_RESTRICT const it,
+                              VP8ModeScore* WEBP_RESTRICT const rd,
+                              uint8_t* WEBP_RESTRICT const yuv_out,
                               int mode) {
   const VP8Encoder* const enc = it->enc_;
   const uint8_t* const ref = it->yuv_p_ + VP8I16ModeOffsets[mode];
@@ -819,10 +821,10 @@
   return nz;
 }
 
-static int ReconstructIntra4(VP8EncIterator* const it,
+static int ReconstructIntra4(VP8EncIterator* WEBP_RESTRICT const it,
                              int16_t levels[16],
-                             const uint8_t* const src,
-                             uint8_t* const yuv_out,
+                             const uint8_t* WEBP_RESTRICT const src,
+                             uint8_t* WEBP_RESTRICT const yuv_out,
                              int mode) {
   const VP8Encoder* const enc = it->enc_;
   const uint8_t* const ref = it->yuv_p_ + VP8I4ModeOffsets[mode];
@@ -855,7 +857,8 @@
 
 // Quantize as usual, but also compute and return the quantization error.
 // Error is already divided by DSHIFT.
-static int QuantizeSingle(int16_t* const v, const VP8Matrix* const mtx) {
+static int QuantizeSingle(int16_t* WEBP_RESTRICT const v,
+                          const VP8Matrix* WEBP_RESTRICT const mtx) {
   int V = *v;
   const int sign = (V < 0);
   if (sign) V = -V;
@@ -869,9 +872,10 @@
   return (sign ? -V : V) >> DSCALE;
 }
 
-static void CorrectDCValues(const VP8EncIterator* const it,
-                            const VP8Matrix* const mtx,
-                            int16_t tmp[][16], VP8ModeScore* const rd) {
+static void CorrectDCValues(const VP8EncIterator* WEBP_RESTRICT const it,
+                            const VP8Matrix* WEBP_RESTRICT const mtx,
+                            int16_t tmp[][16],
+                            VP8ModeScore* WEBP_RESTRICT const rd) {
   //         | top[0] | top[1]
   // --------+--------+---------
   // left[0] | tmp[0]   tmp[1]  <->   err0 err1
@@ -902,8 +906,8 @@
   }
 }
 
-static void StoreDiffusionErrors(VP8EncIterator* const it,
-                                 const VP8ModeScore* const rd) {
+static void StoreDiffusionErrors(VP8EncIterator* WEBP_RESTRICT const it,
+                                 const VP8ModeScore* WEBP_RESTRICT const rd) {
   int ch;
   for (ch = 0; ch <= 1; ++ch) {
     int8_t* const top = it->top_derr_[it->x_][ch];
@@ -922,8 +926,9 @@
 
 //------------------------------------------------------------------------------
 
-static int ReconstructUV(VP8EncIterator* const it, VP8ModeScore* const rd,
-                         uint8_t* const yuv_out, int mode) {
+static int ReconstructUV(VP8EncIterator* WEBP_RESTRICT const it,
+                         VP8ModeScore* WEBP_RESTRICT const rd,
+                         uint8_t* WEBP_RESTRICT const yuv_out, int mode) {
   const VP8Encoder* const enc = it->enc_;
   const uint8_t* const ref = it->yuv_p_ + VP8UVModeOffsets[mode];
   const uint8_t* const src = it->yuv_in_ + U_OFF_ENC;
@@ -994,7 +999,8 @@
   SwapPtr(&it->yuv_out_, &it->yuv_out2_);
 }
 
-static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* rd) {
+static void PickBestIntra16(VP8EncIterator* WEBP_RESTRICT const it,
+                            VP8ModeScore* WEBP_RESTRICT rd) {
   const int kNumBlocks = 16;
   VP8SegmentInfo* const dqm = &it->enc_->dqm_[it->mb_->segment_];
   const int lambda = dqm->lambda_i16_;
@@ -1054,7 +1060,7 @@
 //------------------------------------------------------------------------------
 
 // return the cost array corresponding to the surrounding prediction modes.
-static const uint16_t* GetCostModeI4(VP8EncIterator* const it,
+static const uint16_t* GetCostModeI4(VP8EncIterator* WEBP_RESTRICT const it,
                                      const uint8_t modes[16]) {
   const int preds_w = it->enc_->preds_w_;
   const int x = (it->i4_ & 3), y = it->i4_ >> 2;
@@ -1063,7 +1069,8 @@
   return VP8FixedCostsI4[top][left];
 }
 
-static int PickBestIntra4(VP8EncIterator* const it, VP8ModeScore* const rd) {
+static int PickBestIntra4(VP8EncIterator* WEBP_RESTRICT const it,
+                          VP8ModeScore* WEBP_RESTRICT const rd) {
   const VP8Encoder* const enc = it->enc_;
   const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_];
   const int lambda = dqm->lambda_i4_;
@@ -1159,7 +1166,8 @@
 
 //------------------------------------------------------------------------------
 
-static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) {
+static void PickBestUV(VP8EncIterator* WEBP_RESTRICT const it,
+                       VP8ModeScore* WEBP_RESTRICT const rd) {
   const int kNumBlocks = 8;
   const VP8SegmentInfo* const dqm = &it->enc_->dqm_[it->mb_->segment_];
   const int lambda = dqm->lambda_uv_;
@@ -1211,7 +1219,8 @@
 //------------------------------------------------------------------------------
 // Final reconstruction and quantization.
 
-static void SimpleQuantize(VP8EncIterator* const it, VP8ModeScore* const rd) {
+static void SimpleQuantize(VP8EncIterator* WEBP_RESTRICT const it,
+                           VP8ModeScore* WEBP_RESTRICT const rd) {
   const VP8Encoder* const enc = it->enc_;
   const int is_i16 = (it->mb_->type_ == 1);
   int nz = 0;
@@ -1236,9 +1245,9 @@
 }
 
 // Refine intra16/intra4 sub-modes based on distortion only (not rate).
-static void RefineUsingDistortion(VP8EncIterator* const it,
+static void RefineUsingDistortion(VP8EncIterator* WEBP_RESTRICT const it,
                                   int try_both_modes, int refine_uv_mode,
-                                  VP8ModeScore* const rd) {
+                                  VP8ModeScore* WEBP_RESTRICT const rd) {
   score_t best_score = MAX_COST;
   int nz = 0;
   int mode;
@@ -1352,7 +1361,8 @@
 //------------------------------------------------------------------------------
 // Entry point
 
-int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd,
+int VP8Decimate(VP8EncIterator* WEBP_RESTRICT const it,
+                VP8ModeScore* WEBP_RESTRICT const rd,
                 VP8RDLevel rd_opt) {
   int is_skipped;
   const int method = it->enc_->method_;
diff --git a/src/enc/vp8i_enc.h b/src/enc/vp8i_enc.h
index b4bba08..c9927c4 100644
--- a/src/enc/vp8i_enc.h
+++ b/src/enc/vp8i_enc.h
@@ -31,8 +31,8 @@
 
 // version numbers
 #define ENC_MAJ_VERSION 1
-#define ENC_MIN_VERSION 2
-#define ENC_REV_VERSION 2
+#define ENC_MIN_VERSION 3
+#define ENC_REV_VERSION 0
 
 enum { MAX_LF_LEVELS = 64,       // Maximum loop filter level
        MAX_VARIABLE_LEVEL = 67,  // last (inclusive) level with variable cost
@@ -470,7 +470,8 @@
 // Sets up segment's quantization values, base_quant_ and filter strengths.
 void VP8SetSegmentParams(VP8Encoder* const enc, float quality);
 // Pick best modes and fills the levels. Returns true if skipped.
-int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd,
+int VP8Decimate(VP8EncIterator* WEBP_RESTRICT const it,
+                VP8ModeScore* WEBP_RESTRICT const rd,
                 VP8RDLevel rd_opt);
 
   // in alpha.c
@@ -490,19 +491,24 @@
 
   // misc utils for picture_*.c:
 
+// Returns true if 'picture' is non-NULL and dimensions/colorspace are within
+// their valid ranges. If returning false, the 'error_code' in 'picture' is
+// updated.
+int WebPValidatePicture(const WebPPicture* const picture);
+
 // Remove reference to the ARGB/YUVA buffer (doesn't free anything).
 void WebPPictureResetBuffers(WebPPicture* const picture);
 
-// Allocates ARGB buffer of given dimension (previous one is always free'd).
-// Preserves the YUV(A) buffer. Returns false in case of error (invalid param,
-// out-of-memory).
-int WebPPictureAllocARGB(WebPPicture* const picture, int width, int height);
+// Allocates ARGB buffer according to set width/height (previous one is
+// always free'd). Preserves the YUV(A) buffer. Returns false in case of error
+// (invalid param, out-of-memory).
+int WebPPictureAllocARGB(WebPPicture* const picture);
 
-// Allocates YUVA buffer of given dimension (previous one is always free'd).
-// Uses picture->csp to determine whether an alpha buffer is needed.
+// Allocates YUVA buffer according to set width/height (previous one is always
+// free'd). Uses picture->csp to determine whether an alpha buffer is needed.
 // Preserves the ARGB buffer.
 // Returns false in case of error (invalid param, out-of-memory).
-int WebPPictureAllocYUVA(WebPPicture* const picture, int width, int height);
+int WebPPictureAllocYUVA(WebPPicture* const picture);
 
 // Replace samples that are fully transparent by 'color' to help compressibility
 // (no guarantee, though). Assumes pic->use_argb is true.
diff --git a/src/enc/vp8l_enc.c b/src/enc/vp8l_enc.c
index 38aabb8..cb5549a 100644
--- a/src/enc/vp8l_enc.c
+++ b/src/enc/vp8l_enc.c
@@ -15,15 +15,16 @@
 #include <assert.h>
 #include <stdlib.h>
 
+#include "src/dsp/lossless.h"
+#include "src/dsp/lossless_common.h"
 #include "src/enc/backward_references_enc.h"
 #include "src/enc/histogram_enc.h"
 #include "src/enc/vp8i_enc.h"
 #include "src/enc/vp8li_enc.h"
-#include "src/dsp/lossless.h"
-#include "src/dsp/lossless_common.h"
 #include "src/utils/bit_writer_utils.h"
 #include "src/utils/huffman_encode_utils.h"
 #include "src/utils/utils.h"
+#include "src/webp/encode.h"
 #include "src/webp/format_constants.h"
 
 // Maximum number of histogram images (sub-blocks).
@@ -183,10 +184,9 @@
 }
 
 // Builds the cooccurrence matrix
-static WebPEncodingError CoOccurrenceBuild(const WebPPicture* const pic,
-                                           const uint32_t* const palette,
-                                           uint32_t num_colors,
-                                           uint32_t* cooccurrence) {
+static int CoOccurrenceBuild(const WebPPicture* const pic,
+                             const uint32_t* const palette, uint32_t num_colors,
+                             uint32_t* cooccurrence) {
   uint32_t *lines, *line_top, *line_current, *line_tmp;
   int x, y;
   const uint32_t* src = pic->argb;
@@ -195,7 +195,10 @@
   uint32_t idx_map[MAX_PALETTE_SIZE] = {0};
   uint32_t palette_sorted[MAX_PALETTE_SIZE];
   lines = (uint32_t*)WebPSafeMalloc(2 * pic->width, sizeof(*lines));
-  if (lines == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
+  if (lines == NULL) {
+    WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+    return 0;
+  }
   line_top = &lines[0];
   line_current = &lines[pic->width];
   PrepareMapToPalette(palette, num_colors, palette_sorted, idx_map);
@@ -226,7 +229,7 @@
     src += pic->argb_stride;
   }
   WebPSafeFree(lines);
-  return VP8_ENC_OK;
+  return 1;
 }
 
 struct Sum {
@@ -237,7 +240,7 @@
 // Implements the modified Zeng method from "A Survey on Palette Reordering
 // Methods for Improving the Compression of Color-Indexed Images" by Armando J.
 // Pinho and Antonio J. R. Neves.
-static WebPEncodingError PaletteSortModifiedZeng(
+static int PaletteSortModifiedZeng(
     const WebPPicture* const pic, const uint32_t* const palette_sorted,
     uint32_t num_colors, uint32_t* const palette) {
   uint32_t i, j, ind;
@@ -247,15 +250,17 @@
   uint32_t first, last;
   uint32_t num_sums;
   // TODO(vrabaud) check whether one color images should use palette or not.
-  if (num_colors <= 1) return VP8_ENC_OK;
+  if (num_colors <= 1) return 1;
   // Build the co-occurrence matrix.
   cooccurrence =
       (uint32_t*)WebPSafeCalloc(num_colors * num_colors, sizeof(*cooccurrence));
-  if (cooccurrence == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
-  if (CoOccurrenceBuild(pic, palette_sorted, num_colors, cooccurrence) !=
-      VP8_ENC_OK) {
+  if (cooccurrence == NULL) {
+    WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+    return 0;
+  }
+  if (!CoOccurrenceBuild(pic, palette_sorted, num_colors, cooccurrence)) {
     WebPSafeFree(cooccurrence);
-    return VP8_ENC_ERROR_OUT_OF_MEMORY;
+    return 0;
   }
 
   // Initialize the mapping list with the two best indices.
@@ -316,7 +321,7 @@
   for (i = 0; i < num_colors; ++i) {
     palette[i] = palette_sorted[remapping[(first + i) % num_colors]];
   }
-  return VP8_ENC_OK;
+  return 1;
 }
 
 // -----------------------------------------------------------------------------
@@ -357,10 +362,11 @@
   kHistoTotal  // Must be last.
 } HistoIx;
 
-static void AddSingleSubGreen(int p, uint32_t* const r, uint32_t* const b) {
-  const int green = p >> 8;  // The upper bits are masked away later.
-  ++r[((p >> 16) - green) & 0xff];
-  ++b[((p >>  0) - green) & 0xff];
+static void AddSingleSubGreen(uint32_t p,
+                              uint32_t* const r, uint32_t* const b) {
+  const int green = (int)p >> 8;  // The upper bits are masked away later.
+  ++r[(((int)p >> 16) - green) & 0xff];
+  ++b[(((int)p >>  0) - green) & 0xff];
 }
 
 static void AddSingle(uint32_t p,
@@ -434,8 +440,8 @@
       curr_row += argb_stride;
     }
     {
-      double entropy_comp[kHistoTotal];
-      double entropy[kNumEntropyIx];
+      float entropy_comp[kHistoTotal];
+      float entropy[kNumEntropyIx];
       int k;
       int last_mode_to_analyze = use_palette ? kPalette : kSpatialSubGreen;
       int j;
@@ -949,11 +955,11 @@
   VP8LPutBits(bw, (bits << depth) | symbol, depth + n_bits);
 }
 
-static WebPEncodingError StoreImageToBitMask(
+static int StoreImageToBitMask(
     VP8LBitWriter* const bw, int width, int histo_bits,
     const VP8LBackwardRefs* const refs,
     const uint16_t* histogram_symbols,
-    const HuffmanTreeCode* const huffman_codes) {
+    const HuffmanTreeCode* const huffman_codes, const WebPPicture* const pic) {
   const int histo_xsize = histo_bits ? VP8LSubSampleSize(width, histo_bits) : 1;
   const int tile_mask = (histo_bits == 0) ? 0 : -(1 << histo_bits);
   // x and y trace the position in the image.
@@ -1006,44 +1012,53 @@
     }
     VP8LRefsCursorNext(&c);
   }
-  return bw->error_ ? VP8_ENC_ERROR_OUT_OF_MEMORY : VP8_ENC_OK;
+  if (bw->error_) {
+    WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+    return 0;
+  }
+  return 1;
 }
 
-// Special case of EncodeImageInternal() for cache-bits=0, histo_bits=31
-static WebPEncodingError EncodeImageNoHuffman(
-    VP8LBitWriter* const bw, const uint32_t* const argb,
-    VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs_array,
-    int width, int height, int quality, int low_effort) {
+// Special case of EncodeImageInternal() for cache-bits=0, histo_bits=31.
+// pic and percent are for progress.
+static int EncodeImageNoHuffman(VP8LBitWriter* const bw,
+                                const uint32_t* const argb,
+                                VP8LHashChain* const hash_chain,
+                                VP8LBackwardRefs* const refs_array, int width,
+                                int height, int quality, int low_effort,
+                                const WebPPicture* const pic, int percent_range,
+                                int* const percent) {
   int i;
   int max_tokens = 0;
-  WebPEncodingError err = VP8_ENC_OK;
   VP8LBackwardRefs* refs;
   HuffmanTreeToken* tokens = NULL;
-  HuffmanTreeCode huffman_codes[5] = { { 0, NULL, NULL } };
-  const uint16_t histogram_symbols[1] = { 0 };    // only one tree, one symbol
+  HuffmanTreeCode huffman_codes[5] = {{0, NULL, NULL}};
+  const uint16_t histogram_symbols[1] = {0};  // only one tree, one symbol
   int cache_bits = 0;
   VP8LHistogramSet* histogram_image = NULL;
   HuffmanTree* const huff_tree = (HuffmanTree*)WebPSafeMalloc(
-        3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree));
+      3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree));
   if (huff_tree == NULL) {
-    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+    WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
     goto Error;
   }
 
   // Calculate backward references from ARGB image.
-  if (!VP8LHashChainFill(hash_chain, quality, argb, width, height,
-                         low_effort)) {
-    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+  if (!VP8LHashChainFill(hash_chain, quality, argb, width, height, low_effort,
+                         pic, percent_range / 2, percent)) {
     goto Error;
   }
-  err = VP8LGetBackwardReferences(
-      width, height, argb, quality, /*low_effort=*/0, kLZ77Standard | kLZ77RLE,
-      cache_bits, /*do_no_cache=*/0, hash_chain, refs_array, &cache_bits);
-  if (err != VP8_ENC_OK) goto Error;
+  if (!VP8LGetBackwardReferences(width, height, argb, quality, /*low_effort=*/0,
+                                 kLZ77Standard | kLZ77RLE, cache_bits,
+                                 /*do_no_cache=*/0, hash_chain, refs_array,
+                                 &cache_bits, pic,
+                                 percent_range - percent_range / 2, percent)) {
+    goto Error;
+  }
   refs = &refs_array[0];
   histogram_image = VP8LAllocateHistogramSet(1, cache_bits);
   if (histogram_image == NULL) {
-    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+    WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
     goto Error;
   }
   VP8LHistogramSetClear(histogram_image);
@@ -1054,7 +1069,7 @@
   // Create Huffman bit lengths and codes for each histogram image.
   assert(histogram_image->size == 1);
   if (!GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
-    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+    WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
     goto Error;
   }
 
@@ -1071,7 +1086,7 @@
 
   tokens = (HuffmanTreeToken*)WebPSafeMalloc(max_tokens, sizeof(*tokens));
   if (tokens == NULL) {
-    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+    WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
     goto Error;
   }
 
@@ -1083,27 +1098,32 @@
   }
 
   // Store actual literals.
-  err = StoreImageToBitMask(bw, width, 0, refs, histogram_symbols,
-                            huffman_codes);
+  if (!StoreImageToBitMask(bw, width, 0, refs, histogram_symbols, huffman_codes,
+                           pic)) {
+    goto Error;
+  }
 
  Error:
   WebPSafeFree(tokens);
   WebPSafeFree(huff_tree);
   VP8LFreeHistogramSet(histogram_image);
   WebPSafeFree(huffman_codes[0].codes);
-  return err;
+  return (pic->error_code == VP8_ENC_OK);
 }
 
-static WebPEncodingError EncodeImageInternal(
+// pic and percent are for progress.
+static int EncodeImageInternal(
     VP8LBitWriter* const bw, const uint32_t* const argb,
     VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[4], int width,
     int height, int quality, int low_effort, int use_cache,
     const CrunchConfig* const config, int* cache_bits, int histogram_bits,
-    size_t init_byte_position, int* const hdr_size, int* const data_size) {
-  WebPEncodingError err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+    size_t init_byte_position, int* const hdr_size, int* const data_size,
+    const WebPPicture* const pic, int percent_range, int* const percent) {
   const uint32_t histogram_image_xysize =
       VP8LSubSampleSize(width, histogram_bits) *
       VP8LSubSampleSize(height, histogram_bits);
+  int remaining_percent = percent_range;
+  int percent_start = *percent;
   VP8LHistogramSet* histogram_image = NULL;
   VP8LHistogram* tmp_histo = NULL;
   int histogram_image_size = 0;
@@ -1112,9 +1132,8 @@
       3ULL * CODE_LENGTH_CODES, sizeof(*huff_tree));
   HuffmanTreeToken* tokens = NULL;
   HuffmanTreeCode* huffman_codes = NULL;
-  uint16_t* const histogram_symbols =
-      (uint16_t*)WebPSafeMalloc(histogram_image_xysize,
-                                sizeof(*histogram_symbols));
+  uint16_t* const histogram_symbols = (uint16_t*)WebPSafeMalloc(
+      histogram_image_xysize, sizeof(*histogram_symbols));
   int sub_configs_idx;
   int cache_bits_init, write_histogram_image;
   VP8LBitWriter bw_init = *bw, bw_best;
@@ -1126,14 +1145,27 @@
   assert(hdr_size != NULL);
   assert(data_size != NULL);
 
-  // Make sure we can allocate the different objects.
   memset(&hash_chain_histogram, 0, sizeof(hash_chain_histogram));
-  if (huff_tree == NULL || histogram_symbols == NULL ||
-      !VP8LHashChainInit(&hash_chain_histogram, histogram_image_xysize) ||
-      !VP8LHashChainFill(hash_chain, quality, argb, width, height,
-                         low_effort)) {
+  if (!VP8LBitWriterInit(&bw_best, 0)) {
+    WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
     goto Error;
   }
+
+  // Make sure we can allocate the different objects.
+  if (huff_tree == NULL || histogram_symbols == NULL ||
+      !VP8LHashChainInit(&hash_chain_histogram, histogram_image_xysize)) {
+    WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+    goto Error;
+  }
+
+  percent_range = remaining_percent / 5;
+  if (!VP8LHashChainFill(hash_chain, quality, argb, width, height,
+                         low_effort, pic, percent_range, percent)) {
+    goto Error;
+  }
+  percent_start += percent_range;
+  remaining_percent -= percent_range;
+
   if (use_cache) {
     // If the value is different from zero, it has been set during the
     // palette analysis.
@@ -1142,22 +1174,27 @@
     cache_bits_init = 0;
   }
   // If several iterations will happen, clone into bw_best.
-  if (!VP8LBitWriterInit(&bw_best, 0) ||
-      ((config->sub_configs_size_ > 1 ||
-        config->sub_configs_[0].do_no_cache_) &&
-       !VP8LBitWriterClone(bw, &bw_best))) {
+  if ((config->sub_configs_size_ > 1 || config->sub_configs_[0].do_no_cache_) &&
+      !VP8LBitWriterClone(bw, &bw_best)) {
+    WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
     goto Error;
   }
+
   for (sub_configs_idx = 0; sub_configs_idx < config->sub_configs_size_;
        ++sub_configs_idx) {
     const CrunchSubConfig* const sub_config =
         &config->sub_configs_[sub_configs_idx];
     int cache_bits_best, i_cache;
-    err = VP8LGetBackwardReferences(width, height, argb, quality, low_effort,
-                                    sub_config->lz77_, cache_bits_init,
-                                    sub_config->do_no_cache_, hash_chain,
-                                    &refs_array[0], &cache_bits_best);
-    if (err != VP8_ENC_OK) goto Error;
+    int i_remaining_percent = remaining_percent / config->sub_configs_size_;
+    int i_percent_range = i_remaining_percent / 4;
+    i_remaining_percent -= i_percent_range;
+
+    if (!VP8LGetBackwardReferences(
+            width, height, argb, quality, low_effort, sub_config->lz77_,
+            cache_bits_init, sub_config->do_no_cache_, hash_chain,
+            &refs_array[0], &cache_bits_best, pic, i_percent_range, percent)) {
+      goto Error;
+    }
 
     for (i_cache = 0; i_cache < (sub_config->do_no_cache_ ? 2 : 1); ++i_cache) {
       const int cache_bits_tmp = (i_cache == 0) ? cache_bits_best : 0;
@@ -1172,11 +1209,17 @@
       histogram_image =
           VP8LAllocateHistogramSet(histogram_image_xysize, cache_bits_tmp);
       tmp_histo = VP8LAllocateHistogram(cache_bits_tmp);
-      if (histogram_image == NULL || tmp_histo == NULL ||
-          !VP8LGetHistoImageSymbols(width, height, &refs_array[i_cache],
-                                    quality, low_effort, histogram_bits,
-                                    cache_bits_tmp, histogram_image, tmp_histo,
-                                    histogram_symbols)) {
+      if (histogram_image == NULL || tmp_histo == NULL) {
+        WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+        goto Error;
+      }
+
+      i_percent_range = i_remaining_percent / 3;
+      i_remaining_percent -= i_percent_range;
+      if (!VP8LGetHistoImageSymbols(
+              width, height, &refs_array[i_cache], quality, low_effort,
+              histogram_bits, cache_bits_tmp, histogram_image, tmp_histo,
+              histogram_symbols, pic, i_percent_range, percent)) {
         goto Error;
       }
       // Create Huffman bit lengths and codes for each histogram image.
@@ -1189,6 +1232,7 @@
       // GetHuffBitLengthsAndCodes().
       if (huffman_codes == NULL ||
           !GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) {
+        WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
         goto Error;
       }
       // Free combined histograms.
@@ -1211,12 +1255,14 @@
       write_histogram_image = (histogram_image_size > 1);
       VP8LPutBits(bw, write_histogram_image, 1);
       if (write_histogram_image) {
-        uint32_t* const histogram_argb =
-            (uint32_t*)WebPSafeMalloc(histogram_image_xysize,
-                                      sizeof(*histogram_argb));
+        uint32_t* const histogram_argb = (uint32_t*)WebPSafeMalloc(
+            histogram_image_xysize, sizeof(*histogram_argb));
         int max_index = 0;
         uint32_t i;
-        if (histogram_argb == NULL) goto Error;
+        if (histogram_argb == NULL) {
+          WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+          goto Error;
+        }
         for (i = 0; i < histogram_image_xysize; ++i) {
           const int symbol_index = histogram_symbols[i] & 0xffff;
           histogram_argb[i] = (symbol_index << 8);
@@ -1227,12 +1273,17 @@
         histogram_image_size = max_index;
 
         VP8LPutBits(bw, histogram_bits - 2, 3);
-        err = EncodeImageNoHuffman(
-            bw, histogram_argb, &hash_chain_histogram, &refs_array[2],
-            VP8LSubSampleSize(width, histogram_bits),
-            VP8LSubSampleSize(height, histogram_bits), quality, low_effort);
+        i_percent_range = i_remaining_percent / 2;
+        i_remaining_percent -= i_percent_range;
+        if (!EncodeImageNoHuffman(
+                bw, histogram_argb, &hash_chain_histogram, &refs_array[2],
+                VP8LSubSampleSize(width, histogram_bits),
+                VP8LSubSampleSize(height, histogram_bits), quality, low_effort,
+                pic, i_percent_range, percent)) {
+          WebPSafeFree(histogram_argb);
+          goto Error;
+        }
         WebPSafeFree(histogram_argb);
-        if (err != VP8_ENC_OK) goto Error;
       }
 
       // Store Huffman codes.
@@ -1256,9 +1307,10 @@
       }
       // Store actual literals.
       hdr_size_tmp = (int)(VP8LBitWriterNumBytes(bw) - init_byte_position);
-      err = StoreImageToBitMask(bw, width, histogram_bits, &refs_array[i_cache],
-                                histogram_symbols, huffman_codes);
-      if (err != VP8_ENC_OK) goto Error;
+      if (!StoreImageToBitMask(bw, width, histogram_bits, &refs_array[i_cache],
+                               histogram_symbols, huffman_codes, pic)) {
+        goto Error;
+      }
       // Keep track of the smallest image so far.
       if (VP8LBitWriterNumBytes(bw) < bw_size_best) {
         bw_size_best = VP8LBitWriterNumBytes(bw);
@@ -1278,7 +1330,10 @@
     }
   }
   VP8LBitWriterSwap(bw, &bw_best);
-  err = VP8_ENC_OK;
+
+  if (!WebPReportProgress(pic, percent_start + remaining_percent, percent)) {
+    goto Error;
+  }
 
  Error:
   WebPSafeFree(tokens);
@@ -1292,7 +1347,7 @@
   }
   WebPSafeFree(histogram_symbols);
   VP8LBitWriterWipeOut(&bw_best);
-  return err;
+  return (pic->error_code == VP8_ENC_OK);
 }
 
 // -----------------------------------------------------------------------------
@@ -1301,26 +1356,27 @@
 static void ApplySubtractGreen(VP8LEncoder* const enc, int width, int height,
                                VP8LBitWriter* const bw) {
   VP8LPutBits(bw, TRANSFORM_PRESENT, 1);
-  VP8LPutBits(bw, SUBTRACT_GREEN, 2);
+  VP8LPutBits(bw, SUBTRACT_GREEN_TRANSFORM, 2);
   VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height);
 }
 
-static WebPEncodingError ApplyPredictFilter(const VP8LEncoder* const enc,
-                                            int width, int height,
-                                            int quality, int low_effort,
-                                            int used_subtract_green,
-                                            VP8LBitWriter* const bw) {
+static int ApplyPredictFilter(const VP8LEncoder* const enc, int width,
+                              int height, int quality, int low_effort,
+                              int used_subtract_green, VP8LBitWriter* const bw,
+                              int percent_range, int* const percent) {
   const int pred_bits = enc->transform_bits_;
   const int transform_width = VP8LSubSampleSize(width, pred_bits);
   const int transform_height = VP8LSubSampleSize(height, pred_bits);
   // we disable near-lossless quantization if palette is used.
-  const int near_lossless_strength = enc->use_palette_ ? 100
-                                   : enc->config_->near_lossless;
+  const int near_lossless_strength =
+      enc->use_palette_ ? 100 : enc->config_->near_lossless;
 
-  VP8LResidualImage(width, height, pred_bits, low_effort, enc->argb_,
-                    enc->argb_scratch_, enc->transform_data_,
-                    near_lossless_strength, enc->config_->exact,
-                    used_subtract_green);
+  if (!VP8LResidualImage(
+          width, height, pred_bits, low_effort, enc->argb_, enc->argb_scratch_,
+          enc->transform_data_, near_lossless_strength, enc->config_->exact,
+          used_subtract_green, enc->pic_, percent_range / 2, percent)) {
+    return 0;
+  }
   VP8LPutBits(bw, TRANSFORM_PRESENT, 1);
   VP8LPutBits(bw, PREDICTOR_TRANSFORM, 2);
   assert(pred_bits >= 2);
@@ -1328,19 +1384,23 @@
   return EncodeImageNoHuffman(
       bw, enc->transform_data_, (VP8LHashChain*)&enc->hash_chain_,
       (VP8LBackwardRefs*)&enc->refs_[0], transform_width, transform_height,
-      quality, low_effort);
+      quality, low_effort, enc->pic_, percent_range - percent_range / 2,
+      percent);
 }
 
-static WebPEncodingError ApplyCrossColorFilter(const VP8LEncoder* const enc,
-                                               int width, int height,
-                                               int quality, int low_effort,
-                                               VP8LBitWriter* const bw) {
+static int ApplyCrossColorFilter(const VP8LEncoder* const enc, int width,
+                                 int height, int quality, int low_effort,
+                                 VP8LBitWriter* const bw, int percent_range,
+                                 int* const percent) {
   const int ccolor_transform_bits = enc->transform_bits_;
   const int transform_width = VP8LSubSampleSize(width, ccolor_transform_bits);
   const int transform_height = VP8LSubSampleSize(height, ccolor_transform_bits);
 
-  VP8LColorSpaceTransform(width, height, ccolor_transform_bits, quality,
-                          enc->argb_, enc->transform_data_);
+  if (!VP8LColorSpaceTransform(width, height, ccolor_transform_bits, quality,
+                               enc->argb_, enc->transform_data_, enc->pic_,
+                               percent_range / 2, percent)) {
+    return 0;
+  }
   VP8LPutBits(bw, TRANSFORM_PRESENT, 1);
   VP8LPutBits(bw, CROSS_COLOR_TRANSFORM, 2);
   assert(ccolor_transform_bits >= 2);
@@ -1348,23 +1408,21 @@
   return EncodeImageNoHuffman(
       bw, enc->transform_data_, (VP8LHashChain*)&enc->hash_chain_,
       (VP8LBackwardRefs*)&enc->refs_[0], transform_width, transform_height,
-      quality, low_effort);
+      quality, low_effort, enc->pic_, percent_range - percent_range / 2,
+      percent);
 }
 
 // -----------------------------------------------------------------------------
 
-static WebPEncodingError WriteRiffHeader(const WebPPicture* const pic,
-                                         size_t riff_size, size_t vp8l_size) {
+static int WriteRiffHeader(const WebPPicture* const pic, size_t riff_size,
+                           size_t vp8l_size) {
   uint8_t riff[RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + VP8L_SIGNATURE_SIZE] = {
     'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P',
     'V', 'P', '8', 'L', 0, 0, 0, 0, VP8L_MAGIC_BYTE,
   };
   PutLE32(riff + TAG_SIZE, (uint32_t)riff_size);
   PutLE32(riff + RIFF_HEADER_SIZE + TAG_SIZE, (uint32_t)vp8l_size);
-  if (!pic->writer(riff, sizeof(riff), pic)) {
-    return VP8_ENC_ERROR_BAD_WRITE;
-  }
-  return VP8_ENC_OK;
+  return pic->writer(riff, sizeof(riff), pic);
 }
 
 static int WriteImageSize(const WebPPicture* const pic,
@@ -1384,36 +1442,29 @@
   return !bw->error_;
 }
 
-static WebPEncodingError WriteImage(const WebPPicture* const pic,
-                                    VP8LBitWriter* const bw,
-                                    size_t* const coded_size) {
-  WebPEncodingError err = VP8_ENC_OK;
+static int WriteImage(const WebPPicture* const pic, VP8LBitWriter* const bw,
+                      size_t* const coded_size) {
   const uint8_t* const webpll_data = VP8LBitWriterFinish(bw);
   const size_t webpll_size = VP8LBitWriterNumBytes(bw);
   const size_t vp8l_size = VP8L_SIGNATURE_SIZE + webpll_size;
   const size_t pad = vp8l_size & 1;
   const size_t riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8l_size + pad;
 
-  err = WriteRiffHeader(pic, riff_size, vp8l_size);
-  if (err != VP8_ENC_OK) goto Error;
-
-  if (!pic->writer(webpll_data, webpll_size, pic)) {
-    err = VP8_ENC_ERROR_BAD_WRITE;
-    goto Error;
+  if (!WriteRiffHeader(pic, riff_size, vp8l_size) ||
+      !pic->writer(webpll_data, webpll_size, pic)) {
+    WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
+    return 0;
   }
 
   if (pad) {
     const uint8_t pad_byte[1] = { 0 };
     if (!pic->writer(pad_byte, 1, pic)) {
-      err = VP8_ENC_ERROR_BAD_WRITE;
-      goto Error;
+      WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_WRITE);
+      return 0;
     }
   }
   *coded_size = CHUNK_HEADER_SIZE + riff_size;
-  return VP8_ENC_OK;
-
- Error:
-  return err;
+  return 1;
 }
 
 // -----------------------------------------------------------------------------
@@ -1429,18 +1480,16 @@
 // Flags influencing the memory allocated:
 //  enc->transform_bits_
 //  enc->use_predict_, enc->use_cross_color_
-static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc,
-                                                 int width, int height) {
-  WebPEncodingError err = VP8_ENC_OK;
+static int AllocateTransformBuffer(VP8LEncoder* const enc, int width,
+                                   int height) {
   const uint64_t image_size = width * height;
   // VP8LResidualImage needs room for 2 scanlines of uint32 pixels with an extra
   // pixel in each, plus 2 regular scanlines of bytes.
   // TODO(skal): Clean up by using arithmetic in bytes instead of words.
   const uint64_t argb_scratch_size =
-      enc->use_predict_
-          ? (width + 1) * 2 +
-            (width * 2 + sizeof(uint32_t) - 1) / sizeof(uint32_t)
-          : 0;
+      enc->use_predict_ ? (width + 1) * 2 + (width * 2 + sizeof(uint32_t) - 1) /
+                                                sizeof(uint32_t)
+                        : 0;
   const uint64_t transform_data_size =
       (enc->use_predict_ || enc->use_cross_color_)
           ? VP8LSubSampleSize(width, enc->transform_bits_) *
@@ -1448,17 +1497,16 @@
           : 0;
   const uint64_t max_alignment_in_words =
       (WEBP_ALIGN_CST + sizeof(uint32_t) - 1) / sizeof(uint32_t);
-  const uint64_t mem_size =
-      image_size + max_alignment_in_words +
-      argb_scratch_size + max_alignment_in_words +
-      transform_data_size;
+  const uint64_t mem_size = image_size + max_alignment_in_words +
+                            argb_scratch_size + max_alignment_in_words +
+                            transform_data_size;
   uint32_t* mem = enc->transform_mem_;
   if (mem == NULL || mem_size > enc->transform_mem_size_) {
     ClearTransformBuffer(enc);
     mem = (uint32_t*)WebPSafeMalloc(mem_size, sizeof(*mem));
     if (mem == NULL) {
-      err = VP8_ENC_ERROR_OUT_OF_MEMORY;
-      goto Error;
+      WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
+      return 0;
     }
     enc->transform_mem_ = mem;
     enc->transform_mem_size_ = (size_t)mem_size;
@@ -1471,19 +1519,16 @@
   enc->transform_data_ = mem;
 
   enc->current_width_ = width;
- Error:
-  return err;
+  return 1;
 }
 
-static WebPEncodingError MakeInputImageCopy(VP8LEncoder* const enc) {
-  WebPEncodingError err = VP8_ENC_OK;
+static int MakeInputImageCopy(VP8LEncoder* const enc) {
   const WebPPicture* const picture = enc->pic_;
   const int width = picture->width;
   const int height = picture->height;
 
-  err = AllocateTransformBuffer(enc, width, height);
-  if (err != VP8_ENC_OK) return err;
-  if (enc->argb_content_ == kEncoderARGB) return VP8_ENC_OK;
+  if (!AllocateTransformBuffer(enc, width, height)) return 0;
+  if (enc->argb_content_ == kEncoderARGB) return 1;
 
   {
     uint32_t* dst = enc->argb_;
@@ -1497,7 +1542,7 @@
   }
   enc->argb_content_ = kEncoderARGB;
   assert(enc->current_width_ == width);
-  return VP8_ENC_OK;
+  return 1;
 }
 
 // -----------------------------------------------------------------------------
@@ -1559,16 +1604,19 @@
 // using 'row' as a temporary buffer of size 'width'.
 // We assume that all src[] values have a corresponding entry in the palette.
 // Note: src[] can be the same as dst[]
-static WebPEncodingError ApplyPalette(const uint32_t* src, uint32_t src_stride,
-                                      uint32_t* dst, uint32_t dst_stride,
-                                      const uint32_t* palette, int palette_size,
-                                      int width, int height, int xbits) {
+static int ApplyPalette(const uint32_t* src, uint32_t src_stride, uint32_t* dst,
+                        uint32_t dst_stride, const uint32_t* palette,
+                        int palette_size, int width, int height, int xbits,
+                        const WebPPicture* const pic) {
   // TODO(skal): this tmp buffer is not needed if VP8LBundleColorMap() can be
   // made to work in-place.
   uint8_t* const tmp_row = (uint8_t*)WebPSafeMalloc(width, sizeof(*tmp_row));
   int x, y;
 
-  if (tmp_row == NULL) return VP8_ENC_ERROR_OUT_OF_MEMORY;
+  if (tmp_row == NULL) {
+    WebPEncodingSetError(pic, VP8_ENC_ERROR_OUT_OF_MEMORY);
+    return 0;
+  }
 
   if (palette_size < APPLY_PALETTE_GREEDY_MAX) {
     APPLY_PALETTE_FOR(SearchColorGreedy(palette, palette_size, pix));
@@ -1613,7 +1661,7 @@
     }
   }
   WebPSafeFree(tmp_row);
-  return VP8_ENC_OK;
+  return 1;
 }
 #undef APPLY_PALETTE_FOR
 #undef PALETTE_INV_SIZE_BITS
@@ -1621,9 +1669,7 @@
 #undef APPLY_PALETTE_GREEDY_MAX
 
 // Note: Expects "enc->palette_" to be set properly.
-static WebPEncodingError MapImageFromPalette(VP8LEncoder* const enc,
-                                             int in_place) {
-  WebPEncodingError err = VP8_ENC_OK;
+static int MapImageFromPalette(VP8LEncoder* const enc, int in_place) {
   const WebPPicture* const pic = enc->pic_;
   const int width = pic->width;
   const int height = pic->height;
@@ -1641,19 +1687,22 @@
     xbits = (palette_size <= 16) ? 1 : 0;
   }
 
-  err = AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height);
-  if (err != VP8_ENC_OK) return err;
-
-  err = ApplyPalette(src, src_stride,
+  if (!AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height)) {
+    return 0;
+  }
+  if (!ApplyPalette(src, src_stride,
                      enc->argb_, enc->current_width_,
-                     palette, palette_size, width, height, xbits);
+                     palette, palette_size, width, height, xbits, pic)) {
+    return 0;
+  }
   enc->argb_content_ = kEncoderPalette;
-  return err;
+  return 1;
 }
 
 // Save palette_[] to bitstream.
 static WebPEncodingError EncodePalette(VP8LBitWriter* const bw, int low_effort,
-                                       VP8LEncoder* const enc) {
+                                       VP8LEncoder* const enc,
+                                       int percent_range, int* const percent) {
   int i;
   uint32_t tmp_palette[MAX_PALETTE_SIZE];
   const int palette_size = enc->palette_size_;
@@ -1668,7 +1717,7 @@
   tmp_palette[0] = palette[0];
   return EncodeImageNoHuffman(bw, tmp_palette, &enc->hash_chain_,
                               &enc->refs_[0], palette_size, 1, /*quality=*/20,
-                              low_effort);
+                              low_effort, enc->pic_, percent_range, percent);
 }
 
 // -----------------------------------------------------------------------------
@@ -1712,7 +1761,6 @@
   CrunchConfig crunch_configs_[CRUNCH_CONFIGS_MAX];
   int num_crunch_configs_;
   int red_and_blue_always_zero_;
-  WebPEncodingError err_;
   WebPAuxStats* stats_;
 } StreamEncodeContext;
 
@@ -1729,7 +1777,6 @@
 #if !defined(WEBP_DISABLE_STATS)
   WebPAuxStats* const stats = params->stats_;
 #endif
-  WebPEncodingError err = VP8_ENC_OK;
   const int quality = (int)config->quality;
   const int low_effort = (config->method == 0);
 #if (WEBP_NEAR_LOSSLESS == 1)
@@ -1737,6 +1784,7 @@
 #endif
   const int height = picture->height;
   const size_t byte_position = VP8LBitWriterNumBytes(bw);
+  int percent = 2;  // for WebPProgressHook
 #if (WEBP_NEAR_LOSSLESS == 1)
   int use_near_lossless = 0;
 #endif
@@ -1750,12 +1798,13 @@
 
   if (!VP8LBitWriterInit(&bw_best, 0) ||
       (num_crunch_configs > 1 && !VP8LBitWriterClone(bw, &bw_best))) {
-    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+    WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
     goto Error;
   }
 
   for (idx = 0; idx < num_crunch_configs; ++idx) {
     const int entropy_idx = crunch_configs[idx].entropy_idx_;
+    int remaining_percent = 97 / num_crunch_configs, percent_range;
     enc->use_palette_ =
         (entropy_idx == kPalette) || (entropy_idx == kPaletteAndSpatial);
     enc->use_subtract_green_ =
@@ -1779,11 +1828,10 @@
     use_near_lossless = (config->near_lossless < 100) && !enc->use_palette_ &&
                         !enc->use_predict_;
     if (use_near_lossless) {
-      err = AllocateTransformBuffer(enc, width, height);
-      if (err != VP8_ENC_OK) goto Error;
+      if (!AllocateTransformBuffer(enc, width, height)) goto Error;
       if ((enc->argb_content_ != kEncoderNearLossless) &&
           !VP8ApplyNearLossless(picture, config->near_lossless, enc->argb_)) {
-        err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+        WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
         goto Error;
       }
       enc->argb_content_ = kEncoderNearLossless;
@@ -1805,14 +1853,17 @@
                                   enc->palette_);
       } else {
         assert(crunch_configs[idx].palette_sorting_type_ == kModifiedZeng);
-        err = PaletteSortModifiedZeng(enc->pic_, enc->palette_sorted_,
-                                      enc->palette_size_, enc->palette_);
-        if (err != VP8_ENC_OK) goto Error;
+        if (!PaletteSortModifiedZeng(enc->pic_, enc->palette_sorted_,
+                                      enc->palette_size_, enc->palette_)) {
+          goto Error;
+        }
       }
-      err = EncodePalette(bw, low_effort, enc);
-      if (err != VP8_ENC_OK) goto Error;
-      err = MapImageFromPalette(enc, use_delta_palette);
-      if (err != VP8_ENC_OK) goto Error;
+      percent_range = remaining_percent / 4;
+      if (!EncodePalette(bw, low_effort, enc, percent_range, &percent)) {
+        goto Error;
+      }
+      remaining_percent -= percent_range;
+      if (!MapImageFromPalette(enc, use_delta_palette)) goto Error;
       // If using a color cache, do not have it bigger than the number of
       // colors.
       if (use_cache && enc->palette_size_ < (1 << MAX_COLOR_CACHE_BITS)) {
@@ -1823,8 +1874,7 @@
       // In case image is not packed.
       if (enc->argb_content_ != kEncoderNearLossless &&
           enc->argb_content_ != kEncoderPalette) {
-        err = MakeInputImageCopy(enc);
-        if (err != VP8_ENC_OK) goto Error;
+        if (!MakeInputImageCopy(enc)) goto Error;
       }
 
       // -----------------------------------------------------------------------
@@ -1835,15 +1885,22 @@
       }
 
       if (enc->use_predict_) {
-        err = ApplyPredictFilter(enc, enc->current_width_, height, quality,
-                                 low_effort, enc->use_subtract_green_, bw);
-        if (err != VP8_ENC_OK) goto Error;
+        percent_range = remaining_percent / 3;
+        if (!ApplyPredictFilter(enc, enc->current_width_, height, quality,
+                                low_effort, enc->use_subtract_green_, bw,
+                                percent_range, &percent)) {
+          goto Error;
+        }
+        remaining_percent -= percent_range;
       }
 
       if (enc->use_cross_color_) {
-        err = ApplyCrossColorFilter(enc, enc->current_width_, height, quality,
-                                    low_effort, bw);
-        if (err != VP8_ENC_OK) goto Error;
+        percent_range = remaining_percent / 2;
+        if (!ApplyCrossColorFilter(enc, enc->current_width_, height, quality,
+                                   low_effort, bw, percent_range, &percent)) {
+          goto Error;
+        }
+        remaining_percent -= percent_range;
       }
     }
 
@@ -1851,12 +1908,13 @@
 
     // -------------------------------------------------------------------------
     // Encode and write the transformed image.
-    err = EncodeImageInternal(bw, enc->argb_, &enc->hash_chain_, enc->refs_,
-                              enc->current_width_, height, quality, low_effort,
-                              use_cache, &crunch_configs[idx],
-                              &enc->cache_bits_, enc->histo_bits_,
-                              byte_position, &hdr_size, &data_size);
-    if (err != VP8_ENC_OK) goto Error;
+    if (!EncodeImageInternal(
+            bw, enc->argb_, &enc->hash_chain_, enc->refs_, enc->current_width_,
+            height, quality, low_effort, use_cache, &crunch_configs[idx],
+            &enc->cache_bits_, enc->histo_bits_, byte_position, &hdr_size,
+            &data_size, picture, remaining_percent, &percent)) {
+      goto Error;
+    }
 
     // If we are better than what we already have.
     if (VP8LBitWriterNumBytes(bw) < best_size) {
@@ -1886,18 +1944,15 @@
   }
   VP8LBitWriterSwap(&bw_best, bw);
 
-Error:
+ Error:
   VP8LBitWriterWipeOut(&bw_best);
-  params->err_ = err;
   // The hook should return false in case of error.
-  return (err == VP8_ENC_OK);
+  return (params->picture_->error_code == VP8_ENC_OK);
 }
 
-WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
-                                   const WebPPicture* const picture,
-                                   VP8LBitWriter* const bw_main,
-                                   int use_cache) {
-  WebPEncodingError err = VP8_ENC_OK;
+int VP8LEncodeStream(const WebPConfig* const config,
+                     const WebPPicture* const picture,
+                     VP8LBitWriter* const bw_main, int use_cache) {
   VP8LEncoder* const enc_main = VP8LEncoderNew(config, picture);
   VP8LEncoder* enc_side = NULL;
   CrunchConfig crunch_configs[CRUNCH_CONFIGS_MAX];
@@ -1909,6 +1964,7 @@
   // The main thread uses picture->stats, the side thread uses stats_side.
   WebPAuxStats stats_side;
   VP8LBitWriter bw_side;
+  WebPPicture picture_side;
   const WebPWorkerInterface* const worker_interface = WebPGetWorkerInterface();
   int ok_main;
 
@@ -1918,11 +1974,14 @@
     return 0;
   }
 
+  // Avoid "garbage value" error from Clang's static analysis tool.
+  WebPPictureInit(&picture_side);
+
   // Analyze image (entropy, num_palettes etc)
   if (!EncoderAnalyze(enc_main, crunch_configs, &num_crunch_configs_main,
                       &red_and_blue_always_zero) ||
       !EncoderInit(enc_main)) {
-    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+    WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
     goto Error;
   }
 
@@ -1951,25 +2010,32 @@
       StreamEncodeContext* const param =
           (idx == 0) ? &params_main : &params_side;
       param->config_ = config;
-      param->picture_ = picture;
       param->use_cache_ = use_cache;
       param->red_and_blue_always_zero_ = red_and_blue_always_zero;
       if (idx == 0) {
+        param->picture_ = picture;
         param->stats_ = picture->stats;
         param->bw_ = bw_main;
         param->enc_ = enc_main;
       } else {
+        // Create a side picture (error_code is not thread-safe).
+        if (!WebPPictureView(picture, /*left=*/0, /*top=*/0, picture->width,
+                             picture->height, &picture_side)) {
+          assert(0);
+        }
+        picture_side.progress_hook = NULL;  // Progress hook is not thread-safe.
+        param->picture_ = &picture_side;  // No need to free a view afterwards.
         param->stats_ = (picture->stats == NULL) ? NULL : &stats_side;
         // Create a side bit writer.
         if (!VP8LBitWriterClone(bw_main, &bw_side)) {
-          err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+          WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
           goto Error;
         }
         param->bw_ = &bw_side;
         // Create a side encoder.
-        enc_side = VP8LEncoderNew(config, picture);
+        enc_side = VP8LEncoderNew(config, &picture_side);
         if (enc_side == NULL || !EncoderInit(enc_side)) {
-          err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+          WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
           goto Error;
         }
         // Copy the values that were computed for the main encoder.
@@ -1993,7 +2059,7 @@
   // Start the second thread if needed.
   if (num_crunch_configs_side != 0) {
     if (!worker_interface->Reset(&worker_side)) {
-      err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+      WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
       goto Error;
     }
 #if !defined(WEBP_DISABLE_STATS)
@@ -2003,8 +2069,6 @@
       memcpy(&stats_side, picture->stats, sizeof(stats_side));
     }
 #endif
-    // This line is only useful to remove a Clang static analyzer warning.
-    params_side.err_ = VP8_ENC_OK;
     worker_interface->Launch(&worker_side);
   }
   // Execute the main thread.
@@ -2016,7 +2080,10 @@
     const int ok_side = worker_interface->Sync(&worker_side);
     worker_interface->End(&worker_side);
     if (!ok_main || !ok_side) {
-      err = ok_main ? params_side.err_ : params_main.err_;
+      if (picture->error_code == VP8_ENC_OK) {
+        assert(picture_side.error_code != VP8_ENC_OK);
+        WebPEncodingSetError(picture, picture_side.error_code);
+      }
       goto Error;
     }
     if (VP8LBitWriterNumBytes(&bw_side) < VP8LBitWriterNumBytes(bw_main)) {
@@ -2027,18 +2094,13 @@
       }
 #endif
     }
-  } else {
-    if (!ok_main) {
-      err = params_main.err_;
-      goto Error;
-    }
   }
 
-Error:
+ Error:
   VP8LBitWriterWipeOut(&bw_side);
   VP8LEncoderDelete(enc_main);
   VP8LEncoderDelete(enc_side);
-  return err;
+  return (picture->error_code == VP8_ENC_OK);
 }
 
 #undef CRUNCH_CONFIGS_MAX
@@ -2051,14 +2113,12 @@
   size_t coded_size;
   int percent = 0;
   int initial_size;
-  WebPEncodingError err = VP8_ENC_OK;
   VP8LBitWriter bw;
 
   if (picture == NULL) return 0;
 
   if (config == NULL || picture->argb == NULL) {
-    err = VP8_ENC_ERROR_NULL_PARAMETER;
-    WebPEncodingSetError(picture, err);
+    WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
     return 0;
   }
 
@@ -2069,13 +2129,13 @@
   initial_size = (config->image_hint == WEBP_HINT_GRAPH) ?
       width * height : width * height * 2;
   if (!VP8LBitWriterInit(&bw, initial_size)) {
-    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+    WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
     goto Error;
   }
 
   if (!WebPReportProgress(picture, 1, &percent)) {
  UserAbort:
-    err = VP8_ENC_ERROR_USER_ABORT;
+    WebPEncodingSetError(picture, VP8_ENC_ERROR_USER_ABORT);
     goto Error;
   }
   // Reset stats (for pure lossless coding)
@@ -2091,28 +2151,26 @@
 
   // Write image size.
   if (!WriteImageSize(picture, &bw)) {
-    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+    WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
     goto Error;
   }
 
   has_alpha = WebPPictureHasTransparency(picture);
   // Write the non-trivial Alpha flag and lossless version.
   if (!WriteRealAlphaAndVersion(&bw, has_alpha)) {
-    err = VP8_ENC_ERROR_OUT_OF_MEMORY;
+    WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
     goto Error;
   }
 
-  if (!WebPReportProgress(picture, 5, &percent)) goto UserAbort;
+  if (!WebPReportProgress(picture, 2, &percent)) goto UserAbort;
 
   // Encode main image stream.
-  err = VP8LEncodeStream(config, picture, &bw, 1 /*use_cache*/);
-  if (err != VP8_ENC_OK) goto Error;
+  if (!VP8LEncodeStream(config, picture, &bw, 1 /*use_cache*/)) goto Error;
 
-  if (!WebPReportProgress(picture, 90, &percent)) goto UserAbort;
+  if (!WebPReportProgress(picture, 99, &percent)) goto UserAbort;
 
   // Finish the RIFF chunk.
-  err = WriteImage(picture, &bw, &coded_size);
-  if (err != VP8_ENC_OK) goto Error;
+  if (!WriteImage(picture, &bw, &coded_size)) goto Error;
 
   if (!WebPReportProgress(picture, 100, &percent)) goto UserAbort;
 
@@ -2131,13 +2189,11 @@
   }
 
  Error:
-  if (bw.error_) err = VP8_ENC_ERROR_OUT_OF_MEMORY;
-  VP8LBitWriterWipeOut(&bw);
-  if (err != VP8_ENC_OK) {
-    WebPEncodingSetError(picture, err);
-    return 0;
+  if (bw.error_) {
+    WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
   }
-  return 1;
+  VP8LBitWriterWipeOut(&bw);
+  return (picture->error_code == VP8_ENC_OK);
 }
 
 //------------------------------------------------------------------------------
diff --git a/src/enc/vp8li_enc.h b/src/enc/vp8li_enc.h
index 00de489..3d35e16 100644
--- a/src/enc/vp8li_enc.h
+++ b/src/enc/vp8li_enc.h
@@ -89,9 +89,10 @@
 
 // Encodes the main image stream using the supplied bit writer.
 // If 'use_cache' is false, disables the use of color cache.
-WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
-                                   const WebPPicture* const picture,
-                                   VP8LBitWriter* const bw, int use_cache);
+// Returns false in case of error (stored in picture->error_code).
+int VP8LEncodeStream(const WebPConfig* const config,
+                     const WebPPicture* const picture, VP8LBitWriter* const bw,
+                     int use_cache);
 
 #if (WEBP_NEAR_LOSSLESS == 1)
 // in near_lossless.c
@@ -103,13 +104,18 @@
 //------------------------------------------------------------------------------
 // Image transforms in predictor.c.
 
-void VP8LResidualImage(int width, int height, int bits, int low_effort,
-                       uint32_t* const argb, uint32_t* const argb_scratch,
-                       uint32_t* const image, int near_lossless, int exact,
-                       int used_subtract_green);
+// pic and percent are for progress.
+// Returns false in case of error (stored in pic->error_code).
+int VP8LResidualImage(int width, int height, int bits, int low_effort,
+                      uint32_t* const argb, uint32_t* const argb_scratch,
+                      uint32_t* const image, int near_lossless, int exact,
+                      int used_subtract_green, const WebPPicture* const pic,
+                      int percent_range, int* const percent);
 
-void VP8LColorSpaceTransform(int width, int height, int bits, int quality,
-                             uint32_t* const argb, uint32_t* image);
+int VP8LColorSpaceTransform(int width, int height, int bits, int quality,
+                            uint32_t* const argb, uint32_t* image,
+                            const WebPPicture* const pic, int percent_range,
+                            int* const percent);
 
 //------------------------------------------------------------------------------
 
diff --git a/src/enc/webp_enc.c b/src/enc/webp_enc.c
index ce2db2e..9620e05 100644
--- a/src/enc/webp_enc.c
+++ b/src/enc/webp_enc.c
@@ -336,9 +336,7 @@
   if (!WebPValidateConfig(config)) {
     return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION);
   }
-  if (pic->width <= 0 || pic->height <= 0) {
-    return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
-  }
+  if (!WebPValidatePicture(pic)) return 0;
   if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION) {
     return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
   }
diff --git a/src/libwebp.pc.in b/src/libwebp.pc.in
new file mode 100644
index 0000000..783090e
--- /dev/null
+++ b/src/libwebp.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libwebp
+Description: Library for the WebP graphics format
+Version: @PACKAGE_VERSION@
+Requires.private: libsharpyuv
+Cflags: -I${includedir}
+Libs: -L${libdir} -l@webp_libname_prefix@webp
+Libs.private: -lm @PTHREAD_CFLAGS@ @PTHREAD_LIBS@
diff --git a/src/libwebp.rc b/src/libwebp.rc
new file mode 100644
index 0000000..6b11dc8
--- /dev/null
+++ b/src/libwebp.rc
@@ -0,0 +1,41 @@
+#define APSTUDIO_READONLY_SYMBOLS
+#include "winres.h"
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,3,0
+ PRODUCTVERSION 1,0,3,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", "Google, Inc."
+            VALUE "FileDescription", "libwebp DLL"
+            VALUE "FileVersion", "1.3.0"
+            VALUE "InternalName", "libwebp.dll"
+            VALUE "LegalCopyright", "Copyright (C) 2022"
+            VALUE "OriginalFilename", "libwebp.dll"
+            VALUE "ProductName", "WebP Image Codec"
+            VALUE "ProductVersion", "1.3.0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+#endif    // English (United States) resources
diff --git a/src/libwebpdecoder.pc.in b/src/libwebpdecoder.pc.in
new file mode 100644
index 0000000..134de0e
--- /dev/null
+++ b/src/libwebpdecoder.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libwebpdecoder
+Description: Library for the WebP graphics format (decode only)
+Version: @PACKAGE_VERSION@
+Cflags: -I${includedir}
+Libs: -L${libdir} -l@webp_libname_prefix@webpdecoder
+Libs.private: -lm @PTHREAD_CFLAGS@ @PTHREAD_LIBS@
diff --git a/src/libwebpdecoder.rc b/src/libwebpdecoder.rc
new file mode 100644
index 0000000..44e4f26
--- /dev/null
+++ b/src/libwebpdecoder.rc
@@ -0,0 +1,41 @@
+#define APSTUDIO_READONLY_SYMBOLS
+#include "winres.h"
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,3,0
+ PRODUCTVERSION 1,0,3,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", "Google, Inc."
+            VALUE "FileDescription", "libwebpdecoder DLL"
+            VALUE "FileVersion", "1.3.0"
+            VALUE "InternalName", "libwebpdecoder.dll"
+            VALUE "LegalCopyright", "Copyright (C) 2022"
+            VALUE "OriginalFilename", "libwebpdecoder.dll"
+            VALUE "ProductName", "WebP Image Decoder"
+            VALUE "ProductVersion", "1.3.0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+#endif    // English (United States) resources
diff --git a/src/mux/Makefile.am b/src/mux/Makefile.am
new file mode 100644
index 0000000..8f7f739
--- /dev/null
+++ b/src/mux/Makefile.am
@@ -0,0 +1,22 @@
+AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir)
+lib_LTLIBRARIES = libwebpmux.la
+
+libwebpmux_la_SOURCES =
+libwebpmux_la_SOURCES += anim_encode.c
+libwebpmux_la_SOURCES += animi.h
+libwebpmux_la_SOURCES += muxedit.c
+libwebpmux_la_SOURCES += muxi.h
+libwebpmux_la_SOURCES += muxinternal.c
+libwebpmux_la_SOURCES += muxread.c
+
+libwebpmuxinclude_HEADERS =
+libwebpmuxinclude_HEADERS += ../webp/mux.h
+libwebpmuxinclude_HEADERS += ../webp/mux_types.h
+libwebpmuxinclude_HEADERS += ../webp/types.h
+noinst_HEADERS =
+noinst_HEADERS += ../webp/format_constants.h
+
+libwebpmux_la_LIBADD = ../libwebp.la
+libwebpmux_la_LDFLAGS = -no-undefined -version-info 3:11:0 -lm
+libwebpmuxincludedir = $(includedir)/webp
+pkgconfig_DATA = libwebpmux.pc
diff --git a/src/mux/libwebpmux.pc.in b/src/mux/libwebpmux.pc.in
new file mode 100644
index 0000000..c770daa
--- /dev/null
+++ b/src/mux/libwebpmux.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libwebpmux
+Description: Library for manipulating the WebP graphics format container
+Version: @PACKAGE_VERSION@
+Requires.private: libwebp >= 0.2.0
+Cflags: -I${includedir}
+Libs: -L${libdir} -l@webp_libname_prefix@webpmux
+Libs.private: -lm
diff --git a/src/mux/libwebpmux.rc b/src/mux/libwebpmux.rc
new file mode 100644
index 0000000..a61f537
--- /dev/null
+++ b/src/mux/libwebpmux.rc
@@ -0,0 +1,41 @@
+#define APSTUDIO_READONLY_SYMBOLS
+#include "winres.h"
+#undef APSTUDIO_READONLY_SYMBOLS
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,3,0
+ PRODUCTVERSION 1,0,3,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", "Google, Inc."
+            VALUE "FileDescription", "libwebpmux DLL"
+            VALUE "FileVersion", "1.3.0"
+            VALUE "InternalName", "libwebpmux.dll"
+            VALUE "LegalCopyright", "Copyright (C) 2022"
+            VALUE "OriginalFilename", "libwebpmux.dll"
+            VALUE "ProductName", "WebP Image Muxer"
+            VALUE "ProductVersion", "1.3.0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+#endif    // English (United States) resources
diff --git a/src/mux/muxedit.c b/src/mux/muxedit.c
index 02c3ede..63e71a0 100644
--- a/src/mux/muxedit.c
+++ b/src/mux/muxedit.c
@@ -70,6 +70,7 @@
     err = ChunkAssignData(&chunk, data, copy_data, tag);                       \
     if (err == WEBP_MUX_OK) {                                                  \
       err = ChunkSetHead(&chunk, (LIST));                                      \
+      if (err != WEBP_MUX_OK) ChunkRelease(&chunk);                            \
     }                                                                          \
     return err;                                                                \
   }
diff --git a/src/mux/muxi.h b/src/mux/muxi.h
index d9bf9b3..7929138 100644
--- a/src/mux/muxi.h
+++ b/src/mux/muxi.h
@@ -28,8 +28,8 @@
 // Defines and constants.
 
 #define MUX_MAJ_VERSION 1
-#define MUX_MIN_VERSION 2
-#define MUX_REV_VERSION 2
+#define MUX_MIN_VERSION 3
+#define MUX_REV_VERSION 0
 
 // Chunk object.
 typedef struct WebPChunk WebPChunk;
diff --git a/src/mux/muxinternal.c b/src/mux/muxinternal.c
index b9ee671..75b6b41 100644
--- a/src/mux/muxinternal.c
+++ b/src/mux/muxinternal.c
@@ -155,17 +155,18 @@
 
 WebPMuxError ChunkAppend(WebPChunk* const chunk,
                          WebPChunk*** const chunk_list) {
+  WebPMuxError err;
   assert(chunk_list != NULL && *chunk_list != NULL);
 
   if (**chunk_list == NULL) {
-    ChunkSetHead(chunk, *chunk_list);
+    err = ChunkSetHead(chunk, *chunk_list);
   } else {
     WebPChunk* last_chunk = **chunk_list;
     while (last_chunk->next_ != NULL) last_chunk = last_chunk->next_;
-    ChunkSetHead(chunk, &last_chunk->next_);
-    *chunk_list = &last_chunk->next_;
+    err = ChunkSetHead(chunk, &last_chunk->next_);
+    if (err == WEBP_MUX_OK) *chunk_list = &last_chunk->next_;
   }
-  return WEBP_MUX_OK;
+  return err;
 }
 
 //------------------------------------------------------------------------------
diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am
new file mode 100644
index 0000000..a4bff8b
--- /dev/null
+++ b/src/utils/Makefile.am
@@ -0,0 +1,52 @@
+AM_CPPFLAGS += -I$(top_builddir) -I$(top_srcdir)
+noinst_LTLIBRARIES = libwebputils.la
+
+if BUILD_LIBWEBPDECODER
+  noinst_LTLIBRARIES += libwebputilsdecode.la
+endif
+
+common_HEADERS = ../webp/types.h
+commondir = $(includedir)/webp
+
+noinst_HEADERS =
+noinst_HEADERS += ../dsp/cpu.h
+noinst_HEADERS += ../dsp/dsp.h
+noinst_HEADERS += ../webp/decode.h
+noinst_HEADERS += ../webp/encode.h
+noinst_HEADERS += ../webp/format_constants.h
+
+COMMON_SOURCES =
+COMMON_SOURCES += bit_reader_utils.c
+COMMON_SOURCES += bit_reader_utils.h
+COMMON_SOURCES += bit_reader_inl_utils.h
+COMMON_SOURCES += color_cache_utils.c
+COMMON_SOURCES += color_cache_utils.h
+COMMON_SOURCES += endian_inl_utils.h
+COMMON_SOURCES += filters_utils.c
+COMMON_SOURCES += filters_utils.h
+COMMON_SOURCES += huffman_utils.c
+COMMON_SOURCES += huffman_utils.h
+COMMON_SOURCES += quant_levels_dec_utils.c
+COMMON_SOURCES += quant_levels_dec_utils.h
+COMMON_SOURCES += rescaler_utils.c
+COMMON_SOURCES += rescaler_utils.h
+COMMON_SOURCES += random_utils.c
+COMMON_SOURCES += random_utils.h
+COMMON_SOURCES += thread_utils.c
+COMMON_SOURCES += thread_utils.h
+COMMON_SOURCES += utils.c
+COMMON_SOURCES += utils.h
+
+ENC_SOURCES =
+ENC_SOURCES += bit_writer_utils.c
+ENC_SOURCES += bit_writer_utils.h
+ENC_SOURCES += huffman_encode_utils.c
+ENC_SOURCES += huffman_encode_utils.h
+ENC_SOURCES += quant_levels_utils.c
+ENC_SOURCES += quant_levels_utils.h
+
+libwebputils_la_SOURCES = $(COMMON_SOURCES) $(ENC_SOURCES)
+
+if BUILD_LIBWEBPDECODER
+  libwebputilsdecode_la_SOURCES = $(COMMON_SOURCES)
+endif
diff --git a/src/utils/bit_reader_inl_utils.h b/src/utils/bit_reader_inl_utils.h
index 404b9a6..24f3af7 100644
--- a/src/utils/bit_reader_inl_utils.h
+++ b/src/utils/bit_reader_inl_utils.h
@@ -148,9 +148,9 @@
     const range_t value = (range_t)(br->value_ >> pos);
     const int32_t mask = (int32_t)(split - value) >> 31;  // -1 or 0
     br->bits_ -= 1;
-    br->range_ += mask;
+    br->range_ += (range_t)mask;
     br->range_ |= 1;
-    br->value_ -= (bit_t)((split + 1) & mask) << pos;
+    br->value_ -= (bit_t)((split + 1) & (uint32_t)mask) << pos;
     BT_TRACK(br);
     return (v ^ mask) - mask;
   }
diff --git a/src/utils/huffman_utils.c b/src/utils/huffman_utils.c
index 0cba0fb..90c2fbf 100644
--- a/src/utils/huffman_utils.c
+++ b/src/utils/huffman_utils.c
@@ -142,7 +142,7 @@
 
   {
     int step;              // step size to replicate values in current table
-    uint32_t low = -1;     // low bits for current root entry
+    uint32_t low = 0xffffffffu;        // low bits for current root entry
     uint32_t mask = total_size - 1;    // mask for low bits
     uint32_t key = 0;      // reversed prefix code
     int num_nodes = 1;     // number of Huffman tree nodes
diff --git a/src/utils/utils.h b/src/utils/utils.h
index ef04f10..c5ee873 100644
--- a/src/utils/utils.h
+++ b/src/utils/utils.h
@@ -64,7 +64,8 @@
 // Alignment
 
 #define WEBP_ALIGN_CST 31
-#define WEBP_ALIGN(PTR) (((uintptr_t)(PTR) + WEBP_ALIGN_CST) & ~WEBP_ALIGN_CST)
+#define WEBP_ALIGN(PTR) (((uintptr_t)(PTR) + WEBP_ALIGN_CST) & \
+                         ~(uintptr_t)WEBP_ALIGN_CST)
 
 #include <string.h>
 // memcpy() is the safe way of moving potentially unaligned 32b memory.
@@ -73,10 +74,19 @@
   memcpy(&A, ptr, sizeof(A));
   return A;
 }
+
+static WEBP_INLINE int32_t WebPMemToInt32(const uint8_t* const ptr) {
+  return (int32_t)WebPMemToUint32(ptr);
+}
+
 static WEBP_INLINE void WebPUint32ToMem(uint8_t* const ptr, uint32_t val) {
   memcpy(ptr, &val, sizeof(val));
 }
 
+static WEBP_INLINE void WebPInt32ToMem(uint8_t* const ptr, int val) {
+  WebPUint32ToMem(ptr, (uint32_t)val);
+}
+
 //------------------------------------------------------------------------------
 // Reading/writing data.
 
diff --git a/src/webp/encode.h b/src/webp/encode.h
index b4c599d..56b68e2 100644
--- a/src/webp/encode.h
+++ b/src/webp/encode.h
@@ -441,7 +441,7 @@
 // the original dimension will be lost). Picture 'dst' need not be initialized
 // with WebPPictureInit() if it is different from 'src', since its content will
 // be overwritten.
-// Returns false in case of memory allocation error or invalid parameters.
+// Returns false in case of invalid parameters.
 WEBP_EXTERN int WebPPictureView(const WebPPicture* src,
                                 int left, int top, int width, int height,
                                 WebPPicture* dst);
@@ -455,7 +455,7 @@
 // dimension will be calculated preserving the aspect ratio.
 // No gamma correction is applied.
 // Returns false in case of error (invalid parameter or insufficient memory).
-WEBP_EXTERN int WebPPictureRescale(WebPPicture* pic, int width, int height);
+WEBP_EXTERN int WebPPictureRescale(WebPPicture* picture, int width, int height);
 
 // Colorspace conversion function to import RGB samples.
 // Previous buffer will be free'd, if any.
@@ -526,7 +526,7 @@
 // Remove the transparency information (if present) by blending the color with
 // the background color 'background_rgb' (specified as 24bit RGB triplet).
 // After this call, all alpha values are reset to 0xff.
-WEBP_EXTERN void WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb);
+WEBP_EXTERN void WebPBlendAlpha(WebPPicture* picture, uint32_t background_rgb);
 
 //------------------------------------------------------------------------------
 // Main call
diff --git a/src/webp/format_constants.h b/src/webp/format_constants.h
index eca6981..999035c 100644
--- a/src/webp/format_constants.h
+++ b/src/webp/format_constants.h
@@ -55,7 +55,7 @@
 typedef enum {
   PREDICTOR_TRANSFORM      = 0,
   CROSS_COLOR_TRANSFORM    = 1,
-  SUBTRACT_GREEN           = 2,
+  SUBTRACT_GREEN_TRANSFORM = 2,
   COLOR_INDEXING_TRANSFORM = 3
 } VP8LImageTransformType;
 
diff --git a/src/webp/types.h b/src/webp/types.h
index 47f7f2b..f255432 100644
--- a/src/webp/types.h
+++ b/src/webp/types.h
@@ -42,7 +42,11 @@
 # if defined(__GNUC__) && __GNUC__ >= 4
 #  define WEBP_EXTERN extern __attribute__ ((visibility ("default")))
 # else
-#  define WEBP_EXTERN extern
+#  if defined(_MSC_VER) && defined(WEBP_DLL)
+#   define WEBP_EXTERN __declspec(dllexport)
+#  else
+#   define WEBP_EXTERN extern
+#  endif
 # endif  /* __GNUC__ >= 4 */
 #endif  /* WEBP_EXTERN */
 
diff --git a/swig/README.md b/swig/README.md
new file mode 100644
index 0000000..7fa1c38
--- /dev/null
+++ b/swig/README.md
@@ -0,0 +1,67 @@
+# SWIG bindings
+
+## Building
+
+### JNI SWIG bindings
+
+```shell
+ $ gcc -shared -fPIC -fno-strict-aliasing -O2 \
+       -I/path/to/your/jdk/includes \
+       libwebp_java_wrap.c \
+       -lwebp \
+       -o libwebp_jni.so
+```
+
+Example usage:
+
+```java
+import com.google.webp.libwebp;
+
+import java.lang.reflect.Method;
+
+public class libwebp_jni_example {
+  static {
+    System.loadLibrary("webp_jni");
+  }
+
+  /**
+   * usage: java -cp libwebp.jar:. libwebp_jni_example
+   */
+  public static void main(String argv[]) {
+    final int version = libwebp.WebPGetDecoderVersion();
+    System.out.println("libwebp version: " + Integer.toHexString(version));
+
+    System.out.println("libwebp methods:");
+    final Method[] libwebpMethods = libwebp.class.getDeclaredMethods();
+    for (int i = 0; i < libwebpMethods.length; i++) {
+      System.out.println(libwebpMethods[i]);
+    }
+  }
+}
+```
+
+```shell
+ $ javac -cp libwebp.jar libwebp_jni_example.java
+ $ java -Djava.library.path=. -cp libwebp.jar:. libwebp_jni_example
+```
+
+### Python SWIG bindings:
+
+```shell
+ $ python setup.py build_ext
+ $ python setup.py install --prefix=pylocal
+```
+
+Example usage:
+
+```python
+import glob
+import sys
+sys.path.append(glob.glob('pylocal/lib/python*/site-packages')[0])
+
+from com.google.webp import libwebp
+print "libwebp decoder version: %x" % libwebp.WebPGetDecoderVersion()
+
+print "libwebp attributes:"
+for attr in dir(libwebp): print attr
+```
diff --git a/swig/libwebp.go b/swig/libwebp.go
new file mode 100644
index 0000000..df205aa
--- /dev/null
+++ b/swig/libwebp.go
@@ -0,0 +1,45 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version 2.0.10
+ *
+ * This file is not intended to be easily readable and contains a number of
+ * coding conventions designed to improve portability and efficiency. Do not make
+ * changes to this file unless you know what you are doing--modify the SWIG
+ * interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+package libwebp
+
+import _ "runtime/cgo"
+import "unsafe"
+
+type _ unsafe.Pointer
+
+type _swig_fnptr *byte
+type _swig_memberptr *byte
+
+//extern libwebpSwigCgocall
+func SwigCgocall()
+
+//extern libwebpSwigCgocallDone
+func SwigCgocallDone()
+
+//extern libwebpSwigCgocallBack
+func SwigCgocallBack()
+
+//extern libwebpSwigCgocallBackDone
+func SwigCgocallBackDone()
+
+func WebPGetDecoderVersion() int
+func Wrapped_WebPGetInfo(string, []int, []int) int
+
+// WebPGetInfo has 2 output parameters, provide a version in the more natural
+// go idiom:
+func WebPGetInfo(webp []byte) (ok bool, width int, height int) {
+	w := []int{0}
+	h := []int{0}
+	ok = Wrapped_WebPGetInfo(string(webp), w, h) != 0
+	width = w[0]
+	height = h[0]
+	return
+}
diff --git a/swig/libwebp.jar b/swig/libwebp.jar
new file mode 100644
index 0000000..2fc502b
--- /dev/null
+++ b/swig/libwebp.jar
Binary files differ
diff --git a/swig/libwebp.py b/swig/libwebp.py
new file mode 100644
index 0000000..2d126b5
--- /dev/null
+++ b/swig/libwebp.py
@@ -0,0 +1,235 @@
+# This file was automatically generated by SWIG (http://www.swig.org).
+# Version 3.0.12
+#
+# Do not make changes to this file unless you know what you are doing--modify
+# the SWIG interface file instead.
+
+from sys import version_info as _swig_python_version_info
+if _swig_python_version_info >= (2, 7, 0):
+    def swig_import_helper():
+        import importlib
+        pkg = __name__.rpartition('.')[0]
+        mname = '.'.join((pkg, '_libwebp')).lstrip('.')
+        try:
+            return importlib.import_module(mname)
+        except ImportError:
+            return importlib.import_module('_libwebp')
+    _libwebp = swig_import_helper()
+    del swig_import_helper
+elif _swig_python_version_info >= (2, 6, 0):
+    def swig_import_helper():
+        from os.path import dirname
+        import imp
+        fp = None
+        try:
+            fp, pathname, description = imp.find_module('_libwebp', [dirname(__file__)])
+        except ImportError:
+            import _libwebp
+            return _libwebp
+        try:
+            _mod = imp.load_module('_libwebp', fp, pathname, description)
+        finally:
+            if fp is not None:
+                fp.close()
+        return _mod
+    _libwebp = swig_import_helper()
+    del swig_import_helper
+else:
+    import _libwebp
+del _swig_python_version_info
+
+try:
+    _swig_property = property
+except NameError:
+    pass  # Python < 2.2 doesn't have 'property'.
+
+try:
+    import builtins as __builtin__
+except ImportError:
+    import __builtin__
+
+def _swig_setattr_nondynamic(self, class_type, name, value, static=1):
+    if (name == "thisown"):
+        return self.this.own(value)
+    if (name == "this"):
+        if type(value).__name__ == 'SwigPyObject':
+            self.__dict__[name] = value
+            return
+    method = class_type.__swig_setmethods__.get(name, None)
+    if method:
+        return method(self, value)
+    if (not static):
+        if _newclass:
+            object.__setattr__(self, name, value)
+        else:
+            self.__dict__[name] = value
+    else:
+        raise AttributeError("You cannot add attributes to %s" % self)
+
+
+def _swig_setattr(self, class_type, name, value):
+    return _swig_setattr_nondynamic(self, class_type, name, value, 0)
+
+
+def _swig_getattr(self, class_type, name):
+    if (name == "thisown"):
+        return self.this.own()
+    method = class_type.__swig_getmethods__.get(name, None)
+    if method:
+        return method(self)
+    raise AttributeError("'%s' object has no attribute '%s'" % (class_type.__name__, name))
+
+
+def _swig_repr(self):
+    try:
+        strthis = "proxy of " + self.this.__repr__()
+    except __builtin__.Exception:
+        strthis = ""
+    return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,)
+
+try:
+    _object = object
+    _newclass = 1
+except __builtin__.Exception:
+    class _object:
+        pass
+    _newclass = 0
+
+
+def WebPGetDecoderVersion():
+    """WebPGetDecoderVersion() -> int"""
+    return _libwebp.WebPGetDecoderVersion()
+
+def WebPGetInfo(data):
+    """WebPGetInfo(uint8_t data) -> (width, height)"""
+    return _libwebp.WebPGetInfo(data)
+
+def WebPDecodeRGB(data):
+    """WebPDecodeRGB(uint8_t data) -> (rgb, width, height)"""
+    return _libwebp.WebPDecodeRGB(data)
+
+def WebPDecodeRGBA(data):
+    """WebPDecodeRGBA(uint8_t data) -> (rgb, width, height)"""
+    return _libwebp.WebPDecodeRGBA(data)
+
+def WebPDecodeARGB(data):
+    """WebPDecodeARGB(uint8_t data) -> (rgb, width, height)"""
+    return _libwebp.WebPDecodeARGB(data)
+
+def WebPDecodeBGR(data):
+    """WebPDecodeBGR(uint8_t data) -> (rgb, width, height)"""
+    return _libwebp.WebPDecodeBGR(data)
+
+def WebPDecodeBGRA(data):
+    """WebPDecodeBGRA(uint8_t data) -> (rgb, width, height)"""
+    return _libwebp.WebPDecodeBGRA(data)
+
+def WebPGetEncoderVersion():
+    """WebPGetEncoderVersion() -> int"""
+    return _libwebp.WebPGetEncoderVersion()
+
+def wrap_WebPEncodeRGB(rgb, unused1, unused2, width, height, stride, quality_factor):
+    """private, do not call directly."""
+    return _libwebp.wrap_WebPEncodeRGB(rgb, unused1, unused2, width, height, stride, quality_factor)
+
+def wrap_WebPEncodeBGR(rgb, unused1, unused2, width, height, stride, quality_factor):
+    """private, do not call directly."""
+    return _libwebp.wrap_WebPEncodeBGR(rgb, unused1, unused2, width, height, stride, quality_factor)
+
+def wrap_WebPEncodeRGBA(rgb, unused1, unused2, width, height, stride, quality_factor):
+    """private, do not call directly."""
+    return _libwebp.wrap_WebPEncodeRGBA(rgb, unused1, unused2, width, height, stride, quality_factor)
+
+def wrap_WebPEncodeBGRA(rgb, unused1, unused2, width, height, stride, quality_factor):
+    """private, do not call directly."""
+    return _libwebp.wrap_WebPEncodeBGRA(rgb, unused1, unused2, width, height, stride, quality_factor)
+
+def wrap_WebPEncodeLosslessRGB(rgb, unused1, unused2, width, height, stride):
+    """private, do not call directly."""
+    return _libwebp.wrap_WebPEncodeLosslessRGB(rgb, unused1, unused2, width, height, stride)
+
+def wrap_WebPEncodeLosslessBGR(rgb, unused1, unused2, width, height, stride):
+    """private, do not call directly."""
+    return _libwebp.wrap_WebPEncodeLosslessBGR(rgb, unused1, unused2, width, height, stride)
+
+def wrap_WebPEncodeLosslessRGBA(rgb, unused1, unused2, width, height, stride):
+    """private, do not call directly."""
+    return _libwebp.wrap_WebPEncodeLosslessRGBA(rgb, unused1, unused2, width, height, stride)
+
+def wrap_WebPEncodeLosslessBGRA(rgb, unused1, unused2, width, height, stride):
+    """private, do not call directly."""
+    return _libwebp.wrap_WebPEncodeLosslessBGRA(rgb, unused1, unused2, width, height, stride)
+
+_UNUSED = 1
+
+
+def WebPEncodeRGB(rgb, width, height, stride, quality_factor):
+  """WebPEncodeRGB(uint8_t rgb, int width, int height, int stride, float quality_factor) -> lossy_webp"""
+  webp = wrap_WebPEncodeRGB(
+      rgb, _UNUSED, _UNUSED, width, height, stride, quality_factor)
+  if len(webp[0]) == 0:
+    return None
+  return webp[0]
+
+
+def WebPEncodeRGBA(rgb, width, height, stride, quality_factor):
+  """WebPEncodeRGBA(uint8_t rgb, int width, int height, int stride, float quality_factor) -> lossy_webp"""
+  webp = wrap_WebPEncodeRGBA(
+      rgb, _UNUSED, _UNUSED, width, height, stride, quality_factor)
+  if len(webp[0]) == 0:
+    return None
+  return webp[0]
+
+
+def WebPEncodeBGR(rgb, width, height, stride, quality_factor):
+  """WebPEncodeBGR(uint8_t rgb, int width, int height, int stride, float quality_factor) -> lossy_webp"""
+  webp = wrap_WebPEncodeBGR(
+      rgb, _UNUSED, _UNUSED, width, height, stride, quality_factor)
+  if len(webp[0]) == 0:
+    return None
+  return webp[0]
+
+
+def WebPEncodeBGRA(rgb, width, height, stride, quality_factor):
+  """WebPEncodeBGRA(uint8_t rgb, int width, int height, int stride, float quality_factor) -> lossy_webp"""
+  webp = wrap_WebPEncodeBGRA(
+      rgb, _UNUSED, _UNUSED, width, height, stride, quality_factor)
+  if len(webp[0]) == 0:
+    return None
+  return webp[0]
+
+
+def WebPEncodeLosslessRGB(rgb, width, height, stride):
+  """WebPEncodeLosslessRGB(uint8_t rgb, int width, int height, int stride) -> lossless_webp"""
+  webp = wrap_WebPEncodeLosslessRGB(rgb, _UNUSED, _UNUSED, width, height, stride)
+  if len(webp[0]) == 0:
+    return None
+  return webp[0]
+
+
+def WebPEncodeLosslessRGBA(rgb, width, height, stride):
+  """WebPEncodeLosslessRGBA(uint8_t rgb, int width, int height, int stride) -> lossless_webp"""
+  webp = wrap_WebPEncodeLosslessRGBA(rgb, _UNUSED, _UNUSED, width, height, stride)
+  if len(webp[0]) == 0:
+    return None
+  return webp[0]
+
+
+def WebPEncodeLosslessBGR(rgb, width, height, stride):
+  """WebPEncodeLosslessBGR(uint8_t rgb, int width, int height, int stride) -> lossless_webp"""
+  webp = wrap_WebPEncodeLosslessBGR(rgb, _UNUSED, _UNUSED, width, height, stride)
+  if len(webp[0]) == 0:
+    return None
+  return webp[0]
+
+
+def WebPEncodeLosslessBGRA(rgb, width, height, stride):
+  """WebPEncodeLosslessBGRA(uint8_t rgb, int width, int height, int stride) -> lossless_webp"""
+  webp = wrap_WebPEncodeLosslessBGRA(rgb, _UNUSED, _UNUSED, width, height, stride)
+  if len(webp[0]) == 0:
+    return None
+  return webp[0]
+
+# This file is compatible with both classic and new-style classes.
+
+
diff --git a/swig/libwebp.swig b/swig/libwebp.swig
new file mode 100644
index 0000000..ca38298
--- /dev/null
+++ b/swig/libwebp.swig
@@ -0,0 +1,438 @@
+// Copyright 2011 Google Inc.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the COPYING file in the root of the source
+// tree. An additional intellectual property rights grant can be found
+// in the file PATENTS. All contributing project authors may
+// be found in the AUTHORS file in the root of the source tree.
+// -----------------------------------------------------------------------------
+//
+// libwebp swig interface definition
+//
+// Author: James Zern (jzern@google.com)
+
+/*
+  Go bindings:
+  $ swig -go \
+         -outdir . \
+         -o libwebp_go_wrap.c libwebp.swig
+
+  Java bindings:
+  $ mkdir -p java/com/google/webp
+  $ swig -java \
+         -package com.google.webp \
+         -outdir java/com/google/webp \
+         -o libwebp_java_wrap.c libwebp.swig
+
+  Python bindings:
+  $ swig -python \
+         -outdir . \
+         -o libwebp_python_wrap.c libwebp.swig
+*/
+
+#ifdef SWIGPYTHON
+%module(package="com.google.webp") libwebp
+%begin %{
+#define SWIG_PYTHON_STRICT_BYTE_CHAR
+%}
+#else
+%module libwebp
+#endif  /* SWIGPYTHON */
+
+%include "constraints.i"
+%include "typemaps.i"
+
+#ifdef SWIGGO
+%apply (char* STRING, size_t LENGTH) { (const uint8_t* data, size_t data_size) }
+
+%rename(wrapped_WebPGetInfo) WebPGetInfo(const uint8_t* data, size_t data_size,
+                                         int* width, int* height);
+#endif  /* SWIGGO */
+
+#ifdef SWIGJAVA
+%include "arrays_java.i";
+%include "enums.swg" /*NB: requires JDK-1.5+
+                       See: http://www.swig.org/Doc1.3/Java.html#enumerations */
+
+// map uint8_t* such that a byte[] is used
+%{
+#include "webp/types.h"
+%}
+// from arrays_java.i (signed char)
+JAVA_ARRAYS_DECL(uint8_t, jbyte, Byte, Uint8)
+JAVA_ARRAYS_IMPL(uint8_t, jbyte, Byte, Uint8)
+JAVA_ARRAYS_TYPEMAPS(uint8_t, byte, jbyte, Uint8, "[B")
+%apply uint8_t[] { uint8_t* }
+#endif  /* SWIGJAVA */
+
+#ifdef SWIGPYTHON
+%apply (char* STRING, size_t LENGTH) { (const uint8_t* data, size_t data_size) }
+%typemap(out) uint8_t* {
+  $result = PyString_FromStringAndSize(
+      (const char*)$1,
+      ($1 == NULL) ? 0 : ReturnedBufferSize("$symname", arg3, arg4));
+}
+
+%typemap (in) const uint8_t* rgb (Py_buffer rgb_buffer) {
+  // NB: with Python < 2.6 the old style buffer protocol may be used:
+  // Py_ssize_t unused;
+  // PyObject_AsReadBuffer($input, (const void**)(&$1), &unused);
+  if (!PyObject_CheckBuffer($input)) {
+    SWIG_exception_fail(SWIG_TypeError,
+                        "in method '$symname', argument $argnum"
+                        " does not support the buffer interface");
+  }
+  if (PyObject_GetBuffer($input, &rgb_buffer, PyBUF_SIMPLE)) {
+    SWIG_exception_fail(SWIG_RuntimeError,
+                        "in method '$symname', unable to get buffer view");
+  }
+  $1 = ($1_ltype)rgb_buffer.buf;
+}
+
+%typemap(freearg) const uint8_t* rgb {
+  PyBuffer_Release(&rgb_buffer$argnum);
+}
+
+%define DECODE_AUTODOC(func)
+%feature("autodoc", #func "(uint8_t data) -> (rgb, width, height)") func;
+%enddef
+
+%feature("autodoc", "1");
+DECODE_AUTODOC(WebPDecodeRGB);
+DECODE_AUTODOC(WebPDecodeRGBA);
+DECODE_AUTODOC(WebPDecodeARGB);
+DECODE_AUTODOC(WebPDecodeBGR);
+DECODE_AUTODOC(WebPDecodeBGRA);
+%feature("autodoc", "WebPGetInfo(uint8_t data) -> (width, height)") WebPGetInfo;
+#endif  /* SWIGPYTHON */
+
+//------------------------------------------------------------------------------
+// Decoder specific
+
+%apply int* OUTPUT { int* width, int* height }
+
+int WebPGetDecoderVersion(void);
+int WebPGetInfo(const uint8_t* data, size_t data_size,
+                int* width, int* height);
+
+#if defined(SWIGJAVA) || defined(SWIGPYTHON)
+
+// free the buffer returned by these functions after copying into
+// the native type
+%newobject WebPDecodeRGB;
+%newobject WebPDecodeRGBA;
+%newobject WebPDecodeARGB;
+%newobject WebPDecodeBGR;
+%newobject WebPDecodeBGRA;
+%typemap(newfree) uint8_t* "free($1);"
+
+uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
+                       int* width, int* height);
+uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size,
+                        int* width, int* height);
+uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size,
+                        int* width, int* height);
+uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size,
+                       int* width, int* height);
+uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size,
+                        int* width, int* height);
+
+#endif  /* SWIGJAVA || SWIGPYTHON */
+
+//------------------------------------------------------------------------------
+// Encoder specific
+
+#if defined(SWIGJAVA) || defined(SWIGPYTHON)
+
+int WebPGetEncoderVersion(void);
+
+#endif  /* SWIGJAVA || SWIGPYTHON */
+
+//------------------------------------------------------------------------------
+// Wrapper code additions
+
+%{
+#include "webp/decode.h"
+#include "webp/encode.h"
+%}
+
+#ifdef SWIGJAVA
+%{
+#define FillMeInAsSizeCannotBeDeterminedAutomatically \
+    (result ? (jint)ReturnedBufferSize(__FUNCTION__, arg3, arg4) : 0)
+%}
+#endif  /* SWIGJAVA */
+
+#if defined(SWIGJAVA) || defined(SWIGPYTHON)
+%{
+static size_t ReturnedBufferSize(
+    const char* function, int* width, int* height) {
+  static const struct sizemap {
+    const char* function;
+    int size_multiplier;
+  } size_map[] = {
+#ifdef SWIGJAVA
+    { "Java_com_google_webp_libwebpJNI_WebPDecodeRGB",  3 },
+    { "Java_com_google_webp_libwebpJNI_WebPDecodeRGBA", 4 },
+    { "Java_com_google_webp_libwebpJNI_WebPDecodeARGB", 4 },
+    { "Java_com_google_webp_libwebpJNI_WebPDecodeBGR",  3 },
+    { "Java_com_google_webp_libwebpJNI_WebPDecodeBGRA", 4 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGB",  1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGR",  1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGBA", 1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGRA", 1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGB",  1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGR",  1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGBA", 1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGRA", 1 },
+#endif
+#ifdef SWIGPYTHON
+    { "WebPDecodeRGB",  3 },
+    { "WebPDecodeRGBA", 4 },
+    { "WebPDecodeARGB", 4 },
+    { "WebPDecodeBGR",  3 },
+    { "WebPDecodeBGRA", 4 },
+    { "wrap_WebPEncodeRGB",  1 },
+    { "wrap_WebPEncodeBGR",  1 },
+    { "wrap_WebPEncodeRGBA", 1 },
+    { "wrap_WebPEncodeBGRA", 1 },
+    { "wrap_WebPEncodeLosslessRGB",  1 },
+    { "wrap_WebPEncodeLosslessBGR",  1 },
+    { "wrap_WebPEncodeLosslessRGBA", 1 },
+    { "wrap_WebPEncodeLosslessBGRA", 1 },
+#endif
+    { NULL, 0 }
+  };
+  const struct sizemap* p;
+  size_t size = 0;
+
+  for (p = size_map; p->function; ++p) {
+    if (!strcmp(function, p->function)) {
+      size = *width * *height * p->size_multiplier;
+      break;
+    }
+  }
+
+  return size;
+}
+%}
+
+%{
+typedef size_t (*WebPEncodeFunction)(const uint8_t* rgb,
+                                     int width, int height, int stride,
+                                     float quality_factor, uint8_t** output);
+typedef size_t (*WebPEncodeLosslessFunction)(const uint8_t* rgb,
+                                             int width, int height, int stride,
+                                             uint8_t** output);
+
+static uint8_t* EncodeLossy(const uint8_t* rgb,
+                            int width, int height, int stride,
+                            float quality_factor,
+                            WebPEncodeFunction encfn,
+                            int* output_size, int* unused) {
+  uint8_t* output = NULL;
+  const size_t image_size =
+      encfn(rgb, width, height, stride, quality_factor, &output);
+  // the values of following two will be interpreted by ReturnedBufferSize()
+  // as 'width' and 'height' in the size calculation.
+  *output_size = image_size;
+  *unused = 1;
+  return image_size ? output : NULL;
+}
+
+static uint8_t* EncodeLossless(const uint8_t* rgb,
+                               int width, int height, int stride,
+                               WebPEncodeLosslessFunction encfn,
+                               int* output_size, int* unused) {
+  uint8_t* output = NULL;
+  const size_t image_size = encfn(rgb, width, height, stride, &output);
+  // the values of the following two will be interpreted by
+  // ReturnedBufferSize() as 'width' and 'height' in the size calculation.
+  *output_size = image_size;
+  *unused = 1;
+  return image_size ? output : NULL;
+}
+%}
+
+#endif  /* SWIGJAVA || SWIGPYTHON */
+
+//------------------------------------------------------------------------------
+// libwebp/encode wrapper functions
+
+#if defined(SWIGJAVA) || defined(SWIGPYTHON)
+
+%apply int* INPUT { int* unused1, int* unused2 }
+%apply int* OUTPUT { int* output_size }
+
+// free the buffer returned by these functions after copying into
+// the native type
+%newobject wrap_WebPEncodeRGB;
+%newobject wrap_WebPEncodeBGR;
+%newobject wrap_WebPEncodeRGBA;
+%newobject wrap_WebPEncodeBGRA;
+%newobject wrap_WebPEncodeLosslessRGB;
+%newobject wrap_WebPEncodeLosslessBGR;
+%newobject wrap_WebPEncodeLosslessRGBA;
+%newobject wrap_WebPEncodeLosslessBGRA;
+
+#ifdef SWIGJAVA
+// There's no reason to call these directly
+%javamethodmodifiers wrap_WebPEncodeRGB "private";
+%javamethodmodifiers wrap_WebPEncodeBGR "private";
+%javamethodmodifiers wrap_WebPEncodeRGBA "private";
+%javamethodmodifiers wrap_WebPEncodeBGRA "private";
+%javamethodmodifiers wrap_WebPEncodeLosslessRGB "private";
+%javamethodmodifiers wrap_WebPEncodeLosslessBGR "private";
+%javamethodmodifiers wrap_WebPEncodeLosslessRGBA "private";
+%javamethodmodifiers wrap_WebPEncodeLosslessBGRA "private";
+#endif  /* SWIGJAVA */
+
+#ifdef SWIGPYTHON
+// This autodoc will serve as a catch-all for wrap_*.
+%feature("autodoc", "private, do not call directly.");
+#endif
+
+%inline %{
+// Changes the return type of WebPEncode* to more closely match Decode*.
+// This also makes it easier to wrap the output buffer in a native type rather
+// than dealing with the return pointer.
+// The additional parameters are to allow reuse of ReturnedBufferSize(),
+// unused2 and output_size will be used in this case.
+#define LOSSY_WRAPPER(FUNC)                                             \
+  static uint8_t* wrap_##FUNC(                                          \
+      const uint8_t* rgb, int* unused1, int* unused2, int* output_size, \
+      int width, int height, int stride, float quality_factor) {        \
+    return EncodeLossy(rgb, width, height, stride, quality_factor,      \
+                       FUNC, output_size, unused2);                     \
+  }                                                                     \
+
+LOSSY_WRAPPER(WebPEncodeRGB)
+LOSSY_WRAPPER(WebPEncodeBGR)
+LOSSY_WRAPPER(WebPEncodeRGBA)
+LOSSY_WRAPPER(WebPEncodeBGRA)
+
+#undef LOSSY_WRAPPER
+
+#define LOSSLESS_WRAPPER(FUNC)                                          \
+  static uint8_t* wrap_##FUNC(                                          \
+      const uint8_t* rgb, int* unused1, int* unused2, int* output_size, \
+      int width, int height, int stride) {                              \
+    return EncodeLossless(rgb, width, height, stride,                   \
+                          FUNC, output_size, unused2);                  \
+  }                                                                     \
+
+LOSSLESS_WRAPPER(WebPEncodeLosslessRGB)
+LOSSLESS_WRAPPER(WebPEncodeLosslessBGR)
+LOSSLESS_WRAPPER(WebPEncodeLosslessRGBA)
+LOSSLESS_WRAPPER(WebPEncodeLosslessBGRA)
+
+#undef LOSSLESS_WRAPPER
+
+%}
+
+#endif  /* SWIGJAVA || SWIGPYTHON */
+
+//------------------------------------------------------------------------------
+// Language specific
+
+#ifdef SWIGGO
+%insert(go_wrapper) %{
+
+// WebPGetInfo has 2 output parameters, provide a version in the more natural
+// go idiom:
+func WebPGetInfo(webp []byte) (ok bool, width int, height int) {
+    w := []int{0}
+    h := []int{0}
+    ok = Wrapped_WebPGetInfo(string(webp), w, h) != 0
+    width = w[0]
+    height = h[0]
+    return
+}
+
+%}
+#endif  /* SWIGGO */
+
+#ifdef SWIGJAVA
+%{
+/* Work around broken gcj jni.h */
+#ifdef __GCJ_JNI_H__
+# undef JNIEXPORT
+# define JNIEXPORT
+# undef JNICALL
+# define JNICALL
+#endif
+%}
+
+%pragma(java) modulecode=%{
+  private static final int UNUSED = 1;
+  private static int outputSize[] = { 0 };
+%}
+
+
+%define CALL_ENCODE_LOSSY_WRAPPER(func)
+%pragma(java) modulecode=%{
+  public static byte[] func(
+      byte[] rgb, int width, int height, int stride, float quality_factor) {
+    return wrap_##func(
+        rgb, UNUSED, UNUSED, outputSize, width, height, stride, quality_factor);
+  }
+%}
+%enddef
+
+%define CALL_ENCODE_LOSSLESS_WRAPPER(func)
+%pragma(java) modulecode=%{
+  public static byte[] func(
+      byte[] rgb, int width, int height, int stride) {
+    return wrap_##func(
+        rgb, UNUSED, UNUSED, outputSize, width, height, stride);
+  }
+%}
+%enddef
+
+CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeRGB)
+CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeRGBA)
+CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeBGR)
+CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeBGRA)
+CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessRGB)
+CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessRGBA)
+CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessBGR)
+CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessBGRA)
+#endif  /* SWIGJAVA */
+
+#ifdef SWIGPYTHON
+%pythoncode %{
+_UNUSED = 1
+%}
+
+%define CALL_ENCODE_LOSSY_WRAPPER(func)
+%pythoncode %{
+def func(rgb, width, height, stride, quality_factor):
+  """func(uint8_t rgb, int width, int height, int stride, float quality_factor) -> lossy_webp"""
+  webp = wrap_##func(
+      rgb, _UNUSED, _UNUSED, width, height, stride, quality_factor)
+  if len(webp[0]) == 0:
+    return None
+  return webp[0]
+%}
+%enddef
+
+%define CALL_ENCODE_LOSSLESS_WRAPPER(func)
+%pythoncode %{
+def func(rgb, width, height, stride):
+  """func(uint8_t rgb, int width, int height, int stride) -> lossless_webp"""
+  webp = wrap_##func(rgb, _UNUSED, _UNUSED, width, height, stride)
+  if len(webp[0]) == 0:
+    return None
+  return webp[0]
+%}
+%enddef
+
+CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeRGB)
+CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeRGBA)
+CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeBGR)
+CALL_ENCODE_LOSSY_WRAPPER(WebPEncodeBGRA)
+CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessRGB)
+CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessRGBA)
+CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessBGR)
+CALL_ENCODE_LOSSLESS_WRAPPER(WebPEncodeLosslessBGRA)
+#endif  /* SWIGPYTHON */
diff --git a/swig/libwebp_gc.c b/swig/libwebp_gc.c
new file mode 100644
index 0000000..308b7f8
--- /dev/null
+++ b/swig/libwebp_gc.c
@@ -0,0 +1,52 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version 2.0.10
+ *
+ * This file is not intended to be easily readable and contains a number of
+ * coding conventions designed to improve portability and efficiency. Do not make
+ * changes to this file unless you know what you are doing--modify the SWIG
+ * interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+/* This file should be compiled with 6c/8c.  */
+#pragma dynimport _ _ "libwebp_go.so"
+
+#include "runtime.h"
+#include "cgocall.h"
+
+#ifdef _64BIT
+#define SWIG_PARM_SIZE 8
+#else
+#define SWIG_PARM_SIZE 4
+#endif
+
+#pragma dynimport _wrap_WebPGetDecoderVersion _wrap_WebPGetDecoderVersion ""
+extern void (*_wrap_WebPGetDecoderVersion)(void*);
+static void (*x_wrap_WebPGetDecoderVersion)(void*) = _wrap_WebPGetDecoderVersion;
+
+void
+·WebPGetDecoderVersion(struct {
+  uint8 x[SWIG_PARM_SIZE];
+} p)
+
+{
+  runtime·cgocall(x_wrap_WebPGetDecoderVersion, &p);
+}
+
+
+
+#pragma dynimport _wrap_wrapped_WebPGetInfo _wrap_wrapped_WebPGetInfo ""
+extern void (*_wrap_wrapped_WebPGetInfo)(void*);
+static void (*x_wrap_wrapped_WebPGetInfo)(void*) = _wrap_wrapped_WebPGetInfo;
+
+void
+·Wrapped_WebPGetInfo(struct {
+  uint8 x[(2 * SWIG_PARM_SIZE) + (3 * SWIG_PARM_SIZE) + (3 * SWIG_PARM_SIZE) + SWIG_PARM_SIZE];
+} p)
+
+{
+  runtime·cgocall(x_wrap_wrapped_WebPGetInfo, &p);
+}
+
+
+
diff --git a/swig/libwebp_go_wrap.c b/swig/libwebp_go_wrap.c
new file mode 100644
index 0000000..351d523
--- /dev/null
+++ b/swig/libwebp_go_wrap.c
@@ -0,0 +1,274 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version 2.0.10
+ *
+ * This file is not intended to be easily readable and contains a number of
+ * coding conventions designed to improve portability and efficiency. Do not make
+ * changes to this file unless you know what you are doing--modify the SWIG
+ * interface file instead.
+ * ----------------------------------------------------------------------------- */
+#define SWIGMODULE libwebp
+/* -----------------------------------------------------------------------------
+ *  This section contains generic SWIG labels for method/variable
+ *  declarations/attributes, and other compiler dependent labels.
+ * ----------------------------------------------------------------------------- */
+
+/* template workaround for compilers that cannot correctly implement the C++ standard */
+#ifndef SWIGTEMPLATEDISAMBIGUATOR
+# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560)
+#  define SWIGTEMPLATEDISAMBIGUATOR template
+# elif defined(__HP_aCC)
+/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */
+/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */
+#  define SWIGTEMPLATEDISAMBIGUATOR template
+# else
+#  define SWIGTEMPLATEDISAMBIGUATOR
+# endif
+#endif
+
+/* inline attribute */
+#ifndef SWIGINLINE
+# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__))
+#   define SWIGINLINE inline
+# else
+#   define SWIGINLINE
+# endif
+#endif
+
+/* attribute recognised by some compilers to avoid 'unused' warnings */
+#ifndef SWIGUNUSED
+# if defined(__GNUC__)
+#   if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
+#     define SWIGUNUSED __attribute__ ((__unused__))
+#   else
+#     define SWIGUNUSED
+#   endif
+# elif defined(__ICC)
+#   define SWIGUNUSED __attribute__ ((__unused__))
+# else
+#   define SWIGUNUSED
+# endif
+#endif
+
+#ifndef SWIG_MSC_UNSUPPRESS_4505
+# if defined(_MSC_VER)
+#   pragma warning(disable : 4505) /* unreferenced local function has been removed */
+# endif
+#endif
+
+#ifndef SWIGUNUSEDPARM
+# ifdef __cplusplus
+#   define SWIGUNUSEDPARM(p)
+# else
+#   define SWIGUNUSEDPARM(p) p SWIGUNUSED
+# endif
+#endif
+
+/* internal SWIG method */
+#ifndef SWIGINTERN
+# define SWIGINTERN static SWIGUNUSED
+#endif
+
+/* internal inline SWIG method */
+#ifndef SWIGINTERNINLINE
+# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE
+#endif
+
+/* exporting methods */
+#if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#  ifndef GCC_HASCLASSVISIBILITY
+#    define GCC_HASCLASSVISIBILITY
+#  endif
+#endif
+
+#ifndef SWIGEXPORT
+# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+#   if defined(STATIC_LINKED)
+#     define SWIGEXPORT
+#   else
+#     define SWIGEXPORT __declspec(dllexport)
+#   endif
+# else
+#   if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY)
+#     define SWIGEXPORT __attribute__ ((visibility("default")))
+#   else
+#     define SWIGEXPORT
+#   endif
+# endif
+#endif
+
+/* calling conventions for Windows */
+#ifndef SWIGSTDCALL
+# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+#   define SWIGSTDCALL __stdcall
+# else
+#   define SWIGSTDCALL
+# endif
+#endif
+
+/* Deal with Microsoft's attempt at deprecating C standard runtime functions */
+#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE)
+# define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */
+#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE)
+# define _SCL_SECURE_NO_DEPRECATE
+#endif
+
+
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+
+
+typedef long long intgo;
+typedef unsigned long long uintgo;
+
+
+
+typedef struct { char *p; intgo n; } _gostring_;
+typedef struct { void* array; intgo len; intgo cap; } _goslice_;
+
+
+
+
+#define swiggo_size_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2+1];
+#define swiggo_size_assert(t, n) swiggo_size_assert_eq(sizeof(t), n, swiggo_sizeof_##t##_is_not_##n)
+
+swiggo_size_assert(char, 1)
+swiggo_size_assert(short, 2)
+swiggo_size_assert(int, 4)
+typedef long long swiggo_long_long;
+swiggo_size_assert(swiggo_long_long, 8)
+swiggo_size_assert(float, 4)
+swiggo_size_assert(double, 8)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern void crosscall2(void (*fn)(void *, int), void *, int);
+extern void _cgo_allocate(void *, int);
+extern void _cgo_panic(void *, int);
+#ifdef __cplusplus
+}
+#endif
+
+static void *_swig_goallocate(size_t len) {
+  struct {
+    size_t len;
+    void *ret;
+  } a;
+  a.len = len;
+  crosscall2(_cgo_allocate, &a, (int) sizeof a);
+  return a.ret;
+}
+
+static void _swig_gopanic(const char *p) {
+  struct {
+    const char *p;
+  } a;
+  a.p = p;
+  crosscall2(_cgo_panic, &a, (int) sizeof a);
+}
+
+
+
+
+static _gostring_ _swig_makegostring(const char *p, size_t l) {
+  _gostring_ ret;
+  ret.p = (char*)_swig_goallocate(l + 1);
+  memcpy(ret.p, p, l);
+  ret.n = l;
+  return ret;
+}
+
+#define SWIG_contract_assert(expr, msg) \
+  if (!(expr)) { _swig_gopanic(msg); } else
+
+
+#define SWIG_exception(code, msg) _swig_gopanic(msg)
+
+
+#include "webp/decode.h"
+#include "webp/encode.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+_wrap_WebPGetDecoderVersion(void *swig_v)
+{
+  int result;
+
+  struct swigargs {
+    long : 0;
+    intgo result;
+  } *swig_a = (struct swigargs *) swig_v;
+
+
+  result = (int)WebPGetDecoderVersion();
+  swig_a->result = result;
+}
+
+
+void
+_wrap_wrapped_WebPGetInfo(void *swig_v)
+{
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  size_t arg2 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int temp3 ;
+  int temp4 ;
+  int result;
+
+  struct swigargs {
+    _gostring_ arg1;
+    _goslice_ arg3;
+    _goslice_ arg4;
+    long : 0;
+    intgo result;
+  } *swig_a = (struct swigargs *) swig_v;
+
+
+  arg1 = (uint8_t *)swig_a->arg1.p;
+  arg2 = (size_t)swig_a->arg1.n;
+
+  {
+    if (swig_a->arg3.len == 0) {
+      _swig_gopanic("array must contain at least 1 element");
+    }
+    arg3 = &temp3;
+  }
+  {
+    if (swig_a->arg4.len == 0) {
+      _swig_gopanic("array must contain at least 1 element");
+    }
+    arg4 = &temp4;
+  }
+
+  result = (int)WebPGetInfo((uint8_t const *)arg1,arg2,arg3,arg4);
+  swig_a->result = result;
+  {
+    int* a = (int *) swig_a->arg3.array;
+    a[0] = temp3;
+  }
+  {
+    int* a = (int *) swig_a->arg4.array;
+    a[0] = temp4;
+  }
+
+
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/swig/libwebp_java_wrap.c b/swig/libwebp_java_wrap.c
new file mode 100644
index 0000000..c8d4b13
--- /dev/null
+++ b/swig/libwebp_java_wrap.c
@@ -0,0 +1,1765 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version 2.0.4
+ *
+ * This file is not intended to be easily readable and contains a number of
+ * coding conventions designed to improve portability and efficiency. Do not make
+ * changes to this file unless you know what you are doing--modify the SWIG
+ * interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+#define SWIGJAVA
+
+/* -----------------------------------------------------------------------------
+ *  This section contains generic SWIG labels for method/variable
+ *  declarations/attributes, and other compiler dependent labels.
+ * ----------------------------------------------------------------------------- */
+
+/* template workaround for compilers that cannot correctly implement the C++ standard */
+#ifndef SWIGTEMPLATEDISAMBIGUATOR
+# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560)
+#  define SWIGTEMPLATEDISAMBIGUATOR template
+# elif defined(__HP_aCC)
+/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */
+/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */
+#  define SWIGTEMPLATEDISAMBIGUATOR template
+# else
+#  define SWIGTEMPLATEDISAMBIGUATOR
+# endif
+#endif
+
+/* inline attribute */
+#ifndef SWIGINLINE
+# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__))
+#   define SWIGINLINE inline
+# else
+#   define SWIGINLINE
+# endif
+#endif
+
+/* attribute recognised by some compilers to avoid 'unused' warnings */
+#ifndef SWIGUNUSED
+# if defined(__GNUC__)
+#   if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
+#     define SWIGUNUSED __attribute__ ((__unused__))
+#   else
+#     define SWIGUNUSED
+#   endif
+# elif defined(__ICC)
+#   define SWIGUNUSED __attribute__ ((__unused__))
+# else
+#   define SWIGUNUSED
+# endif
+#endif
+
+#ifndef SWIG_MSC_UNSUPPRESS_4505
+# if defined(_MSC_VER)
+#   pragma warning(disable : 4505) /* unreferenced local function has been removed */
+# endif
+#endif
+
+#ifndef SWIGUNUSEDPARM
+# ifdef __cplusplus
+#   define SWIGUNUSEDPARM(p)
+# else
+#   define SWIGUNUSEDPARM(p) p SWIGUNUSED
+# endif
+#endif
+
+/* internal SWIG method */
+#ifndef SWIGINTERN
+# define SWIGINTERN static SWIGUNUSED
+#endif
+
+/* internal inline SWIG method */
+#ifndef SWIGINTERNINLINE
+# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE
+#endif
+
+/* exporting methods */
+#if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#  ifndef GCC_HASCLASSVISIBILITY
+#    define GCC_HASCLASSVISIBILITY
+#  endif
+#endif
+
+#ifndef SWIGEXPORT
+# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+#   if defined(STATIC_LINKED)
+#     define SWIGEXPORT
+#   else
+#     define SWIGEXPORT __declspec(dllexport)
+#   endif
+# else
+#   if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY)
+#     define SWIGEXPORT __attribute__ ((visibility("default")))
+#   else
+#     define SWIGEXPORT
+#   endif
+# endif
+#endif
+
+/* calling conventions for Windows */
+#ifndef SWIGSTDCALL
+# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+#   define SWIGSTDCALL __stdcall
+# else
+#   define SWIGSTDCALL
+# endif
+#endif
+
+/* Deal with Microsoft's attempt at deprecating C standard runtime functions */
+#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE)
+# define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */
+#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE)
+# define _SCL_SECURE_NO_DEPRECATE
+#endif
+
+
+
+/* Fix for jlong on some versions of gcc on Windows */
+#if defined(__GNUC__) && !defined(__INTEL_COMPILER)
+  typedef long long __int64;
+#endif
+
+/* Fix for jlong on 64-bit x86 Solaris */
+#if defined(__x86_64)
+# ifdef _LP64
+#   undef _LP64
+# endif
+#endif
+
+#include <jni.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/* Support for throwing Java exceptions */
+typedef enum {
+  SWIG_JavaOutOfMemoryError = 1,
+  SWIG_JavaIOException,
+  SWIG_JavaRuntimeException,
+  SWIG_JavaIndexOutOfBoundsException,
+  SWIG_JavaArithmeticException,
+  SWIG_JavaIllegalArgumentException,
+  SWIG_JavaNullPointerException,
+  SWIG_JavaDirectorPureVirtual,
+  SWIG_JavaUnknownError
+} SWIG_JavaExceptionCodes;
+
+typedef struct {
+  SWIG_JavaExceptionCodes code;
+  const char *java_exception;
+} SWIG_JavaExceptions_t;
+
+
+static void SWIGUNUSED SWIG_JavaThrowException(JNIEnv *jenv, SWIG_JavaExceptionCodes code, const char *msg) {
+  jclass excep;
+  static const SWIG_JavaExceptions_t java_exceptions[] = {
+    { SWIG_JavaOutOfMemoryError, "java/lang/OutOfMemoryError" },
+    { SWIG_JavaIOException, "java/io/IOException" },
+    { SWIG_JavaRuntimeException, "java/lang/RuntimeException" },
+    { SWIG_JavaIndexOutOfBoundsException, "java/lang/IndexOutOfBoundsException" },
+    { SWIG_JavaArithmeticException, "java/lang/ArithmeticException" },
+    { SWIG_JavaIllegalArgumentException, "java/lang/IllegalArgumentException" },
+    { SWIG_JavaNullPointerException, "java/lang/NullPointerException" },
+    { SWIG_JavaDirectorPureVirtual, "java/lang/RuntimeException" },
+    { SWIG_JavaUnknownError,  "java/lang/UnknownError" },
+    { (SWIG_JavaExceptionCodes)0,  "java/lang/UnknownError" }
+  };
+  const SWIG_JavaExceptions_t *except_ptr = java_exceptions;
+
+  while (except_ptr->code != code && except_ptr->code)
+    except_ptr++;
+
+  (*jenv)->ExceptionClear(jenv);
+  excep = (*jenv)->FindClass(jenv, except_ptr->java_exception);
+  if (excep)
+    (*jenv)->ThrowNew(jenv, excep, msg);
+}
+
+
+/* Contract support */
+
+#define SWIG_contract_assert(nullreturn, expr, msg) if (!(expr)) {SWIG_JavaThrowException(jenv, SWIG_JavaIllegalArgumentException, msg); return nullreturn; } else
+
+/*  Errors in SWIG */
+#define  SWIG_UnknownError         -1
+#define  SWIG_IOError              -2
+#define  SWIG_RuntimeError         -3
+#define  SWIG_IndexError           -4
+#define  SWIG_TypeError            -5
+#define  SWIG_DivisionByZero       -6
+#define  SWIG_OverflowError        -7
+#define  SWIG_SyntaxError          -8
+#define  SWIG_ValueError           -9
+#define  SWIG_SystemError          -10
+#define  SWIG_AttributeError       -11
+#define  SWIG_MemoryError          -12
+#define  SWIG_NullReferenceError   -13
+
+
+
+
+SWIGINTERN void SWIG_JavaException(JNIEnv *jenv, int code, const char *msg) {
+  SWIG_JavaExceptionCodes exception_code = SWIG_JavaUnknownError;
+  switch(code) {
+  case SWIG_MemoryError:
+    exception_code = SWIG_JavaOutOfMemoryError;
+    break;
+  case SWIG_IOError:
+    exception_code = SWIG_JavaIOException;
+    break;
+  case SWIG_SystemError:
+  case SWIG_RuntimeError:
+    exception_code = SWIG_JavaRuntimeException;
+    break;
+  case SWIG_OverflowError:
+  case SWIG_IndexError:
+    exception_code = SWIG_JavaIndexOutOfBoundsException;
+    break;
+  case SWIG_DivisionByZero:
+    exception_code = SWIG_JavaArithmeticException;
+    break;
+  case SWIG_SyntaxError:
+  case SWIG_ValueError:
+  case SWIG_TypeError:
+    exception_code = SWIG_JavaIllegalArgumentException;
+    break;
+  case SWIG_UnknownError:
+  default:
+    exception_code = SWIG_JavaUnknownError;
+    break;
+  }
+  SWIG_JavaThrowException(jenv, exception_code, msg);
+}
+
+
+#if defined(SWIG_NOINCLUDE) || defined(SWIG_NOARRAYS)
+
+
+int SWIG_JavaArrayInSchar (JNIEnv *jenv, jbyte **jarr, signed char **carr, jbyteArray input);
+void SWIG_JavaArrayArgoutSchar (JNIEnv *jenv, jbyte *jarr, signed char *carr, jbyteArray input);
+jbyteArray SWIG_JavaArrayOutSchar (JNIEnv *jenv, signed char *result, jsize sz);
+
+
+int SWIG_JavaArrayInUchar (JNIEnv *jenv, jshort **jarr, unsigned char **carr, jshortArray input);
+void SWIG_JavaArrayArgoutUchar (JNIEnv *jenv, jshort *jarr, unsigned char *carr, jshortArray input);
+jshortArray SWIG_JavaArrayOutUchar (JNIEnv *jenv, unsigned char *result, jsize sz);
+
+
+int SWIG_JavaArrayInShort (JNIEnv *jenv, jshort **jarr, short **carr, jshortArray input);
+void SWIG_JavaArrayArgoutShort (JNIEnv *jenv, jshort *jarr, short *carr, jshortArray input);
+jshortArray SWIG_JavaArrayOutShort (JNIEnv *jenv, short *result, jsize sz);
+
+
+int SWIG_JavaArrayInUshort (JNIEnv *jenv, jint **jarr, unsigned short **carr, jintArray input);
+void SWIG_JavaArrayArgoutUshort (JNIEnv *jenv, jint *jarr, unsigned short *carr, jintArray input);
+jintArray SWIG_JavaArrayOutUshort (JNIEnv *jenv, unsigned short *result, jsize sz);
+
+
+int SWIG_JavaArrayInInt (JNIEnv *jenv, jint **jarr, int **carr, jintArray input);
+void SWIG_JavaArrayArgoutInt (JNIEnv *jenv, jint *jarr, int *carr, jintArray input);
+jintArray SWIG_JavaArrayOutInt (JNIEnv *jenv, int *result, jsize sz);
+
+
+int SWIG_JavaArrayInUint (JNIEnv *jenv, jlong **jarr, unsigned int **carr, jlongArray input);
+void SWIG_JavaArrayArgoutUint (JNIEnv *jenv, jlong *jarr, unsigned int *carr, jlongArray input);
+jlongArray SWIG_JavaArrayOutUint (JNIEnv *jenv, unsigned int *result, jsize sz);
+
+
+int SWIG_JavaArrayInLong (JNIEnv *jenv, jint **jarr, long **carr, jintArray input);
+void SWIG_JavaArrayArgoutLong (JNIEnv *jenv, jint *jarr, long *carr, jintArray input);
+jintArray SWIG_JavaArrayOutLong (JNIEnv *jenv, long *result, jsize sz);
+
+
+int SWIG_JavaArrayInUlong (JNIEnv *jenv, jlong **jarr, unsigned long **carr, jlongArray input);
+void SWIG_JavaArrayArgoutUlong (JNIEnv *jenv, jlong *jarr, unsigned long *carr, jlongArray input);
+jlongArray SWIG_JavaArrayOutUlong (JNIEnv *jenv, unsigned long *result, jsize sz);
+
+
+int SWIG_JavaArrayInLonglong (JNIEnv *jenv, jlong **jarr, jlong **carr, jlongArray input);
+void SWIG_JavaArrayArgoutLonglong (JNIEnv *jenv, jlong *jarr, jlong *carr, jlongArray input);
+jlongArray SWIG_JavaArrayOutLonglong (JNIEnv *jenv, jlong *result, jsize sz);
+
+
+int SWIG_JavaArrayInFloat (JNIEnv *jenv, jfloat **jarr, float **carr, jfloatArray input);
+void SWIG_JavaArrayArgoutFloat (JNIEnv *jenv, jfloat *jarr, float *carr, jfloatArray input);
+jfloatArray SWIG_JavaArrayOutFloat (JNIEnv *jenv, float *result, jsize sz);
+
+
+int SWIG_JavaArrayInDouble (JNIEnv *jenv, jdouble **jarr, double **carr, jdoubleArray input);
+void SWIG_JavaArrayArgoutDouble (JNIEnv *jenv, jdouble *jarr, double *carr, jdoubleArray input);
+jdoubleArray SWIG_JavaArrayOutDouble (JNIEnv *jenv, double *result, jsize sz);
+
+
+#else
+
+
+/* signed char[] support */
+int SWIG_JavaArrayInSchar (JNIEnv *jenv, jbyte **jarr, signed char **carr, jbyteArray input) {
+  int i;
+  jsize sz;
+  if (!input) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+    return 0;
+  }
+  sz = (*jenv)->GetArrayLength(jenv, input);
+  *jarr = (*jenv)->GetByteArrayElements(jenv, input, 0);
+  if (!*jarr)
+    return 0;
+  *carr = (signed char*) calloc(sz, sizeof(signed char));
+  if (!*carr) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+    return 0;
+  }
+  for (i=0; i<sz; i++)
+    (*carr)[i] = (signed char)(*jarr)[i];
+  return 1;
+}
+
+void SWIG_JavaArrayArgoutSchar (JNIEnv *jenv, jbyte *jarr, signed char *carr, jbyteArray input) {
+  int i;
+  jsize sz = (*jenv)->GetArrayLength(jenv, input);
+  for (i=0; i<sz; i++)
+    jarr[i] = (jbyte)carr[i];
+  (*jenv)->ReleaseByteArrayElements(jenv, input, jarr, 0);
+}
+
+jbyteArray SWIG_JavaArrayOutSchar (JNIEnv *jenv, signed char *result, jsize sz) {
+  jbyte *arr;
+  int i;
+  jbyteArray jresult = (*jenv)->NewByteArray(jenv, sz);
+  if (!jresult)
+    return NULL;
+  arr = (*jenv)->GetByteArrayElements(jenv, jresult, 0);
+  if (!arr)
+    return NULL;
+  for (i=0; i<sz; i++)
+    arr[i] = (jbyte)result[i];
+  (*jenv)->ReleaseByteArrayElements(jenv, jresult, arr, 0);
+  return jresult;
+}
+
+
+/* unsigned char[] support */
+int SWIG_JavaArrayInUchar (JNIEnv *jenv, jshort **jarr, unsigned char **carr, jshortArray input) {
+  int i;
+  jsize sz;
+  if (!input) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+    return 0;
+  }
+  sz = (*jenv)->GetArrayLength(jenv, input);
+  *jarr = (*jenv)->GetShortArrayElements(jenv, input, 0);
+  if (!*jarr)
+    return 0;
+  *carr = (unsigned char*) calloc(sz, sizeof(unsigned char));
+  if (!*carr) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+    return 0;
+  }
+  for (i=0; i<sz; i++)
+    (*carr)[i] = (unsigned char)(*jarr)[i];
+  return 1;
+}
+
+void SWIG_JavaArrayArgoutUchar (JNIEnv *jenv, jshort *jarr, unsigned char *carr, jshortArray input) {
+  int i;
+  jsize sz = (*jenv)->GetArrayLength(jenv, input);
+  for (i=0; i<sz; i++)
+    jarr[i] = (jshort)carr[i];
+  (*jenv)->ReleaseShortArrayElements(jenv, input, jarr, 0);
+}
+
+jshortArray SWIG_JavaArrayOutUchar (JNIEnv *jenv, unsigned char *result, jsize sz) {
+  jshort *arr;
+  int i;
+  jshortArray jresult = (*jenv)->NewShortArray(jenv, sz);
+  if (!jresult)
+    return NULL;
+  arr = (*jenv)->GetShortArrayElements(jenv, jresult, 0);
+  if (!arr)
+    return NULL;
+  for (i=0; i<sz; i++)
+    arr[i] = (jshort)result[i];
+  (*jenv)->ReleaseShortArrayElements(jenv, jresult, arr, 0);
+  return jresult;
+}
+
+
+/* short[] support */
+int SWIG_JavaArrayInShort (JNIEnv *jenv, jshort **jarr, short **carr, jshortArray input) {
+  int i;
+  jsize sz;
+  if (!input) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+    return 0;
+  }
+  sz = (*jenv)->GetArrayLength(jenv, input);
+  *jarr = (*jenv)->GetShortArrayElements(jenv, input, 0);
+  if (!*jarr)
+    return 0;
+  *carr = (short*) calloc(sz, sizeof(short));
+  if (!*carr) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+    return 0;
+  }
+  for (i=0; i<sz; i++)
+    (*carr)[i] = (short)(*jarr)[i];
+  return 1;
+}
+
+void SWIG_JavaArrayArgoutShort (JNIEnv *jenv, jshort *jarr, short *carr, jshortArray input) {
+  int i;
+  jsize sz = (*jenv)->GetArrayLength(jenv, input);
+  for (i=0; i<sz; i++)
+    jarr[i] = (jshort)carr[i];
+  (*jenv)->ReleaseShortArrayElements(jenv, input, jarr, 0);
+}
+
+jshortArray SWIG_JavaArrayOutShort (JNIEnv *jenv, short *result, jsize sz) {
+  jshort *arr;
+  int i;
+  jshortArray jresult = (*jenv)->NewShortArray(jenv, sz);
+  if (!jresult)
+    return NULL;
+  arr = (*jenv)->GetShortArrayElements(jenv, jresult, 0);
+  if (!arr)
+    return NULL;
+  for (i=0; i<sz; i++)
+    arr[i] = (jshort)result[i];
+  (*jenv)->ReleaseShortArrayElements(jenv, jresult, arr, 0);
+  return jresult;
+}
+
+
+/* unsigned short[] support */
+int SWIG_JavaArrayInUshort (JNIEnv *jenv, jint **jarr, unsigned short **carr, jintArray input) {
+  int i;
+  jsize sz;
+  if (!input) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+    return 0;
+  }
+  sz = (*jenv)->GetArrayLength(jenv, input);
+  *jarr = (*jenv)->GetIntArrayElements(jenv, input, 0);
+  if (!*jarr)
+    return 0;
+  *carr = (unsigned short*) calloc(sz, sizeof(unsigned short));
+  if (!*carr) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+    return 0;
+  }
+  for (i=0; i<sz; i++)
+    (*carr)[i] = (unsigned short)(*jarr)[i];
+  return 1;
+}
+
+void SWIG_JavaArrayArgoutUshort (JNIEnv *jenv, jint *jarr, unsigned short *carr, jintArray input) {
+  int i;
+  jsize sz = (*jenv)->GetArrayLength(jenv, input);
+  for (i=0; i<sz; i++)
+    jarr[i] = (jint)carr[i];
+  (*jenv)->ReleaseIntArrayElements(jenv, input, jarr, 0);
+}
+
+jintArray SWIG_JavaArrayOutUshort (JNIEnv *jenv, unsigned short *result, jsize sz) {
+  jint *arr;
+  int i;
+  jintArray jresult = (*jenv)->NewIntArray(jenv, sz);
+  if (!jresult)
+    return NULL;
+  arr = (*jenv)->GetIntArrayElements(jenv, jresult, 0);
+  if (!arr)
+    return NULL;
+  for (i=0; i<sz; i++)
+    arr[i] = (jint)result[i];
+  (*jenv)->ReleaseIntArrayElements(jenv, jresult, arr, 0);
+  return jresult;
+}
+
+
+/* int[] support */
+int SWIG_JavaArrayInInt (JNIEnv *jenv, jint **jarr, int **carr, jintArray input) {
+  int i;
+  jsize sz;
+  if (!input) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+    return 0;
+  }
+  sz = (*jenv)->GetArrayLength(jenv, input);
+  *jarr = (*jenv)->GetIntArrayElements(jenv, input, 0);
+  if (!*jarr)
+    return 0;
+  *carr = (int*) calloc(sz, sizeof(int));
+  if (!*carr) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+    return 0;
+  }
+  for (i=0; i<sz; i++)
+    (*carr)[i] = (int)(*jarr)[i];
+  return 1;
+}
+
+void SWIG_JavaArrayArgoutInt (JNIEnv *jenv, jint *jarr, int *carr, jintArray input) {
+  int i;
+  jsize sz = (*jenv)->GetArrayLength(jenv, input);
+  for (i=0; i<sz; i++)
+    jarr[i] = (jint)carr[i];
+  (*jenv)->ReleaseIntArrayElements(jenv, input, jarr, 0);
+}
+
+jintArray SWIG_JavaArrayOutInt (JNIEnv *jenv, int *result, jsize sz) {
+  jint *arr;
+  int i;
+  jintArray jresult = (*jenv)->NewIntArray(jenv, sz);
+  if (!jresult)
+    return NULL;
+  arr = (*jenv)->GetIntArrayElements(jenv, jresult, 0);
+  if (!arr)
+    return NULL;
+  for (i=0; i<sz; i++)
+    arr[i] = (jint)result[i];
+  (*jenv)->ReleaseIntArrayElements(jenv, jresult, arr, 0);
+  return jresult;
+}
+
+
+/* unsigned int[] support */
+int SWIG_JavaArrayInUint (JNIEnv *jenv, jlong **jarr, unsigned int **carr, jlongArray input) {
+  int i;
+  jsize sz;
+  if (!input) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+    return 0;
+  }
+  sz = (*jenv)->GetArrayLength(jenv, input);
+  *jarr = (*jenv)->GetLongArrayElements(jenv, input, 0);
+  if (!*jarr)
+    return 0;
+  *carr = (unsigned int*) calloc(sz, sizeof(unsigned int));
+  if (!*carr) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+    return 0;
+  }
+  for (i=0; i<sz; i++)
+    (*carr)[i] = (unsigned int)(*jarr)[i];
+  return 1;
+}
+
+void SWIG_JavaArrayArgoutUint (JNIEnv *jenv, jlong *jarr, unsigned int *carr, jlongArray input) {
+  int i;
+  jsize sz = (*jenv)->GetArrayLength(jenv, input);
+  for (i=0; i<sz; i++)
+    jarr[i] = (jlong)carr[i];
+  (*jenv)->ReleaseLongArrayElements(jenv, input, jarr, 0);
+}
+
+jlongArray SWIG_JavaArrayOutUint (JNIEnv *jenv, unsigned int *result, jsize sz) {
+  jlong *arr;
+  int i;
+  jlongArray jresult = (*jenv)->NewLongArray(jenv, sz);
+  if (!jresult)
+    return NULL;
+  arr = (*jenv)->GetLongArrayElements(jenv, jresult, 0);
+  if (!arr)
+    return NULL;
+  for (i=0; i<sz; i++)
+    arr[i] = (jlong)result[i];
+  (*jenv)->ReleaseLongArrayElements(jenv, jresult, arr, 0);
+  return jresult;
+}
+
+
+/* long[] support */
+int SWIG_JavaArrayInLong (JNIEnv *jenv, jint **jarr, long **carr, jintArray input) {
+  int i;
+  jsize sz;
+  if (!input) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+    return 0;
+  }
+  sz = (*jenv)->GetArrayLength(jenv, input);
+  *jarr = (*jenv)->GetIntArrayElements(jenv, input, 0);
+  if (!*jarr)
+    return 0;
+  *carr = (long*) calloc(sz, sizeof(long));
+  if (!*carr) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+    return 0;
+  }
+  for (i=0; i<sz; i++)
+    (*carr)[i] = (long)(*jarr)[i];
+  return 1;
+}
+
+void SWIG_JavaArrayArgoutLong (JNIEnv *jenv, jint *jarr, long *carr, jintArray input) {
+  int i;
+  jsize sz = (*jenv)->GetArrayLength(jenv, input);
+  for (i=0; i<sz; i++)
+    jarr[i] = (jint)carr[i];
+  (*jenv)->ReleaseIntArrayElements(jenv, input, jarr, 0);
+}
+
+jintArray SWIG_JavaArrayOutLong (JNIEnv *jenv, long *result, jsize sz) {
+  jint *arr;
+  int i;
+  jintArray jresult = (*jenv)->NewIntArray(jenv, sz);
+  if (!jresult)
+    return NULL;
+  arr = (*jenv)->GetIntArrayElements(jenv, jresult, 0);
+  if (!arr)
+    return NULL;
+  for (i=0; i<sz; i++)
+    arr[i] = (jint)result[i];
+  (*jenv)->ReleaseIntArrayElements(jenv, jresult, arr, 0);
+  return jresult;
+}
+
+
+/* unsigned long[] support */
+int SWIG_JavaArrayInUlong (JNIEnv *jenv, jlong **jarr, unsigned long **carr, jlongArray input) {
+  int i;
+  jsize sz;
+  if (!input) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+    return 0;
+  }
+  sz = (*jenv)->GetArrayLength(jenv, input);
+  *jarr = (*jenv)->GetLongArrayElements(jenv, input, 0);
+  if (!*jarr)
+    return 0;
+  *carr = (unsigned long*) calloc(sz, sizeof(unsigned long));
+  if (!*carr) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+    return 0;
+  }
+  for (i=0; i<sz; i++)
+    (*carr)[i] = (unsigned long)(*jarr)[i];
+  return 1;
+}
+
+void SWIG_JavaArrayArgoutUlong (JNIEnv *jenv, jlong *jarr, unsigned long *carr, jlongArray input) {
+  int i;
+  jsize sz = (*jenv)->GetArrayLength(jenv, input);
+  for (i=0; i<sz; i++)
+    jarr[i] = (jlong)carr[i];
+  (*jenv)->ReleaseLongArrayElements(jenv, input, jarr, 0);
+}
+
+jlongArray SWIG_JavaArrayOutUlong (JNIEnv *jenv, unsigned long *result, jsize sz) {
+  jlong *arr;
+  int i;
+  jlongArray jresult = (*jenv)->NewLongArray(jenv, sz);
+  if (!jresult)
+    return NULL;
+  arr = (*jenv)->GetLongArrayElements(jenv, jresult, 0);
+  if (!arr)
+    return NULL;
+  for (i=0; i<sz; i++)
+    arr[i] = (jlong)result[i];
+  (*jenv)->ReleaseLongArrayElements(jenv, jresult, arr, 0);
+  return jresult;
+}
+
+
+/* jlong[] support */
+int SWIG_JavaArrayInLonglong (JNIEnv *jenv, jlong **jarr, jlong **carr, jlongArray input) {
+  int i;
+  jsize sz;
+  if (!input) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+    return 0;
+  }
+  sz = (*jenv)->GetArrayLength(jenv, input);
+  *jarr = (*jenv)->GetLongArrayElements(jenv, input, 0);
+  if (!*jarr)
+    return 0;
+  *carr = (jlong*) calloc(sz, sizeof(jlong));
+  if (!*carr) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+    return 0;
+  }
+  for (i=0; i<sz; i++)
+    (*carr)[i] = (jlong)(*jarr)[i];
+  return 1;
+}
+
+void SWIG_JavaArrayArgoutLonglong (JNIEnv *jenv, jlong *jarr, jlong *carr, jlongArray input) {
+  int i;
+  jsize sz = (*jenv)->GetArrayLength(jenv, input);
+  for (i=0; i<sz; i++)
+    jarr[i] = (jlong)carr[i];
+  (*jenv)->ReleaseLongArrayElements(jenv, input, jarr, 0);
+}
+
+jlongArray SWIG_JavaArrayOutLonglong (JNIEnv *jenv, jlong *result, jsize sz) {
+  jlong *arr;
+  int i;
+  jlongArray jresult = (*jenv)->NewLongArray(jenv, sz);
+  if (!jresult)
+    return NULL;
+  arr = (*jenv)->GetLongArrayElements(jenv, jresult, 0);
+  if (!arr)
+    return NULL;
+  for (i=0; i<sz; i++)
+    arr[i] = (jlong)result[i];
+  (*jenv)->ReleaseLongArrayElements(jenv, jresult, arr, 0);
+  return jresult;
+}
+
+
+/* float[] support */
+int SWIG_JavaArrayInFloat (JNIEnv *jenv, jfloat **jarr, float **carr, jfloatArray input) {
+  int i;
+  jsize sz;
+  if (!input) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+    return 0;
+  }
+  sz = (*jenv)->GetArrayLength(jenv, input);
+  *jarr = (*jenv)->GetFloatArrayElements(jenv, input, 0);
+  if (!*jarr)
+    return 0;
+  *carr = (float*) calloc(sz, sizeof(float));
+  if (!*carr) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+    return 0;
+  }
+  for (i=0; i<sz; i++)
+    (*carr)[i] = (float)(*jarr)[i];
+  return 1;
+}
+
+void SWIG_JavaArrayArgoutFloat (JNIEnv *jenv, jfloat *jarr, float *carr, jfloatArray input) {
+  int i;
+  jsize sz = (*jenv)->GetArrayLength(jenv, input);
+  for (i=0; i<sz; i++)
+    jarr[i] = (jfloat)carr[i];
+  (*jenv)->ReleaseFloatArrayElements(jenv, input, jarr, 0);
+}
+
+jfloatArray SWIG_JavaArrayOutFloat (JNIEnv *jenv, float *result, jsize sz) {
+  jfloat *arr;
+  int i;
+  jfloatArray jresult = (*jenv)->NewFloatArray(jenv, sz);
+  if (!jresult)
+    return NULL;
+  arr = (*jenv)->GetFloatArrayElements(jenv, jresult, 0);
+  if (!arr)
+    return NULL;
+  for (i=0; i<sz; i++)
+    arr[i] = (jfloat)result[i];
+  (*jenv)->ReleaseFloatArrayElements(jenv, jresult, arr, 0);
+  return jresult;
+}
+
+
+/* double[] support */
+int SWIG_JavaArrayInDouble (JNIEnv *jenv, jdouble **jarr, double **carr, jdoubleArray input) {
+  int i;
+  jsize sz;
+  if (!input) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+    return 0;
+  }
+  sz = (*jenv)->GetArrayLength(jenv, input);
+  *jarr = (*jenv)->GetDoubleArrayElements(jenv, input, 0);
+  if (!*jarr)
+    return 0;
+  *carr = (double*) calloc(sz, sizeof(double));
+  if (!*carr) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+    return 0;
+  }
+  for (i=0; i<sz; i++)
+    (*carr)[i] = (double)(*jarr)[i];
+  return 1;
+}
+
+void SWIG_JavaArrayArgoutDouble (JNIEnv *jenv, jdouble *jarr, double *carr, jdoubleArray input) {
+  int i;
+  jsize sz = (*jenv)->GetArrayLength(jenv, input);
+  for (i=0; i<sz; i++)
+    jarr[i] = (jdouble)carr[i];
+  (*jenv)->ReleaseDoubleArrayElements(jenv, input, jarr, 0);
+}
+
+jdoubleArray SWIG_JavaArrayOutDouble (JNIEnv *jenv, double *result, jsize sz) {
+  jdouble *arr;
+  int i;
+  jdoubleArray jresult = (*jenv)->NewDoubleArray(jenv, sz);
+  if (!jresult)
+    return NULL;
+  arr = (*jenv)->GetDoubleArrayElements(jenv, jresult, 0);
+  if (!arr)
+    return NULL;
+  for (i=0; i<sz; i++)
+    arr[i] = (jdouble)result[i];
+  (*jenv)->ReleaseDoubleArrayElements(jenv, jresult, arr, 0);
+  return jresult;
+}
+
+
+#endif
+
+
+#include "webp/types.h"
+
+
+int SWIG_JavaArrayInUint8 (JNIEnv *jenv, jbyte **jarr, uint8_t **carr, jbyteArray input);
+void SWIG_JavaArrayArgoutUint8 (JNIEnv *jenv, jbyte *jarr, uint8_t *carr, jbyteArray input);
+jbyteArray SWIG_JavaArrayOutUint8 (JNIEnv *jenv, uint8_t *result, jsize sz);
+
+
+/* uint8_t[] support */
+int SWIG_JavaArrayInUint8 (JNIEnv *jenv, jbyte **jarr, uint8_t **carr, jbyteArray input) {
+  int i;
+  jsize sz;
+  if (!input) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "null array");
+    return 0;
+  }
+  sz = (*jenv)->GetArrayLength(jenv, input);
+  *jarr = (*jenv)->GetByteArrayElements(jenv, input, 0);
+  if (!*jarr)
+    return 0;
+  *carr = (uint8_t*) calloc(sz, sizeof(uint8_t));
+  if (!*carr) {
+    SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, "array memory allocation failed");
+    return 0;
+  }
+  for (i=0; i<sz; i++)
+    (*carr)[i] = (uint8_t)(*jarr)[i];
+  return 1;
+}
+
+void SWIG_JavaArrayArgoutUint8 (JNIEnv *jenv, jbyte *jarr, uint8_t *carr, jbyteArray input) {
+  int i;
+  jsize sz = (*jenv)->GetArrayLength(jenv, input);
+  for (i=0; i<sz; i++)
+    jarr[i] = (jbyte)carr[i];
+  (*jenv)->ReleaseByteArrayElements(jenv, input, jarr, 0);
+}
+
+jbyteArray SWIG_JavaArrayOutUint8 (JNIEnv *jenv, uint8_t *result, jsize sz) {
+  jbyte *arr;
+  int i;
+  jbyteArray jresult = (*jenv)->NewByteArray(jenv, sz);
+  if (!jresult)
+    return NULL;
+  arr = (*jenv)->GetByteArrayElements(jenv, jresult, 0);
+  if (!arr)
+    return NULL;
+  for (i=0; i<sz; i++)
+    arr[i] = (jbyte)result[i];
+  (*jenv)->ReleaseByteArrayElements(jenv, jresult, arr, 0);
+  return jresult;
+}
+
+
+#include "webp/decode.h"
+#include "webp/encode.h"
+
+
+#define FillMeInAsSizeCannotBeDeterminedAutomatically \
+    (result ? (jint)ReturnedBufferSize(__FUNCTION__, arg3, arg4) : 0)
+
+
+static size_t ReturnedBufferSize(
+    const char* function, int* width, int* height) {
+  static const struct sizemap {
+    const char* function;
+    int size_multiplier;
+  } size_map[] = {
+#ifdef SWIGJAVA
+    { "Java_com_google_webp_libwebpJNI_WebPDecodeRGB",  3 },
+    { "Java_com_google_webp_libwebpJNI_WebPDecodeRGBA", 4 },
+    { "Java_com_google_webp_libwebpJNI_WebPDecodeARGB", 4 },
+    { "Java_com_google_webp_libwebpJNI_WebPDecodeBGR",  3 },
+    { "Java_com_google_webp_libwebpJNI_WebPDecodeBGRA", 4 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGB",  1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGR",  1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGBA", 1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGRA", 1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGB",  1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGR",  1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGBA", 1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGRA", 1 },
+#endif
+#ifdef SWIGPYTHON
+    { "WebPDecodeRGB",  3 },
+    { "WebPDecodeRGBA", 4 },
+    { "WebPDecodeARGB", 4 },
+    { "WebPDecodeBGR",  3 },
+    { "WebPDecodeBGRA", 4 },
+    { "wrap_WebPEncodeRGB",  1 },
+    { "wrap_WebPEncodeBGR",  1 },
+    { "wrap_WebPEncodeRGBA", 1 },
+    { "wrap_WebPEncodeBGRA", 1 },
+    { "wrap_WebPEncodeLosslessRGB",  1 },
+    { "wrap_WebPEncodeLosslessBGR",  1 },
+    { "wrap_WebPEncodeLosslessRGBA", 1 },
+    { "wrap_WebPEncodeLosslessBGRA", 1 },
+#endif
+    { NULL, 0 }
+  };
+  const struct sizemap* p;
+  size_t size = 0;
+
+  for (p = size_map; p->function; ++p) {
+    if (!strcmp(function, p->function)) {
+      size = *width * *height * p->size_multiplier;
+      break;
+    }
+  }
+
+  return size;
+}
+
+
+typedef size_t (*WebPEncodeFunction)(const uint8_t* rgb,
+                                     int width, int height, int stride,
+                                     float quality_factor, uint8_t** output);
+typedef size_t (*WebPEncodeLosslessFunction)(const uint8_t* rgb,
+                                             int width, int height, int stride,
+                                             uint8_t** output);
+
+static uint8_t* EncodeLossy(const uint8_t* rgb,
+                            int width, int height, int stride,
+                            float quality_factor,
+                            WebPEncodeFunction encfn,
+                            int* output_size, int* unused) {
+  uint8_t* output = NULL;
+  const size_t image_size =
+      encfn(rgb, width, height, stride, quality_factor, &output);
+  // the values of following two will be interpreted by ReturnedBufferSize()
+  // as 'width' and 'height' in the size calculation.
+  *output_size = image_size;
+  *unused = 1;
+  return image_size ? output : NULL;
+}
+
+static uint8_t* EncodeLossless(const uint8_t* rgb,
+                               int width, int height, int stride,
+                               WebPEncodeLosslessFunction encfn,
+                               int* output_size, int* unused) {
+  uint8_t* output = NULL;
+  const size_t image_size = encfn(rgb, width, height, stride, &output);
+  // the values of the following two will be interpreted by
+  // ReturnedBufferSize() as 'width' and 'height' in the size calculation.
+  *output_size = image_size;
+  *unused = 1;
+  return image_size ? output : NULL;
+}
+
+
+// Changes the return type of WebPEncode* to more closely match Decode*.
+// This also makes it easier to wrap the output buffer in a native type rather
+// than dealing with the return pointer.
+// The additional parameters are to allow reuse of ReturnedBufferSize(),
+// unused2 and output_size will be used in this case.
+#define LOSSY_WRAPPER(FUNC)                                             \
+  static uint8_t* wrap_##FUNC(                                          \
+      const uint8_t* rgb, int* unused1, int* unused2, int* output_size, \
+      int width, int height, int stride, float quality_factor) {        \
+    return EncodeLossy(rgb, width, height, stride, quality_factor,      \
+                       FUNC, output_size, unused2);                     \
+  }                                                                     \
+
+LOSSY_WRAPPER(WebPEncodeRGB)
+LOSSY_WRAPPER(WebPEncodeBGR)
+LOSSY_WRAPPER(WebPEncodeRGBA)
+LOSSY_WRAPPER(WebPEncodeBGRA)
+
+#undef LOSSY_WRAPPER
+
+#define LOSSLESS_WRAPPER(FUNC)                                          \
+  static uint8_t* wrap_##FUNC(                                          \
+      const uint8_t* rgb, int* unused1, int* unused2, int* output_size, \
+      int width, int height, int stride) {                              \
+    return EncodeLossless(rgb, width, height, stride,                   \
+                          FUNC, output_size, unused2);                  \
+  }                                                                     \
+
+LOSSLESS_WRAPPER(WebPEncodeLosslessRGB)
+LOSSLESS_WRAPPER(WebPEncodeLosslessBGR)
+LOSSLESS_WRAPPER(WebPEncodeLosslessRGBA)
+LOSSLESS_WRAPPER(WebPEncodeLosslessBGRA)
+
+#undef LOSSLESS_WRAPPER
+
+
+
+/* Work around broken gcj jni.h */
+#ifdef __GCJ_JNI_H__
+# undef JNIEXPORT
+# define JNIEXPORT
+# undef JNICALL
+# define JNICALL
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+SWIGEXPORT jint JNICALL Java_com_google_webp_libwebpJNI_WebPGetDecoderVersion(JNIEnv *jenv, jclass jcls) {
+  jint jresult = 0 ;
+  int result;
+
+  (void)jenv;
+  (void)jcls;
+  result = (int)WebPGetDecoderVersion();
+  jresult = (jint)result;
+  return jresult;
+}
+
+
+SWIGEXPORT jint JNICALL Java_com_google_webp_libwebpJNI_WebPGetInfo(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2, jintArray jarg3, jintArray jarg4) {
+  jint jresult = 0 ;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  size_t arg2 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  jbyte *jarr1 ;
+  int temp3 ;
+  int temp4 ;
+  int result;
+
+  (void)jenv;
+  (void)jcls;
+  if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+  arg2 = (size_t)jarg2;
+  {
+    if (!jarg3) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg3) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg3 = &temp3;
+  }
+  {
+    if (!jarg4) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg4 = &temp4;
+  }
+  result = (int)WebPGetInfo((uint8_t const *)arg1,arg2,arg3,arg4);
+  jresult = (jint)result;
+  SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+  {
+    jint jvalue = (jint)temp3;
+    (*jenv)->SetIntArrayRegion(jenv, jarg3, 0, 1, &jvalue);
+  }
+  {
+    jint jvalue = (jint)temp4;
+    (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+  }
+  free(arg1);
+
+
+  return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_WebPDecodeRGB(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2, jintArray jarg3, jintArray jarg4) {
+  jbyteArray jresult = 0 ;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  size_t arg2 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  jbyte *jarr1 ;
+  int temp3 ;
+  int temp4 ;
+  uint8_t *result = 0 ;
+
+  (void)jenv;
+  (void)jcls;
+  if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+  arg2 = (size_t)jarg2;
+  {
+    if (!jarg3) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg3) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg3 = &temp3;
+  }
+  {
+    if (!jarg4) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg4 = &temp4;
+  }
+  result = (uint8_t *)WebPDecodeRGB((uint8_t const *)arg1,arg2,arg3,arg4);
+  jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+  SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+  {
+    jint jvalue = (jint)temp3;
+    (*jenv)->SetIntArrayRegion(jenv, jarg3, 0, 1, &jvalue);
+  }
+  {
+    jint jvalue = (jint)temp4;
+    (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+  }
+  free(arg1);
+
+
+  free(result);
+  return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_WebPDecodeRGBA(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2, jintArray jarg3, jintArray jarg4) {
+  jbyteArray jresult = 0 ;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  size_t arg2 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  jbyte *jarr1 ;
+  int temp3 ;
+  int temp4 ;
+  uint8_t *result = 0 ;
+
+  (void)jenv;
+  (void)jcls;
+  if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+  arg2 = (size_t)jarg2;
+  {
+    if (!jarg3) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg3) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg3 = &temp3;
+  }
+  {
+    if (!jarg4) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg4 = &temp4;
+  }
+  result = (uint8_t *)WebPDecodeRGBA((uint8_t const *)arg1,arg2,arg3,arg4);
+  jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+  SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+  {
+    jint jvalue = (jint)temp3;
+    (*jenv)->SetIntArrayRegion(jenv, jarg3, 0, 1, &jvalue);
+  }
+  {
+    jint jvalue = (jint)temp4;
+    (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+  }
+  free(arg1);
+
+
+  free(result);
+  return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_WebPDecodeARGB(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2, jintArray jarg3, jintArray jarg4) {
+  jbyteArray jresult = 0 ;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  size_t arg2 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  jbyte *jarr1 ;
+  int temp3 ;
+  int temp4 ;
+  uint8_t *result = 0 ;
+
+  (void)jenv;
+  (void)jcls;
+  if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+  arg2 = (size_t)jarg2;
+  {
+    if (!jarg3) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg3) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg3 = &temp3;
+  }
+  {
+    if (!jarg4) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg4 = &temp4;
+  }
+  result = (uint8_t *)WebPDecodeARGB((uint8_t const *)arg1,arg2,arg3,arg4);
+  jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+  SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+  {
+    jint jvalue = (jint)temp3;
+    (*jenv)->SetIntArrayRegion(jenv, jarg3, 0, 1, &jvalue);
+  }
+  {
+    jint jvalue = (jint)temp4;
+    (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+  }
+  free(arg1);
+
+
+  free(result);
+  return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_WebPDecodeBGR(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2, jintArray jarg3, jintArray jarg4) {
+  jbyteArray jresult = 0 ;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  size_t arg2 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  jbyte *jarr1 ;
+  int temp3 ;
+  int temp4 ;
+  uint8_t *result = 0 ;
+
+  (void)jenv;
+  (void)jcls;
+  if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+  arg2 = (size_t)jarg2;
+  {
+    if (!jarg3) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg3) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg3 = &temp3;
+  }
+  {
+    if (!jarg4) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg4 = &temp4;
+  }
+  result = (uint8_t *)WebPDecodeBGR((uint8_t const *)arg1,arg2,arg3,arg4);
+  jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+  SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+  {
+    jint jvalue = (jint)temp3;
+    (*jenv)->SetIntArrayRegion(jenv, jarg3, 0, 1, &jvalue);
+  }
+  {
+    jint jvalue = (jint)temp4;
+    (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+  }
+  free(arg1);
+
+
+  free(result);
+  return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_WebPDecodeBGRA(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2, jintArray jarg3, jintArray jarg4) {
+  jbyteArray jresult = 0 ;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  size_t arg2 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  jbyte *jarr1 ;
+  int temp3 ;
+  int temp4 ;
+  uint8_t *result = 0 ;
+
+  (void)jenv;
+  (void)jcls;
+  if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+  arg2 = (size_t)jarg2;
+  {
+    if (!jarg3) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg3) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg3 = &temp3;
+  }
+  {
+    if (!jarg4) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg4 = &temp4;
+  }
+  result = (uint8_t *)WebPDecodeBGRA((uint8_t const *)arg1,arg2,arg3,arg4);
+  jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+  SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+  {
+    jint jvalue = (jint)temp3;
+    (*jenv)->SetIntArrayRegion(jenv, jarg3, 0, 1, &jvalue);
+  }
+  {
+    jint jvalue = (jint)temp4;
+    (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+  }
+  free(arg1);
+
+
+  free(result);
+  return jresult;
+}
+
+
+SWIGEXPORT jint JNICALL Java_com_google_webp_libwebpJNI_WebPGetEncoderVersion(JNIEnv *jenv, jclass jcls) {
+  jint jresult = 0 ;
+  int result;
+
+  (void)jenv;
+  (void)jcls;
+  result = (int)WebPGetEncoderVersion();
+  jresult = (jint)result;
+  return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGB(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7, jfloat jarg8) {
+  jbyteArray jresult = 0 ;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  int *arg2 = (int *) 0 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int arg5 ;
+  int arg6 ;
+  int arg7 ;
+  float arg8 ;
+  jbyte *jarr1 ;
+  int temp4 ;
+  uint8_t *result = 0 ;
+
+  (void)jenv;
+  (void)jcls;
+  if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+  arg2 = (int *)&jarg2;
+  arg3 = (int *)&jarg3;
+  {
+    if (!jarg4) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg4 = &temp4;
+  }
+  arg5 = (int)jarg5;
+  arg6 = (int)jarg6;
+  arg7 = (int)jarg7;
+  arg8 = (float)jarg8;
+  result = (uint8_t *)wrap_WebPEncodeRGB((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8);
+  jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+  SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+  {
+    jint jvalue = (jint)temp4;
+    (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+  }
+  free(arg1);
+
+
+
+  free(result);
+  return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGR(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7, jfloat jarg8) {
+  jbyteArray jresult = 0 ;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  int *arg2 = (int *) 0 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int arg5 ;
+  int arg6 ;
+  int arg7 ;
+  float arg8 ;
+  jbyte *jarr1 ;
+  int temp4 ;
+  uint8_t *result = 0 ;
+
+  (void)jenv;
+  (void)jcls;
+  if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+  arg2 = (int *)&jarg2;
+  arg3 = (int *)&jarg3;
+  {
+    if (!jarg4) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg4 = &temp4;
+  }
+  arg5 = (int)jarg5;
+  arg6 = (int)jarg6;
+  arg7 = (int)jarg7;
+  arg8 = (float)jarg8;
+  result = (uint8_t *)wrap_WebPEncodeBGR((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8);
+  jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+  SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+  {
+    jint jvalue = (jint)temp4;
+    (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+  }
+  free(arg1);
+
+
+
+  free(result);
+  return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGBA(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7, jfloat jarg8) {
+  jbyteArray jresult = 0 ;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  int *arg2 = (int *) 0 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int arg5 ;
+  int arg6 ;
+  int arg7 ;
+  float arg8 ;
+  jbyte *jarr1 ;
+  int temp4 ;
+  uint8_t *result = 0 ;
+
+  (void)jenv;
+  (void)jcls;
+  if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+  arg2 = (int *)&jarg2;
+  arg3 = (int *)&jarg3;
+  {
+    if (!jarg4) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg4 = &temp4;
+  }
+  arg5 = (int)jarg5;
+  arg6 = (int)jarg6;
+  arg7 = (int)jarg7;
+  arg8 = (float)jarg8;
+  result = (uint8_t *)wrap_WebPEncodeRGBA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8);
+  jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+  SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+  {
+    jint jvalue = (jint)temp4;
+    (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+  }
+  free(arg1);
+
+
+
+  free(result);
+  return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGRA(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7, jfloat jarg8) {
+  jbyteArray jresult = 0 ;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  int *arg2 = (int *) 0 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int arg5 ;
+  int arg6 ;
+  int arg7 ;
+  float arg8 ;
+  jbyte *jarr1 ;
+  int temp4 ;
+  uint8_t *result = 0 ;
+
+  (void)jenv;
+  (void)jcls;
+  if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+  arg2 = (int *)&jarg2;
+  arg3 = (int *)&jarg3;
+  {
+    if (!jarg4) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg4 = &temp4;
+  }
+  arg5 = (int)jarg5;
+  arg6 = (int)jarg6;
+  arg7 = (int)jarg7;
+  arg8 = (float)jarg8;
+  result = (uint8_t *)wrap_WebPEncodeBGRA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8);
+  jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+  SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+  {
+    jint jvalue = (jint)temp4;
+    (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+  }
+  free(arg1);
+
+
+
+  free(result);
+  return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGB(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7) {
+  jbyteArray jresult = 0 ;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  int *arg2 = (int *) 0 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int arg5 ;
+  int arg6 ;
+  int arg7 ;
+  jbyte *jarr1 ;
+  int temp4 ;
+  uint8_t *result = 0 ;
+
+  (void)jenv;
+  (void)jcls;
+  if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+  arg2 = (int *)&jarg2;
+  arg3 = (int *)&jarg3;
+  {
+    if (!jarg4) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg4 = &temp4;
+  }
+  arg5 = (int)jarg5;
+  arg6 = (int)jarg6;
+  arg7 = (int)jarg7;
+  result = (uint8_t *)wrap_WebPEncodeLosslessRGB((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+  jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+  SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+  {
+    jint jvalue = (jint)temp4;
+    (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+  }
+  free(arg1);
+
+
+
+  free(result);
+  return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGR(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7) {
+  jbyteArray jresult = 0 ;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  int *arg2 = (int *) 0 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int arg5 ;
+  int arg6 ;
+  int arg7 ;
+  jbyte *jarr1 ;
+  int temp4 ;
+  uint8_t *result = 0 ;
+
+  (void)jenv;
+  (void)jcls;
+  if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+  arg2 = (int *)&jarg2;
+  arg3 = (int *)&jarg3;
+  {
+    if (!jarg4) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg4 = &temp4;
+  }
+  arg5 = (int)jarg5;
+  arg6 = (int)jarg6;
+  arg7 = (int)jarg7;
+  result = (uint8_t *)wrap_WebPEncodeLosslessBGR((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+  jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+  SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+  {
+    jint jvalue = (jint)temp4;
+    (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+  }
+  free(arg1);
+
+
+
+  free(result);
+  return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGBA(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7) {
+  jbyteArray jresult = 0 ;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  int *arg2 = (int *) 0 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int arg5 ;
+  int arg6 ;
+  int arg7 ;
+  jbyte *jarr1 ;
+  int temp4 ;
+  uint8_t *result = 0 ;
+
+  (void)jenv;
+  (void)jcls;
+  if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+  arg2 = (int *)&jarg2;
+  arg3 = (int *)&jarg3;
+  {
+    if (!jarg4) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg4 = &temp4;
+  }
+  arg5 = (int)jarg5;
+  arg6 = (int)jarg6;
+  arg7 = (int)jarg7;
+  result = (uint8_t *)wrap_WebPEncodeLosslessRGBA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+  jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+  SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+  {
+    jint jvalue = (jint)temp4;
+    (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+  }
+  free(arg1);
+
+
+
+  free(result);
+  return jresult;
+}
+
+
+SWIGEXPORT jbyteArray JNICALL Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGRA(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jint jarg2, jint jarg3, jintArray jarg4, jint jarg5, jint jarg6, jint jarg7) {
+  jbyteArray jresult = 0 ;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  int *arg2 = (int *) 0 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int arg5 ;
+  int arg6 ;
+  int arg7 ;
+  jbyte *jarr1 ;
+  int temp4 ;
+  uint8_t *result = 0 ;
+
+  (void)jenv;
+  (void)jcls;
+  if (!SWIG_JavaArrayInUint8(jenv, &jarr1, &arg1, jarg1)) return 0;
+  arg2 = (int *)&jarg2;
+  arg3 = (int *)&jarg3;
+  {
+    if (!jarg4) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "array null");
+      return 0;
+    }
+    if ((*jenv)->GetArrayLength(jenv, jarg4) == 0) {
+      SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, "Array must contain at least 1 element");
+      return 0;
+    }
+    arg4 = &temp4;
+  }
+  arg5 = (int)jarg5;
+  arg6 = (int)jarg6;
+  arg7 = (int)jarg7;
+  result = (uint8_t *)wrap_WebPEncodeLosslessBGRA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+  jresult = SWIG_JavaArrayOutUint8(jenv, result, FillMeInAsSizeCannotBeDeterminedAutomatically);
+  SWIG_JavaArrayArgoutUint8(jenv, jarr1, arg1, jarg1);
+  {
+    jint jvalue = (jint)temp4;
+    (*jenv)->SetIntArrayRegion(jenv, jarg4, 0, 1, &jvalue);
+  }
+  free(arg1);
+
+
+
+  free(result);
+  return jresult;
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/swig/libwebp_python_wrap.c b/swig/libwebp_python_wrap.c
new file mode 100644
index 0000000..3aa2728
--- /dev/null
+++ b/swig/libwebp_python_wrap.c
@@ -0,0 +1,5628 @@
+/* ----------------------------------------------------------------------------
+ * This file was automatically generated by SWIG (http://www.swig.org).
+ * Version 3.0.12
+ *
+ * This file is not intended to be easily readable and contains a number of
+ * coding conventions designed to improve portability and efficiency. Do not make
+ * changes to this file unless you know what you are doing--modify the SWIG
+ * interface file instead.
+ * ----------------------------------------------------------------------------- */
+
+#define SWIG_PYTHON_STRICT_BYTE_CHAR
+
+
+
+#ifndef SWIGPYTHON
+#define SWIGPYTHON
+#endif
+
+#define SWIG_PYTHON_DIRECTOR_NO_VTABLE
+
+/* -----------------------------------------------------------------------------
+ *  This section contains generic SWIG labels for method/variable
+ *  declarations/attributes, and other compiler dependent labels.
+ * ----------------------------------------------------------------------------- */
+
+/* template workaround for compilers that cannot correctly implement the C++ standard */
+#ifndef SWIGTEMPLATEDISAMBIGUATOR
+# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560)
+#  define SWIGTEMPLATEDISAMBIGUATOR template
+# elif defined(__HP_aCC)
+/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */
+/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */
+#  define SWIGTEMPLATEDISAMBIGUATOR template
+# else
+#  define SWIGTEMPLATEDISAMBIGUATOR
+# endif
+#endif
+
+/* inline attribute */
+#ifndef SWIGINLINE
+# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__))
+#   define SWIGINLINE inline
+# else
+#   define SWIGINLINE
+# endif
+#endif
+
+/* attribute recognised by some compilers to avoid 'unused' warnings */
+#ifndef SWIGUNUSED
+# if defined(__GNUC__)
+#   if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
+#     define SWIGUNUSED __attribute__ ((__unused__))
+#   else
+#     define SWIGUNUSED
+#   endif
+# elif defined(__ICC)
+#   define SWIGUNUSED __attribute__ ((__unused__))
+# else
+#   define SWIGUNUSED
+# endif
+#endif
+
+#ifndef SWIG_MSC_UNSUPPRESS_4505
+# if defined(_MSC_VER)
+#   pragma warning(disable : 4505) /* unreferenced local function has been removed */
+# endif
+#endif
+
+#ifndef SWIGUNUSEDPARM
+# ifdef __cplusplus
+#   define SWIGUNUSEDPARM(p)
+# else
+#   define SWIGUNUSEDPARM(p) p SWIGUNUSED
+# endif
+#endif
+
+/* internal SWIG method */
+#ifndef SWIGINTERN
+# define SWIGINTERN static SWIGUNUSED
+#endif
+
+/* internal inline SWIG method */
+#ifndef SWIGINTERNINLINE
+# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE
+#endif
+
+/* exporting methods */
+#if defined(__GNUC__)
+#  if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#    ifndef GCC_HASCLASSVISIBILITY
+#      define GCC_HASCLASSVISIBILITY
+#    endif
+#  endif
+#endif
+
+#ifndef SWIGEXPORT
+# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+#   if defined(STATIC_LINKED)
+#     define SWIGEXPORT
+#   else
+#     define SWIGEXPORT __declspec(dllexport)
+#   endif
+# else
+#   if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY)
+#     define SWIGEXPORT __attribute__ ((visibility("default")))
+#   else
+#     define SWIGEXPORT
+#   endif
+# endif
+#endif
+
+/* calling conventions for Windows */
+#ifndef SWIGSTDCALL
+# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+#   define SWIGSTDCALL __stdcall
+# else
+#   define SWIGSTDCALL
+# endif
+#endif
+
+/* Deal with Microsoft's attempt at deprecating C standard runtime functions */
+#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE)
+# define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */
+#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE)
+# define _SCL_SECURE_NO_DEPRECATE
+#endif
+
+/* Deal with Apple's deprecated 'AssertMacros.h' from Carbon-framework */
+#if defined(__APPLE__) && !defined(__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES)
+# define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0
+#endif
+
+/* Intel's compiler complains if a variable which was never initialised is
+ * cast to void, which is a common idiom which we use to indicate that we
+ * are aware a variable isn't used.  So we just silence that warning.
+ * See: https://github.com/swig/swig/issues/192 for more discussion.
+ */
+#ifdef __INTEL_COMPILER
+# pragma warning disable 592
+#endif
+
+
+#if defined(_DEBUG) && defined(SWIG_PYTHON_INTERPRETER_NO_DEBUG)
+/* Use debug wrappers with the Python release dll */
+# undef _DEBUG
+# include <Python.h>
+# define _DEBUG
+#else
+# include <Python.h>
+#endif
+
+/* -----------------------------------------------------------------------------
+ * swigrun.swg
+ *
+ * This file contains generic C API SWIG runtime support for pointer
+ * type checking.
+ * ----------------------------------------------------------------------------- */
+
+/* This should only be incremented when either the layout of swig_type_info changes,
+   or for whatever reason, the runtime changes incompatibly */
+#define SWIG_RUNTIME_VERSION "4"
+
+/* define SWIG_TYPE_TABLE_NAME as "SWIG_TYPE_TABLE" */
+#ifdef SWIG_TYPE_TABLE
+# define SWIG_QUOTE_STRING(x) #x
+# define SWIG_EXPAND_AND_QUOTE_STRING(x) SWIG_QUOTE_STRING(x)
+# define SWIG_TYPE_TABLE_NAME SWIG_EXPAND_AND_QUOTE_STRING(SWIG_TYPE_TABLE)
+#else
+# define SWIG_TYPE_TABLE_NAME
+#endif
+
+/*
+  You can use the SWIGRUNTIME and SWIGRUNTIMEINLINE macros for
+  creating a static or dynamic library from the SWIG runtime code.
+  In 99.9% of the cases, SWIG just needs to declare them as 'static'.
+
+  But only do this if strictly necessary, ie, if you have problems
+  with your compiler or suchlike.
+*/
+
+#ifndef SWIGRUNTIME
+# define SWIGRUNTIME SWIGINTERN
+#endif
+
+#ifndef SWIGRUNTIMEINLINE
+# define SWIGRUNTIMEINLINE SWIGRUNTIME SWIGINLINE
+#endif
+
+/*  Generic buffer size */
+#ifndef SWIG_BUFFER_SIZE
+# define SWIG_BUFFER_SIZE 1024
+#endif
+
+/* Flags for pointer conversions */
+#define SWIG_POINTER_DISOWN        0x1
+#define SWIG_CAST_NEW_MEMORY       0x2
+
+/* Flags for new pointer objects */
+#define SWIG_POINTER_OWN           0x1
+
+
+/*
+   Flags/methods for returning states.
+
+   The SWIG conversion methods, as ConvertPtr, return an integer
+   that tells if the conversion was successful or not. And if not,
+   an error code can be returned (see swigerrors.swg for the codes).
+
+   Use the following macros/flags to set or process the returning
+   states.
+
+   In old versions of SWIG, code such as the following was usually written:
+
+     if (SWIG_ConvertPtr(obj,vptr,ty.flags) != -1) {
+       // success code
+     } else {
+       //fail code
+     }
+
+   Now you can be more explicit:
+
+    int res = SWIG_ConvertPtr(obj,vptr,ty.flags);
+    if (SWIG_IsOK(res)) {
+      // success code
+    } else {
+      // fail code
+    }
+
+   which is the same really, but now you can also do
+
+    Type *ptr;
+    int res = SWIG_ConvertPtr(obj,(void **)(&ptr),ty.flags);
+    if (SWIG_IsOK(res)) {
+      // success code
+      if (SWIG_IsNewObj(res) {
+        ...
+        delete *ptr;
+      } else {
+        ...
+      }
+    } else {
+      // fail code
+    }
+
+   I.e., now SWIG_ConvertPtr can return new objects and you can
+   identify the case and take care of the deallocation. Of course that
+   also requires SWIG_ConvertPtr to return new result values, such as
+
+      int SWIG_ConvertPtr(obj, ptr,...) {
+        if (<obj is ok>) {
+          if (<need new object>) {
+            *ptr = <ptr to new allocated object>;
+            return SWIG_NEWOBJ;
+          } else {
+            *ptr = <ptr to old object>;
+            return SWIG_OLDOBJ;
+          }
+        } else {
+          return SWIG_BADOBJ;
+        }
+      }
+
+   Of course, returning the plain '0(success)/-1(fail)' still works, but you can be
+   more explicit by returning SWIG_BADOBJ, SWIG_ERROR or any of the
+   SWIG errors code.
+
+   Finally, if the SWIG_CASTRANK_MODE is enabled, the result code
+   allows to return the 'cast rank', for example, if you have this
+
+       int food(double)
+       int fooi(int);
+
+   and you call
+
+      food(1)   // cast rank '1'  (1 -> 1.0)
+      fooi(1)   // cast rank '0'
+
+   just use the SWIG_AddCast()/SWIG_CheckState()
+*/
+
+#define SWIG_OK                    (0)
+#define SWIG_ERROR                 (-1)
+#define SWIG_IsOK(r)               (r >= 0)
+#define SWIG_ArgError(r)           ((r != SWIG_ERROR) ? r : SWIG_TypeError)
+
+/* The CastRankLimit says how many bits are used for the cast rank */
+#define SWIG_CASTRANKLIMIT         (1 << 8)
+/* The NewMask denotes the object was created (using new/malloc) */
+#define SWIG_NEWOBJMASK            (SWIG_CASTRANKLIMIT  << 1)
+/* The TmpMask is for in/out typemaps that use temporal objects */
+#define SWIG_TMPOBJMASK            (SWIG_NEWOBJMASK << 1)
+/* Simple returning values */
+#define SWIG_BADOBJ                (SWIG_ERROR)
+#define SWIG_OLDOBJ                (SWIG_OK)
+#define SWIG_NEWOBJ                (SWIG_OK | SWIG_NEWOBJMASK)
+#define SWIG_TMPOBJ                (SWIG_OK | SWIG_TMPOBJMASK)
+/* Check, add and del mask methods */
+#define SWIG_AddNewMask(r)         (SWIG_IsOK(r) ? (r | SWIG_NEWOBJMASK) : r)
+#define SWIG_DelNewMask(r)         (SWIG_IsOK(r) ? (r & ~SWIG_NEWOBJMASK) : r)
+#define SWIG_IsNewObj(r)           (SWIG_IsOK(r) && (r & SWIG_NEWOBJMASK))
+#define SWIG_AddTmpMask(r)         (SWIG_IsOK(r) ? (r | SWIG_TMPOBJMASK) : r)
+#define SWIG_DelTmpMask(r)         (SWIG_IsOK(r) ? (r & ~SWIG_TMPOBJMASK) : r)
+#define SWIG_IsTmpObj(r)           (SWIG_IsOK(r) && (r & SWIG_TMPOBJMASK))
+
+/* Cast-Rank Mode */
+#if defined(SWIG_CASTRANK_MODE)
+#  ifndef SWIG_TypeRank
+#    define SWIG_TypeRank             unsigned long
+#  endif
+#  ifndef SWIG_MAXCASTRANK            /* Default cast allowed */
+#    define SWIG_MAXCASTRANK          (2)
+#  endif
+#  define SWIG_CASTRANKMASK          ((SWIG_CASTRANKLIMIT) -1)
+#  define SWIG_CastRank(r)           (r & SWIG_CASTRANKMASK)
+SWIGINTERNINLINE int SWIG_AddCast(int r) {
+  return SWIG_IsOK(r) ? ((SWIG_CastRank(r) < SWIG_MAXCASTRANK) ? (r + 1) : SWIG_ERROR) : r;
+}
+SWIGINTERNINLINE int SWIG_CheckState(int r) {
+  return SWIG_IsOK(r) ? SWIG_CastRank(r) + 1 : 0;
+}
+#else /* no cast-rank mode */
+#  define SWIG_AddCast(r) (r)
+#  define SWIG_CheckState(r) (SWIG_IsOK(r) ? 1 : 0)
+#endif
+
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void *(*swig_converter_func)(void *, int *);
+typedef struct swig_type_info *(*swig_dycast_func)(void **);
+
+/* Structure to store information on one type */
+typedef struct swig_type_info {
+  const char             *name;                 /* mangled name of this type */
+  const char             *str;                  /* human readable name of this type */
+  swig_dycast_func        dcast;                /* dynamic cast function down a hierarchy */
+  struct swig_cast_info  *cast;                 /* linked list of types that can cast into this type */
+  void                   *clientdata;           /* language specific type data */
+  int                    owndata;               /* flag if the structure owns the clientdata */
+} swig_type_info;
+
+/* Structure to store a type and conversion function used for casting */
+typedef struct swig_cast_info {
+  swig_type_info         *type;                 /* pointer to type that is equivalent to this type */
+  swig_converter_func     converter;            /* function to cast the void pointers */
+  struct swig_cast_info  *next;                 /* pointer to next cast in linked list */
+  struct swig_cast_info  *prev;                 /* pointer to the previous cast */
+} swig_cast_info;
+
+/* Structure used to store module information
+ * Each module generates one structure like this, and the runtime collects
+ * all of these structures and stores them in a circularly linked list.*/
+typedef struct swig_module_info {
+  swig_type_info         **types;               /* Array of pointers to swig_type_info structures that are in this module */
+  size_t                 size;                  /* Number of types in this module */
+  struct swig_module_info *next;                /* Pointer to next element in circularly linked list */
+  swig_type_info         **type_initial;        /* Array of initially generated type structures */
+  swig_cast_info         **cast_initial;        /* Array of initially generated casting structures */
+  void                    *clientdata;          /* Language specific module data */
+} swig_module_info;
+
+/*
+  Compare two type names skipping the space characters, therefore
+  "char*" == "char *" and "Class<int>" == "Class<int >", etc.
+
+  Return 0 when the two name types are equivalent, as in
+  strncmp, but skipping ' '.
+*/
+SWIGRUNTIME int
+SWIG_TypeNameComp(const char *f1, const char *l1,
+                  const char *f2, const char *l2) {
+  for (;(f1 != l1) && (f2 != l2); ++f1, ++f2) {
+    while ((*f1 == ' ') && (f1 != l1)) ++f1;
+    while ((*f2 == ' ') && (f2 != l2)) ++f2;
+    if (*f1 != *f2) return (*f1 > *f2) ? 1 : -1;
+  }
+  return (int)((l1 - f1) - (l2 - f2));
+}
+
+/*
+  Check type equivalence in a name list like <name1>|<name2>|...
+  Return 0 if equal, -1 if nb < tb, 1 if nb > tb
+*/
+SWIGRUNTIME int
+SWIG_TypeCmp(const char *nb, const char *tb) {
+  int equiv = 1;
+  const char* te = tb + strlen(tb);
+  const char* ne = nb;
+  while (equiv != 0 && *ne) {
+    for (nb = ne; *ne; ++ne) {
+      if (*ne == '|') break;
+    }
+    equiv = SWIG_TypeNameComp(nb, ne, tb, te);
+    if (*ne) ++ne;
+  }
+  return equiv;
+}
+
+/*
+  Check type equivalence in a name list like <name1>|<name2>|...
+  Return 0 if not equal, 1 if equal
+*/
+SWIGRUNTIME int
+SWIG_TypeEquiv(const char *nb, const char *tb) {
+  return SWIG_TypeCmp(nb, tb) == 0 ? 1 : 0;
+}
+
+/*
+  Check the typename
+*/
+SWIGRUNTIME swig_cast_info *
+SWIG_TypeCheck(const char *c, swig_type_info *ty) {
+  if (ty) {
+    swig_cast_info *iter = ty->cast;
+    while (iter) {
+      if (strcmp(iter->type->name, c) == 0) {
+        if (iter == ty->cast)
+          return iter;
+        /* Move iter to the top of the linked list */
+        iter->prev->next = iter->next;
+        if (iter->next)
+          iter->next->prev = iter->prev;
+        iter->next = ty->cast;
+        iter->prev = 0;
+        if (ty->cast) ty->cast->prev = iter;
+        ty->cast = iter;
+        return iter;
+      }
+      iter = iter->next;
+    }
+  }
+  return 0;
+}
+
+/*
+  Identical to SWIG_TypeCheck, except strcmp is replaced with a pointer comparison
+*/
+SWIGRUNTIME swig_cast_info *
+SWIG_TypeCheckStruct(swig_type_info *from, swig_type_info *ty) {
+  if (ty) {
+    swig_cast_info *iter = ty->cast;
+    while (iter) {
+      if (iter->type == from) {
+        if (iter == ty->cast)
+          return iter;
+        /* Move iter to the top of the linked list */
+        iter->prev->next = iter->next;
+        if (iter->next)
+          iter->next->prev = iter->prev;
+        iter->next = ty->cast;
+        iter->prev = 0;
+        if (ty->cast) ty->cast->prev = iter;
+        ty->cast = iter;
+        return iter;
+      }
+      iter = iter->next;
+    }
+  }
+  return 0;
+}
+
+/*
+  Cast a pointer up an inheritance hierarchy
+*/
+SWIGRUNTIMEINLINE void *
+SWIG_TypeCast(swig_cast_info *ty, void *ptr, int *newmemory) {
+  return ((!ty) || (!ty->converter)) ? ptr : (*ty->converter)(ptr, newmemory);
+}
+
+/*
+   Dynamic pointer casting. Down an inheritance hierarchy
+*/
+SWIGRUNTIME swig_type_info *
+SWIG_TypeDynamicCast(swig_type_info *ty, void **ptr) {
+  swig_type_info *lastty = ty;
+  if (!ty || !ty->dcast) return ty;
+  while (ty && (ty->dcast)) {
+    ty = (*ty->dcast)(ptr);
+    if (ty) lastty = ty;
+  }
+  return lastty;
+}
+
+/*
+  Return the name associated with this type
+*/
+SWIGRUNTIMEINLINE const char *
+SWIG_TypeName(const swig_type_info *ty) {
+  return ty->name;
+}
+
+/*
+  Return the pretty name associated with this type,
+  that is an unmangled type name in a form presentable to the user.
+*/
+SWIGRUNTIME const char *
+SWIG_TypePrettyName(const swig_type_info *type) {
+  /* The "str" field contains the equivalent pretty names of the
+     type, separated by vertical-bar characters.  We choose
+     to print the last name, as it is often (?) the most
+     specific. */
+  if (!type) return NULL;
+  if (type->str != NULL) {
+    const char *last_name = type->str;
+    const char *s;
+    for (s = type->str; *s; s++)
+      if (*s == '|') last_name = s+1;
+    return last_name;
+  }
+  else
+    return type->name;
+}
+
+/*
+   Set the clientdata field for a type
+*/
+SWIGRUNTIME void
+SWIG_TypeClientData(swig_type_info *ti, void *clientdata) {
+  swig_cast_info *cast = ti->cast;
+  /* if (ti->clientdata == clientdata) return; */
+  ti->clientdata = clientdata;
+
+  while (cast) {
+    if (!cast->converter) {
+      swig_type_info *tc = cast->type;
+      if (!tc->clientdata) {
+        SWIG_TypeClientData(tc, clientdata);
+      }
+    }
+    cast = cast->next;
+  }
+}
+SWIGRUNTIME void
+SWIG_TypeNewClientData(swig_type_info *ti, void *clientdata) {
+  SWIG_TypeClientData(ti, clientdata);
+  ti->owndata = 1;
+}
+
+/*
+  Search for a swig_type_info structure only by mangled name
+  Search is a O(log #types)
+
+  We start searching at module start, and finish searching when start == end.
+  Note: if start == end at the beginning of the function, we go all the way around
+  the circular list.
+*/
+SWIGRUNTIME swig_type_info *
+SWIG_MangledTypeQueryModule(swig_module_info *start,
+                            swig_module_info *end,
+                            const char *name) {
+  swig_module_info *iter = start;
+  do {
+    if (iter->size) {
+      size_t l = 0;
+      size_t r = iter->size - 1;
+      do {
+        /* since l+r >= 0, we can (>> 1) instead (/ 2) */
+        size_t i = (l + r) >> 1;
+        const char *iname = iter->types[i]->name;
+        if (iname) {
+          int compare = strcmp(name, iname);
+          if (compare == 0) {
+            return iter->types[i];
+          } else if (compare < 0) {
+            if (i) {
+              r = i - 1;
+            } else {
+              break;
+            }
+          } else if (compare > 0) {
+            l = i + 1;
+          }
+        } else {
+          break; /* should never happen */
+        }
+      } while (l <= r);
+    }
+    iter = iter->next;
+  } while (iter != end);
+  return 0;
+}
+
+/*
+  Search for a swig_type_info structure for either a mangled name or a human readable name.
+  It first searches the mangled names of the types, which is a O(log #types)
+  If a type is not found it then searches the human readable names, which is O(#types).
+
+  We start searching at module start, and finish searching when start == end.
+  Note: if start == end at the beginning of the function, we go all the way around
+  the circular list.
+*/
+SWIGRUNTIME swig_type_info *
+SWIG_TypeQueryModule(swig_module_info *start,
+                     swig_module_info *end,
+                     const char *name) {
+  /* STEP 1: Search the name field using binary search */
+  swig_type_info *ret = SWIG_MangledTypeQueryModule(start, end, name);
+  if (ret) {
+    return ret;
+  } else {
+    /* STEP 2: If the type hasn't been found, do a complete search
+       of the str field (the human readable name) */
+    swig_module_info *iter = start;
+    do {
+      size_t i = 0;
+      for (; i < iter->size; ++i) {
+        if (iter->types[i]->str && (SWIG_TypeEquiv(iter->types[i]->str, name)))
+          return iter->types[i];
+      }
+      iter = iter->next;
+    } while (iter != end);
+  }
+
+  /* neither found a match */
+  return 0;
+}
+
+/*
+   Pack binary data into a string
+*/
+SWIGRUNTIME char *
+SWIG_PackData(char *c, void *ptr, size_t sz) {
+  static const char hex[17] = "0123456789abcdef";
+  const unsigned char *u = (unsigned char *) ptr;
+  const unsigned char *eu =  u + sz;
+  for (; u != eu; ++u) {
+    unsigned char uu = *u;
+    *(c++) = hex[(uu & 0xf0) >> 4];
+    *(c++) = hex[uu & 0xf];
+  }
+  return c;
+}
+
+/*
+   Unpack binary data from a string
+*/
+SWIGRUNTIME const char *
+SWIG_UnpackData(const char *c, void *ptr, size_t sz) {
+  unsigned char *u = (unsigned char *) ptr;
+  const unsigned char *eu = u + sz;
+  for (; u != eu; ++u) {
+    char d = *(c++);
+    unsigned char uu;
+    if ((d >= '0') && (d <= '9'))
+      uu = (unsigned char)((d - '0') << 4);
+    else if ((d >= 'a') && (d <= 'f'))
+      uu = (unsigned char)((d - ('a'-10)) << 4);
+    else
+      return (char *) 0;
+    d = *(c++);
+    if ((d >= '0') && (d <= '9'))
+      uu |= (unsigned char)(d - '0');
+    else if ((d >= 'a') && (d <= 'f'))
+      uu |= (unsigned char)(d - ('a'-10));
+    else
+      return (char *) 0;
+    *u = uu;
+  }
+  return c;
+}
+
+/*
+   Pack 'void *' into a string buffer.
+*/
+SWIGRUNTIME char *
+SWIG_PackVoidPtr(char *buff, void *ptr, const char *name, size_t bsz) {
+  char *r = buff;
+  if ((2*sizeof(void *) + 2) > bsz) return 0;
+  *(r++) = '_';
+  r = SWIG_PackData(r,&ptr,sizeof(void *));
+  if (strlen(name) + 1 > (bsz - (r - buff))) return 0;
+  strcpy(r,name);
+  return buff;
+}
+
+SWIGRUNTIME const char *
+SWIG_UnpackVoidPtr(const char *c, void **ptr, const char *name) {
+  if (*c != '_') {
+    if (strcmp(c,"NULL") == 0) {
+      *ptr = (void *) 0;
+      return name;
+    } else {
+      return 0;
+    }
+  }
+  return SWIG_UnpackData(++c,ptr,sizeof(void *));
+}
+
+SWIGRUNTIME char *
+SWIG_PackDataName(char *buff, void *ptr, size_t sz, const char *name, size_t bsz) {
+  char *r = buff;
+  size_t lname = (name ? strlen(name) : 0);
+  if ((2*sz + 2 + lname) > bsz) return 0;
+  *(r++) = '_';
+  r = SWIG_PackData(r,ptr,sz);
+  if (lname) {
+    strncpy(r,name,lname+1);
+  } else {
+    *r = 0;
+  }
+  return buff;
+}
+
+SWIGRUNTIME const char *
+SWIG_UnpackDataName(const char *c, void *ptr, size_t sz, const char *name) {
+  if (*c != '_') {
+    if (strcmp(c,"NULL") == 0) {
+      memset(ptr,0,sz);
+      return name;
+    } else {
+      return 0;
+    }
+  }
+  return SWIG_UnpackData(++c,ptr,sz);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+/*  Errors in SWIG */
+#define  SWIG_UnknownError         -1
+#define  SWIG_IOError              -2
+#define  SWIG_RuntimeError         -3
+#define  SWIG_IndexError           -4
+#define  SWIG_TypeError            -5
+#define  SWIG_DivisionByZero       -6
+#define  SWIG_OverflowError        -7
+#define  SWIG_SyntaxError          -8
+#define  SWIG_ValueError           -9
+#define  SWIG_SystemError          -10
+#define  SWIG_AttributeError       -11
+#define  SWIG_MemoryError          -12
+#define  SWIG_NullReferenceError   -13
+
+
+
+/* Compatibility macros for Python 3 */
+#if PY_VERSION_HEX >= 0x03000000
+
+#define PyClass_Check(obj) PyObject_IsInstance(obj, (PyObject *)&PyType_Type)
+#define PyInt_Check(x) PyLong_Check(x)
+#define PyInt_AsLong(x) PyLong_AsLong(x)
+#define PyInt_FromLong(x) PyLong_FromLong(x)
+#define PyInt_FromSize_t(x) PyLong_FromSize_t(x)
+#define PyString_Check(name) PyBytes_Check(name)
+#define PyString_FromString(x) PyUnicode_FromString(x)
+#define PyString_FromStringAndSize(x, y) PyBytes_FromStringAndSize(x, y)
+#define PyString_Format(fmt, args)  PyUnicode_Format(fmt, args)
+#define PyString_AsString(str) PyBytes_AsString(str)
+#define PyString_Size(str) PyBytes_Size(str)
+#define PyString_InternFromString(key) PyUnicode_InternFromString(key)
+#define Py_TPFLAGS_HAVE_CLASS Py_TPFLAGS_BASETYPE
+#define PyString_AS_STRING(x) PyUnicode_AS_STRING(x)
+#define _PyLong_FromSsize_t(x) PyLong_FromSsize_t(x)
+
+#endif
+
+#ifndef Py_TYPE
+#  define Py_TYPE(op) ((op)->ob_type)
+#endif
+
+/* SWIG APIs for compatibility of both Python 2 & 3 */
+
+#if PY_VERSION_HEX >= 0x03000000
+#  define SWIG_Python_str_FromFormat PyUnicode_FromFormat
+#else
+#  define SWIG_Python_str_FromFormat PyString_FromFormat
+#endif
+
+
+/* Warning: This function will allocate a new string in Python 3,
+ * so please call SWIG_Python_str_DelForPy3(x) to free the space.
+ */
+SWIGINTERN char*
+SWIG_Python_str_AsChar(PyObject *str)
+{
+#if PY_VERSION_HEX >= 0x03000000
+  char *cstr;
+  char *newstr;
+  Py_ssize_t len;
+  str = PyUnicode_AsUTF8String(str);
+  PyBytes_AsStringAndSize(str, &cstr, &len);
+  newstr = (char *) malloc(len+1);
+  memcpy(newstr, cstr, len+1);
+  Py_XDECREF(str);
+  return newstr;
+#else
+  return PyString_AsString(str);
+#endif
+}
+
+#if PY_VERSION_HEX >= 0x03000000
+#  define SWIG_Python_str_DelForPy3(x) free( (void*) (x) )
+#else
+#  define SWIG_Python_str_DelForPy3(x)
+#endif
+
+
+SWIGINTERN PyObject*
+SWIG_Python_str_FromChar(const char *c)
+{
+#if PY_VERSION_HEX >= 0x03000000
+  return PyUnicode_FromString(c);
+#else
+  return PyString_FromString(c);
+#endif
+}
+
+/* Add PyOS_snprintf for old Pythons */
+#if PY_VERSION_HEX < 0x02020000
+# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(_WATCOM)
+#  define PyOS_snprintf _snprintf
+# else
+#  define PyOS_snprintf snprintf
+# endif
+#endif
+
+/* A crude PyString_FromFormat implementation for old Pythons */
+#if PY_VERSION_HEX < 0x02020000
+
+#ifndef SWIG_PYBUFFER_SIZE
+# define SWIG_PYBUFFER_SIZE 1024
+#endif
+
+static PyObject *
+PyString_FromFormat(const char *fmt, ...) {
+  va_list ap;
+  char buf[SWIG_PYBUFFER_SIZE * 2];
+  int res;
+  va_start(ap, fmt);
+  res = vsnprintf(buf, sizeof(buf), fmt, ap);
+  va_end(ap);
+  return (res < 0 || res >= (int)sizeof(buf)) ? 0 : PyString_FromString(buf);
+}
+#endif
+
+#ifndef PyObject_DEL
+# define PyObject_DEL PyObject_Del
+#endif
+
+/* A crude PyExc_StopIteration exception for old Pythons */
+#if PY_VERSION_HEX < 0x02020000
+# ifndef PyExc_StopIteration
+#  define PyExc_StopIteration PyExc_RuntimeError
+# endif
+# ifndef PyObject_GenericGetAttr
+#  define PyObject_GenericGetAttr 0
+# endif
+#endif
+
+/* Py_NotImplemented is defined in 2.1 and up. */
+#if PY_VERSION_HEX < 0x02010000
+# ifndef Py_NotImplemented
+#  define Py_NotImplemented PyExc_RuntimeError
+# endif
+#endif
+
+/* A crude PyString_AsStringAndSize implementation for old Pythons */
+#if PY_VERSION_HEX < 0x02010000
+# ifndef PyString_AsStringAndSize
+#  define PyString_AsStringAndSize(obj, s, len) {*s = PyString_AsString(obj); *len = *s ? strlen(*s) : 0;}
+# endif
+#endif
+
+/* PySequence_Size for old Pythons */
+#if PY_VERSION_HEX < 0x02000000
+# ifndef PySequence_Size
+#  define PySequence_Size PySequence_Length
+# endif
+#endif
+
+/* PyBool_FromLong for old Pythons */
+#if PY_VERSION_HEX < 0x02030000
+static
+PyObject *PyBool_FromLong(long ok)
+{
+  PyObject *result = ok ? Py_True : Py_False;
+  Py_INCREF(result);
+  return result;
+}
+#endif
+
+/* Py_ssize_t for old Pythons */
+/* This code is as recommended by: */
+/* http://www.python.org/dev/peps/pep-0353/#conversion-guidelines */
+#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
+typedef int Py_ssize_t;
+# define PY_SSIZE_T_MAX INT_MAX
+# define PY_SSIZE_T_MIN INT_MIN
+typedef inquiry lenfunc;
+typedef intargfunc ssizeargfunc;
+typedef intintargfunc ssizessizeargfunc;
+typedef intobjargproc ssizeobjargproc;
+typedef intintobjargproc ssizessizeobjargproc;
+typedef getreadbufferproc readbufferproc;
+typedef getwritebufferproc writebufferproc;
+typedef getsegcountproc segcountproc;
+typedef getcharbufferproc charbufferproc;
+static long PyNumber_AsSsize_t (PyObject *x, void *SWIGUNUSEDPARM(exc))
+{
+  long result = 0;
+  PyObject *i = PyNumber_Int(x);
+  if (i) {
+    result = PyInt_AsLong(i);
+    Py_DECREF(i);
+  }
+  return result;
+}
+#endif
+
+#if PY_VERSION_HEX < 0x02050000
+#define PyInt_FromSize_t(x) PyInt_FromLong((long)x)
+#endif
+
+#if PY_VERSION_HEX < 0x02040000
+#define Py_VISIT(op)                            \
+  do {                                          \
+    if (op) {                                   \
+      int vret = visit((op), arg);              \
+      if (vret)                                 \
+        return vret;                            \
+    }                                           \
+  } while (0)
+#endif
+
+#if PY_VERSION_HEX < 0x02030000
+typedef struct {
+  PyTypeObject type;
+  PyNumberMethods as_number;
+  PyMappingMethods as_mapping;
+  PySequenceMethods as_sequence;
+  PyBufferProcs as_buffer;
+  PyObject *name, *slots;
+} PyHeapTypeObject;
+#endif
+
+#if PY_VERSION_HEX < 0x02030000
+typedef destructor freefunc;
+#endif
+
+#if ((PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 6) || \
+     (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION > 0) || \
+     (PY_MAJOR_VERSION > 3))
+# define SWIGPY_USE_CAPSULE
+# define SWIGPY_CAPSULE_NAME ((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION ".type_pointer_capsule" SWIG_TYPE_TABLE_NAME)
+#endif
+
+#if PY_VERSION_HEX < 0x03020000
+#define PyDescr_TYPE(x) (((PyDescrObject *)(x))->d_type)
+#define PyDescr_NAME(x) (((PyDescrObject *)(x))->d_name)
+#define Py_hash_t long
+#endif
+
+/* -----------------------------------------------------------------------------
+ * error manipulation
+ * ----------------------------------------------------------------------------- */
+
+SWIGRUNTIME PyObject*
+SWIG_Python_ErrorType(int code) {
+  PyObject* type = 0;
+  switch(code) {
+  case SWIG_MemoryError:
+    type = PyExc_MemoryError;
+    break;
+  case SWIG_IOError:
+    type = PyExc_IOError;
+    break;
+  case SWIG_RuntimeError:
+    type = PyExc_RuntimeError;
+    break;
+  case SWIG_IndexError:
+    type = PyExc_IndexError;
+    break;
+  case SWIG_TypeError:
+    type = PyExc_TypeError;
+    break;
+  case SWIG_DivisionByZero:
+    type = PyExc_ZeroDivisionError;
+    break;
+  case SWIG_OverflowError:
+    type = PyExc_OverflowError;
+    break;
+  case SWIG_SyntaxError:
+    type = PyExc_SyntaxError;
+    break;
+  case SWIG_ValueError:
+    type = PyExc_ValueError;
+    break;
+  case SWIG_SystemError:
+    type = PyExc_SystemError;
+    break;
+  case SWIG_AttributeError:
+    type = PyExc_AttributeError;
+    break;
+  default:
+    type = PyExc_RuntimeError;
+  }
+  return type;
+}
+
+
+SWIGRUNTIME void
+SWIG_Python_AddErrorMsg(const char* mesg)
+{
+  PyObject *type = 0;
+  PyObject *value = 0;
+  PyObject *traceback = 0;
+
+  if (PyErr_Occurred()) PyErr_Fetch(&type, &value, &traceback);
+  if (value) {
+    char *tmp;
+    PyObject *old_str = PyObject_Str(value);
+    PyErr_Clear();
+    Py_XINCREF(type);
+
+    PyErr_Format(type, "%s %s", tmp = SWIG_Python_str_AsChar(old_str), mesg);
+    SWIG_Python_str_DelForPy3(tmp);
+    Py_DECREF(old_str);
+    Py_DECREF(value);
+  } else {
+    PyErr_SetString(PyExc_RuntimeError, mesg);
+  }
+}
+
+#if defined(SWIG_PYTHON_NO_THREADS)
+#  if defined(SWIG_PYTHON_THREADS)
+#    undef SWIG_PYTHON_THREADS
+#  endif
+#endif
+#if defined(SWIG_PYTHON_THREADS) /* Threading support is enabled */
+#  if !defined(SWIG_PYTHON_USE_GIL) && !defined(SWIG_PYTHON_NO_USE_GIL)
+#    if (PY_VERSION_HEX >= 0x02030000) /* For 2.3 or later, use the PyGILState calls */
+#      define SWIG_PYTHON_USE_GIL
+#    endif
+#  endif
+#  if defined(SWIG_PYTHON_USE_GIL) /* Use PyGILState threads calls */
+#    ifndef SWIG_PYTHON_INITIALIZE_THREADS
+#     define SWIG_PYTHON_INITIALIZE_THREADS  PyEval_InitThreads()
+#    endif
+#    ifdef __cplusplus /* C++ code */
+       class SWIG_Python_Thread_Block {
+         bool status;
+         PyGILState_STATE state;
+       public:
+         void end() { if (status) { PyGILState_Release(state); status = false;} }
+         SWIG_Python_Thread_Block() : status(true), state(PyGILState_Ensure()) {}
+         ~SWIG_Python_Thread_Block() { end(); }
+       };
+       class SWIG_Python_Thread_Allow {
+         bool status;
+         PyThreadState *save;
+       public:
+         void end() { if (status) { PyEval_RestoreThread(save); status = false; }}
+         SWIG_Python_Thread_Allow() : status(true), save(PyEval_SaveThread()) {}
+         ~SWIG_Python_Thread_Allow() { end(); }
+       };
+#      define SWIG_PYTHON_THREAD_BEGIN_BLOCK   SWIG_Python_Thread_Block _swig_thread_block
+#      define SWIG_PYTHON_THREAD_END_BLOCK     _swig_thread_block.end()
+#      define SWIG_PYTHON_THREAD_BEGIN_ALLOW   SWIG_Python_Thread_Allow _swig_thread_allow
+#      define SWIG_PYTHON_THREAD_END_ALLOW     _swig_thread_allow.end()
+#    else /* C code */
+#      define SWIG_PYTHON_THREAD_BEGIN_BLOCK   PyGILState_STATE _swig_thread_block = PyGILState_Ensure()
+#      define SWIG_PYTHON_THREAD_END_BLOCK     PyGILState_Release(_swig_thread_block)
+#      define SWIG_PYTHON_THREAD_BEGIN_ALLOW   PyThreadState *_swig_thread_allow = PyEval_SaveThread()
+#      define SWIG_PYTHON_THREAD_END_ALLOW     PyEval_RestoreThread(_swig_thread_allow)
+#    endif
+#  else /* Old thread way, not implemented, user must provide it */
+#    if !defined(SWIG_PYTHON_INITIALIZE_THREADS)
+#      define SWIG_PYTHON_INITIALIZE_THREADS
+#    endif
+#    if !defined(SWIG_PYTHON_THREAD_BEGIN_BLOCK)
+#      define SWIG_PYTHON_THREAD_BEGIN_BLOCK
+#    endif
+#    if !defined(SWIG_PYTHON_THREAD_END_BLOCK)
+#      define SWIG_PYTHON_THREAD_END_BLOCK
+#    endif
+#    if !defined(SWIG_PYTHON_THREAD_BEGIN_ALLOW)
+#      define SWIG_PYTHON_THREAD_BEGIN_ALLOW
+#    endif
+#    if !defined(SWIG_PYTHON_THREAD_END_ALLOW)
+#      define SWIG_PYTHON_THREAD_END_ALLOW
+#    endif
+#  endif
+#else /* No thread support */
+#  define SWIG_PYTHON_INITIALIZE_THREADS
+#  define SWIG_PYTHON_THREAD_BEGIN_BLOCK
+#  define SWIG_PYTHON_THREAD_END_BLOCK
+#  define SWIG_PYTHON_THREAD_BEGIN_ALLOW
+#  define SWIG_PYTHON_THREAD_END_ALLOW
+#endif
+
+/* -----------------------------------------------------------------------------
+ * Python API portion that goes into the runtime
+ * ----------------------------------------------------------------------------- */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* -----------------------------------------------------------------------------
+ * Constant declarations
+ * ----------------------------------------------------------------------------- */
+
+/* Constant Types */
+#define SWIG_PY_POINTER 4
+#define SWIG_PY_BINARY  5
+
+/* Constant information structure */
+typedef struct swig_const_info {
+  int type;
+  char *name;
+  long lvalue;
+  double dvalue;
+  void   *pvalue;
+  swig_type_info **ptype;
+} swig_const_info;
+
+
+/* -----------------------------------------------------------------------------
+ * Wrapper of PyInstanceMethod_New() used in Python 3
+ * It is exported to the generated module, used for -fastproxy
+ * ----------------------------------------------------------------------------- */
+#if PY_VERSION_HEX >= 0x03000000
+SWIGRUNTIME PyObject* SWIG_PyInstanceMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *func)
+{
+  return PyInstanceMethod_New(func);
+}
+#else
+SWIGRUNTIME PyObject* SWIG_PyInstanceMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *SWIGUNUSEDPARM(func))
+{
+  return NULL;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/* -----------------------------------------------------------------------------
+ * pyrun.swg
+ *
+ * This file contains the runtime support for Python modules
+ * and includes code for managing global variables and pointer
+ * type checking.
+ *
+ * ----------------------------------------------------------------------------- */
+
+/* Common SWIG API */
+
+/* for raw pointers */
+#define SWIG_Python_ConvertPtr(obj, pptr, type, flags)  SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, 0)
+#define SWIG_ConvertPtr(obj, pptr, type, flags)         SWIG_Python_ConvertPtr(obj, pptr, type, flags)
+#define SWIG_ConvertPtrAndOwn(obj,pptr,type,flags,own)  SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, own)
+
+#ifdef SWIGPYTHON_BUILTIN
+#define SWIG_NewPointerObj(ptr, type, flags)            SWIG_Python_NewPointerObj(self, ptr, type, flags)
+#else
+#define SWIG_NewPointerObj(ptr, type, flags)            SWIG_Python_NewPointerObj(NULL, ptr, type, flags)
+#endif
+
+#define SWIG_InternalNewPointerObj(ptr, type, flags)    SWIG_Python_NewPointerObj(NULL, ptr, type, flags)
+
+#define SWIG_CheckImplicit(ty)                          SWIG_Python_CheckImplicit(ty)
+#define SWIG_AcquirePtr(ptr, src)                       SWIG_Python_AcquirePtr(ptr, src)
+#define swig_owntype                                    int
+
+/* for raw packed data */
+#define SWIG_ConvertPacked(obj, ptr, sz, ty)            SWIG_Python_ConvertPacked(obj, ptr, sz, ty)
+#define SWIG_NewPackedObj(ptr, sz, type)                SWIG_Python_NewPackedObj(ptr, sz, type)
+
+/* for class or struct pointers */
+#define SWIG_ConvertInstance(obj, pptr, type, flags)    SWIG_ConvertPtr(obj, pptr, type, flags)
+#define SWIG_NewInstanceObj(ptr, type, flags)           SWIG_NewPointerObj(ptr, type, flags)
+
+/* for C or C++ function pointers */
+#define SWIG_ConvertFunctionPtr(obj, pptr, type)        SWIG_Python_ConvertFunctionPtr(obj, pptr, type)
+#define SWIG_NewFunctionPtrObj(ptr, type)               SWIG_Python_NewPointerObj(NULL, ptr, type, 0)
+
+/* for C++ member pointers, ie, member methods */
+#define SWIG_ConvertMember(obj, ptr, sz, ty)            SWIG_Python_ConvertPacked(obj, ptr, sz, ty)
+#define SWIG_NewMemberObj(ptr, sz, type)                SWIG_Python_NewPackedObj(ptr, sz, type)
+
+
+/* Runtime API */
+
+#define SWIG_GetModule(clientdata)                      SWIG_Python_GetModule(clientdata)
+#define SWIG_SetModule(clientdata, pointer)             SWIG_Python_SetModule(pointer)
+#define SWIG_NewClientData(obj)                         SwigPyClientData_New(obj)
+
+#define SWIG_SetErrorObj                                SWIG_Python_SetErrorObj
+#define SWIG_SetErrorMsg                                SWIG_Python_SetErrorMsg
+#define SWIG_ErrorType(code)                            SWIG_Python_ErrorType(code)
+#define SWIG_Error(code, msg)                           SWIG_Python_SetErrorMsg(SWIG_ErrorType(code), msg)
+#define SWIG_fail                                       goto fail
+
+
+/* Runtime API implementation */
+
+/* Error manipulation */
+
+SWIGINTERN void
+SWIG_Python_SetErrorObj(PyObject *errtype, PyObject *obj) {
+  SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+  PyErr_SetObject(errtype, obj);
+  Py_DECREF(obj);
+  SWIG_PYTHON_THREAD_END_BLOCK;
+}
+
+SWIGINTERN void
+SWIG_Python_SetErrorMsg(PyObject *errtype, const char *msg) {
+  SWIG_PYTHON_THREAD_BEGIN_BLOCK;
+  PyErr_SetString(errtype, msg);
+  SWIG_PYTHON_THREAD_END_BLOCK;
+}
+
+#define SWIG_Python_Raise(obj, type, desc)  SWIG_Python_SetErrorObj(SWIG_Python_ExceptionType(desc), obj)
+
+/* Set a constant value */
+
+#if defined(SWIGPYTHON_BUILTIN)
+
+SWIGINTERN void
+SwigPyBuiltin_AddPublicSymbol(PyObject *seq, const char *key) {
+  PyObject *s = PyString_InternFromString(key);
+  PyList_Append(seq, s);
+  Py_DECREF(s);
+}
+
+SWIGINTERN void
+SWIG_Python_SetConstant(PyObject *d, PyObject *public_interface, const char *name, PyObject *obj) {
+#if PY_VERSION_HEX < 0x02030000
+  PyDict_SetItemString(d, (char *)name, obj);
+#else
+  PyDict_SetItemString(d, name, obj);
+#endif
+  Py_DECREF(obj);
+  if (public_interface)
+    SwigPyBuiltin_AddPublicSymbol(public_interface, name);
+}
+
+#else
+
+SWIGINTERN void
+SWIG_Python_SetConstant(PyObject *d, const char *name, PyObject *obj) {
+#if PY_VERSION_HEX < 0x02030000
+  PyDict_SetItemString(d, (char *)name, obj);
+#else
+  PyDict_SetItemString(d, name, obj);
+#endif
+  Py_DECREF(obj);
+}
+
+#endif
+
+/* Append a value to the result obj */
+
+SWIGINTERN PyObject*
+SWIG_Python_AppendOutput(PyObject* result, PyObject* obj) {
+#if !defined(SWIG_PYTHON_OUTPUT_TUPLE)
+  if (!result) {
+    result = obj;
+  } else if (result == Py_None) {
+    Py_DECREF(result);
+    result = obj;
+  } else {
+    if (!PyList_Check(result)) {
+      PyObject *o2 = result;
+      result = PyList_New(1);
+      PyList_SetItem(result, 0, o2);
+    }
+    PyList_Append(result,obj);
+    Py_DECREF(obj);
+  }
+  return result;
+#else
+  PyObject*   o2;
+  PyObject*   o3;
+  if (!result) {
+    result = obj;
+  } else if (result == Py_None) {
+    Py_DECREF(result);
+    result = obj;
+  } else {
+    if (!PyTuple_Check(result)) {
+      o2 = result;
+      result = PyTuple_New(1);
+      PyTuple_SET_ITEM(result, 0, o2);
+    }
+    o3 = PyTuple_New(1);
+    PyTuple_SET_ITEM(o3, 0, obj);
+    o2 = result;
+    result = PySequence_Concat(o2, o3);
+    Py_DECREF(o2);
+    Py_DECREF(o3);
+  }
+  return result;
+#endif
+}
+
+/* Unpack the argument tuple */
+
+SWIGINTERN Py_ssize_t
+SWIG_Python_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, PyObject **objs)
+{
+  if (!args) {
+    if (!min && !max) {
+      return 1;
+    } else {
+      PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got none",
+                   name, (min == max ? "" : "at least "), (int)min);
+      return 0;
+    }
+  }
+  if (!PyTuple_Check(args)) {
+    if (min <= 1 && max >= 1) {
+      Py_ssize_t i;
+      objs[0] = args;
+      for (i = 1; i < max; ++i) {
+        objs[i] = 0;
+      }
+      return 2;
+    }
+    PyErr_SetString(PyExc_SystemError, "UnpackTuple() argument list is not a tuple");
+    return 0;
+  } else {
+    Py_ssize_t l = PyTuple_GET_SIZE(args);
+    if (l < min) {
+      PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d",
+                   name, (min == max ? "" : "at least "), (int)min, (int)l);
+      return 0;
+    } else if (l > max) {
+      PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d",
+                   name, (min == max ? "" : "at most "), (int)max, (int)l);
+      return 0;
+    } else {
+      Py_ssize_t i;
+      for (i = 0; i < l; ++i) {
+        objs[i] = PyTuple_GET_ITEM(args, i);
+      }
+      for (; l < max; ++l) {
+        objs[l] = 0;
+      }
+      return i + 1;
+    }
+  }
+}
+
+/* A functor is a function object with one single object argument */
+#if PY_VERSION_HEX >= 0x02020000
+#define SWIG_Python_CallFunctor(functor, obj)           PyObject_CallFunctionObjArgs(functor, obj, NULL);
+#else
+#define SWIG_Python_CallFunctor(functor, obj)           PyObject_CallFunction(functor, "O", obj);
+#endif
+
+/*
+  Helper for static pointer initialization for both C and C++ code, for example
+  static PyObject *SWIG_STATIC_POINTER(MyVar) = NewSomething(...);
+*/
+#ifdef __cplusplus
+#define SWIG_STATIC_POINTER(var)  var
+#else
+#define SWIG_STATIC_POINTER(var)  var = 0; if (!var) var
+#endif
+
+/* -----------------------------------------------------------------------------
+ * Pointer declarations
+ * ----------------------------------------------------------------------------- */
+
+/* Flags for new pointer objects */
+#define SWIG_POINTER_NOSHADOW       (SWIG_POINTER_OWN      << 1)
+#define SWIG_POINTER_NEW            (SWIG_POINTER_NOSHADOW | SWIG_POINTER_OWN)
+
+#define SWIG_POINTER_IMPLICIT_CONV  (SWIG_POINTER_DISOWN   << 1)
+
+#define SWIG_BUILTIN_TP_INIT        (SWIG_POINTER_OWN << 2)
+#define SWIG_BUILTIN_INIT           (SWIG_BUILTIN_TP_INIT | SWIG_POINTER_OWN)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*  How to access Py_None */
+#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+#  ifndef SWIG_PYTHON_NO_BUILD_NONE
+#    ifndef SWIG_PYTHON_BUILD_NONE
+#      define SWIG_PYTHON_BUILD_NONE
+#    endif
+#  endif
+#endif
+
+#ifdef SWIG_PYTHON_BUILD_NONE
+#  ifdef Py_None
+#   undef Py_None
+#   define Py_None SWIG_Py_None()
+#  endif
+SWIGRUNTIMEINLINE PyObject *
+_SWIG_Py_None(void)
+{
+  PyObject *none = Py_BuildValue((char*)"");
+  Py_DECREF(none);
+  return none;
+}
+SWIGRUNTIME PyObject *
+SWIG_Py_None(void)
+{
+  static PyObject *SWIG_STATIC_POINTER(none) = _SWIG_Py_None();
+  return none;
+}
+#endif
+
+/* The python void return value */
+
+SWIGRUNTIMEINLINE PyObject *
+SWIG_Py_Void(void)
+{
+  PyObject *none = Py_None;
+  Py_INCREF(none);
+  return none;
+}
+
+/* SwigPyClientData */
+
+typedef struct {
+  PyObject *klass;
+  PyObject *newraw;
+  PyObject *newargs;
+  PyObject *destroy;
+  int delargs;
+  int implicitconv;
+  PyTypeObject *pytype;
+} SwigPyClientData;
+
+SWIGRUNTIMEINLINE int
+SWIG_Python_CheckImplicit(swig_type_info *ty)
+{
+  SwigPyClientData *data = (SwigPyClientData *)ty->clientdata;
+  return data ? data->implicitconv : 0;
+}
+
+SWIGRUNTIMEINLINE PyObject *
+SWIG_Python_ExceptionType(swig_type_info *desc) {
+  SwigPyClientData *data = desc ? (SwigPyClientData *) desc->clientdata : 0;
+  PyObject *klass = data ? data->klass : 0;
+  return (klass ? klass : PyExc_RuntimeError);
+}
+
+
+SWIGRUNTIME SwigPyClientData *
+SwigPyClientData_New(PyObject* obj)
+{
+  if (!obj) {
+    return 0;
+  } else {
+    SwigPyClientData *data = (SwigPyClientData *)malloc(sizeof(SwigPyClientData));
+    /* the klass element */
+    data->klass = obj;
+    Py_INCREF(data->klass);
+    /* the newraw method and newargs arguments used to create a new raw instance */
+    if (PyClass_Check(obj)) {
+      data->newraw = 0;
+      data->newargs = obj;
+      Py_INCREF(obj);
+    } else {
+#if (PY_VERSION_HEX < 0x02020000)
+      data->newraw = 0;
+#else
+      data->newraw = PyObject_GetAttrString(data->klass, (char *)"__new__");
+#endif
+      if (data->newraw) {
+        Py_INCREF(data->newraw);
+        data->newargs = PyTuple_New(1);
+        PyTuple_SetItem(data->newargs, 0, obj);
+      } else {
+        data->newargs = obj;
+      }
+      Py_INCREF(data->newargs);
+    }
+    /* the destroy method, aka as the C++ delete method */
+    data->destroy = PyObject_GetAttrString(data->klass, (char *)"__swig_destroy__");
+    if (PyErr_Occurred()) {
+      PyErr_Clear();
+      data->destroy = 0;
+    }
+    if (data->destroy) {
+      int flags;
+      Py_INCREF(data->destroy);
+      flags = PyCFunction_GET_FLAGS(data->destroy);
+#ifdef METH_O
+      data->delargs = !(flags & (METH_O));
+#else
+      data->delargs = 0;
+#endif
+    } else {
+      data->delargs = 0;
+    }
+    data->implicitconv = 0;
+    data->pytype = 0;
+    return data;
+  }
+}
+
+SWIGRUNTIME void
+SwigPyClientData_Del(SwigPyClientData *data) {
+  Py_XDECREF(data->newraw);
+  Py_XDECREF(data->newargs);
+  Py_XDECREF(data->destroy);
+}
+
+/* =============== SwigPyObject =====================*/
+
+typedef struct {
+  PyObject_HEAD
+  void *ptr;
+  swig_type_info *ty;
+  int own;
+  PyObject *next;
+#ifdef SWIGPYTHON_BUILTIN
+  PyObject *dict;
+#endif
+} SwigPyObject;
+
+
+#ifdef SWIGPYTHON_BUILTIN
+
+SWIGRUNTIME PyObject *
+SwigPyObject_get___dict__(PyObject *v, PyObject *SWIGUNUSEDPARM(args))
+{
+  SwigPyObject *sobj = (SwigPyObject *)v;
+
+  if (!sobj->dict)
+    sobj->dict = PyDict_New();
+
+  Py_INCREF(sobj->dict);
+  return sobj->dict;
+}
+
+#endif
+
+SWIGRUNTIME PyObject *
+SwigPyObject_long(SwigPyObject *v)
+{
+  return PyLong_FromVoidPtr(v->ptr);
+}
+
+SWIGRUNTIME PyObject *
+SwigPyObject_format(const char* fmt, SwigPyObject *v)
+{
+  PyObject *res = NULL;
+  PyObject *args = PyTuple_New(1);
+  if (args) {
+    if (PyTuple_SetItem(args, 0, SwigPyObject_long(v)) == 0) {
+      PyObject *ofmt = SWIG_Python_str_FromChar(fmt);
+      if (ofmt) {
+#if PY_VERSION_HEX >= 0x03000000
+        res = PyUnicode_Format(ofmt,args);
+#else
+        res = PyString_Format(ofmt,args);
+#endif
+        Py_DECREF(ofmt);
+      }
+      Py_DECREF(args);
+    }
+  }
+  return res;
+}
+
+SWIGRUNTIME PyObject *
+SwigPyObject_oct(SwigPyObject *v)
+{
+  return SwigPyObject_format("%o",v);
+}
+
+SWIGRUNTIME PyObject *
+SwigPyObject_hex(SwigPyObject *v)
+{
+  return SwigPyObject_format("%x",v);
+}
+
+SWIGRUNTIME PyObject *
+#ifdef METH_NOARGS
+SwigPyObject_repr(SwigPyObject *v)
+#else
+SwigPyObject_repr(SwigPyObject *v, PyObject *args)
+#endif
+{
+  const char *name = SWIG_TypePrettyName(v->ty);
+  PyObject *repr = SWIG_Python_str_FromFormat("<Swig Object of type '%s' at %p>", (name ? name : "unknown"), (void *)v);
+  if (v->next) {
+# ifdef METH_NOARGS
+    PyObject *nrep = SwigPyObject_repr((SwigPyObject *)v->next);
+# else
+    PyObject *nrep = SwigPyObject_repr((SwigPyObject *)v->next, args);
+# endif
+# if PY_VERSION_HEX >= 0x03000000
+    PyObject *joined = PyUnicode_Concat(repr, nrep);
+    Py_DecRef(repr);
+    Py_DecRef(nrep);
+    repr = joined;
+# else
+    PyString_ConcatAndDel(&repr,nrep);
+# endif
+  }
+  return repr;
+}
+
+/* We need a version taking two PyObject* parameters so it's a valid
+ * PyCFunction to use in swigobject_methods[]. */
+SWIGRUNTIME PyObject *
+SwigPyObject_repr2(PyObject *v, PyObject *SWIGUNUSEDPARM(args))
+{
+  return SwigPyObject_repr((SwigPyObject*)v);
+}
+
+SWIGRUNTIME int
+SwigPyObject_compare(SwigPyObject *v, SwigPyObject *w)
+{
+  void *i = v->ptr;
+  void *j = w->ptr;
+  return (i < j) ? -1 : ((i > j) ? 1 : 0);
+}
+
+/* Added for Python 3.x, would it also be useful for Python 2.x? */
+SWIGRUNTIME PyObject*
+SwigPyObject_richcompare(SwigPyObject *v, SwigPyObject *w, int op)
+{
+  PyObject* res;
+  if( op != Py_EQ && op != Py_NE ) {
+    Py_INCREF(Py_NotImplemented);
+    return Py_NotImplemented;
+  }
+  res = PyBool_FromLong( (SwigPyObject_compare(v, w)==0) == (op == Py_EQ) ? 1 : 0);
+  return res;
+}
+
+
+SWIGRUNTIME PyTypeObject* SwigPyObject_TypeOnce(void);
+
+#ifdef SWIGPYTHON_BUILTIN
+static swig_type_info *SwigPyObject_stype = 0;
+SWIGRUNTIME PyTypeObject*
+SwigPyObject_type(void) {
+    SwigPyClientData *cd;
+    assert(SwigPyObject_stype);
+    cd = (SwigPyClientData*) SwigPyObject_stype->clientdata;
+    assert(cd);
+    assert(cd->pytype);
+    return cd->pytype;
+}
+#else
+SWIGRUNTIME PyTypeObject*
+SwigPyObject_type(void) {
+  static PyTypeObject *SWIG_STATIC_POINTER(type) = SwigPyObject_TypeOnce();
+  return type;
+}
+#endif
+
+SWIGRUNTIMEINLINE int
+SwigPyObject_Check(PyObject *op) {
+#ifdef SWIGPYTHON_BUILTIN
+  PyTypeObject *target_tp = SwigPyObject_type();
+  if (PyType_IsSubtype(op->ob_type, target_tp))
+    return 1;
+  return (strcmp(op->ob_type->tp_name, "SwigPyObject") == 0);
+#else
+  return (Py_TYPE(op) == SwigPyObject_type())
+    || (strcmp(Py_TYPE(op)->tp_name,"SwigPyObject") == 0);
+#endif
+}
+
+SWIGRUNTIME PyObject *
+SwigPyObject_New(void *ptr, swig_type_info *ty, int own);
+
+SWIGRUNTIME void
+SwigPyObject_dealloc(PyObject *v)
+{
+  SwigPyObject *sobj = (SwigPyObject *) v;
+  PyObject *next = sobj->next;
+  if (sobj->own == SWIG_POINTER_OWN) {
+    swig_type_info *ty = sobj->ty;
+    SwigPyClientData *data = ty ? (SwigPyClientData *) ty->clientdata : 0;
+    PyObject *destroy = data ? data->destroy : 0;
+    if (destroy) {
+      /* destroy is always a VARARGS method */
+      PyObject *res;
+
+      /* PyObject_CallFunction() has the potential to silently drop
+         the active active exception.  In cases of unnamed temporary
+         variable or where we just finished iterating over a generator
+         StopIteration will be active right now, and this needs to
+         remain true upon return from SwigPyObject_dealloc.  So save
+         and restore. */
+
+      PyObject *val = NULL, *type = NULL, *tb = NULL;
+      PyErr_Fetch(&val, &type, &tb);
+
+      if (data->delargs) {
+        /* we need to create a temporary object to carry the destroy operation */
+        PyObject *tmp = SwigPyObject_New(sobj->ptr, ty, 0);
+        res = SWIG_Python_CallFunctor(destroy, tmp);
+        Py_DECREF(tmp);
+      } else {
+        PyCFunction meth = PyCFunction_GET_FUNCTION(destroy);
+        PyObject *mself = PyCFunction_GET_SELF(destroy);
+        res = ((*meth)(mself, v));
+      }
+      if (!res)
+        PyErr_WriteUnraisable(destroy);
+
+      PyErr_Restore(val, type, tb);
+
+      Py_XDECREF(res);
+    }
+#if !defined(SWIG_PYTHON_SILENT_MEMLEAK)
+    else {
+      const char *name = SWIG_TypePrettyName(ty);
+      printf("swig/python detected a memory leak of type '%s', no destructor found.\n", (name ? name : "unknown"));
+    }
+#endif
+  }
+  Py_XDECREF(next);
+  PyObject_DEL(v);
+}
+
+SWIGRUNTIME PyObject*
+SwigPyObject_append(PyObject* v, PyObject* next)
+{
+  SwigPyObject *sobj = (SwigPyObject *) v;
+#ifndef METH_O
+  PyObject *tmp = 0;
+  if (!PyArg_ParseTuple(next,(char *)"O:append", &tmp)) return NULL;
+  next = tmp;
+#endif
+  if (!SwigPyObject_Check(next)) {
+    PyErr_SetString(PyExc_TypeError, "Attempt to append a non SwigPyObject");
+    return NULL;
+  }
+  sobj->next = next;
+  Py_INCREF(next);
+  return SWIG_Py_Void();
+}
+
+SWIGRUNTIME PyObject*
+SwigPyObject_next(PyObject* v, PyObject *SWIGUNUSEDPARM(args))
+{
+  SwigPyObject *sobj = (SwigPyObject *) v;
+  if (sobj->next) {
+    Py_INCREF(sobj->next);
+    return sobj->next;
+  } else {
+    return SWIG_Py_Void();
+  }
+}
+
+SWIGINTERN PyObject*
+#ifdef METH_NOARGS
+SwigPyObject_disown(PyObject *v)
+#else
+SwigPyObject_disown(PyObject* v, PyObject *SWIGUNUSEDPARM(args))
+#endif
+{
+  SwigPyObject *sobj = (SwigPyObject *)v;
+  sobj->own = 0;
+  return SWIG_Py_Void();
+}
+
+SWIGINTERN PyObject*
+#ifdef METH_NOARGS
+SwigPyObject_acquire(PyObject *v)
+#else
+SwigPyObject_acquire(PyObject* v, PyObject *SWIGUNUSEDPARM(args))
+#endif
+{
+  SwigPyObject *sobj = (SwigPyObject *)v;
+  sobj->own = SWIG_POINTER_OWN;
+  return SWIG_Py_Void();
+}
+
+#ifdef METH_NOARGS
+static PyObject*
+SwigPyObject_disown2(PyObject* v, PyObject *SWIGUNUSEDPARM(args))
+{
+  return SwigPyObject_disown(v);
+}
+
+static PyObject*
+SwigPyObject_acquire2(PyObject* v, PyObject *SWIGUNUSEDPARM(args))
+{
+  return SwigPyObject_acquire(v);
+}
+#endif
+
+SWIGINTERN PyObject*
+SwigPyObject_own(PyObject *v, PyObject *args)
+{
+  PyObject *val = 0;
+#if (PY_VERSION_HEX < 0x02020000)
+  if (!PyArg_ParseTuple(args,(char *)"|O:own",&val))
+#elif (PY_VERSION_HEX < 0x02050000)
+  if (!PyArg_UnpackTuple(args, (char *)"own", 0, 1, &val))
+#else
+  if (!PyArg_UnpackTuple(args, "own", 0, 1, &val))
+#endif
+    {
+      return NULL;
+    }
+  else
+    {
+      SwigPyObject *sobj = (SwigPyObject *)v;
+      PyObject *obj = PyBool_FromLong(sobj->own);
+      if (val) {
+#ifdef METH_NOARGS
+        if (PyObject_IsTrue(val)) {
+          SwigPyObject_acquire(v);
+        } else {
+          SwigPyObject_disown(v);
+        }
+#else
+        if (PyObject_IsTrue(val)) {
+          SwigPyObject_acquire(v,args);
+        } else {
+          SwigPyObject_disown(v,args);
+        }
+#endif
+      }
+      return obj;
+    }
+}
+
+#ifdef METH_O
+static PyMethodDef
+swigobject_methods[] = {
+  {(char *)"disown",  (PyCFunction)SwigPyObject_disown2, METH_NOARGS,  (char *)"releases ownership of the pointer"},
+  {(char *)"acquire", (PyCFunction)SwigPyObject_acquire2,METH_NOARGS,  (char *)"acquires ownership of the pointer"},
+  {(char *)"own",     (PyCFunction)SwigPyObject_own,     METH_VARARGS, (char *)"returns/sets ownership of the pointer"},
+  {(char *)"append",  (PyCFunction)SwigPyObject_append,  METH_O,       (char *)"appends another 'this' object"},
+  {(char *)"next",    (PyCFunction)SwigPyObject_next,    METH_NOARGS,  (char *)"returns the next 'this' object"},
+  {(char *)"__repr__",(PyCFunction)SwigPyObject_repr2,   METH_NOARGS,  (char *)"returns object representation"},
+  {0, 0, 0, 0}
+};
+#else
+static PyMethodDef
+swigobject_methods[] = {
+  {(char *)"disown",  (PyCFunction)SwigPyObject_disown,  METH_VARARGS,  (char *)"releases ownership of the pointer"},
+  {(char *)"acquire", (PyCFunction)SwigPyObject_acquire, METH_VARARGS,  (char *)"acquires ownership of the pointer"},
+  {(char *)"own",     (PyCFunction)SwigPyObject_own,     METH_VARARGS,  (char *)"returns/sets ownership of the pointer"},
+  {(char *)"append",  (PyCFunction)SwigPyObject_append,  METH_VARARGS,  (char *)"appends another 'this' object"},
+  {(char *)"next",    (PyCFunction)SwigPyObject_next,    METH_VARARGS,  (char *)"returns the next 'this' object"},
+  {(char *)"__repr__",(PyCFunction)SwigPyObject_repr,    METH_VARARGS,  (char *)"returns object representation"},
+  {0, 0, 0, 0}
+};
+#endif
+
+#if PY_VERSION_HEX < 0x02020000
+SWIGINTERN PyObject *
+SwigPyObject_getattr(SwigPyObject *sobj,char *name)
+{
+  return Py_FindMethod(swigobject_methods, (PyObject *)sobj, name);
+}
+#endif
+
+SWIGRUNTIME PyTypeObject*
+SwigPyObject_TypeOnce(void) {
+  static char swigobject_doc[] = "Swig object carries a C/C++ instance pointer";
+
+  static PyNumberMethods SwigPyObject_as_number = {
+    (binaryfunc)0, /*nb_add*/
+    (binaryfunc)0, /*nb_subtract*/
+    (binaryfunc)0, /*nb_multiply*/
+    /* nb_divide removed in Python 3 */
+#if PY_VERSION_HEX < 0x03000000
+    (binaryfunc)0, /*nb_divide*/
+#endif
+    (binaryfunc)0, /*nb_remainder*/
+    (binaryfunc)0, /*nb_divmod*/
+    (ternaryfunc)0,/*nb_power*/
+    (unaryfunc)0,  /*nb_negative*/
+    (unaryfunc)0,  /*nb_positive*/
+    (unaryfunc)0,  /*nb_absolute*/
+    (inquiry)0,    /*nb_nonzero*/
+    0,             /*nb_invert*/
+    0,             /*nb_lshift*/
+    0,             /*nb_rshift*/
+    0,             /*nb_and*/
+    0,             /*nb_xor*/
+    0,             /*nb_or*/
+#if PY_VERSION_HEX < 0x03000000
+    0,   /*nb_coerce*/
+#endif
+    (unaryfunc)SwigPyObject_long, /*nb_int*/
+#if PY_VERSION_HEX < 0x03000000
+    (unaryfunc)SwigPyObject_long, /*nb_long*/
+#else
+    0, /*nb_reserved*/
+#endif
+    (unaryfunc)0,                 /*nb_float*/
+#if PY_VERSION_HEX < 0x03000000
+    (unaryfunc)SwigPyObject_oct,  /*nb_oct*/
+    (unaryfunc)SwigPyObject_hex,  /*nb_hex*/
+#endif
+#if PY_VERSION_HEX >= 0x03050000 /* 3.5 */
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_matrix_multiply */
+#elif PY_VERSION_HEX >= 0x03000000 /* 3.0 */
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index, nb_inplace_divide removed */
+#elif PY_VERSION_HEX >= 0x02050000 /* 2.5.0 */
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index */
+#elif PY_VERSION_HEX >= 0x02020000 /* 2.2.0 */
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_true_divide */
+#elif PY_VERSION_HEX >= 0x02000000 /* 2.0.0 */
+    0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_or */
+#endif
+  };
+
+  static PyTypeObject swigpyobject_type;
+  static int type_init = 0;
+  if (!type_init) {
+    const PyTypeObject tmp = {
+#if PY_VERSION_HEX >= 0x03000000
+      PyVarObject_HEAD_INIT(NULL, 0)
+#else
+      PyObject_HEAD_INIT(NULL)
+      0,                                    /* ob_size */
+#endif
+      (char *)"SwigPyObject",               /* tp_name */
+      sizeof(SwigPyObject),                 /* tp_basicsize */
+      0,                                    /* tp_itemsize */
+      (destructor)SwigPyObject_dealloc,     /* tp_dealloc */
+      0,                                    /* tp_print */
+#if PY_VERSION_HEX < 0x02020000
+      (getattrfunc)SwigPyObject_getattr,    /* tp_getattr */
+#else
+      (getattrfunc)0,                       /* tp_getattr */
+#endif
+      (setattrfunc)0,                       /* tp_setattr */
+#if PY_VERSION_HEX >= 0x03000000
+      0, /* tp_reserved in 3.0.1, tp_compare in 3.0.0 but not used */
+#else
+      (cmpfunc)SwigPyObject_compare,        /* tp_compare */
+#endif
+      (reprfunc)SwigPyObject_repr,          /* tp_repr */
+      &SwigPyObject_as_number,              /* tp_as_number */
+      0,                                    /* tp_as_sequence */
+      0,                                    /* tp_as_mapping */
+      (hashfunc)0,                          /* tp_hash */
+      (ternaryfunc)0,                       /* tp_call */
+      0,                                    /* tp_str */
+      PyObject_GenericGetAttr,              /* tp_getattro */
+      0,                                    /* tp_setattro */
+      0,                                    /* tp_as_buffer */
+      Py_TPFLAGS_DEFAULT,                   /* tp_flags */
+      swigobject_doc,                       /* tp_doc */
+      0,                                    /* tp_traverse */
+      0,                                    /* tp_clear */
+      (richcmpfunc)SwigPyObject_richcompare,/* tp_richcompare */
+      0,                                    /* tp_weaklistoffset */
+#if PY_VERSION_HEX >= 0x02020000
+      0,                                    /* tp_iter */
+      0,                                    /* tp_iternext */
+      swigobject_methods,                   /* tp_methods */
+      0,                                    /* tp_members */
+      0,                                    /* tp_getset */
+      0,                                    /* tp_base */
+      0,                                    /* tp_dict */
+      0,                                    /* tp_descr_get */
+      0,                                    /* tp_descr_set */
+      0,                                    /* tp_dictoffset */
+      0,                                    /* tp_init */
+      0,                                    /* tp_alloc */
+      0,                                    /* tp_new */
+      0,                                    /* tp_free */
+      0,                                    /* tp_is_gc */
+      0,                                    /* tp_bases */
+      0,                                    /* tp_mro */
+      0,                                    /* tp_cache */
+      0,                                    /* tp_subclasses */
+      0,                                    /* tp_weaklist */
+#endif
+#if PY_VERSION_HEX >= 0x02030000
+      0,                                    /* tp_del */
+#endif
+#if PY_VERSION_HEX >= 0x02060000
+      0,                                    /* tp_version_tag */
+#endif
+#if PY_VERSION_HEX >= 0x03040000
+      0,                                    /* tp_finalize */
+#endif
+#ifdef COUNT_ALLOCS
+      0,                                    /* tp_allocs */
+      0,                                    /* tp_frees */
+      0,                                    /* tp_maxalloc */
+#if PY_VERSION_HEX >= 0x02050000
+      0,                                    /* tp_prev */
+#endif
+      0                                     /* tp_next */
+#endif
+    };
+    swigpyobject_type = tmp;
+    type_init = 1;
+#if PY_VERSION_HEX < 0x02020000
+    swigpyobject_type.ob_type = &PyType_Type;
+#else
+    if (PyType_Ready(&swigpyobject_type) < 0)
+      return NULL;
+#endif
+  }
+  return &swigpyobject_type;
+}
+
+SWIGRUNTIME PyObject *
+SwigPyObject_New(void *ptr, swig_type_info *ty, int own)
+{
+  SwigPyObject *sobj = PyObject_NEW(SwigPyObject, SwigPyObject_type());
+  if (sobj) {
+    sobj->ptr  = ptr;
+    sobj->ty   = ty;
+    sobj->own  = own;
+    sobj->next = 0;
+  }
+  return (PyObject *)sobj;
+}
+
+/* -----------------------------------------------------------------------------
+ * Implements a simple Swig Packed type, and use it instead of string
+ * ----------------------------------------------------------------------------- */
+
+typedef struct {
+  PyObject_HEAD
+  void *pack;
+  swig_type_info *ty;
+  size_t size;
+} SwigPyPacked;
+
+SWIGRUNTIME int
+SwigPyPacked_print(SwigPyPacked *v, FILE *fp, int SWIGUNUSEDPARM(flags))
+{
+  char result[SWIG_BUFFER_SIZE];
+  fputs("<Swig Packed ", fp);
+  if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))) {
+    fputs("at ", fp);
+    fputs(result, fp);
+  }
+  fputs(v->ty->name,fp);
+  fputs(">", fp);
+  return 0;
+}
+
+SWIGRUNTIME PyObject *
+SwigPyPacked_repr(SwigPyPacked *v)
+{
+  char result[SWIG_BUFFER_SIZE];
+  if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))) {
+    return SWIG_Python_str_FromFormat("<Swig Packed at %s%s>", result, v->ty->name);
+  } else {
+    return SWIG_Python_str_FromFormat("<Swig Packed %s>", v->ty->name);
+  }
+}
+
+SWIGRUNTIME PyObject *
+SwigPyPacked_str(SwigPyPacked *v)
+{
+  char result[SWIG_BUFFER_SIZE];
+  if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))){
+    return SWIG_Python_str_FromFormat("%s%s", result, v->ty->name);
+  } else {
+    return SWIG_Python_str_FromChar(v->ty->name);
+  }
+}
+
+SWIGRUNTIME int
+SwigPyPacked_compare(SwigPyPacked *v, SwigPyPacked *w)
+{
+  size_t i = v->size;
+  size_t j = w->size;
+  int s = (i < j) ? -1 : ((i > j) ? 1 : 0);
+  return s ? s : strncmp((char *)v->pack, (char *)w->pack, 2*v->size);
+}
+
+SWIGRUNTIME PyTypeObject* SwigPyPacked_TypeOnce(void);
+
+SWIGRUNTIME PyTypeObject*
+SwigPyPacked_type(void) {
+  static PyTypeObject *SWIG_STATIC_POINTER(type) = SwigPyPacked_TypeOnce();
+  return type;
+}
+
+SWIGRUNTIMEINLINE int
+SwigPyPacked_Check(PyObject *op) {
+  return ((op)->ob_type == SwigPyPacked_TypeOnce())
+    || (strcmp((op)->ob_type->tp_name,"SwigPyPacked") == 0);
+}
+
+SWIGRUNTIME void
+SwigPyPacked_dealloc(PyObject *v)
+{
+  if (SwigPyPacked_Check(v)) {
+    SwigPyPacked *sobj = (SwigPyPacked *) v;
+    free(sobj->pack);
+  }
+  PyObject_DEL(v);
+}
+
+SWIGRUNTIME PyTypeObject*
+SwigPyPacked_TypeOnce(void) {
+  static char swigpacked_doc[] = "Swig object carries a C/C++ instance pointer";
+  static PyTypeObject swigpypacked_type;
+  static int type_init = 0;
+  if (!type_init) {
+    const PyTypeObject tmp = {
+#if PY_VERSION_HEX>=0x03000000
+      PyVarObject_HEAD_INIT(NULL, 0)
+#else
+      PyObject_HEAD_INIT(NULL)
+      0,                                    /* ob_size */
+#endif
+      (char *)"SwigPyPacked",               /* tp_name */
+      sizeof(SwigPyPacked),                 /* tp_basicsize */
+      0,                                    /* tp_itemsize */
+      (destructor)SwigPyPacked_dealloc,     /* tp_dealloc */
+      (printfunc)SwigPyPacked_print,        /* tp_print */
+      (getattrfunc)0,                       /* tp_getattr */
+      (setattrfunc)0,                       /* tp_setattr */
+#if PY_VERSION_HEX>=0x03000000
+      0, /* tp_reserved in 3.0.1 */
+#else
+      (cmpfunc)SwigPyPacked_compare,        /* tp_compare */
+#endif
+      (reprfunc)SwigPyPacked_repr,          /* tp_repr */
+      0,                                    /* tp_as_number */
+      0,                                    /* tp_as_sequence */
+      0,                                    /* tp_as_mapping */
+      (hashfunc)0,                          /* tp_hash */
+      (ternaryfunc)0,                       /* tp_call */
+      (reprfunc)SwigPyPacked_str,           /* tp_str */
+      PyObject_GenericGetAttr,              /* tp_getattro */
+      0,                                    /* tp_setattro */
+      0,                                    /* tp_as_buffer */
+      Py_TPFLAGS_DEFAULT,                   /* tp_flags */
+      swigpacked_doc,                       /* tp_doc */
+      0,                                    /* tp_traverse */
+      0,                                    /* tp_clear */
+      0,                                    /* tp_richcompare */
+      0,                                    /* tp_weaklistoffset */
+#if PY_VERSION_HEX >= 0x02020000
+      0,                                    /* tp_iter */
+      0,                                    /* tp_iternext */
+      0,                                    /* tp_methods */
+      0,                                    /* tp_members */
+      0,                                    /* tp_getset */
+      0,                                    /* tp_base */
+      0,                                    /* tp_dict */
+      0,                                    /* tp_descr_get */
+      0,                                    /* tp_descr_set */
+      0,                                    /* tp_dictoffset */
+      0,                                    /* tp_init */
+      0,                                    /* tp_alloc */
+      0,                                    /* tp_new */
+      0,                                    /* tp_free */
+      0,                                    /* tp_is_gc */
+      0,                                    /* tp_bases */
+      0,                                    /* tp_mro */
+      0,                                    /* tp_cache */
+      0,                                    /* tp_subclasses */
+      0,                                    /* tp_weaklist */
+#endif
+#if PY_VERSION_HEX >= 0x02030000
+      0,                                    /* tp_del */
+#endif
+#if PY_VERSION_HEX >= 0x02060000
+      0,                                    /* tp_version_tag */
+#endif
+#if PY_VERSION_HEX >= 0x03040000
+      0,                                    /* tp_finalize */
+#endif
+#ifdef COUNT_ALLOCS
+      0,                                    /* tp_allocs */
+      0,                                    /* tp_frees */
+      0,                                    /* tp_maxalloc */
+#if PY_VERSION_HEX >= 0x02050000
+      0,                                    /* tp_prev */
+#endif
+      0                                     /* tp_next */
+#endif
+    };
+    swigpypacked_type = tmp;
+    type_init = 1;
+#if PY_VERSION_HEX < 0x02020000
+    swigpypacked_type.ob_type = &PyType_Type;
+#else
+    if (PyType_Ready(&swigpypacked_type) < 0)
+      return NULL;
+#endif
+  }
+  return &swigpypacked_type;
+}
+
+SWIGRUNTIME PyObject *
+SwigPyPacked_New(void *ptr, size_t size, swig_type_info *ty)
+{
+  SwigPyPacked *sobj = PyObject_NEW(SwigPyPacked, SwigPyPacked_type());
+  if (sobj) {
+    void *pack = malloc(size);
+    if (pack) {
+      memcpy(pack, ptr, size);
+      sobj->pack = pack;
+      sobj->ty   = ty;
+      sobj->size = size;
+    } else {
+      PyObject_DEL((PyObject *) sobj);
+      sobj = 0;
+    }
+  }
+  return (PyObject *) sobj;
+}
+
+SWIGRUNTIME swig_type_info *
+SwigPyPacked_UnpackData(PyObject *obj, void *ptr, size_t size)
+{
+  if (SwigPyPacked_Check(obj)) {
+    SwigPyPacked *sobj = (SwigPyPacked *)obj;
+    if (sobj->size != size) return 0;
+    memcpy(ptr, sobj->pack, size);
+    return sobj->ty;
+  } else {
+    return 0;
+  }
+}
+
+/* -----------------------------------------------------------------------------
+ * pointers/data manipulation
+ * ----------------------------------------------------------------------------- */
+
+SWIGRUNTIMEINLINE PyObject *
+_SWIG_This(void)
+{
+    return SWIG_Python_str_FromChar("this");
+}
+
+static PyObject *swig_this = NULL;
+
+SWIGRUNTIME PyObject *
+SWIG_This(void)
+{
+  if (swig_this == NULL)
+    swig_this = _SWIG_This();
+  return swig_this;
+}
+
+/* #define SWIG_PYTHON_SLOW_GETSET_THIS */
+
+/* TODO: I don't know how to implement the fast getset in Python 3 right now */
+#if PY_VERSION_HEX>=0x03000000
+#define SWIG_PYTHON_SLOW_GETSET_THIS
+#endif
+
+SWIGRUNTIME SwigPyObject *
+SWIG_Python_GetSwigThis(PyObject *pyobj)
+{
+  PyObject *obj;
+
+  if (SwigPyObject_Check(pyobj))
+    return (SwigPyObject *) pyobj;
+
+#ifdef SWIGPYTHON_BUILTIN
+  (void)obj;
+# ifdef PyWeakref_CheckProxy
+  if (PyWeakref_CheckProxy(pyobj)) {
+    pyobj = PyWeakref_GET_OBJECT(pyobj);
+    if (pyobj && SwigPyObject_Check(pyobj))
+      return (SwigPyObject*) pyobj;
+  }
+# endif
+  return NULL;
+#else
+
+  obj = 0;
+
+#if (!defined(SWIG_PYTHON_SLOW_GETSET_THIS) && (PY_VERSION_HEX >= 0x02030000))
+  if (PyInstance_Check(pyobj)) {
+    obj = _PyInstance_Lookup(pyobj, SWIG_This());
+  } else {
+    PyObject **dictptr = _PyObject_GetDictPtr(pyobj);
+    if (dictptr != NULL) {
+      PyObject *dict = *dictptr;
+      obj = dict ? PyDict_GetItem(dict, SWIG_This()) : 0;
+    } else {
+#ifdef PyWeakref_CheckProxy
+      if (PyWeakref_CheckProxy(pyobj)) {
+        PyObject *wobj = PyWeakref_GET_OBJECT(pyobj);
+        return wobj ? SWIG_Python_GetSwigThis(wobj) : 0;
+      }
+#endif
+      obj = PyObject_GetAttr(pyobj,SWIG_This());
+      if (obj) {
+        Py_DECREF(obj);
+      } else {
+        if (PyErr_Occurred()) PyErr_Clear();
+        return 0;
+      }
+    }
+  }
+#else
+  obj = PyObject_GetAttr(pyobj,SWIG_This());
+  if (obj) {
+    Py_DECREF(obj);
+  } else {
+    if (PyErr_Occurred()) PyErr_Clear();
+    return 0;
+  }
+#endif
+  if (obj && !SwigPyObject_Check(obj)) {
+    /* a PyObject is called 'this', try to get the 'real this'
+       SwigPyObject from it */
+    return SWIG_Python_GetSwigThis(obj);
+  }
+  return (SwigPyObject *)obj;
+#endif
+}
+
+/* Acquire a pointer value */
+
+SWIGRUNTIME int
+SWIG_Python_AcquirePtr(PyObject *obj, int own) {
+  if (own == SWIG_POINTER_OWN) {
+    SwigPyObject *sobj = SWIG_Python_GetSwigThis(obj);
+    if (sobj) {
+      int oldown = sobj->own;
+      sobj->own = own;
+      return oldown;
+    }
+  }
+  return 0;
+}
+
+/* Convert a pointer value */
+
+SWIGRUNTIME int
+SWIG_Python_ConvertPtrAndOwn(PyObject *obj, void **ptr, swig_type_info *ty, int flags, int *own) {
+  int res;
+  SwigPyObject *sobj;
+  int implicit_conv = (flags & SWIG_POINTER_IMPLICIT_CONV) != 0;
+
+  if (!obj)
+    return SWIG_ERROR;
+  if (obj == Py_None && !implicit_conv) {
+    if (ptr)
+      *ptr = 0;
+    return SWIG_OK;
+  }
+
+  res = SWIG_ERROR;
+
+  sobj = SWIG_Python_GetSwigThis(obj);
+  if (own)
+    *own = 0;
+  while (sobj) {
+    void *vptr = sobj->ptr;
+    if (ty) {
+      swig_type_info *to = sobj->ty;
+      if (to == ty) {
+        /* no type cast needed */
+        if (ptr) *ptr = vptr;
+        break;
+      } else {
+        swig_cast_info *tc = SWIG_TypeCheck(to->name,ty);
+        if (!tc) {
+          sobj = (SwigPyObject *)sobj->next;
+        } else {
+          if (ptr) {
+            int newmemory = 0;
+            *ptr = SWIG_TypeCast(tc,vptr,&newmemory);
+            if (newmemory == SWIG_CAST_NEW_MEMORY) {
+              assert(own); /* badly formed typemap which will lead to a memory leak - it must set and use own to delete *ptr */
+              if (own)
+                *own = *own | SWIG_CAST_NEW_MEMORY;
+            }
+          }
+          break;
+        }
+      }
+    } else {
+      if (ptr) *ptr = vptr;
+      break;
+    }
+  }
+  if (sobj) {
+    if (own)
+      *own = *own | sobj->own;
+    if (flags & SWIG_POINTER_DISOWN) {
+      sobj->own = 0;
+    }
+    res = SWIG_OK;
+  } else {
+    if (implicit_conv) {
+      SwigPyClientData *data = ty ? (SwigPyClientData *) ty->clientdata : 0;
+      if (data && !data->implicitconv) {
+        PyObject *klass = data->klass;
+        if (klass) {
+          PyObject *impconv;
+          data->implicitconv = 1; /* avoid recursion and call 'explicit' constructors*/
+          impconv = SWIG_Python_CallFunctor(klass, obj);
+          data->implicitconv = 0;
+          if (PyErr_Occurred()) {
+            PyErr_Clear();
+            impconv = 0;
+          }
+          if (impconv) {
+            SwigPyObject *iobj = SWIG_Python_GetSwigThis(impconv);
+            if (iobj) {
+              void *vptr;
+              res = SWIG_Python_ConvertPtrAndOwn((PyObject*)iobj, &vptr, ty, 0, 0);
+              if (SWIG_IsOK(res)) {
+                if (ptr) {
+                  *ptr = vptr;
+                  /* transfer the ownership to 'ptr' */
+                  iobj->own = 0;
+                  res = SWIG_AddCast(res);
+                  res = SWIG_AddNewMask(res);
+                } else {
+                  res = SWIG_AddCast(res);
+                }
+              }
+            }
+            Py_DECREF(impconv);
+          }
+        }
+      }
+    }
+    if (!SWIG_IsOK(res) && obj == Py_None) {
+      if (ptr)
+        *ptr = 0;
+      if (PyErr_Occurred())
+        PyErr_Clear();
+      res = SWIG_OK;
+    }
+  }
+  return res;
+}
+
+/* Convert a function ptr value */
+
+SWIGRUNTIME int
+SWIG_Python_ConvertFunctionPtr(PyObject *obj, void **ptr, swig_type_info *ty) {
+  if (!PyCFunction_Check(obj)) {
+    return SWIG_ConvertPtr(obj, ptr, ty, 0);
+  } else {
+    void *vptr = 0;
+
+    /* here we get the method pointer for callbacks */
+    const char *doc = (((PyCFunctionObject *)obj) -> m_ml -> ml_doc);
+    const char *desc = doc ? strstr(doc, "swig_ptr: ") : 0;
+    if (desc)
+      desc = ty ? SWIG_UnpackVoidPtr(desc + 10, &vptr, ty->name) : 0;
+    if (!desc)
+      return SWIG_ERROR;
+    if (ty) {
+      swig_cast_info *tc = SWIG_TypeCheck(desc,ty);
+      if (tc) {
+        int newmemory = 0;
+        *ptr = SWIG_TypeCast(tc,vptr,&newmemory);
+        assert(!newmemory); /* newmemory handling not yet implemented */
+      } else {
+        return SWIG_ERROR;
+      }
+    } else {
+      *ptr = vptr;
+    }
+    return SWIG_OK;
+  }
+}
+
+/* Convert a packed value value */
+
+SWIGRUNTIME int
+SWIG_Python_ConvertPacked(PyObject *obj, void *ptr, size_t sz, swig_type_info *ty) {
+  swig_type_info *to = SwigPyPacked_UnpackData(obj, ptr, sz);
+  if (!to) return SWIG_ERROR;
+  if (ty) {
+    if (to != ty) {
+      /* check type cast? */
+      swig_cast_info *tc = SWIG_TypeCheck(to->name,ty);
+      if (!tc) return SWIG_ERROR;
+    }
+  }
+  return SWIG_OK;
+}
+
+/* -----------------------------------------------------------------------------
+ * Create a new pointer object
+ * ----------------------------------------------------------------------------- */
+
+/*
+  Create a new instance object, without calling __init__, and set the
+  'this' attribute.
+*/
+
+SWIGRUNTIME PyObject*
+SWIG_Python_NewShadowInstance(SwigPyClientData *data, PyObject *swig_this)
+{
+#if (PY_VERSION_HEX >= 0x02020000)
+  PyObject *inst = 0;
+  PyObject *newraw = data->newraw;
+  if (newraw) {
+    inst = PyObject_Call(newraw, data->newargs, NULL);
+    if (inst) {
+#if !defined(SWIG_PYTHON_SLOW_GETSET_THIS)
+      PyObject **dictptr = _PyObject_GetDictPtr(inst);
+      if (dictptr != NULL) {
+        PyObject *dict = *dictptr;
+        if (dict == NULL) {
+          dict = PyDict_New();
+          *dictptr = dict;
+          PyDict_SetItem(dict, SWIG_This(), swig_this);
+        }
+      }
+#else
+      PyObject *key = SWIG_This();
+      PyObject_SetAttr(inst, key, swig_this);
+#endif
+    }
+  } else {
+#if PY_VERSION_HEX >= 0x03000000
+    inst = ((PyTypeObject*) data->newargs)->tp_new((PyTypeObject*) data->newargs, Py_None, Py_None);
+    if (inst) {
+      PyObject_SetAttr(inst, SWIG_This(), swig_this);
+      Py_TYPE(inst)->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
+    }
+#else
+    PyObject *dict = PyDict_New();
+    if (dict) {
+      PyDict_SetItem(dict, SWIG_This(), swig_this);
+      inst = PyInstance_NewRaw(data->newargs, dict);
+      Py_DECREF(dict);
+    }
+#endif
+  }
+  return inst;
+#else
+#if (PY_VERSION_HEX >= 0x02010000)
+  PyObject *inst = 0;
+  PyObject *dict = PyDict_New();
+  if (dict) {
+    PyDict_SetItem(dict, SWIG_This(), swig_this);
+    inst = PyInstance_NewRaw(data->newargs, dict);
+    Py_DECREF(dict);
+  }
+  return (PyObject *) inst;
+#else
+  PyInstanceObject *inst = PyObject_NEW(PyInstanceObject, &PyInstance_Type);
+  if (inst == NULL) {
+    return NULL;
+  }
+  inst->in_class = (PyClassObject *)data->newargs;
+  Py_INCREF(inst->in_class);
+  inst->in_dict = PyDict_New();
+  if (inst->in_dict == NULL) {
+    Py_DECREF(inst);
+    return NULL;
+  }
+#ifdef Py_TPFLAGS_HAVE_WEAKREFS
+  inst->in_weakreflist = NULL;
+#endif
+#ifdef Py_TPFLAGS_GC
+  PyObject_GC_Init(inst);
+#endif
+  PyDict_SetItem(inst->in_dict, SWIG_This(), swig_this);
+  return (PyObject *) inst;
+#endif
+#endif
+}
+
+SWIGRUNTIME void
+SWIG_Python_SetSwigThis(PyObject *inst, PyObject *swig_this)
+{
+ PyObject *dict;
+#if (PY_VERSION_HEX >= 0x02020000) && !defined(SWIG_PYTHON_SLOW_GETSET_THIS)
+ PyObject **dictptr = _PyObject_GetDictPtr(inst);
+ if (dictptr != NULL) {
+   dict = *dictptr;
+   if (dict == NULL) {
+     dict = PyDict_New();
+     *dictptr = dict;
+   }
+   PyDict_SetItem(dict, SWIG_This(), swig_this);
+   return;
+ }
+#endif
+ dict = PyObject_GetAttrString(inst, (char*)"__dict__");
+ PyDict_SetItem(dict, SWIG_This(), swig_this);
+ Py_DECREF(dict);
+}
+
+
+SWIGINTERN PyObject *
+SWIG_Python_InitShadowInstance(PyObject *args) {
+  PyObject *obj[2];
+  if (!SWIG_Python_UnpackTuple(args, "swiginit", 2, 2, obj)) {
+    return NULL;
+  } else {
+    SwigPyObject *sthis = SWIG_Python_GetSwigThis(obj[0]);
+    if (sthis) {
+      SwigPyObject_append((PyObject*) sthis, obj[1]);
+    } else {
+      SWIG_Python_SetSwigThis(obj[0], obj[1]);
+    }
+    return SWIG_Py_Void();
+  }
+}
+
+/* Create a new pointer object */
+
+SWIGRUNTIME PyObject *
+SWIG_Python_NewPointerObj(PyObject *self, void *ptr, swig_type_info *type, int flags) {
+  SwigPyClientData *clientdata;
+  PyObject * robj;
+  int own;
+
+  if (!ptr)
+    return SWIG_Py_Void();
+
+  clientdata = type ? (SwigPyClientData *)(type->clientdata) : 0;
+  own = (flags & SWIG_POINTER_OWN) ? SWIG_POINTER_OWN : 0;
+  if (clientdata && clientdata->pytype) {
+    SwigPyObject *newobj;
+    if (flags & SWIG_BUILTIN_TP_INIT) {
+      newobj = (SwigPyObject*) self;
+      if (newobj->ptr) {
+        PyObject *next_self = clientdata->pytype->tp_alloc(clientdata->pytype, 0);
+        while (newobj->next)
+          newobj = (SwigPyObject *) newobj->next;
+        newobj->next = next_self;
+        newobj = (SwigPyObject *)next_self;
+#ifdef SWIGPYTHON_BUILTIN
+        newobj->dict = 0;
+#endif
+      }
+    } else {
+      newobj = PyObject_New(SwigPyObject, clientdata->pytype);
+#ifdef SWIGPYTHON_BUILTIN
+      newobj->dict = 0;
+#endif
+    }
+    if (newobj) {
+      newobj->ptr = ptr;
+      newobj->ty = type;
+      newobj->own = own;
+      newobj->next = 0;
+      return (PyObject*) newobj;
+    }
+    return SWIG_Py_Void();
+  }
+
+  assert(!(flags & SWIG_BUILTIN_TP_INIT));
+
+  robj = SwigPyObject_New(ptr, type, own);
+  if (robj && clientdata && !(flags & SWIG_POINTER_NOSHADOW)) {
+    PyObject *inst = SWIG_Python_NewShadowInstance(clientdata, robj);
+    Py_DECREF(robj);
+    robj = inst;
+  }
+  return robj;
+}
+
+/* Create a new packed object */
+
+SWIGRUNTIMEINLINE PyObject *
+SWIG_Python_NewPackedObj(void *ptr, size_t sz, swig_type_info *type) {
+  return ptr ? SwigPyPacked_New((void *) ptr, sz, type) : SWIG_Py_Void();
+}
+
+/* -----------------------------------------------------------------------------*
+ *  Get type list
+ * -----------------------------------------------------------------------------*/
+
+#ifdef SWIG_LINK_RUNTIME
+void *SWIG_ReturnGlobalTypeList(void *);
+#endif
+
+SWIGRUNTIME swig_module_info *
+SWIG_Python_GetModule(void *SWIGUNUSEDPARM(clientdata)) {
+  static void *type_pointer = (void *)0;
+  /* first check if module already created */
+  if (!type_pointer) {
+#ifdef SWIG_LINK_RUNTIME
+    type_pointer = SWIG_ReturnGlobalTypeList((void *)0);
+#else
+# ifdef SWIGPY_USE_CAPSULE
+    type_pointer = PyCapsule_Import(SWIGPY_CAPSULE_NAME, 0);
+# else
+    type_pointer = PyCObject_Import((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION,
+                                    (char*)"type_pointer" SWIG_TYPE_TABLE_NAME);
+# endif
+    if (PyErr_Occurred()) {
+      PyErr_Clear();
+      type_pointer = (void *)0;
+    }
+#endif
+  }
+  return (swig_module_info *) type_pointer;
+}
+
+#if PY_MAJOR_VERSION < 2
+/* PyModule_AddObject function was introduced in Python 2.0.  The following function
+   is copied out of Python/modsupport.c in python version 2.3.4 */
+SWIGINTERN int
+PyModule_AddObject(PyObject *m, char *name, PyObject *o)
+{
+  PyObject *dict;
+  if (!PyModule_Check(m)) {
+    PyErr_SetString(PyExc_TypeError, "PyModule_AddObject() needs module as first arg");
+    return SWIG_ERROR;
+  }
+  if (!o) {
+    PyErr_SetString(PyExc_TypeError, "PyModule_AddObject() needs non-NULL value");
+    return SWIG_ERROR;
+  }
+
+  dict = PyModule_GetDict(m);
+  if (dict == NULL) {
+    /* Internal error -- modules must have a dict! */
+    PyErr_Format(PyExc_SystemError, "module '%s' has no __dict__",
+                 PyModule_GetName(m));
+    return SWIG_ERROR;
+  }
+  if (PyDict_SetItemString(dict, name, o))
+    return SWIG_ERROR;
+  Py_DECREF(o);
+  return SWIG_OK;
+}
+#endif
+
+SWIGRUNTIME void
+#ifdef SWIGPY_USE_CAPSULE
+SWIG_Python_DestroyModule(PyObject *obj)
+#else
+SWIG_Python_DestroyModule(void *vptr)
+#endif
+{
+#ifdef SWIGPY_USE_CAPSULE
+  swig_module_info *swig_module = (swig_module_info *) PyCapsule_GetPointer(obj, SWIGPY_CAPSULE_NAME);
+#else
+  swig_module_info *swig_module = (swig_module_info *) vptr;
+#endif
+  swig_type_info **types = swig_module->types;
+  size_t i;
+  for (i =0; i < swig_module->size; ++i) {
+    swig_type_info *ty = types[i];
+    if (ty->owndata) {
+      SwigPyClientData *data = (SwigPyClientData *) ty->clientdata;
+      if (data) SwigPyClientData_Del(data);
+    }
+  }
+  Py_DECREF(SWIG_This());
+  swig_this = NULL;
+}
+
+SWIGRUNTIME void
+SWIG_Python_SetModule(swig_module_info *swig_module) {
+#if PY_VERSION_HEX >= 0x03000000
+ /* Add a placeholder module object into sys.modules */
+  PyObject *module = PyImport_AddModule((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION);
+#else
+  static PyMethodDef swig_empty_runtime_method_table[] = { {NULL, NULL, 0, NULL} }; /* Sentinel */
+  PyObject *module = Py_InitModule((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION, swig_empty_runtime_method_table);
+#endif
+#ifdef SWIGPY_USE_CAPSULE
+  PyObject *pointer = PyCapsule_New((void *) swig_module, SWIGPY_CAPSULE_NAME, SWIG_Python_DestroyModule);
+  if (pointer && module) {
+    PyModule_AddObject(module, (char*)"type_pointer_capsule" SWIG_TYPE_TABLE_NAME, pointer);
+  } else {
+    Py_XDECREF(pointer);
+  }
+#else
+  PyObject *pointer = PyCObject_FromVoidPtr((void *) swig_module, SWIG_Python_DestroyModule);
+  if (pointer && module) {
+    PyModule_AddObject(module, (char*)"type_pointer" SWIG_TYPE_TABLE_NAME, pointer);
+  } else {
+    Py_XDECREF(pointer);
+  }
+#endif
+}
+
+/* The python cached type query */
+SWIGRUNTIME PyObject *
+SWIG_Python_TypeCache(void) {
+  static PyObject *SWIG_STATIC_POINTER(cache) = PyDict_New();
+  return cache;
+}
+
+SWIGRUNTIME swig_type_info *
+SWIG_Python_TypeQuery(const char *type)
+{
+  PyObject *cache = SWIG_Python_TypeCache();
+  PyObject *key = SWIG_Python_str_FromChar(type);
+  PyObject *obj = PyDict_GetItem(cache, key);
+  swig_type_info *descriptor;
+  if (obj) {
+#ifdef SWIGPY_USE_CAPSULE
+    descriptor = (swig_type_info *) PyCapsule_GetPointer(obj, NULL);
+#else
+    descriptor = (swig_type_info *) PyCObject_AsVoidPtr(obj);
+#endif
+  } else {
+    swig_module_info *swig_module = SWIG_GetModule(0);
+    descriptor = SWIG_TypeQueryModule(swig_module, swig_module, type);
+    if (descriptor) {
+#ifdef SWIGPY_USE_CAPSULE
+      obj = PyCapsule_New((void*) descriptor, NULL, NULL);
+#else
+      obj = PyCObject_FromVoidPtr(descriptor, NULL);
+#endif
+      PyDict_SetItem(cache, key, obj);
+      Py_DECREF(obj);
+    }
+  }
+  Py_DECREF(key);
+  return descriptor;
+}
+
+/*
+   For backward compatibility only
+*/
+#define SWIG_POINTER_EXCEPTION  0
+#define SWIG_arg_fail(arg)      SWIG_Python_ArgFail(arg)
+#define SWIG_MustGetPtr(p, type, argnum, flags)  SWIG_Python_MustGetPtr(p, type, argnum, flags)
+
+SWIGRUNTIME int
+SWIG_Python_AddErrMesg(const char* mesg, int infront)
+{
+  if (PyErr_Occurred()) {
+    PyObject *type = 0;
+    PyObject *value = 0;
+    PyObject *traceback = 0;
+    PyErr_Fetch(&type, &value, &traceback);
+    if (value) {
+      char *tmp;
+      PyObject *old_str = PyObject_Str(value);
+      Py_XINCREF(type);
+      PyErr_Clear();
+      if (infront) {
+        PyErr_Format(type, "%s %s", mesg, tmp = SWIG_Python_str_AsChar(old_str));
+      } else {
+        PyErr_Format(type, "%s %s", tmp = SWIG_Python_str_AsChar(old_str), mesg);
+      }
+      SWIG_Python_str_DelForPy3(tmp);
+      Py_DECREF(old_str);
+    }
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+SWIGRUNTIME int
+SWIG_Python_ArgFail(int argnum)
+{
+  if (PyErr_Occurred()) {
+    /* add information about failing argument */
+    char mesg[256];
+    PyOS_snprintf(mesg, sizeof(mesg), "argument number %d:", argnum);
+    return SWIG_Python_AddErrMesg(mesg, 1);
+  } else {
+    return 0;
+  }
+}
+
+SWIGRUNTIMEINLINE const char *
+SwigPyObject_GetDesc(PyObject *self)
+{
+  SwigPyObject *v = (SwigPyObject *)self;
+  swig_type_info *ty = v ? v->ty : 0;
+  return ty ? ty->str : "";
+}
+
+SWIGRUNTIME void
+SWIG_Python_TypeError(const char *type, PyObject *obj)
+{
+  if (type) {
+#if defined(SWIG_COBJECT_TYPES)
+    if (obj && SwigPyObject_Check(obj)) {
+      const char *otype = (const char *) SwigPyObject_GetDesc(obj);
+      if (otype) {
+        PyErr_Format(PyExc_TypeError, "a '%s' is expected, 'SwigPyObject(%s)' is received",
+                     type, otype);
+        return;
+      }
+    } else
+#endif
+    {
+      const char *otype = (obj ? obj->ob_type->tp_name : 0);
+      if (otype) {
+        PyObject *str = PyObject_Str(obj);
+        const char *cstr = str ? SWIG_Python_str_AsChar(str) : 0;
+        if (cstr) {
+          PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s(%s)' is received",
+                       type, otype, cstr);
+          SWIG_Python_str_DelForPy3(cstr);
+        } else {
+          PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s' is received",
+                       type, otype);
+        }
+        Py_XDECREF(str);
+        return;
+      }
+    }
+    PyErr_Format(PyExc_TypeError, "a '%s' is expected", type);
+  } else {
+    PyErr_Format(PyExc_TypeError, "unexpected type is received");
+  }
+}
+
+
+/* Convert a pointer value, signal an exception on a type mismatch */
+SWIGRUNTIME void *
+SWIG_Python_MustGetPtr(PyObject *obj, swig_type_info *ty, int SWIGUNUSEDPARM(argnum), int flags) {
+  void *result;
+  if (SWIG_Python_ConvertPtr(obj, &result, ty, flags) == -1) {
+    PyErr_Clear();
+#if SWIG_POINTER_EXCEPTION
+    if (flags) {
+      SWIG_Python_TypeError(SWIG_TypePrettyName(ty), obj);
+      SWIG_Python_ArgFail(argnum);
+    }
+#endif
+  }
+  return result;
+}
+
+#ifdef SWIGPYTHON_BUILTIN
+SWIGRUNTIME int
+SWIG_Python_NonDynamicSetAttr(PyObject *obj, PyObject *name, PyObject *value) {
+  PyTypeObject *tp = obj->ob_type;
+  PyObject *descr;
+  PyObject *encoded_name;
+  descrsetfunc f;
+  int res = -1;
+
+# ifdef Py_USING_UNICODE
+  if (PyString_Check(name)) {
+    name = PyUnicode_Decode(PyString_AsString(name), PyString_Size(name), NULL, NULL);
+    if (!name)
+      return -1;
+  } else if (!PyUnicode_Check(name))
+# else
+  if (!PyString_Check(name))
+# endif
+  {
+    PyErr_Format(PyExc_TypeError, "attribute name must be string, not '%.200s'", name->ob_type->tp_name);
+    return -1;
+  } else {
+    Py_INCREF(name);
+  }
+
+  if (!tp->tp_dict) {
+    if (PyType_Ready(tp) < 0)
+      goto done;
+  }
+
+  descr = _PyType_Lookup(tp, name);
+  f = NULL;
+  if (descr != NULL)
+    f = descr->ob_type->tp_descr_set;
+  if (!f) {
+    if (PyString_Check(name)) {
+      encoded_name = name;
+      Py_INCREF(name);
+    } else {
+      encoded_name = PyUnicode_AsUTF8String(name);
+    }
+    PyErr_Format(PyExc_AttributeError, "'%.100s' object has no attribute '%.200s'", tp->tp_name, PyString_AsString(encoded_name));
+    Py_DECREF(encoded_name);
+  } else {
+    res = f(descr, obj, value);
+  }
+
+  done:
+  Py_DECREF(name);
+  return res;
+}
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
+#define SWIG_exception_fail(code, msg) do { SWIG_Error(code, msg); SWIG_fail; } while(0)
+
+#define SWIG_contract_assert(expr, msg) if (!(expr)) { SWIG_Error(SWIG_RuntimeError, msg); SWIG_fail; } else
+
+
+
+  #define SWIG_exception(code, msg) do { SWIG_Error(code, msg); SWIG_fail;; } while(0)
+
+
+/* -------- TYPES TABLE (BEGIN) -------- */
+
+#define SWIGTYPE_p_char swig_types[0]
+#define SWIGTYPE_p_int swig_types[1]
+#define SWIGTYPE_p_uint8_t swig_types[2]
+static swig_type_info *swig_types[4];
+static swig_module_info swig_module = {swig_types, 3, 0, 0, 0, 0};
+#define SWIG_TypeQuery(name) SWIG_TypeQueryModule(&swig_module, &swig_module, name)
+#define SWIG_MangledTypeQuery(name) SWIG_MangledTypeQueryModule(&swig_module, &swig_module, name)
+
+/* -------- TYPES TABLE (END) -------- */
+
+#if (PY_VERSION_HEX <= 0x02000000)
+# if !defined(SWIG_PYTHON_CLASSIC)
+#  error "This python version requires swig to be run with the '-classic' option"
+# endif
+#endif
+
+/*-----------------------------------------------
+              @(target):= _libwebp.so
+  ------------------------------------------------*/
+#if PY_VERSION_HEX >= 0x03000000
+#  define SWIG_init    PyInit__libwebp
+
+#else
+#  define SWIG_init    init_libwebp
+
+#endif
+#define SWIG_name    "_libwebp"
+
+#define SWIGVERSION 0x030012
+#define SWIG_VERSION SWIGVERSION
+
+
+#define SWIG_as_voidptr(a) (void *)((const void *)(a))
+#define SWIG_as_voidptrptr(a) ((void)SWIG_as_voidptr(*a),(void**)(a))
+
+
+SWIGINTERNINLINE PyObject*
+  SWIG_From_int  (int value)
+{
+  return PyInt_FromLong((long) value);
+}
+
+
+SWIGINTERN swig_type_info*
+SWIG_pchar_descriptor(void)
+{
+  static int init = 0;
+  static swig_type_info* info = 0;
+  if (!init) {
+    info = SWIG_TypeQuery("_p_char");
+    init = 1;
+  }
+  return info;
+}
+
+
+SWIGINTERN int
+SWIG_AsCharPtrAndSize(PyObject *obj, char** cptr, size_t* psize, int *alloc)
+{
+#if PY_VERSION_HEX>=0x03000000
+#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR)
+  if (PyBytes_Check(obj))
+#else
+  if (PyUnicode_Check(obj))
+#endif
+#else
+  if (PyString_Check(obj))
+#endif
+  {
+    char *cstr; Py_ssize_t len;
+#if PY_VERSION_HEX>=0x03000000
+#if !defined(SWIG_PYTHON_STRICT_BYTE_CHAR)
+    if (!alloc && cptr) {
+        /* We can't allow converting without allocation, since the internal
+           representation of string in Python 3 is UCS-2/UCS-4 but we require
+           a UTF-8 representation.
+           TODO(bhy) More detailed explanation */
+        return SWIG_RuntimeError;
+    }
+    obj = PyUnicode_AsUTF8String(obj);
+    if(alloc) *alloc = SWIG_NEWOBJ;
+#endif
+    PyBytes_AsStringAndSize(obj, &cstr, &len);
+#else
+    PyString_AsStringAndSize(obj, &cstr, &len);
+#endif
+    if (cptr) {
+      if (alloc) {
+        /*
+           In python the user should not be able to modify the inner
+           string representation. To warranty that, if you define
+           SWIG_PYTHON_SAFE_CSTRINGS, a new/copy of the python string
+           buffer is always returned.
+
+           The default behavior is just to return the pointer value,
+           so, be careful.
+        */
+#if defined(SWIG_PYTHON_SAFE_CSTRINGS)
+        if (*alloc != SWIG_OLDOBJ)
+#else
+        if (*alloc == SWIG_NEWOBJ)
+#endif
+        {
+          *cptr = (char *)memcpy(malloc((len + 1)*sizeof(char)), cstr, sizeof(char)*(len + 1));
+          *alloc = SWIG_NEWOBJ;
+        } else {
+          *cptr = cstr;
+          *alloc = SWIG_OLDOBJ;
+        }
+      } else {
+#if PY_VERSION_HEX>=0x03000000
+#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR)
+        *cptr = PyBytes_AsString(obj);
+#else
+        assert(0); /* Should never reach here with Unicode strings in Python 3 */
+#endif
+#else
+        *cptr = SWIG_Python_str_AsChar(obj);
+#endif
+      }
+    }
+    if (psize) *psize = len + 1;
+#if PY_VERSION_HEX>=0x03000000 && !defined(SWIG_PYTHON_STRICT_BYTE_CHAR)
+    Py_XDECREF(obj);
+#endif
+    return SWIG_OK;
+  } else {
+#if defined(SWIG_PYTHON_2_UNICODE)
+#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR)
+#error "Cannot use both SWIG_PYTHON_2_UNICODE and SWIG_PYTHON_STRICT_BYTE_CHAR at once"
+#endif
+#if PY_VERSION_HEX<0x03000000
+    if (PyUnicode_Check(obj)) {
+      char *cstr; Py_ssize_t len;
+      if (!alloc && cptr) {
+        return SWIG_RuntimeError;
+      }
+      obj = PyUnicode_AsUTF8String(obj);
+      if (PyString_AsStringAndSize(obj, &cstr, &len) != -1) {
+        if (cptr) {
+          if (alloc) *alloc = SWIG_NEWOBJ;
+          *cptr = (char *)memcpy(malloc((len + 1)*sizeof(char)), cstr, sizeof(char)*(len + 1));
+        }
+        if (psize) *psize = len + 1;
+
+        Py_XDECREF(obj);
+        return SWIG_OK;
+      } else {
+        Py_XDECREF(obj);
+      }
+    }
+#endif
+#endif
+
+    swig_type_info* pchar_descriptor = SWIG_pchar_descriptor();
+    if (pchar_descriptor) {
+      void* vptr = 0;
+      if (SWIG_ConvertPtr(obj, &vptr, pchar_descriptor, 0) == SWIG_OK) {
+        if (cptr) *cptr = (char *) vptr;
+        if (psize) *psize = vptr ? (strlen((char *)vptr) + 1) : 0;
+        if (alloc) *alloc = SWIG_OLDOBJ;
+        return SWIG_OK;
+      }
+    }
+  }
+  return SWIG_TypeError;
+}
+
+
+SWIGINTERN int
+SWIG_AsVal_double (PyObject *obj, double *val)
+{
+  int res = SWIG_TypeError;
+  if (PyFloat_Check(obj)) {
+    if (val) *val = PyFloat_AsDouble(obj);
+    return SWIG_OK;
+#if PY_VERSION_HEX < 0x03000000
+  } else if (PyInt_Check(obj)) {
+    if (val) *val = (double) PyInt_AsLong(obj);
+    return SWIG_OK;
+#endif
+  } else if (PyLong_Check(obj)) {
+    double v = PyLong_AsDouble(obj);
+    if (!PyErr_Occurred()) {
+      if (val) *val = v;
+      return SWIG_OK;
+    } else {
+      PyErr_Clear();
+    }
+  }
+#ifdef SWIG_PYTHON_CAST_MODE
+  {
+    int dispatch = 0;
+    double d = PyFloat_AsDouble(obj);
+    if (!PyErr_Occurred()) {
+      if (val) *val = d;
+      return SWIG_AddCast(SWIG_OK);
+    } else {
+      PyErr_Clear();
+    }
+    if (!dispatch) {
+      long v = PyLong_AsLong(obj);
+      if (!PyErr_Occurred()) {
+        if (val) *val = v;
+        return SWIG_AddCast(SWIG_AddCast(SWIG_OK));
+      } else {
+        PyErr_Clear();
+      }
+    }
+  }
+#endif
+  return res;
+}
+
+
+#include <float.h>
+
+
+#include <math.h>
+
+
+SWIGINTERNINLINE int
+SWIG_CanCastAsInteger(double *d, double min, double max) {
+  double x = *d;
+  if ((min <= x && x <= max)) {
+   double fx = floor(x);
+   double cx = ceil(x);
+   double rd =  ((x - fx) < 0.5) ? fx : cx; /* simple rint */
+   if ((errno == EDOM) || (errno == ERANGE)) {
+     errno = 0;
+   } else {
+     double summ, reps, diff;
+     if (rd < x) {
+       diff = x - rd;
+     } else if (rd > x) {
+       diff = rd - x;
+     } else {
+       return 1;
+     }
+     summ = rd + x;
+     reps = diff/summ;
+     if (reps < 8*DBL_EPSILON) {
+       *d = rd;
+       return 1;
+     }
+   }
+  }
+  return 0;
+}
+
+
+SWIGINTERN int
+SWIG_AsVal_unsigned_SS_long (PyObject *obj, unsigned long *val)
+{
+#if PY_VERSION_HEX < 0x03000000
+  if (PyInt_Check(obj)) {
+    long v = PyInt_AsLong(obj);
+    if (v >= 0) {
+      if (val) *val = v;
+      return SWIG_OK;
+    } else {
+      return SWIG_OverflowError;
+    }
+  } else
+#endif
+  if (PyLong_Check(obj)) {
+    unsigned long v = PyLong_AsUnsignedLong(obj);
+    if (!PyErr_Occurred()) {
+      if (val) *val = v;
+      return SWIG_OK;
+    } else {
+      PyErr_Clear();
+      return SWIG_OverflowError;
+    }
+  }
+#ifdef SWIG_PYTHON_CAST_MODE
+  {
+    int dispatch = 0;
+    unsigned long v = PyLong_AsUnsignedLong(obj);
+    if (!PyErr_Occurred()) {
+      if (val) *val = v;
+      return SWIG_AddCast(SWIG_OK);
+    } else {
+      PyErr_Clear();
+    }
+    if (!dispatch) {
+      double d;
+      int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d));
+      if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, ULONG_MAX)) {
+        if (val) *val = (unsigned long)(d);
+        return res;
+      }
+    }
+  }
+#endif
+  return SWIG_TypeError;
+}
+
+
+#include <limits.h>
+#if !defined(SWIG_NO_LLONG_MAX)
+# if !defined(LLONG_MAX) && defined(__GNUC__) && defined (__LONG_LONG_MAX__)
+#   define LLONG_MAX __LONG_LONG_MAX__
+#   define LLONG_MIN (-LLONG_MAX - 1LL)
+#   define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL)
+# endif
+#endif
+
+
+#if defined(LLONG_MAX) && !defined(SWIG_LONG_LONG_AVAILABLE)
+#  define SWIG_LONG_LONG_AVAILABLE
+#endif
+
+
+#ifdef SWIG_LONG_LONG_AVAILABLE
+SWIGINTERN int
+SWIG_AsVal_unsigned_SS_long_SS_long (PyObject *obj, unsigned long long *val)
+{
+  int res = SWIG_TypeError;
+  if (PyLong_Check(obj)) {
+    unsigned long long v = PyLong_AsUnsignedLongLong(obj);
+    if (!PyErr_Occurred()) {
+      if (val) *val = v;
+      return SWIG_OK;
+    } else {
+      PyErr_Clear();
+      res = SWIG_OverflowError;
+    }
+  } else {
+    unsigned long v;
+    res = SWIG_AsVal_unsigned_SS_long (obj,&v);
+    if (SWIG_IsOK(res)) {
+      if (val) *val = v;
+      return res;
+    }
+  }
+#ifdef SWIG_PYTHON_CAST_MODE
+  {
+    const double mant_max = 1LL << DBL_MANT_DIG;
+    double d;
+    res = SWIG_AsVal_double (obj,&d);
+    if (SWIG_IsOK(res) && !SWIG_CanCastAsInteger(&d, 0, mant_max))
+      return SWIG_OverflowError;
+    if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, mant_max)) {
+      if (val) *val = (unsigned long long)(d);
+      return SWIG_AddCast(res);
+    }
+    res = SWIG_TypeError;
+  }
+#endif
+  return res;
+}
+#endif
+
+
+SWIGINTERNINLINE int
+SWIG_AsVal_size_t (PyObject * obj, size_t *val)
+{
+  int res = SWIG_TypeError;
+#ifdef SWIG_LONG_LONG_AVAILABLE
+  if (sizeof(size_t) <= sizeof(unsigned long)) {
+#endif
+    unsigned long v;
+    res = SWIG_AsVal_unsigned_SS_long (obj, val ? &v : 0);
+    if (SWIG_IsOK(res) && val) *val = (size_t)(v);
+#ifdef SWIG_LONG_LONG_AVAILABLE
+  } else if (sizeof(size_t) <= sizeof(unsigned long long)) {
+    unsigned long long v;
+    res = SWIG_AsVal_unsigned_SS_long_SS_long (obj, val ? &v : 0);
+    if (SWIG_IsOK(res) && val) *val = (size_t)(v);
+  }
+#endif
+  return res;
+}
+
+
+#include "webp/decode.h"
+#include "webp/encode.h"
+
+
+static size_t ReturnedBufferSize(
+    const char* function, int* width, int* height) {
+  static const struct sizemap {
+    const char* function;
+    int size_multiplier;
+  } size_map[] = {
+#ifdef SWIGJAVA
+    { "Java_com_google_webp_libwebpJNI_WebPDecodeRGB",  3 },
+    { "Java_com_google_webp_libwebpJNI_WebPDecodeRGBA", 4 },
+    { "Java_com_google_webp_libwebpJNI_WebPDecodeARGB", 4 },
+    { "Java_com_google_webp_libwebpJNI_WebPDecodeBGR",  3 },
+    { "Java_com_google_webp_libwebpJNI_WebPDecodeBGRA", 4 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGB",  1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGR",  1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeRGBA", 1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeBGRA", 1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGB",  1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGR",  1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessRGBA", 1 },
+    { "Java_com_google_webp_libwebpJNI_wrap_1WebPEncodeLosslessBGRA", 1 },
+#endif
+#ifdef SWIGPYTHON
+    { "WebPDecodeRGB",  3 },
+    { "WebPDecodeRGBA", 4 },
+    { "WebPDecodeARGB", 4 },
+    { "WebPDecodeBGR",  3 },
+    { "WebPDecodeBGRA", 4 },
+    { "wrap_WebPEncodeRGB",  1 },
+    { "wrap_WebPEncodeBGR",  1 },
+    { "wrap_WebPEncodeRGBA", 1 },
+    { "wrap_WebPEncodeBGRA", 1 },
+    { "wrap_WebPEncodeLosslessRGB",  1 },
+    { "wrap_WebPEncodeLosslessBGR",  1 },
+    { "wrap_WebPEncodeLosslessRGBA", 1 },
+    { "wrap_WebPEncodeLosslessBGRA", 1 },
+#endif
+    { NULL, 0 }
+  };
+  const struct sizemap* p;
+  size_t size = 0;
+
+  for (p = size_map; p->function; ++p) {
+    if (!strcmp(function, p->function)) {
+      size = *width * *height * p->size_multiplier;
+      break;
+    }
+  }
+
+  return size;
+}
+
+
+typedef size_t (*WebPEncodeFunction)(const uint8_t* rgb,
+                                     int width, int height, int stride,
+                                     float quality_factor, uint8_t** output);
+typedef size_t (*WebPEncodeLosslessFunction)(const uint8_t* rgb,
+                                             int width, int height, int stride,
+                                             uint8_t** output);
+
+static uint8_t* EncodeLossy(const uint8_t* rgb,
+                            int width, int height, int stride,
+                            float quality_factor,
+                            WebPEncodeFunction encfn,
+                            int* output_size, int* unused) {
+  uint8_t* output = NULL;
+  const size_t image_size =
+      encfn(rgb, width, height, stride, quality_factor, &output);
+  // the values of following two will be interpreted by ReturnedBufferSize()
+  // as 'width' and 'height' in the size calculation.
+  *output_size = image_size;
+  *unused = 1;
+  return image_size ? output : NULL;
+}
+
+static uint8_t* EncodeLossless(const uint8_t* rgb,
+                               int width, int height, int stride,
+                               WebPEncodeLosslessFunction encfn,
+                               int* output_size, int* unused) {
+  uint8_t* output = NULL;
+  const size_t image_size = encfn(rgb, width, height, stride, &output);
+  // the values of the following two will be interpreted by
+  // ReturnedBufferSize() as 'width' and 'height' in the size calculation.
+  *output_size = image_size;
+  *unused = 1;
+  return image_size ? output : NULL;
+}
+
+
+// Changes the return type of WebPEncode* to more closely match Decode*.
+// This also makes it easier to wrap the output buffer in a native type rather
+// than dealing with the return pointer.
+// The additional parameters are to allow reuse of ReturnedBufferSize(),
+// unused2 and output_size will be used in this case.
+#define LOSSY_WRAPPER(FUNC)                                             \
+  static uint8_t* wrap_##FUNC(                                          \
+      const uint8_t* rgb, int* unused1, int* unused2, int* output_size, \
+      int width, int height, int stride, float quality_factor) {        \
+    return EncodeLossy(rgb, width, height, stride, quality_factor,      \
+                       FUNC, output_size, unused2);                     \
+  }                                                                     \
+
+LOSSY_WRAPPER(WebPEncodeRGB)
+LOSSY_WRAPPER(WebPEncodeBGR)
+LOSSY_WRAPPER(WebPEncodeRGBA)
+LOSSY_WRAPPER(WebPEncodeBGRA)
+
+#undef LOSSY_WRAPPER
+
+#define LOSSLESS_WRAPPER(FUNC)                                          \
+  static uint8_t* wrap_##FUNC(                                          \
+      const uint8_t* rgb, int* unused1, int* unused2, int* output_size, \
+      int width, int height, int stride) {                              \
+    return EncodeLossless(rgb, width, height, stride,                   \
+                          FUNC, output_size, unused2);                  \
+  }                                                                     \
+
+LOSSLESS_WRAPPER(WebPEncodeLosslessRGB)
+LOSSLESS_WRAPPER(WebPEncodeLosslessBGR)
+LOSSLESS_WRAPPER(WebPEncodeLosslessRGBA)
+LOSSLESS_WRAPPER(WebPEncodeLosslessBGRA)
+
+#undef LOSSLESS_WRAPPER
+
+
+
+SWIGINTERN int
+SWIG_AsVal_long (PyObject *obj, long* val)
+{
+#if PY_VERSION_HEX < 0x03000000
+  if (PyInt_Check(obj)) {
+    if (val) *val = PyInt_AsLong(obj);
+    return SWIG_OK;
+  } else
+#endif
+  if (PyLong_Check(obj)) {
+    long v = PyLong_AsLong(obj);
+    if (!PyErr_Occurred()) {
+      if (val) *val = v;
+      return SWIG_OK;
+    } else {
+      PyErr_Clear();
+      return SWIG_OverflowError;
+    }
+  }
+#ifdef SWIG_PYTHON_CAST_MODE
+  {
+    int dispatch = 0;
+    long v = PyInt_AsLong(obj);
+    if (!PyErr_Occurred()) {
+      if (val) *val = v;
+      return SWIG_AddCast(SWIG_OK);
+    } else {
+      PyErr_Clear();
+    }
+    if (!dispatch) {
+      double d;
+      int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d));
+      if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, LONG_MAX)) {
+        if (val) *val = (long)(d);
+        return res;
+      }
+    }
+  }
+#endif
+  return SWIG_TypeError;
+}
+
+
+SWIGINTERN int
+SWIG_AsVal_int (PyObject * obj, int *val)
+{
+  long v;
+  int res = SWIG_AsVal_long (obj, &v);
+  if (SWIG_IsOK(res)) {
+    if ((v < INT_MIN || v > INT_MAX)) {
+      return SWIG_OverflowError;
+    } else {
+      if (val) *val = (int)(v);
+    }
+  }
+  return res;
+}
+
+
+/* Getting isfinite working pre C99 across multiple platforms is non-trivial. Users can provide SWIG_isfinite on older platforms. */
+#ifndef SWIG_isfinite
+/* isfinite() is a macro for C99 */
+# if defined(isfinite)
+#  define SWIG_isfinite(X) (isfinite(X))
+# elif defined __cplusplus && __cplusplus >= 201103L
+/* Use a template so that this works whether isfinite() is std::isfinite() or
+ * in the global namespace.  The reality seems to vary between compiler
+ * versions.
+ *
+ * Make sure namespace std exists to avoid compiler warnings.
+ *
+ * extern "C++" is required as this fragment can end up inside an extern "C" { } block
+ */
+namespace std { }
+extern "C++" template<typename T>
+inline int SWIG_isfinite_func(T x) {
+  using namespace std;
+  return isfinite(x);
+}
+#  define SWIG_isfinite(X) (SWIG_isfinite_func(X))
+# elif defined(_MSC_VER)
+#  define SWIG_isfinite(X) (_finite(X))
+# elif defined(__sun) && defined(__SVR4)
+#  include <ieeefp.h>
+#  define SWIG_isfinite(X) (finite(X))
+# endif
+#endif
+
+
+/* Accept infinite as a valid float value unless we are unable to check if a value is finite */
+#ifdef SWIG_isfinite
+# define SWIG_Float_Overflow_Check(X) ((X < -FLT_MAX || X > FLT_MAX) && SWIG_isfinite(X))
+#else
+# define SWIG_Float_Overflow_Check(X) ((X < -FLT_MAX || X > FLT_MAX))
+#endif
+
+
+SWIGINTERN int
+SWIG_AsVal_float (PyObject * obj, float *val)
+{
+  double v;
+  int res = SWIG_AsVal_double (obj, &v);
+  if (SWIG_IsOK(res)) {
+    if (SWIG_Float_Overflow_Check(v)) {
+      return SWIG_OverflowError;
+    } else {
+      if (val) *val = (float)(v);
+    }
+  }
+  return res;
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+SWIGINTERN PyObject *_wrap_WebPGetDecoderVersion(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  int result;
+
+  if (!PyArg_ParseTuple(args,(char *)":WebPGetDecoderVersion")) SWIG_fail;
+  result = (int)WebPGetDecoderVersion();
+  resultobj = SWIG_From_int((int)(result));
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_WebPGetInfo(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  size_t arg2 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int res1 ;
+  char *buf1 = 0 ;
+  size_t size1 = 0 ;
+  int alloc1 = 0 ;
+  int temp3 ;
+  int res3 = SWIG_TMPOBJ ;
+  int temp4 ;
+  int res4 = SWIG_TMPOBJ ;
+  PyObject * obj0 = 0 ;
+  int result;
+
+  arg3 = &temp3;
+  arg4 = &temp4;
+  if (!PyArg_ParseTuple(args,(char *)"O:WebPGetInfo",&obj0)) SWIG_fail;
+  res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, &size1, &alloc1);
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "WebPGetInfo" "', argument " "1"" of type '" "uint8_t const *""'");
+  }
+  arg1 = (uint8_t *)(buf1);
+  arg2 = (size_t)(size1 - 1);
+  result = (int)WebPGetInfo((uint8_t const *)arg1,arg2,arg3,arg4);
+  resultobj = SWIG_From_int((int)(result));
+  if (SWIG_IsTmpObj(res3)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg3)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags));
+  }
+  if (SWIG_IsTmpObj(res4)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+  }
+  if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+  return resultobj;
+fail:
+  if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_WebPDecodeRGB(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  size_t arg2 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int res1 ;
+  char *buf1 = 0 ;
+  size_t size1 = 0 ;
+  int alloc1 = 0 ;
+  int temp3 ;
+  int res3 = SWIG_TMPOBJ ;
+  int temp4 ;
+  int res4 = SWIG_TMPOBJ ;
+  PyObject * obj0 = 0 ;
+  uint8_t *result = 0 ;
+
+  arg3 = &temp3;
+  arg4 = &temp4;
+  if (!PyArg_ParseTuple(args,(char *)"O:WebPDecodeRGB",&obj0)) SWIG_fail;
+  res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, &size1, &alloc1);
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "WebPDecodeRGB" "', argument " "1"" of type '" "uint8_t const *""'");
+  }
+  arg1 = (uint8_t *)(buf1);
+  arg2 = (size_t)(size1 - 1);
+  result = (uint8_t *)WebPDecodeRGB((uint8_t const *)arg1,arg2,arg3,arg4);
+  {
+    resultobj = PyString_FromStringAndSize(
+      (const char*)result,
+      (result == NULL) ? 0 : ReturnedBufferSize("WebPDecodeRGB", arg3, arg4));
+  }
+  if (SWIG_IsTmpObj(res3)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg3)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags));
+  }
+  if (SWIG_IsTmpObj(res4)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+  }
+  if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+  free(result);
+  return resultobj;
+fail:
+  if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_WebPDecodeRGBA(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  size_t arg2 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int res1 ;
+  char *buf1 = 0 ;
+  size_t size1 = 0 ;
+  int alloc1 = 0 ;
+  int temp3 ;
+  int res3 = SWIG_TMPOBJ ;
+  int temp4 ;
+  int res4 = SWIG_TMPOBJ ;
+  PyObject * obj0 = 0 ;
+  uint8_t *result = 0 ;
+
+  arg3 = &temp3;
+  arg4 = &temp4;
+  if (!PyArg_ParseTuple(args,(char *)"O:WebPDecodeRGBA",&obj0)) SWIG_fail;
+  res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, &size1, &alloc1);
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "WebPDecodeRGBA" "', argument " "1"" of type '" "uint8_t const *""'");
+  }
+  arg1 = (uint8_t *)(buf1);
+  arg2 = (size_t)(size1 - 1);
+  result = (uint8_t *)WebPDecodeRGBA((uint8_t const *)arg1,arg2,arg3,arg4);
+  {
+    resultobj = PyString_FromStringAndSize(
+      (const char*)result,
+      (result == NULL) ? 0 : ReturnedBufferSize("WebPDecodeRGBA", arg3, arg4));
+  }
+  if (SWIG_IsTmpObj(res3)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg3)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags));
+  }
+  if (SWIG_IsTmpObj(res4)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+  }
+  if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+  free(result);
+  return resultobj;
+fail:
+  if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_WebPDecodeARGB(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  size_t arg2 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int res1 ;
+  char *buf1 = 0 ;
+  size_t size1 = 0 ;
+  int alloc1 = 0 ;
+  int temp3 ;
+  int res3 = SWIG_TMPOBJ ;
+  int temp4 ;
+  int res4 = SWIG_TMPOBJ ;
+  PyObject * obj0 = 0 ;
+  uint8_t *result = 0 ;
+
+  arg3 = &temp3;
+  arg4 = &temp4;
+  if (!PyArg_ParseTuple(args,(char *)"O:WebPDecodeARGB",&obj0)) SWIG_fail;
+  res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, &size1, &alloc1);
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "WebPDecodeARGB" "', argument " "1"" of type '" "uint8_t const *""'");
+  }
+  arg1 = (uint8_t *)(buf1);
+  arg2 = (size_t)(size1 - 1);
+  result = (uint8_t *)WebPDecodeARGB((uint8_t const *)arg1,arg2,arg3,arg4);
+  {
+    resultobj = PyString_FromStringAndSize(
+      (const char*)result,
+      (result == NULL) ? 0 : ReturnedBufferSize("WebPDecodeARGB", arg3, arg4));
+  }
+  if (SWIG_IsTmpObj(res3)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg3)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags));
+  }
+  if (SWIG_IsTmpObj(res4)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+  }
+  if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+  free(result);
+  return resultobj;
+fail:
+  if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_WebPDecodeBGR(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  size_t arg2 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int res1 ;
+  char *buf1 = 0 ;
+  size_t size1 = 0 ;
+  int alloc1 = 0 ;
+  int temp3 ;
+  int res3 = SWIG_TMPOBJ ;
+  int temp4 ;
+  int res4 = SWIG_TMPOBJ ;
+  PyObject * obj0 = 0 ;
+  uint8_t *result = 0 ;
+
+  arg3 = &temp3;
+  arg4 = &temp4;
+  if (!PyArg_ParseTuple(args,(char *)"O:WebPDecodeBGR",&obj0)) SWIG_fail;
+  res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, &size1, &alloc1);
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "WebPDecodeBGR" "', argument " "1"" of type '" "uint8_t const *""'");
+  }
+  arg1 = (uint8_t *)(buf1);
+  arg2 = (size_t)(size1 - 1);
+  result = (uint8_t *)WebPDecodeBGR((uint8_t const *)arg1,arg2,arg3,arg4);
+  {
+    resultobj = PyString_FromStringAndSize(
+      (const char*)result,
+      (result == NULL) ? 0 : ReturnedBufferSize("WebPDecodeBGR", arg3, arg4));
+  }
+  if (SWIG_IsTmpObj(res3)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg3)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags));
+  }
+  if (SWIG_IsTmpObj(res4)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+  }
+  if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+  free(result);
+  return resultobj;
+fail:
+  if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_WebPDecodeBGRA(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  size_t arg2 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int res1 ;
+  char *buf1 = 0 ;
+  size_t size1 = 0 ;
+  int alloc1 = 0 ;
+  int temp3 ;
+  int res3 = SWIG_TMPOBJ ;
+  int temp4 ;
+  int res4 = SWIG_TMPOBJ ;
+  PyObject * obj0 = 0 ;
+  uint8_t *result = 0 ;
+
+  arg3 = &temp3;
+  arg4 = &temp4;
+  if (!PyArg_ParseTuple(args,(char *)"O:WebPDecodeBGRA",&obj0)) SWIG_fail;
+  res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, &size1, &alloc1);
+  if (!SWIG_IsOK(res1)) {
+    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "WebPDecodeBGRA" "', argument " "1"" of type '" "uint8_t const *""'");
+  }
+  arg1 = (uint8_t *)(buf1);
+  arg2 = (size_t)(size1 - 1);
+  result = (uint8_t *)WebPDecodeBGRA((uint8_t const *)arg1,arg2,arg3,arg4);
+  {
+    resultobj = PyString_FromStringAndSize(
+      (const char*)result,
+      (result == NULL) ? 0 : ReturnedBufferSize("WebPDecodeBGRA", arg3, arg4));
+  }
+  if (SWIG_IsTmpObj(res3)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg3)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res3) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg3), SWIGTYPE_p_int, new_flags));
+  }
+  if (SWIG_IsTmpObj(res4)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+  }
+  if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+  free(result);
+  return resultobj;
+fail:
+  if (alloc1 == SWIG_NEWOBJ) free((char*)buf1);
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_WebPGetEncoderVersion(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  int result;
+
+  if (!PyArg_ParseTuple(args,(char *)":WebPGetEncoderVersion")) SWIG_fail;
+  result = (int)WebPGetEncoderVersion();
+  resultobj = SWIG_From_int((int)(result));
+  return resultobj;
+fail:
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_wrap_WebPEncodeRGB(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  int *arg2 = (int *) 0 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int arg5 ;
+  int arg6 ;
+  int arg7 ;
+  float arg8 ;
+  Py_buffer rgb_buffer1 ;
+  int temp2 ;
+  int res2 = 0 ;
+  int temp3 ;
+  int res3 = 0 ;
+  int temp4 ;
+  int res4 = SWIG_TMPOBJ ;
+  int val5 ;
+  int ecode5 = 0 ;
+  int val6 ;
+  int ecode6 = 0 ;
+  int val7 ;
+  int ecode7 = 0 ;
+  float val8 ;
+  int ecode8 = 0 ;
+  PyObject * obj0 = 0 ;
+  PyObject * obj1 = 0 ;
+  PyObject * obj2 = 0 ;
+  PyObject * obj3 = 0 ;
+  PyObject * obj4 = 0 ;
+  PyObject * obj5 = 0 ;
+  PyObject * obj6 = 0 ;
+  uint8_t *result = 0 ;
+
+  arg4 = &temp4;
+  if (!PyArg_ParseTuple(args,(char *)"OOOOOOO:wrap_WebPEncodeRGB",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5,&obj6)) SWIG_fail;
+  {
+    // NB: with Python < 2.6 the old style buffer protocol may be used:
+    // Py_ssize_t unused;
+    // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused);
+    if (!PyObject_CheckBuffer(obj0)) {
+      SWIG_exception_fail(SWIG_TypeError,
+        "in method 'wrap_WebPEncodeRGB', argument 1"
+        " does not support the buffer interface");
+    }
+    if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) {
+      SWIG_exception_fail(SWIG_RuntimeError,
+        "in method 'wrap_WebPEncodeRGB', unable to get buffer view");
+    }
+    arg1 = (uint8_t *)rgb_buffer1.buf;
+  }
+  if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) {
+    int val;
+    int ecode = SWIG_AsVal_int(obj1, &val);
+    if (!SWIG_IsOK(ecode)) {
+      SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeRGB" "', argument " "2"" of type '" "int""'");
+    }
+    temp2 = (int)(val);
+    arg2 = &temp2;
+    res2 = SWIG_AddTmpMask(ecode);
+  }
+  if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) {
+    int val;
+    int ecode = SWIG_AsVal_int(obj2, &val);
+    if (!SWIG_IsOK(ecode)) {
+      SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeRGB" "', argument " "3"" of type '" "int""'");
+    }
+    temp3 = (int)(val);
+    arg3 = &temp3;
+    res3 = SWIG_AddTmpMask(ecode);
+  }
+  ecode5 = SWIG_AsVal_int(obj3, &val5);
+  if (!SWIG_IsOK(ecode5)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeRGB" "', argument " "5"" of type '" "int""'");
+  }
+  arg5 = (int)(val5);
+  ecode6 = SWIG_AsVal_int(obj4, &val6);
+  if (!SWIG_IsOK(ecode6)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeRGB" "', argument " "6"" of type '" "int""'");
+  }
+  arg6 = (int)(val6);
+  ecode7 = SWIG_AsVal_int(obj5, &val7);
+  if (!SWIG_IsOK(ecode7)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeRGB" "', argument " "7"" of type '" "int""'");
+  }
+  arg7 = (int)(val7);
+  ecode8 = SWIG_AsVal_float(obj6, &val8);
+  if (!SWIG_IsOK(ecode8)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "wrap_WebPEncodeRGB" "', argument " "8"" of type '" "float""'");
+  }
+  arg8 = (float)(val8);
+  result = (uint8_t *)wrap_WebPEncodeRGB((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8);
+  {
+    resultobj = PyString_FromStringAndSize(
+      (const char*)result,
+      (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeRGB", arg3, arg4));
+  }
+  if (SWIG_IsTmpObj(res4)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+  }
+  {
+    PyBuffer_Release(&rgb_buffer1);
+  }
+  if (SWIG_IsNewObj(res2)) free((char*)arg2);
+  if (SWIG_IsNewObj(res3)) free((char*)arg3);
+  free(result);
+  return resultobj;
+fail:
+  {
+    PyBuffer_Release(&rgb_buffer1);
+  }
+  if (SWIG_IsNewObj(res2)) free((char*)arg2);
+  if (SWIG_IsNewObj(res3)) free((char*)arg3);
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_wrap_WebPEncodeBGR(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  int *arg2 = (int *) 0 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int arg5 ;
+  int arg6 ;
+  int arg7 ;
+  float arg8 ;
+  Py_buffer rgb_buffer1 ;
+  int temp2 ;
+  int res2 = 0 ;
+  int temp3 ;
+  int res3 = 0 ;
+  int temp4 ;
+  int res4 = SWIG_TMPOBJ ;
+  int val5 ;
+  int ecode5 = 0 ;
+  int val6 ;
+  int ecode6 = 0 ;
+  int val7 ;
+  int ecode7 = 0 ;
+  float val8 ;
+  int ecode8 = 0 ;
+  PyObject * obj0 = 0 ;
+  PyObject * obj1 = 0 ;
+  PyObject * obj2 = 0 ;
+  PyObject * obj3 = 0 ;
+  PyObject * obj4 = 0 ;
+  PyObject * obj5 = 0 ;
+  PyObject * obj6 = 0 ;
+  uint8_t *result = 0 ;
+
+  arg4 = &temp4;
+  if (!PyArg_ParseTuple(args,(char *)"OOOOOOO:wrap_WebPEncodeBGR",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5,&obj6)) SWIG_fail;
+  {
+    // NB: with Python < 2.6 the old style buffer protocol may be used:
+    // Py_ssize_t unused;
+    // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused);
+    if (!PyObject_CheckBuffer(obj0)) {
+      SWIG_exception_fail(SWIG_TypeError,
+        "in method 'wrap_WebPEncodeBGR', argument 1"
+        " does not support the buffer interface");
+    }
+    if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) {
+      SWIG_exception_fail(SWIG_RuntimeError,
+        "in method 'wrap_WebPEncodeBGR', unable to get buffer view");
+    }
+    arg1 = (uint8_t *)rgb_buffer1.buf;
+  }
+  if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) {
+    int val;
+    int ecode = SWIG_AsVal_int(obj1, &val);
+    if (!SWIG_IsOK(ecode)) {
+      SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeBGR" "', argument " "2"" of type '" "int""'");
+    }
+    temp2 = (int)(val);
+    arg2 = &temp2;
+    res2 = SWIG_AddTmpMask(ecode);
+  }
+  if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) {
+    int val;
+    int ecode = SWIG_AsVal_int(obj2, &val);
+    if (!SWIG_IsOK(ecode)) {
+      SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeBGR" "', argument " "3"" of type '" "int""'");
+    }
+    temp3 = (int)(val);
+    arg3 = &temp3;
+    res3 = SWIG_AddTmpMask(ecode);
+  }
+  ecode5 = SWIG_AsVal_int(obj3, &val5);
+  if (!SWIG_IsOK(ecode5)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeBGR" "', argument " "5"" of type '" "int""'");
+  }
+  arg5 = (int)(val5);
+  ecode6 = SWIG_AsVal_int(obj4, &val6);
+  if (!SWIG_IsOK(ecode6)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeBGR" "', argument " "6"" of type '" "int""'");
+  }
+  arg6 = (int)(val6);
+  ecode7 = SWIG_AsVal_int(obj5, &val7);
+  if (!SWIG_IsOK(ecode7)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeBGR" "', argument " "7"" of type '" "int""'");
+  }
+  arg7 = (int)(val7);
+  ecode8 = SWIG_AsVal_float(obj6, &val8);
+  if (!SWIG_IsOK(ecode8)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "wrap_WebPEncodeBGR" "', argument " "8"" of type '" "float""'");
+  }
+  arg8 = (float)(val8);
+  result = (uint8_t *)wrap_WebPEncodeBGR((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8);
+  {
+    resultobj = PyString_FromStringAndSize(
+      (const char*)result,
+      (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeBGR", arg3, arg4));
+  }
+  if (SWIG_IsTmpObj(res4)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+  }
+  {
+    PyBuffer_Release(&rgb_buffer1);
+  }
+  if (SWIG_IsNewObj(res2)) free((char*)arg2);
+  if (SWIG_IsNewObj(res3)) free((char*)arg3);
+  free(result);
+  return resultobj;
+fail:
+  {
+    PyBuffer_Release(&rgb_buffer1);
+  }
+  if (SWIG_IsNewObj(res2)) free((char*)arg2);
+  if (SWIG_IsNewObj(res3)) free((char*)arg3);
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_wrap_WebPEncodeRGBA(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  int *arg2 = (int *) 0 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int arg5 ;
+  int arg6 ;
+  int arg7 ;
+  float arg8 ;
+  Py_buffer rgb_buffer1 ;
+  int temp2 ;
+  int res2 = 0 ;
+  int temp3 ;
+  int res3 = 0 ;
+  int temp4 ;
+  int res4 = SWIG_TMPOBJ ;
+  int val5 ;
+  int ecode5 = 0 ;
+  int val6 ;
+  int ecode6 = 0 ;
+  int val7 ;
+  int ecode7 = 0 ;
+  float val8 ;
+  int ecode8 = 0 ;
+  PyObject * obj0 = 0 ;
+  PyObject * obj1 = 0 ;
+  PyObject * obj2 = 0 ;
+  PyObject * obj3 = 0 ;
+  PyObject * obj4 = 0 ;
+  PyObject * obj5 = 0 ;
+  PyObject * obj6 = 0 ;
+  uint8_t *result = 0 ;
+
+  arg4 = &temp4;
+  if (!PyArg_ParseTuple(args,(char *)"OOOOOOO:wrap_WebPEncodeRGBA",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5,&obj6)) SWIG_fail;
+  {
+    // NB: with Python < 2.6 the old style buffer protocol may be used:
+    // Py_ssize_t unused;
+    // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused);
+    if (!PyObject_CheckBuffer(obj0)) {
+      SWIG_exception_fail(SWIG_TypeError,
+        "in method 'wrap_WebPEncodeRGBA', argument 1"
+        " does not support the buffer interface");
+    }
+    if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) {
+      SWIG_exception_fail(SWIG_RuntimeError,
+        "in method 'wrap_WebPEncodeRGBA', unable to get buffer view");
+    }
+    arg1 = (uint8_t *)rgb_buffer1.buf;
+  }
+  if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) {
+    int val;
+    int ecode = SWIG_AsVal_int(obj1, &val);
+    if (!SWIG_IsOK(ecode)) {
+      SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeRGBA" "', argument " "2"" of type '" "int""'");
+    }
+    temp2 = (int)(val);
+    arg2 = &temp2;
+    res2 = SWIG_AddTmpMask(ecode);
+  }
+  if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) {
+    int val;
+    int ecode = SWIG_AsVal_int(obj2, &val);
+    if (!SWIG_IsOK(ecode)) {
+      SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeRGBA" "', argument " "3"" of type '" "int""'");
+    }
+    temp3 = (int)(val);
+    arg3 = &temp3;
+    res3 = SWIG_AddTmpMask(ecode);
+  }
+  ecode5 = SWIG_AsVal_int(obj3, &val5);
+  if (!SWIG_IsOK(ecode5)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeRGBA" "', argument " "5"" of type '" "int""'");
+  }
+  arg5 = (int)(val5);
+  ecode6 = SWIG_AsVal_int(obj4, &val6);
+  if (!SWIG_IsOK(ecode6)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeRGBA" "', argument " "6"" of type '" "int""'");
+  }
+  arg6 = (int)(val6);
+  ecode7 = SWIG_AsVal_int(obj5, &val7);
+  if (!SWIG_IsOK(ecode7)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeRGBA" "', argument " "7"" of type '" "int""'");
+  }
+  arg7 = (int)(val7);
+  ecode8 = SWIG_AsVal_float(obj6, &val8);
+  if (!SWIG_IsOK(ecode8)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "wrap_WebPEncodeRGBA" "', argument " "8"" of type '" "float""'");
+  }
+  arg8 = (float)(val8);
+  result = (uint8_t *)wrap_WebPEncodeRGBA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8);
+  {
+    resultobj = PyString_FromStringAndSize(
+      (const char*)result,
+      (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeRGBA", arg3, arg4));
+  }
+  if (SWIG_IsTmpObj(res4)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+  }
+  {
+    PyBuffer_Release(&rgb_buffer1);
+  }
+  if (SWIG_IsNewObj(res2)) free((char*)arg2);
+  if (SWIG_IsNewObj(res3)) free((char*)arg3);
+  free(result);
+  return resultobj;
+fail:
+  {
+    PyBuffer_Release(&rgb_buffer1);
+  }
+  if (SWIG_IsNewObj(res2)) free((char*)arg2);
+  if (SWIG_IsNewObj(res3)) free((char*)arg3);
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_wrap_WebPEncodeBGRA(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  int *arg2 = (int *) 0 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int arg5 ;
+  int arg6 ;
+  int arg7 ;
+  float arg8 ;
+  Py_buffer rgb_buffer1 ;
+  int temp2 ;
+  int res2 = 0 ;
+  int temp3 ;
+  int res3 = 0 ;
+  int temp4 ;
+  int res4 = SWIG_TMPOBJ ;
+  int val5 ;
+  int ecode5 = 0 ;
+  int val6 ;
+  int ecode6 = 0 ;
+  int val7 ;
+  int ecode7 = 0 ;
+  float val8 ;
+  int ecode8 = 0 ;
+  PyObject * obj0 = 0 ;
+  PyObject * obj1 = 0 ;
+  PyObject * obj2 = 0 ;
+  PyObject * obj3 = 0 ;
+  PyObject * obj4 = 0 ;
+  PyObject * obj5 = 0 ;
+  PyObject * obj6 = 0 ;
+  uint8_t *result = 0 ;
+
+  arg4 = &temp4;
+  if (!PyArg_ParseTuple(args,(char *)"OOOOOOO:wrap_WebPEncodeBGRA",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5,&obj6)) SWIG_fail;
+  {
+    // NB: with Python < 2.6 the old style buffer protocol may be used:
+    // Py_ssize_t unused;
+    // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused);
+    if (!PyObject_CheckBuffer(obj0)) {
+      SWIG_exception_fail(SWIG_TypeError,
+        "in method 'wrap_WebPEncodeBGRA', argument 1"
+        " does not support the buffer interface");
+    }
+    if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) {
+      SWIG_exception_fail(SWIG_RuntimeError,
+        "in method 'wrap_WebPEncodeBGRA', unable to get buffer view");
+    }
+    arg1 = (uint8_t *)rgb_buffer1.buf;
+  }
+  if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) {
+    int val;
+    int ecode = SWIG_AsVal_int(obj1, &val);
+    if (!SWIG_IsOK(ecode)) {
+      SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeBGRA" "', argument " "2"" of type '" "int""'");
+    }
+    temp2 = (int)(val);
+    arg2 = &temp2;
+    res2 = SWIG_AddTmpMask(ecode);
+  }
+  if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) {
+    int val;
+    int ecode = SWIG_AsVal_int(obj2, &val);
+    if (!SWIG_IsOK(ecode)) {
+      SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeBGRA" "', argument " "3"" of type '" "int""'");
+    }
+    temp3 = (int)(val);
+    arg3 = &temp3;
+    res3 = SWIG_AddTmpMask(ecode);
+  }
+  ecode5 = SWIG_AsVal_int(obj3, &val5);
+  if (!SWIG_IsOK(ecode5)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeBGRA" "', argument " "5"" of type '" "int""'");
+  }
+  arg5 = (int)(val5);
+  ecode6 = SWIG_AsVal_int(obj4, &val6);
+  if (!SWIG_IsOK(ecode6)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeBGRA" "', argument " "6"" of type '" "int""'");
+  }
+  arg6 = (int)(val6);
+  ecode7 = SWIG_AsVal_int(obj5, &val7);
+  if (!SWIG_IsOK(ecode7)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeBGRA" "', argument " "7"" of type '" "int""'");
+  }
+  arg7 = (int)(val7);
+  ecode8 = SWIG_AsVal_float(obj6, &val8);
+  if (!SWIG_IsOK(ecode8)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "wrap_WebPEncodeBGRA" "', argument " "8"" of type '" "float""'");
+  }
+  arg8 = (float)(val8);
+  result = (uint8_t *)wrap_WebPEncodeBGRA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8);
+  {
+    resultobj = PyString_FromStringAndSize(
+      (const char*)result,
+      (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeBGRA", arg3, arg4));
+  }
+  if (SWIG_IsTmpObj(res4)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+  }
+  {
+    PyBuffer_Release(&rgb_buffer1);
+  }
+  if (SWIG_IsNewObj(res2)) free((char*)arg2);
+  if (SWIG_IsNewObj(res3)) free((char*)arg3);
+  free(result);
+  return resultobj;
+fail:
+  {
+    PyBuffer_Release(&rgb_buffer1);
+  }
+  if (SWIG_IsNewObj(res2)) free((char*)arg2);
+  if (SWIG_IsNewObj(res3)) free((char*)arg3);
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_wrap_WebPEncodeLosslessRGB(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  int *arg2 = (int *) 0 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int arg5 ;
+  int arg6 ;
+  int arg7 ;
+  Py_buffer rgb_buffer1 ;
+  int temp2 ;
+  int res2 = 0 ;
+  int temp3 ;
+  int res3 = 0 ;
+  int temp4 ;
+  int res4 = SWIG_TMPOBJ ;
+  int val5 ;
+  int ecode5 = 0 ;
+  int val6 ;
+  int ecode6 = 0 ;
+  int val7 ;
+  int ecode7 = 0 ;
+  PyObject * obj0 = 0 ;
+  PyObject * obj1 = 0 ;
+  PyObject * obj2 = 0 ;
+  PyObject * obj3 = 0 ;
+  PyObject * obj4 = 0 ;
+  PyObject * obj5 = 0 ;
+  uint8_t *result = 0 ;
+
+  arg4 = &temp4;
+  if (!PyArg_ParseTuple(args,(char *)"OOOOOO:wrap_WebPEncodeLosslessRGB",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5)) SWIG_fail;
+  {
+    // NB: with Python < 2.6 the old style buffer protocol may be used:
+    // Py_ssize_t unused;
+    // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused);
+    if (!PyObject_CheckBuffer(obj0)) {
+      SWIG_exception_fail(SWIG_TypeError,
+        "in method 'wrap_WebPEncodeLosslessRGB', argument 1"
+        " does not support the buffer interface");
+    }
+    if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) {
+      SWIG_exception_fail(SWIG_RuntimeError,
+        "in method 'wrap_WebPEncodeLosslessRGB', unable to get buffer view");
+    }
+    arg1 = (uint8_t *)rgb_buffer1.buf;
+  }
+  if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) {
+    int val;
+    int ecode = SWIG_AsVal_int(obj1, &val);
+    if (!SWIG_IsOK(ecode)) {
+      SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessRGB" "', argument " "2"" of type '" "int""'");
+    }
+    temp2 = (int)(val);
+    arg2 = &temp2;
+    res2 = SWIG_AddTmpMask(ecode);
+  }
+  if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) {
+    int val;
+    int ecode = SWIG_AsVal_int(obj2, &val);
+    if (!SWIG_IsOK(ecode)) {
+      SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessRGB" "', argument " "3"" of type '" "int""'");
+    }
+    temp3 = (int)(val);
+    arg3 = &temp3;
+    res3 = SWIG_AddTmpMask(ecode);
+  }
+  ecode5 = SWIG_AsVal_int(obj3, &val5);
+  if (!SWIG_IsOK(ecode5)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeLosslessRGB" "', argument " "5"" of type '" "int""'");
+  }
+  arg5 = (int)(val5);
+  ecode6 = SWIG_AsVal_int(obj4, &val6);
+  if (!SWIG_IsOK(ecode6)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeLosslessRGB" "', argument " "6"" of type '" "int""'");
+  }
+  arg6 = (int)(val6);
+  ecode7 = SWIG_AsVal_int(obj5, &val7);
+  if (!SWIG_IsOK(ecode7)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeLosslessRGB" "', argument " "7"" of type '" "int""'");
+  }
+  arg7 = (int)(val7);
+  result = (uint8_t *)wrap_WebPEncodeLosslessRGB((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+  {
+    resultobj = PyString_FromStringAndSize(
+      (const char*)result,
+      (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeLosslessRGB", arg3, arg4));
+  }
+  if (SWIG_IsTmpObj(res4)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+  }
+  {
+    PyBuffer_Release(&rgb_buffer1);
+  }
+  if (SWIG_IsNewObj(res2)) free((char*)arg2);
+  if (SWIG_IsNewObj(res3)) free((char*)arg3);
+  free(result);
+  return resultobj;
+fail:
+  {
+    PyBuffer_Release(&rgb_buffer1);
+  }
+  if (SWIG_IsNewObj(res2)) free((char*)arg2);
+  if (SWIG_IsNewObj(res3)) free((char*)arg3);
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_wrap_WebPEncodeLosslessBGR(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  int *arg2 = (int *) 0 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int arg5 ;
+  int arg6 ;
+  int arg7 ;
+  Py_buffer rgb_buffer1 ;
+  int temp2 ;
+  int res2 = 0 ;
+  int temp3 ;
+  int res3 = 0 ;
+  int temp4 ;
+  int res4 = SWIG_TMPOBJ ;
+  int val5 ;
+  int ecode5 = 0 ;
+  int val6 ;
+  int ecode6 = 0 ;
+  int val7 ;
+  int ecode7 = 0 ;
+  PyObject * obj0 = 0 ;
+  PyObject * obj1 = 0 ;
+  PyObject * obj2 = 0 ;
+  PyObject * obj3 = 0 ;
+  PyObject * obj4 = 0 ;
+  PyObject * obj5 = 0 ;
+  uint8_t *result = 0 ;
+
+  arg4 = &temp4;
+  if (!PyArg_ParseTuple(args,(char *)"OOOOOO:wrap_WebPEncodeLosslessBGR",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5)) SWIG_fail;
+  {
+    // NB: with Python < 2.6 the old style buffer protocol may be used:
+    // Py_ssize_t unused;
+    // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused);
+    if (!PyObject_CheckBuffer(obj0)) {
+      SWIG_exception_fail(SWIG_TypeError,
+        "in method 'wrap_WebPEncodeLosslessBGR', argument 1"
+        " does not support the buffer interface");
+    }
+    if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) {
+      SWIG_exception_fail(SWIG_RuntimeError,
+        "in method 'wrap_WebPEncodeLosslessBGR', unable to get buffer view");
+    }
+    arg1 = (uint8_t *)rgb_buffer1.buf;
+  }
+  if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) {
+    int val;
+    int ecode = SWIG_AsVal_int(obj1, &val);
+    if (!SWIG_IsOK(ecode)) {
+      SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessBGR" "', argument " "2"" of type '" "int""'");
+    }
+    temp2 = (int)(val);
+    arg2 = &temp2;
+    res2 = SWIG_AddTmpMask(ecode);
+  }
+  if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) {
+    int val;
+    int ecode = SWIG_AsVal_int(obj2, &val);
+    if (!SWIG_IsOK(ecode)) {
+      SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessBGR" "', argument " "3"" of type '" "int""'");
+    }
+    temp3 = (int)(val);
+    arg3 = &temp3;
+    res3 = SWIG_AddTmpMask(ecode);
+  }
+  ecode5 = SWIG_AsVal_int(obj3, &val5);
+  if (!SWIG_IsOK(ecode5)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeLosslessBGR" "', argument " "5"" of type '" "int""'");
+  }
+  arg5 = (int)(val5);
+  ecode6 = SWIG_AsVal_int(obj4, &val6);
+  if (!SWIG_IsOK(ecode6)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeLosslessBGR" "', argument " "6"" of type '" "int""'");
+  }
+  arg6 = (int)(val6);
+  ecode7 = SWIG_AsVal_int(obj5, &val7);
+  if (!SWIG_IsOK(ecode7)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeLosslessBGR" "', argument " "7"" of type '" "int""'");
+  }
+  arg7 = (int)(val7);
+  result = (uint8_t *)wrap_WebPEncodeLosslessBGR((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+  {
+    resultobj = PyString_FromStringAndSize(
+      (const char*)result,
+      (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeLosslessBGR", arg3, arg4));
+  }
+  if (SWIG_IsTmpObj(res4)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+  }
+  {
+    PyBuffer_Release(&rgb_buffer1);
+  }
+  if (SWIG_IsNewObj(res2)) free((char*)arg2);
+  if (SWIG_IsNewObj(res3)) free((char*)arg3);
+  free(result);
+  return resultobj;
+fail:
+  {
+    PyBuffer_Release(&rgb_buffer1);
+  }
+  if (SWIG_IsNewObj(res2)) free((char*)arg2);
+  if (SWIG_IsNewObj(res3)) free((char*)arg3);
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_wrap_WebPEncodeLosslessRGBA(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  int *arg2 = (int *) 0 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int arg5 ;
+  int arg6 ;
+  int arg7 ;
+  Py_buffer rgb_buffer1 ;
+  int temp2 ;
+  int res2 = 0 ;
+  int temp3 ;
+  int res3 = 0 ;
+  int temp4 ;
+  int res4 = SWIG_TMPOBJ ;
+  int val5 ;
+  int ecode5 = 0 ;
+  int val6 ;
+  int ecode6 = 0 ;
+  int val7 ;
+  int ecode7 = 0 ;
+  PyObject * obj0 = 0 ;
+  PyObject * obj1 = 0 ;
+  PyObject * obj2 = 0 ;
+  PyObject * obj3 = 0 ;
+  PyObject * obj4 = 0 ;
+  PyObject * obj5 = 0 ;
+  uint8_t *result = 0 ;
+
+  arg4 = &temp4;
+  if (!PyArg_ParseTuple(args,(char *)"OOOOOO:wrap_WebPEncodeLosslessRGBA",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5)) SWIG_fail;
+  {
+    // NB: with Python < 2.6 the old style buffer protocol may be used:
+    // Py_ssize_t unused;
+    // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused);
+    if (!PyObject_CheckBuffer(obj0)) {
+      SWIG_exception_fail(SWIG_TypeError,
+        "in method 'wrap_WebPEncodeLosslessRGBA', argument 1"
+        " does not support the buffer interface");
+    }
+    if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) {
+      SWIG_exception_fail(SWIG_RuntimeError,
+        "in method 'wrap_WebPEncodeLosslessRGBA', unable to get buffer view");
+    }
+    arg1 = (uint8_t *)rgb_buffer1.buf;
+  }
+  if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) {
+    int val;
+    int ecode = SWIG_AsVal_int(obj1, &val);
+    if (!SWIG_IsOK(ecode)) {
+      SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessRGBA" "', argument " "2"" of type '" "int""'");
+    }
+    temp2 = (int)(val);
+    arg2 = &temp2;
+    res2 = SWIG_AddTmpMask(ecode);
+  }
+  if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) {
+    int val;
+    int ecode = SWIG_AsVal_int(obj2, &val);
+    if (!SWIG_IsOK(ecode)) {
+      SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessRGBA" "', argument " "3"" of type '" "int""'");
+    }
+    temp3 = (int)(val);
+    arg3 = &temp3;
+    res3 = SWIG_AddTmpMask(ecode);
+  }
+  ecode5 = SWIG_AsVal_int(obj3, &val5);
+  if (!SWIG_IsOK(ecode5)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeLosslessRGBA" "', argument " "5"" of type '" "int""'");
+  }
+  arg5 = (int)(val5);
+  ecode6 = SWIG_AsVal_int(obj4, &val6);
+  if (!SWIG_IsOK(ecode6)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeLosslessRGBA" "', argument " "6"" of type '" "int""'");
+  }
+  arg6 = (int)(val6);
+  ecode7 = SWIG_AsVal_int(obj5, &val7);
+  if (!SWIG_IsOK(ecode7)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeLosslessRGBA" "', argument " "7"" of type '" "int""'");
+  }
+  arg7 = (int)(val7);
+  result = (uint8_t *)wrap_WebPEncodeLosslessRGBA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+  {
+    resultobj = PyString_FromStringAndSize(
+      (const char*)result,
+      (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeLosslessRGBA", arg3, arg4));
+  }
+  if (SWIG_IsTmpObj(res4)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+  }
+  {
+    PyBuffer_Release(&rgb_buffer1);
+  }
+  if (SWIG_IsNewObj(res2)) free((char*)arg2);
+  if (SWIG_IsNewObj(res3)) free((char*)arg3);
+  free(result);
+  return resultobj;
+fail:
+  {
+    PyBuffer_Release(&rgb_buffer1);
+  }
+  if (SWIG_IsNewObj(res2)) free((char*)arg2);
+  if (SWIG_IsNewObj(res3)) free((char*)arg3);
+  return NULL;
+}
+
+
+SWIGINTERN PyObject *_wrap_wrap_WebPEncodeLosslessBGRA(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
+  PyObject *resultobj = 0;
+  uint8_t *arg1 = (uint8_t *) 0 ;
+  int *arg2 = (int *) 0 ;
+  int *arg3 = (int *) 0 ;
+  int *arg4 = (int *) 0 ;
+  int arg5 ;
+  int arg6 ;
+  int arg7 ;
+  Py_buffer rgb_buffer1 ;
+  int temp2 ;
+  int res2 = 0 ;
+  int temp3 ;
+  int res3 = 0 ;
+  int temp4 ;
+  int res4 = SWIG_TMPOBJ ;
+  int val5 ;
+  int ecode5 = 0 ;
+  int val6 ;
+  int ecode6 = 0 ;
+  int val7 ;
+  int ecode7 = 0 ;
+  PyObject * obj0 = 0 ;
+  PyObject * obj1 = 0 ;
+  PyObject * obj2 = 0 ;
+  PyObject * obj3 = 0 ;
+  PyObject * obj4 = 0 ;
+  PyObject * obj5 = 0 ;
+  uint8_t *result = 0 ;
+
+  arg4 = &temp4;
+  if (!PyArg_ParseTuple(args,(char *)"OOOOOO:wrap_WebPEncodeLosslessBGRA",&obj0,&obj1,&obj2,&obj3,&obj4,&obj5)) SWIG_fail;
+  {
+    // NB: with Python < 2.6 the old style buffer protocol may be used:
+    // Py_ssize_t unused;
+    // PyObject_AsReadBuffer(obj0, (const void**)(&arg1), &unused);
+    if (!PyObject_CheckBuffer(obj0)) {
+      SWIG_exception_fail(SWIG_TypeError,
+        "in method 'wrap_WebPEncodeLosslessBGRA', argument 1"
+        " does not support the buffer interface");
+    }
+    if (PyObject_GetBuffer(obj0, &rgb_buffer1, PyBUF_SIMPLE)) {
+      SWIG_exception_fail(SWIG_RuntimeError,
+        "in method 'wrap_WebPEncodeLosslessBGRA', unable to get buffer view");
+    }
+    arg1 = (uint8_t *)rgb_buffer1.buf;
+  }
+  if (!(SWIG_IsOK((res2 = SWIG_ConvertPtr(obj1,SWIG_as_voidptrptr(&arg2),SWIGTYPE_p_int,0))))) {
+    int val;
+    int ecode = SWIG_AsVal_int(obj1, &val);
+    if (!SWIG_IsOK(ecode)) {
+      SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessBGRA" "', argument " "2"" of type '" "int""'");
+    }
+    temp2 = (int)(val);
+    arg2 = &temp2;
+    res2 = SWIG_AddTmpMask(ecode);
+  }
+  if (!(SWIG_IsOK((res3 = SWIG_ConvertPtr(obj2,SWIG_as_voidptrptr(&arg3),SWIGTYPE_p_int,0))))) {
+    int val;
+    int ecode = SWIG_AsVal_int(obj2, &val);
+    if (!SWIG_IsOK(ecode)) {
+      SWIG_exception_fail(SWIG_ArgError(ecode), "in method '" "wrap_WebPEncodeLosslessBGRA" "', argument " "3"" of type '" "int""'");
+    }
+    temp3 = (int)(val);
+    arg3 = &temp3;
+    res3 = SWIG_AddTmpMask(ecode);
+  }
+  ecode5 = SWIG_AsVal_int(obj3, &val5);
+  if (!SWIG_IsOK(ecode5)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "wrap_WebPEncodeLosslessBGRA" "', argument " "5"" of type '" "int""'");
+  }
+  arg5 = (int)(val5);
+  ecode6 = SWIG_AsVal_int(obj4, &val6);
+  if (!SWIG_IsOK(ecode6)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "wrap_WebPEncodeLosslessBGRA" "', argument " "6"" of type '" "int""'");
+  }
+  arg6 = (int)(val6);
+  ecode7 = SWIG_AsVal_int(obj5, &val7);
+  if (!SWIG_IsOK(ecode7)) {
+    SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "wrap_WebPEncodeLosslessBGRA" "', argument " "7"" of type '" "int""'");
+  }
+  arg7 = (int)(val7);
+  result = (uint8_t *)wrap_WebPEncodeLosslessBGRA((uint8_t const *)arg1,arg2,arg3,arg4,arg5,arg6,arg7);
+  {
+    resultobj = PyString_FromStringAndSize(
+      (const char*)result,
+      (result == NULL) ? 0 : ReturnedBufferSize("wrap_WebPEncodeLosslessBGRA", arg3, arg4));
+  }
+  if (SWIG_IsTmpObj(res4)) {
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_From_int((*arg4)));
+  } else {
+    int new_flags = SWIG_IsNewObj(res4) ? (SWIG_POINTER_OWN |  0 ) :  0 ;
+    resultobj = SWIG_Python_AppendOutput(resultobj, SWIG_NewPointerObj((void*)(arg4), SWIGTYPE_p_int, new_flags));
+  }
+  {
+    PyBuffer_Release(&rgb_buffer1);
+  }
+  if (SWIG_IsNewObj(res2)) free((char*)arg2);
+  if (SWIG_IsNewObj(res3)) free((char*)arg3);
+  free(result);
+  return resultobj;
+fail:
+  {
+    PyBuffer_Release(&rgb_buffer1);
+  }
+  if (SWIG_IsNewObj(res2)) free((char*)arg2);
+  if (SWIG_IsNewObj(res3)) free((char*)arg3);
+  return NULL;
+}
+
+
+static PyMethodDef SwigMethods[] = {
+         { "SWIG_PyInstanceMethod_New", SWIG_PyInstanceMethod_New, METH_O, NULL},
+         { "WebPGetDecoderVersion", _wrap_WebPGetDecoderVersion, METH_VARARGS, (char *)"WebPGetDecoderVersion() -> int"},
+         { "WebPGetInfo", _wrap_WebPGetInfo, METH_VARARGS, (char *)"WebPGetInfo(uint8_t data) -> (width, height)"},
+         { "WebPDecodeRGB", _wrap_WebPDecodeRGB, METH_VARARGS, (char *)"WebPDecodeRGB(uint8_t data) -> (rgb, width, height)"},
+         { "WebPDecodeRGBA", _wrap_WebPDecodeRGBA, METH_VARARGS, (char *)"WebPDecodeRGBA(uint8_t data) -> (rgb, width, height)"},
+         { "WebPDecodeARGB", _wrap_WebPDecodeARGB, METH_VARARGS, (char *)"WebPDecodeARGB(uint8_t data) -> (rgb, width, height)"},
+         { "WebPDecodeBGR", _wrap_WebPDecodeBGR, METH_VARARGS, (char *)"WebPDecodeBGR(uint8_t data) -> (rgb, width, height)"},
+         { "WebPDecodeBGRA", _wrap_WebPDecodeBGRA, METH_VARARGS, (char *)"WebPDecodeBGRA(uint8_t data) -> (rgb, width, height)"},
+         { "WebPGetEncoderVersion", _wrap_WebPGetEncoderVersion, METH_VARARGS, (char *)"WebPGetEncoderVersion() -> int"},
+         { "wrap_WebPEncodeRGB", _wrap_wrap_WebPEncodeRGB, METH_VARARGS, (char *)"private, do not call directly."},
+         { "wrap_WebPEncodeBGR", _wrap_wrap_WebPEncodeBGR, METH_VARARGS, (char *)"private, do not call directly."},
+         { "wrap_WebPEncodeRGBA", _wrap_wrap_WebPEncodeRGBA, METH_VARARGS, (char *)"private, do not call directly."},
+         { "wrap_WebPEncodeBGRA", _wrap_wrap_WebPEncodeBGRA, METH_VARARGS, (char *)"private, do not call directly."},
+         { "wrap_WebPEncodeLosslessRGB", _wrap_wrap_WebPEncodeLosslessRGB, METH_VARARGS, (char *)"private, do not call directly."},
+         { "wrap_WebPEncodeLosslessBGR", _wrap_wrap_WebPEncodeLosslessBGR, METH_VARARGS, (char *)"private, do not call directly."},
+         { "wrap_WebPEncodeLosslessRGBA", _wrap_wrap_WebPEncodeLosslessRGBA, METH_VARARGS, (char *)"private, do not call directly."},
+         { "wrap_WebPEncodeLosslessBGRA", _wrap_wrap_WebPEncodeLosslessBGRA, METH_VARARGS, (char *)"private, do not call directly."},
+         { NULL, NULL, 0, NULL }
+};
+
+
+/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (BEGIN) -------- */
+
+static swig_type_info _swigt__p_char = {"_p_char", "char *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_int = {"_p_int", "int *", 0, 0, (void*)0, 0};
+static swig_type_info _swigt__p_uint8_t = {"_p_uint8_t", "uint8_t *", 0, 0, (void*)0, 0};
+
+static swig_type_info *swig_type_initial[] = {
+  &_swigt__p_char,
+  &_swigt__p_int,
+  &_swigt__p_uint8_t,
+};
+
+static swig_cast_info _swigc__p_char[] = {  {&_swigt__p_char, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_int[] = {  {&_swigt__p_int, 0, 0, 0},{0, 0, 0, 0}};
+static swig_cast_info _swigc__p_uint8_t[] = {  {&_swigt__p_uint8_t, 0, 0, 0},{0, 0, 0, 0}};
+
+static swig_cast_info *swig_cast_initial[] = {
+  _swigc__p_char,
+  _swigc__p_int,
+  _swigc__p_uint8_t,
+};
+
+
+/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (END) -------- */
+
+static swig_const_info swig_const_table[] = {
+{0, 0, 0, 0.0, 0, 0}};
+
+#ifdef __cplusplus
+}
+#endif
+/* -----------------------------------------------------------------------------
+ * Type initialization:
+ * This problem is tough by the requirement that no dynamic
+ * memory is used. Also, since swig_type_info structures store pointers to
+ * swig_cast_info structures and swig_cast_info structures store pointers back
+ * to swig_type_info structures, we need some lookup code at initialization.
+ * The idea is that swig generates all the structures that are needed.
+ * The runtime then collects these partially filled structures.
+ * The SWIG_InitializeModule function takes these initial arrays out of
+ * swig_module, and does all the lookup, filling in the swig_module.types
+ * array with the correct data and linking the correct swig_cast_info
+ * structures together.
+ *
+ * The generated swig_type_info structures are assigned statically to an initial
+ * array. We just loop through that array, and handle each type individually.
+ * First we lookup if this type has been already loaded, and if so, use the
+ * loaded structure instead of the generated one. Then we have to fill in the
+ * cast linked list. The cast data is initially stored in something like a
+ * two-dimensional array. Each row corresponds to a type (there are the same
+ * number of rows as there are in the swig_type_initial array). Each entry in
+ * a column is one of the swig_cast_info structures for that type.
+ * The cast_initial array is actually an array of arrays, because each row has
+ * a variable number of columns. So to actually build the cast linked list,
+ * we find the array of casts associated with the type, and loop through it
+ * adding the casts to the list. The one last trick we need to do is making
+ * sure the type pointer in the swig_cast_info struct is correct.
+ *
+ * First off, we lookup the cast->type name to see if it is already loaded.
+ * There are three cases to handle:
+ *  1) If the cast->type has already been loaded AND the type we are adding
+ *     casting info to has not been loaded (it is in this module), THEN we
+ *     replace the cast->type pointer with the type pointer that has already
+ *     been loaded.
+ *  2) If BOTH types (the one we are adding casting info to, and the
+ *     cast->type) are loaded, THEN the cast info has already been loaded by
+ *     the previous module so we just ignore it.
+ *  3) Finally, if cast->type has not already been loaded, then we add that
+ *     swig_cast_info to the linked list (because the cast->type) pointer will
+ *     be correct.
+ * ----------------------------------------------------------------------------- */
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+} /* c-mode */
+#endif
+#endif
+
+#if 0
+#define SWIGRUNTIME_DEBUG
+#endif
+
+
+SWIGRUNTIME void
+SWIG_InitializeModule(void *clientdata) {
+  size_t i;
+  swig_module_info *module_head, *iter;
+  int init;
+
+  /* check to see if the circular list has been setup, if not, set it up */
+  if (swig_module.next==0) {
+    /* Initialize the swig_module */
+    swig_module.type_initial = swig_type_initial;
+    swig_module.cast_initial = swig_cast_initial;
+    swig_module.next = &swig_module;
+    init = 1;
+  } else {
+    init = 0;
+  }
+
+  /* Try and load any already created modules */
+  module_head = SWIG_GetModule(clientdata);
+  if (!module_head) {
+    /* This is the first module loaded for this interpreter */
+    /* so set the swig module into the interpreter */
+    SWIG_SetModule(clientdata, &swig_module);
+  } else {
+    /* the interpreter has loaded a SWIG module, but has it loaded this one? */
+    iter=module_head;
+    do {
+      if (iter==&swig_module) {
+        /* Our module is already in the list, so there's nothing more to do. */
+        return;
+      }
+      iter=iter->next;
+    } while (iter!= module_head);
+
+    /* otherwise we must add our module into the list */
+    swig_module.next = module_head->next;
+    module_head->next = &swig_module;
+  }
+
+  /* When multiple interpreters are used, a module could have already been initialized in
+       a different interpreter, but not yet have a pointer in this interpreter.
+       In this case, we do not want to continue adding types... everything should be
+       set up already */
+  if (init == 0) return;
+
+  /* Now work on filling in swig_module.types */
+#ifdef SWIGRUNTIME_DEBUG
+  printf("SWIG_InitializeModule: size %d\n", swig_module.size);
+#endif
+  for (i = 0; i < swig_module.size; ++i) {
+    swig_type_info *type = 0;
+    swig_type_info *ret;
+    swig_cast_info *cast;
+
+#ifdef SWIGRUNTIME_DEBUG
+    printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name);
+#endif
+
+    /* if there is another module already loaded */
+    if (swig_module.next != &swig_module) {
+      type = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, swig_module.type_initial[i]->name);
+    }
+    if (type) {
+      /* Overwrite clientdata field */
+#ifdef SWIGRUNTIME_DEBUG
+      printf("SWIG_InitializeModule: found type %s\n", type->name);
+#endif
+      if (swig_module.type_initial[i]->clientdata) {
+        type->clientdata = swig_module.type_initial[i]->clientdata;
+#ifdef SWIGRUNTIME_DEBUG
+        printf("SWIG_InitializeModule: found and overwrite type %s \n", type->name);
+#endif
+      }
+    } else {
+      type = swig_module.type_initial[i];
+    }
+
+    /* Insert casting types */
+    cast = swig_module.cast_initial[i];
+    while (cast->type) {
+      /* Don't need to add information already in the list */
+      ret = 0;
+#ifdef SWIGRUNTIME_DEBUG
+      printf("SWIG_InitializeModule: look cast %s\n", cast->type->name);
+#endif
+      if (swig_module.next != &swig_module) {
+        ret = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, cast->type->name);
+#ifdef SWIGRUNTIME_DEBUG
+        if (ret) printf("SWIG_InitializeModule: found cast %s\n", ret->name);
+#endif
+      }
+      if (ret) {
+        if (type == swig_module.type_initial[i]) {
+#ifdef SWIGRUNTIME_DEBUG
+          printf("SWIG_InitializeModule: skip old type %s\n", ret->name);
+#endif
+          cast->type = ret;
+          ret = 0;
+        } else {
+          /* Check for casting already in the list */
+          swig_cast_info *ocast = SWIG_TypeCheck(ret->name, type);
+#ifdef SWIGRUNTIME_DEBUG
+          if (ocast) printf("SWIG_InitializeModule: skip old cast %s\n", ret->name);
+#endif
+          if (!ocast) ret = 0;
+        }
+      }
+
+      if (!ret) {
+#ifdef SWIGRUNTIME_DEBUG
+        printf("SWIG_InitializeModule: adding cast %s\n", cast->type->name);
+#endif
+        if (type->cast) {
+          type->cast->prev = cast;
+          cast->next = type->cast;
+        }
+        type->cast = cast;
+      }
+      cast++;
+    }
+    /* Set entry in modules->types array equal to the type */
+    swig_module.types[i] = type;
+  }
+  swig_module.types[i] = 0;
+
+#ifdef SWIGRUNTIME_DEBUG
+  printf("**** SWIG_InitializeModule: Cast List ******\n");
+  for (i = 0; i < swig_module.size; ++i) {
+    int j = 0;
+    swig_cast_info *cast = swig_module.cast_initial[i];
+    printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name);
+    while (cast->type) {
+      printf("SWIG_InitializeModule: cast type %s\n", cast->type->name);
+      cast++;
+      ++j;
+    }
+    printf("---- Total casts: %d\n",j);
+  }
+  printf("**** SWIG_InitializeModule: Cast List ******\n");
+#endif
+}
+
+/* This function will propagate the clientdata field of type to
+* any new swig_type_info structures that have been added into the list
+* of equivalent types.  It is like calling
+* SWIG_TypeClientData(type, clientdata) a second time.
+*/
+SWIGRUNTIME void
+SWIG_PropagateClientData(void) {
+  size_t i;
+  swig_cast_info *equiv;
+  static int init_run = 0;
+
+  if (init_run) return;
+  init_run = 1;
+
+  for (i = 0; i < swig_module.size; i++) {
+    if (swig_module.types[i]->clientdata) {
+      equiv = swig_module.types[i]->cast;
+      while (equiv) {
+        if (!equiv->converter) {
+          if (equiv->type && !equiv->type->clientdata)
+          SWIG_TypeClientData(equiv->type, swig_module.types[i]->clientdata);
+        }
+        equiv = equiv->next;
+      }
+    }
+  }
+}
+
+#ifdef __cplusplus
+#if 0
+{
+  /* c-mode */
+#endif
+}
+#endif
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  /* Python-specific SWIG API */
+#define SWIG_newvarlink()                             SWIG_Python_newvarlink()
+#define SWIG_addvarlink(p, name, get_attr, set_attr)  SWIG_Python_addvarlink(p, name, get_attr, set_attr)
+#define SWIG_InstallConstants(d, constants)           SWIG_Python_InstallConstants(d, constants)
+
+  /* -----------------------------------------------------------------------------
+   * global variable support code.
+   * ----------------------------------------------------------------------------- */
+
+  typedef struct swig_globalvar {
+    char       *name;                  /* Name of global variable */
+    PyObject *(*get_attr)(void);       /* Return the current value */
+    int       (*set_attr)(PyObject *); /* Set the value */
+    struct swig_globalvar *next;
+  } swig_globalvar;
+
+  typedef struct swig_varlinkobject {
+    PyObject_HEAD
+    swig_globalvar *vars;
+  } swig_varlinkobject;
+
+  SWIGINTERN PyObject *
+  swig_varlink_repr(swig_varlinkobject *SWIGUNUSEDPARM(v)) {
+#if PY_VERSION_HEX >= 0x03000000
+    return PyUnicode_InternFromString("<Swig global variables>");
+#else
+    return PyString_FromString("<Swig global variables>");
+#endif
+  }
+
+  SWIGINTERN PyObject *
+  swig_varlink_str(swig_varlinkobject *v) {
+#if PY_VERSION_HEX >= 0x03000000
+    PyObject *str = PyUnicode_InternFromString("(");
+    PyObject *tail;
+    PyObject *joined;
+    swig_globalvar *var;
+    for (var = v->vars; var; var=var->next) {
+      tail = PyUnicode_FromString(var->name);
+      joined = PyUnicode_Concat(str, tail);
+      Py_DecRef(str);
+      Py_DecRef(tail);
+      str = joined;
+      if (var->next) {
+        tail = PyUnicode_InternFromString(", ");
+        joined = PyUnicode_Concat(str, tail);
+        Py_DecRef(str);
+        Py_DecRef(tail);
+        str = joined;
+      }
+    }
+    tail = PyUnicode_InternFromString(")");
+    joined = PyUnicode_Concat(str, tail);
+    Py_DecRef(str);
+    Py_DecRef(tail);
+    str = joined;
+#else
+    PyObject *str = PyString_FromString("(");
+    swig_globalvar *var;
+    for (var = v->vars; var; var=var->next) {
+      PyString_ConcatAndDel(&str,PyString_FromString(var->name));
+      if (var->next) PyString_ConcatAndDel(&str,PyString_FromString(", "));
+    }
+    PyString_ConcatAndDel(&str,PyString_FromString(")"));
+#endif
+    return str;
+  }
+
+  SWIGINTERN int
+  swig_varlink_print(swig_varlinkobject *v, FILE *fp, int SWIGUNUSEDPARM(flags)) {
+    char *tmp;
+    PyObject *str = swig_varlink_str(v);
+    fprintf(fp,"Swig global variables ");
+    fprintf(fp,"%s\n", tmp = SWIG_Python_str_AsChar(str));
+    SWIG_Python_str_DelForPy3(tmp);
+    Py_DECREF(str);
+    return 0;
+  }
+
+  SWIGINTERN void
+  swig_varlink_dealloc(swig_varlinkobject *v) {
+    swig_globalvar *var = v->vars;
+    while (var) {
+      swig_globalvar *n = var->next;
+      free(var->name);
+      free(var);
+      var = n;
+    }
+  }
+
+  SWIGINTERN PyObject *
+  swig_varlink_getattr(swig_varlinkobject *v, char *n) {
+    PyObject *res = NULL;
+    swig_globalvar *var = v->vars;
+    while (var) {
+      if (strcmp(var->name,n) == 0) {
+        res = (*var->get_attr)();
+        break;
+      }
+      var = var->next;
+    }
+    if (res == NULL && !PyErr_Occurred()) {
+      PyErr_Format(PyExc_AttributeError, "Unknown C global variable '%s'", n);
+    }
+    return res;
+  }
+
+  SWIGINTERN int
+  swig_varlink_setattr(swig_varlinkobject *v, char *n, PyObject *p) {
+    int res = 1;
+    swig_globalvar *var = v->vars;
+    while (var) {
+      if (strcmp(var->name,n) == 0) {
+        res = (*var->set_attr)(p);
+        break;
+      }
+      var = var->next;
+    }
+    if (res == 1 && !PyErr_Occurred()) {
+      PyErr_Format(PyExc_AttributeError, "Unknown C global variable '%s'", n);
+    }
+    return res;
+  }
+
+  SWIGINTERN PyTypeObject*
+  swig_varlink_type(void) {
+    static char varlink__doc__[] = "Swig var link object";
+    static PyTypeObject varlink_type;
+    static int type_init = 0;
+    if (!type_init) {
+      const PyTypeObject tmp = {
+#if PY_VERSION_HEX >= 0x03000000
+        PyVarObject_HEAD_INIT(NULL, 0)
+#else
+        PyObject_HEAD_INIT(NULL)
+        0,                                  /* ob_size */
+#endif
+        (char *)"swigvarlink",              /* tp_name */
+        sizeof(swig_varlinkobject),         /* tp_basicsize */
+        0,                                  /* tp_itemsize */
+        (destructor) swig_varlink_dealloc,  /* tp_dealloc */
+        (printfunc) swig_varlink_print,     /* tp_print */
+        (getattrfunc) swig_varlink_getattr, /* tp_getattr */
+        (setattrfunc) swig_varlink_setattr, /* tp_setattr */
+        0,                                  /* tp_compare */
+        (reprfunc) swig_varlink_repr,       /* tp_repr */
+        0,                                  /* tp_as_number */
+        0,                                  /* tp_as_sequence */
+        0,                                  /* tp_as_mapping */
+        0,                                  /* tp_hash */
+        0,                                  /* tp_call */
+        (reprfunc) swig_varlink_str,        /* tp_str */
+        0,                                  /* tp_getattro */
+        0,                                  /* tp_setattro */
+        0,                                  /* tp_as_buffer */
+        0,                                  /* tp_flags */
+        varlink__doc__,                     /* tp_doc */
+        0,                                  /* tp_traverse */
+        0,                                  /* tp_clear */
+        0,                                  /* tp_richcompare */
+        0,                                  /* tp_weaklistoffset */
+#if PY_VERSION_HEX >= 0x02020000
+        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* tp_iter -> tp_weaklist */
+#endif
+#if PY_VERSION_HEX >= 0x02030000
+        0,                                  /* tp_del */
+#endif
+#if PY_VERSION_HEX >= 0x02060000
+        0,                                  /* tp_version_tag */
+#endif
+#if PY_VERSION_HEX >= 0x03040000
+        0,                                  /* tp_finalize */
+#endif
+#ifdef COUNT_ALLOCS
+        0,                                  /* tp_allocs */
+        0,                                  /* tp_frees */
+        0,                                  /* tp_maxalloc */
+#if PY_VERSION_HEX >= 0x02050000
+        0,                                  /* tp_prev */
+#endif
+        0                                   /* tp_next */
+#endif
+      };
+      varlink_type = tmp;
+      type_init = 1;
+#if PY_VERSION_HEX < 0x02020000
+      varlink_type.ob_type = &PyType_Type;
+#else
+      if (PyType_Ready(&varlink_type) < 0)
+      return NULL;
+#endif
+    }
+    return &varlink_type;
+  }
+
+  /* Create a variable linking object for use later */
+  SWIGINTERN PyObject *
+  SWIG_Python_newvarlink(void) {
+    swig_varlinkobject *result = PyObject_NEW(swig_varlinkobject, swig_varlink_type());
+    if (result) {
+      result->vars = 0;
+    }
+    return ((PyObject*) result);
+  }
+
+  SWIGINTERN void
+  SWIG_Python_addvarlink(PyObject *p, char *name, PyObject *(*get_attr)(void), int (*set_attr)(PyObject *p)) {
+    swig_varlinkobject *v = (swig_varlinkobject *) p;
+    swig_globalvar *gv = (swig_globalvar *) malloc(sizeof(swig_globalvar));
+    if (gv) {
+      size_t size = strlen(name)+1;
+      gv->name = (char *)malloc(size);
+      if (gv->name) {
+        strncpy(gv->name,name,size);
+        gv->get_attr = get_attr;
+        gv->set_attr = set_attr;
+        gv->next = v->vars;
+      }
+    }
+    v->vars = gv;
+  }
+
+  SWIGINTERN PyObject *
+  SWIG_globals(void) {
+    static PyObject *_SWIG_globals = 0;
+    if (!_SWIG_globals) _SWIG_globals = SWIG_newvarlink();
+    return _SWIG_globals;
+  }
+
+  /* -----------------------------------------------------------------------------
+   * constants/methods manipulation
+   * ----------------------------------------------------------------------------- */
+
+  /* Install Constants */
+  SWIGINTERN void
+  SWIG_Python_InstallConstants(PyObject *d, swig_const_info constants[]) {
+    PyObject *obj = 0;
+    size_t i;
+    for (i = 0; constants[i].type; ++i) {
+      switch(constants[i].type) {
+      case SWIG_PY_POINTER:
+        obj = SWIG_InternalNewPointerObj(constants[i].pvalue, *(constants[i]).ptype,0);
+        break;
+      case SWIG_PY_BINARY:
+        obj = SWIG_NewPackedObj(constants[i].pvalue, constants[i].lvalue, *(constants[i].ptype));
+        break;
+      default:
+        obj = 0;
+        break;
+      }
+      if (obj) {
+        PyDict_SetItemString(d, constants[i].name, obj);
+        Py_DECREF(obj);
+      }
+    }
+  }
+
+  /* -----------------------------------------------------------------------------*/
+  /* Fix SwigMethods to carry the callback ptrs when needed */
+  /* -----------------------------------------------------------------------------*/
+
+  SWIGINTERN void
+  SWIG_Python_FixMethods(PyMethodDef *methods,
+    swig_const_info *const_table,
+    swig_type_info **types,
+    swig_type_info **types_initial) {
+    size_t i;
+    for (i = 0; methods[i].ml_name; ++i) {
+      const char *c = methods[i].ml_doc;
+      if (!c) continue;
+      c = strstr(c, "swig_ptr: ");
+      if (c) {
+        int j;
+        swig_const_info *ci = 0;
+        const char *name = c + 10;
+        for (j = 0; const_table[j].type; ++j) {
+          if (strncmp(const_table[j].name, name,
+              strlen(const_table[j].name)) == 0) {
+            ci = &(const_table[j]);
+            break;
+          }
+        }
+        if (ci) {
+          void *ptr = (ci->type == SWIG_PY_POINTER) ? ci->pvalue : 0;
+          if (ptr) {
+            size_t shift = (ci->ptype) - types;
+            swig_type_info *ty = types_initial[shift];
+            size_t ldoc = (c - methods[i].ml_doc);
+            size_t lptr = strlen(ty->name)+2*sizeof(void*)+2;
+            char *ndoc = (char*)malloc(ldoc + lptr + 10);
+            if (ndoc) {
+              char *buff = ndoc;
+              memcpy(buff, methods[i].ml_doc, ldoc);
+              buff += ldoc;
+              memcpy(buff, "swig_ptr: ", 10);
+              buff += 10;
+              SWIG_PackVoidPtr(buff, ptr, ty->name, lptr);
+              methods[i].ml_doc = ndoc;
+            }
+          }
+        }
+      }
+    }
+  }
+
+#ifdef __cplusplus
+}
+#endif
+
+/* -----------------------------------------------------------------------------*
+ *  Partial Init method
+ * -----------------------------------------------------------------------------*/
+
+#ifdef __cplusplus
+extern "C"
+#endif
+
+SWIGEXPORT
+#if PY_VERSION_HEX >= 0x03000000
+PyObject*
+#else
+void
+#endif
+SWIG_init(void) {
+  PyObject *m, *d, *md;
+#if PY_VERSION_HEX >= 0x03000000
+  static struct PyModuleDef SWIG_module = {
+# if PY_VERSION_HEX >= 0x03020000
+    PyModuleDef_HEAD_INIT,
+# else
+    {
+      PyObject_HEAD_INIT(NULL)
+      NULL, /* m_init */
+      0,    /* m_index */
+      NULL, /* m_copy */
+    },
+# endif
+    (char *) SWIG_name,
+    NULL,
+    -1,
+    SwigMethods,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+  };
+#endif
+
+#if defined(SWIGPYTHON_BUILTIN)
+  static SwigPyClientData SwigPyObject_clientdata = {
+    0, 0, 0, 0, 0, 0, 0
+  };
+  static PyGetSetDef this_getset_def = {
+    (char *)"this", &SwigPyBuiltin_ThisClosure, NULL, NULL, NULL
+  };
+  static SwigPyGetSet thisown_getset_closure = {
+    SwigPyObject_own,
+    SwigPyObject_own
+  };
+  static PyGetSetDef thisown_getset_def = {
+    (char *)"thisown", SwigPyBuiltin_GetterClosure, SwigPyBuiltin_SetterClosure, NULL, &thisown_getset_closure
+  };
+  PyTypeObject *builtin_pytype;
+  int builtin_base_count;
+  swig_type_info *builtin_basetype;
+  PyObject *tuple;
+  PyGetSetDescrObject *static_getset;
+  PyTypeObject *metatype;
+  PyTypeObject *swigpyobject;
+  SwigPyClientData *cd;
+  PyObject *public_interface, *public_symbol;
+  PyObject *this_descr;
+  PyObject *thisown_descr;
+  PyObject *self = 0;
+  int i;
+
+  (void)builtin_pytype;
+  (void)builtin_base_count;
+  (void)builtin_basetype;
+  (void)tuple;
+  (void)static_getset;
+  (void)self;
+
+  /* Metaclass is used to implement static member variables */
+  metatype = SwigPyObjectType();
+  assert(metatype);
+#endif
+
+  /* Fix SwigMethods to carry the callback ptrs when needed */
+  SWIG_Python_FixMethods(SwigMethods, swig_const_table, swig_types, swig_type_initial);
+
+#if PY_VERSION_HEX >= 0x03000000
+  m = PyModule_Create(&SWIG_module);
+#else
+  m = Py_InitModule((char *) SWIG_name, SwigMethods);
+#endif
+
+  md = d = PyModule_GetDict(m);
+  (void)md;
+
+  SWIG_InitializeModule(0);
+
+#ifdef SWIGPYTHON_BUILTIN
+  swigpyobject = SwigPyObject_TypeOnce();
+
+  SwigPyObject_stype = SWIG_MangledTypeQuery("_p_SwigPyObject");
+  assert(SwigPyObject_stype);
+  cd = (SwigPyClientData*) SwigPyObject_stype->clientdata;
+  if (!cd) {
+    SwigPyObject_stype->clientdata = &SwigPyObject_clientdata;
+    SwigPyObject_clientdata.pytype = swigpyobject;
+  } else if (swigpyobject->tp_basicsize != cd->pytype->tp_basicsize) {
+    PyErr_SetString(PyExc_RuntimeError, "Import error: attempted to load two incompatible swig-generated modules.");
+# if PY_VERSION_HEX >= 0x03000000
+    return NULL;
+# else
+    return;
+# endif
+  }
+
+  /* All objects have a 'this' attribute */
+  this_descr = PyDescr_NewGetSet(SwigPyObject_type(), &this_getset_def);
+  (void)this_descr;
+
+  /* All objects have a 'thisown' attribute */
+  thisown_descr = PyDescr_NewGetSet(SwigPyObject_type(), &thisown_getset_def);
+  (void)thisown_descr;
+
+  public_interface = PyList_New(0);
+  public_symbol = 0;
+  (void)public_symbol;
+
+  PyDict_SetItemString(md, "__all__", public_interface);
+  Py_DECREF(public_interface);
+  for (i = 0; SwigMethods[i].ml_name != NULL; ++i)
+  SwigPyBuiltin_AddPublicSymbol(public_interface, SwigMethods[i].ml_name);
+  for (i = 0; swig_const_table[i].name != 0; ++i)
+  SwigPyBuiltin_AddPublicSymbol(public_interface, swig_const_table[i].name);
+#endif
+
+  SWIG_InstallConstants(d,swig_const_table);
+
+#if PY_VERSION_HEX >= 0x03000000
+  return m;
+#else
+  return;
+#endif
+}
+
diff --git a/swig/setup.py b/swig/setup.py
new file mode 100644
index 0000000..3a3bfe1
--- /dev/null
+++ b/swig/setup.py
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+
+"""distutils script for libwebp python module."""
+
+from distutils.core import setup
+from distutils.extension import Extension
+import os
+import shutil
+import tempfile
+
+tmpdir = tempfile.mkdtemp()
+package = "com.google.webp"
+package_path = os.path.join(tmpdir, *package.split("."))
+os.makedirs(package_path)
+
+# Create __init_.py files along the package path.
+initpy_path = tmpdir
+for d in package.split("."):
+  initpy_path = os.path.join(initpy_path, d)
+  open(os.path.join(initpy_path, "__init__.py"), "w").close()
+
+shutil.copy2("libwebp.py", package_path)
+setup(name="libwebp",
+      version="0.0",
+      description="libwebp python wrapper",
+      long_description="Provides access to 'simple' libwebp decode interface",
+      license="BSD",
+      url="http://developers.google.com/speed/webp",
+      ext_package=package,
+      ext_modules=[Extension("_libwebp",
+                             ["libwebp_python_wrap.c"],
+                             libraries=["webp"],
+                            ),
+                  ],
+      package_dir={"": tmpdir},
+      packages=["com", "com.google", "com.google.webp"],
+      py_modules=[package + ".libwebp"],
+     )
+
+shutil.rmtree(tmpdir)
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 0000000..91daba2
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,18 @@
+# Tests
+
+This is a collection of tests for the libwebp libraries, currently covering
+fuzzing through the APIs. Additional test vector coverage can be found at:
+https://chromium.googlesource.com/webm/libwebp-test-data
+
+## Building
+
+### Fuzzers
+
+Follow the [build instructions](../doc/building.md) for libwebp, optionally
+adding build flags for various sanitizers (e.g., -fsanitize=address).
+
+`fuzzer/makefile.unix` can then be used to compile the fuzzer targets:
+
+```shell
+$ make -C fuzzer -f makefile.unix
+```
diff --git a/tests/fuzzer/advanced_api_fuzzer.c b/tests/fuzzer/advanced_api_fuzzer.c
index da4613b..1378d0b 100644
--- a/tests/fuzzer/advanced_api_fuzzer.c
+++ b/tests/fuzzer/advanced_api_fuzzer.c
@@ -14,8 +14,12 @@
 //
 ////////////////////////////////////////////////////////////////////////////////
 
+#include <stdint.h>
+#include <string.h>
+
 #include "./fuzz_utils.h"
-#include "webp/decode.h"
+#include "src/utils/rescaler_utils.h"
+#include "src/webp/decode.h"
 
 int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
   WebPDecoderConfig config;
@@ -59,39 +63,74 @@
   config.output.colorspace = (WEBP_CSP_MODE)(value % MODE_LAST);
 #endif  // WEBP_REDUCE_CSP
 
-  if (size % 3) {
-    // Decodes incrementally in chunks of increasing size.
-    WebPIDecoder* idec = WebPIDecode(NULL, 0, &config);
-    if (!idec) return 0;
-    VP8StatusCode status;
-    if (size & 8) {
-      size_t available_size = value + 1;
-      while (1) {
-        if (available_size > size) available_size = size;
-        status = WebPIUpdate(idec, data, available_size);
-        if (status != VP8_STATUS_SUSPENDED || available_size == size) break;
-        available_size *= 2;
-      }
-    } else {
-      // WebPIAppend expects new data and its size with each call.
-      // Implemented here by simply advancing the pointer into data.
-      const uint8_t* new_data = data;
-      size_t new_size = value + 1;
-      while (1) {
-        if (new_data + new_size > data + size) {
-          new_size = data + size - new_data;
+  for (int i = 0; i < 2; ++i) {
+    if (i == 1) {
+      // Use the bitstream data to generate extreme ranges for the options. An
+      // alternative approach would be to use a custom corpus containing webp
+      // files prepended with sizeof(config.options) zeroes to allow the fuzzer
+      // to modify these independently.
+      const int data_offset = 50;
+      if (data_offset + sizeof(config.options) >= size) break;
+      memcpy(&config.options, data + data_offset, sizeof(config.options));
+
+      // Skip easily avoidable out-of-memory fuzzing errors.
+      if (config.options.use_scaling) {
+        int scaled_width = config.options.scaled_width;
+        int scaled_height = config.options.scaled_height;
+        if (WebPRescalerGetScaledDimensions(config.input.width,
+                                            config.input.height, &scaled_width,
+                                            &scaled_height)) {
+          size_t fuzz_px_limit = kFuzzPxLimit;
+          if (scaled_width != config.input.width ||
+              scaled_height != config.input.height) {
+            // Using the WebPRescalerImport internally can significantly slow
+            // down the execution. Avoid timeouts due to that.
+            fuzz_px_limit /= 2;
+          }
+          // A big output canvas can lead to out-of-memory and timeout issues,
+          // but a big internal working buffer can too.
+          if ((uint64_t)scaled_width * scaled_height > fuzz_px_limit ||
+              (uint64_t)config.input.width * config.input.height >
+                  fuzz_px_limit) {
+            break;
+          }
         }
-        status = WebPIAppend(idec, new_data, new_size);
-        if (status != VP8_STATUS_SUSPENDED || new_size == 0) break;
-        new_data += new_size;
-        new_size *= 2;
       }
     }
-    WebPIDelete(idec);
-  } else {
-    WebPDecode(data, size, &config);
-  }
+    if (size % 3) {
+      // Decodes incrementally in chunks of increasing size.
+      WebPIDecoder* idec = WebPIDecode(NULL, 0, &config);
+      if (!idec) return 0;
+      VP8StatusCode status;
+      if (size & 8) {
+        size_t available_size = value + 1;
+        while (1) {
+          if (available_size > size) available_size = size;
+          status = WebPIUpdate(idec, data, available_size);
+          if (status != VP8_STATUS_SUSPENDED || available_size == size) break;
+          available_size *= 2;
+        }
+      } else {
+        // WebPIAppend expects new data and its size with each call.
+        // Implemented here by simply advancing the pointer into data.
+        const uint8_t* new_data = data;
+        size_t new_size = value + 1;
+        while (1) {
+          if (new_data + new_size > data + size) {
+            new_size = data + size - new_data;
+          }
+          status = WebPIAppend(idec, new_data, new_size);
+          if (status != VP8_STATUS_SUSPENDED || new_size == 0) break;
+          new_data += new_size;
+          new_size *= 2;
+        }
+      }
+      WebPIDelete(idec);
+    } else {
+      WebPDecode(data, size, &config);
+    }
 
-  WebPFreeDecBuffer(&config.output);
+    WebPFreeDecBuffer(&config.output);
+  }
   return 0;
 }
diff --git a/tests/fuzzer/animation_api_fuzzer.c b/tests/fuzzer/animation_api_fuzzer.c
index 30d6074..187ed24 100644
--- a/tests/fuzzer/animation_api_fuzzer.c
+++ b/tests/fuzzer/animation_api_fuzzer.c
@@ -15,9 +15,9 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 #include "./fuzz_utils.h"
-#include "webp/decode.h"
-#include "webp/demux.h"
-#include "webp/mux_types.h"
+#include "src/webp/decode.h"
+#include "src/webp/demux.h"
+#include "src/webp/mux_types.h"
 
 int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
   WebPData webp_data;
diff --git a/tests/fuzzer/animdecoder_fuzzer.cc b/tests/fuzzer/animdecoder_fuzzer.cc
index 2d9e2d5..a79712d 100644
--- a/tests/fuzzer/animdecoder_fuzzer.cc
+++ b/tests/fuzzer/animdecoder_fuzzer.cc
@@ -16,7 +16,7 @@
 
 #include "examples/anim_util.h"
 #include "imageio/imageio_util.h"
-#include "webp/demux.h"
+#include "src/webp/demux.h"
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
   // WebPAnimDecoderGetInfo() is too late to check the canvas size as
@@ -35,13 +35,18 @@
   if (dec == NULL) return 0;
 
   WebPAnimInfo info;
-  if (!WebPAnimDecoderGetInfo(dec, &info)) return 0;
+  if (!WebPAnimDecoderGetInfo(dec, &info)) goto End;
+  if (!ImgIoUtilCheckSizeArgumentsOverflow(info.canvas_width * 4,
+                                           info.canvas_height)) {
+    goto End;
+  }
 
   while (WebPAnimDecoderHasMoreFrames(dec)) {
     uint8_t* buf;
     int timestamp;
     if (!WebPAnimDecoderGetNext(dec, &buf, &timestamp)) break;
   }
+ End:
   WebPAnimDecoderDelete(dec);
   return 0;
 }
diff --git a/tests/fuzzer/animencoder_fuzzer.cc b/tests/fuzzer/animencoder_fuzzer.cc
index 58a266f..1bd7871 100644
--- a/tests/fuzzer/animencoder_fuzzer.cc
+++ b/tests/fuzzer/animencoder_fuzzer.cc
@@ -18,8 +18,8 @@
 #include <stdlib.h>
 
 #include "./fuzz_utils.h"
-#include "webp/encode.h"
-#include "webp/mux.h"
+#include "src/webp/encode.h"
+#include "src/webp/mux.h"
 
 namespace {
 
@@ -46,24 +46,32 @@
 
   // Read the source picture.
   if (!ExtractSourcePicture(&pic, data, size, bit_pos)) {
-    fprintf(stderr, "Can't read input image.\n");
+    const WebPEncodingError error_code = pic.error_code;
     WebPPictureFree(&pic);
+    if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
+    fprintf(stderr, "Can't read input image. Error code: %d\n", error_code);
     abort();
   }
 
   // Crop and scale.
   if (*enc == nullptr) {  // First frame will set canvas width and height.
     if (!ExtractAndCropOrScale(&pic, data, size, bit_pos)) {
-      fprintf(stderr, "ExtractAndCropOrScale failed.");
+      const WebPEncodingError error_code = pic.error_code;
       WebPPictureFree(&pic);
+      if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
+      fprintf(stderr, "ExtractAndCropOrScale failed. Error code: %d\n",
+              error_code);
       abort();
     }
   } else {  // Other frames will be resized to the first frame's dimensions.
     if (!WebPPictureRescale(&pic, *width, *height)) {
-      fprintf(stderr, "WebPPictureRescale failed. Size: %d,%d\n", *width,
-              *height);
+      const WebPEncodingError error_code = pic.error_code;
       WebPAnimEncoderDelete(*enc);
       WebPPictureFree(&pic);
+      if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
+      fprintf(stderr,
+              "WebPPictureRescale failed. Size: %d,%d. Error code: %d\n",
+              *width, *height, error_code);
       abort();
     }
   }
@@ -74,9 +82,8 @@
     *height = pic.height;
     *enc = WebPAnimEncoderNew(*width, *height, &anim_config);
     if (*enc == nullptr) {
-      fprintf(stderr, "WebPAnimEncoderNew failed.\n");
       WebPPictureFree(&pic);
-      abort();
+      return 0;
     }
   }
 
@@ -98,9 +105,11 @@
 
   // Encode.
   if (!WebPAnimEncoderAdd(*enc, &pic, timestamp_ms, &config)) {
-    fprintf(stderr, "WebPEncode failed. Error code: %d\n", pic.error_code);
+    const WebPEncodingError error_code = pic.error_code;
     WebPAnimEncoderDelete(*enc);
     WebPPictureFree(&pic);
+    if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
+    fprintf(stderr, "WebPEncode failed. Error code: %d\n", error_code);
     abort();
   }
 
@@ -147,14 +156,16 @@
 
   // Assemble.
   if (!WebPAnimEncoderAdd(enc, nullptr, timestamp_ms, nullptr)) {
-    fprintf(stderr, "Last WebPAnimEncoderAdd failed.");
+    fprintf(stderr, "Last WebPAnimEncoderAdd failed: %s.\n",
+            WebPAnimEncoderGetError(enc));
     WebPAnimEncoderDelete(enc);
     abort();
   }
   WebPData webp_data;
   WebPDataInit(&webp_data);
   if (!WebPAnimEncoderAssemble(enc, &webp_data)) {
-    fprintf(stderr, "WebPAnimEncoderAssemble failed.");
+    fprintf(stderr, "WebPAnimEncoderAssemble failed: %s.\n",
+            WebPAnimEncoderGetError(enc));
     WebPAnimEncoderDelete(enc);
     WebPDataClear(&webp_data);
     abort();
diff --git a/tests/fuzzer/enc_dec_fuzzer.cc b/tests/fuzzer/enc_dec_fuzzer.cc
index d4e59a8..187b516 100644
--- a/tests/fuzzer/enc_dec_fuzzer.cc
+++ b/tests/fuzzer/enc_dec_fuzzer.cc
@@ -18,8 +18,8 @@
 #include <stdlib.h>
 
 #include "./fuzz_utils.h"
-#include "webp/decode.h"
-#include "webp/encode.h"
+#include "src/webp/decode.h"
+#include "src/webp/encode.h"
 
 namespace {
 
@@ -42,15 +42,20 @@
 
   // Read the source picture.
   if (!ExtractSourcePicture(&pic, data, size, &bit_pos)) {
-    fprintf(stderr, "Can't read input image.\n");
+    const WebPEncodingError error_code = pic.error_code;
     WebPPictureFree(&pic);
+    if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
+    fprintf(stderr, "Can't read input image. Error code: %d\n", error_code);
     abort();
   }
 
   // Crop and scale.
   if (!ExtractAndCropOrScale(&pic, data, size, &bit_pos)) {
-    fprintf(stderr, "ExtractAndCropOrScale failed.");
+    const WebPEncodingError error_code = pic.error_code;
     WebPPictureFree(&pic);
+    if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
+    fprintf(stderr, "ExtractAndCropOrScale failed. Error code: %d\n",
+            error_code);
     abort();
   }
 
@@ -83,9 +88,11 @@
   pic.writer = WebPMemoryWrite;
   pic.custom_ptr = &memory_writer;
   if (!WebPEncode(&config, &pic)) {
-    fprintf(stderr, "WebPEncode failed. Error code: %d\n", pic.error_code);
+    const WebPEncodingError error_code = pic.error_code;
     WebPMemoryWriterClear(&memory_writer);
     WebPPictureFree(&pic);
+    if (error_code == VP8_ENC_ERROR_OUT_OF_MEMORY) return 0;
+    fprintf(stderr, "WebPEncode failed. Error code: %d\n", error_code);
     abort();
   }
 
diff --git a/tests/fuzzer/fuzz_utils.h b/tests/fuzzer/fuzz_utils.h
index 1d74611..713a5f4 100644
--- a/tests/fuzzer/fuzz_utils.h
+++ b/tests/fuzzer/fuzz_utils.h
@@ -23,8 +23,8 @@
 #include "./img_alpha.h"
 #include "./img_grid.h"
 #include "./img_peak.h"
-#include "dsp/dsp.h"
-#include "webp/encode.h"
+#include "src/dsp/dsp.h"
+#include "src/webp/encode.h"
 
 //------------------------------------------------------------------------------
 // Arbitrary limits to prevent OOM, timeout, or slow execution.
diff --git a/tests/fuzzer/makefile.unix b/tests/fuzzer/makefile.unix
index e242563..4a9bff3 100644
--- a/tests/fuzzer/makefile.unix
+++ b/tests/fuzzer/makefile.unix
@@ -9,6 +9,7 @@
 LDFLAGS = -fsanitize=fuzzer
 LDLIBS = ../../src/mux/libwebpmux.a ../../src/demux/libwebpdemux.a
 LDLIBS += ../../src/libwebp.a ../../imageio/libimageio_util.a
+LDLIBS += ../../sharpyuv/libsharpyuv.a
 
 FUZZERS = advanced_api_fuzzer animation_api_fuzzer animencoder_fuzzer
 FUZZERS += animdecoder_fuzzer mux_demux_api_fuzzer enc_dec_fuzzer
diff --git a/tests/fuzzer/mux_demux_api_fuzzer.c b/tests/fuzzer/mux_demux_api_fuzzer.c
index a8f81bf..4ed0142 100644
--- a/tests/fuzzer/mux_demux_api_fuzzer.c
+++ b/tests/fuzzer/mux_demux_api_fuzzer.c
@@ -15,8 +15,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 #include "./fuzz_utils.h"
-#include "webp/demux.h"
-#include "webp/mux.h"
+#include "src/webp/demux.h"
+#include "src/webp/mux.h"
 
 int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
   WebPData webp_data;
diff --git a/tests/fuzzer/simple_api_fuzzer.c b/tests/fuzzer/simple_api_fuzzer.c
index fbc9310..7d2b7f8 100644
--- a/tests/fuzzer/simple_api_fuzzer.c
+++ b/tests/fuzzer/simple_api_fuzzer.c
@@ -15,7 +15,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 #include "./fuzz_utils.h"
-#include "webp/decode.h"
+#include "src/webp/decode.h"
 
 int LLVMFuzzerTestOneInput(const uint8_t* const data, size_t size) {
   int w, h;
diff --git a/webp_js/README.md b/webp_js/README.md
new file mode 100644
index 0000000..824afa0
--- /dev/null
+++ b/webp_js/README.md
@@ -0,0 +1,81 @@
+# WebP JavaScript decoder
+
+```
+ __   __ ____ ____ ____     __  ____
+/  \\/  \  _ \  _ \  _ \   (__)/  __\
+\       /  __/ _  \  __/   _)  \_   \
+ \__\__/_____/____/_/     /____/____/
+```
+
+This file describes the compilation of libwebp into a JavaScript decoder using
+Emscripten and CMake.
+
+-   install the Emscripten SDK following the procedure described at:
+    https://emscripten.org/docs/getting_started/downloads.html#installation-instructions-using-the-emsdk-recommended
+    After installation, you should have some global variable positioned to the
+    location of the SDK. In particular, $EMSDK should point to the top-level
+    directory containing Emscripten tools.
+
+-   configure the project 'WEBP_JS' with CMake using:
+
+    ```shell
+    cd webp_js && \
+    emcmake cmake -DWEBP_BUILD_WEBP_JS=ON \
+          ../
+    ```
+
+-   compile webp.js using 'emmake make'.
+
+-   that's it! Upon completion, you should have the 'webp.js', 'webp.js.mem',
+    'webp_wasm.js' and 'webp_wasm.wasm' files generated.
+
+The callable JavaScript function is WebPToSDL(), which decodes a raw WebP
+bitstream into a canvas. See webp_js/index.html for a simple usage sample (see
+below for instructions).
+
+## Demo HTML page
+
+The HTML page webp_js/index.html requires the built files 'webp.js' and
+'webp.js.mem' to be copied to webp_js/. An HTTP server to serve the WebP image
+example is also needed. With Python, just run:
+
+```shell
+cd webp_js && python3 -m http.server 8080
+```
+
+and then navigate to http://localhost:8080 in your favorite browser.
+
+## Web-Assembly (WASM) version:
+
+CMakeLists.txt is configured to build the WASM version when using the option
+WEBP_BUILD_WEBP_JS=ON. The compilation step will assemble the files
+'webp_wasm.js' and 'webp_wasm.wasm' that you then need to copy to the webp_js/
+directory.
+
+See webp_js/index_wasm.html for a simple demo page using the WASM version of the
+library.
+
+You will need a fairly recent version of Emscripten (at least 2.0.18,
+latest-upstream is recommended) and of your WASM-enabled browser to run this
+version.
+
+## Caveats
+
+-   First decoding using the library is usually slower, due to just-in-time
+    compilation.
+
+-   Some versions of llvm produce the following compile error when SSE2 is
+    enabled.
+
+    ```
+    "Unsupported:   %516 = bitcast <8 x i16> %481 to i128
+    LLVM ERROR: BitCast Instruction not yet supported for integer types larger than 64 bits"
+    ```
+
+    The corresponding Emscripten bug is at:
+    https://github.com/kripken/emscripten/issues/3788
+
+    Therefore, SSE2 optimization is currently disabled in CMakeLists.txt.
+
+-   If WEBP_ENABLE_SIMD is set to 1 the JavaScript version (webp.js) will be
+    disabled as wasm2js does not support SIMD.
diff --git a/webp_js/index.html b/webp_js/index.html
new file mode 100644
index 0000000..33cacb4
--- /dev/null
+++ b/webp_js/index.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <meta charset="UTF-8">
+  <title>simple Javascript WebP decoding demo</title>
+  <script type="text/javascript">
+    var Module = {
+      noInitialRun : true
+    };
+  </script>
+  <script type="text/javascript" src="./webp.js"></script>
+  <script type="text/javascript">
+
+'use strict';
+
+// main wrapper for the function decoding a WebP into a canvas object
+var WebpToCanvas;
+
+function init() {
+  WebpToCanvas = Module.cwrap('WebPToSDL', 'number', ['array', 'number']);
+}
+window.onload = init;
+
+function decode(webp_data, canvas_id) {
+  // get the canvas to decode into
+  var canvas = document.getElementById(canvas_id);
+  if (canvas == null) return;
+  // clear previous picture (if any)
+  Module.canvas = canvas;
+  canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
+  // decode and measure timing
+  var start = new Date();
+  var ret = WebpToCanvas(webp_data, webp_data.length);
+  var end = new Date();
+  var speed_result = document.getElementById('timing');
+  // display timing result
+  if (speed_result != null) {
+    var decode_time = end - start;
+    speed_result.innerHTML = '<p>decoding time: ' + decode_time +' ms.</p>';
+  }
+}
+
+function loadfile(filename, canvas_id) {
+  var xhr = new XMLHttpRequest();
+  xhr.open('GET', filename);
+  xhr.responseType = 'arraybuffer';
+  xhr.onreadystatechange = function() {
+    if (xhr.readyState == 4 && xhr.status == 200) {
+      var webp_data = new Uint8Array(xhr.response);
+      decode(webp_data, canvas_id);
+    }
+  };
+  xhr.send();
+}
+  </script>
+</head>
+
+<body>
+  <p>
+    <strong>WebP in JavaScript demo</strong> -
+  </p>
+  <p>
+    WebP decoder in JavaScript, using libwebp compiled with
+    <a href="https://github.com/kripken/emscripten/wiki">Emscripten</a>.
+  </p>
+  <p id="image_buttons">
+    <input type="button" value="test image!" name="./test_webp_js.webp"
+           onclick="loadfile(this.name, 'output_canvas')">
+  </p>
+  <p id="timing">Timing: N/A</p>
+  <canvas id="output_canvas">Your browser does not support canvas</canvas>
+
+</body>
+</html>
diff --git a/webp_js/index_wasm.html b/webp_js/index_wasm.html
new file mode 100644
index 0000000..5d7c17e
--- /dev/null
+++ b/webp_js/index_wasm.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <meta charset="UTF-8">
+  <title>simple Javascript WebP decoding demo, using Web-Assembly (WASM)</title>
+  <script type="text/javascript">
+    var Module = {
+      noInitialRun : true
+    };
+  </script>
+  <script type="text/javascript">
+
+'use strict';
+
+// main wrapper for the function decoding a WebP into a canvas object
+var WebpToCanvas;
+
+function init() {
+  var xhr = new XMLHttpRequest();
+  xhr.open('GET', 'webp_wasm.wasm', true);
+  xhr.responseType = 'arraybuffer';
+  xhr.onload = function() {
+    Module.wasmBinary = xhr.response;
+    var script = document.createElement('script');
+    script.src = "webp_wasm.js";
+    document.body.appendChild(script);
+  };
+  xhr.send(null);
+}
+window.onload = init;
+
+function decode(webp_data, canvas_id) {
+  var result;
+  if (Module["asm"] != undefined) {
+    // wrapper for the function decoding a WebP into a canvas object
+    WebpToCanvas = Module.cwrap('WebPToSDL', 'number', ['array', 'number']);
+    // get the canvas to decode into
+    var canvas = document.getElementById(canvas_id);
+    if (canvas == null) return;
+    // clear previous picture (if any)
+    Module.canvas = canvas;
+    canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
+    // decode and measure timing
+    var start = new Date();
+    var ret = WebpToCanvas(webp_data, webp_data.length);
+    var end = new Date();
+    var decode_time = end - start;
+    result = 'decoding time: ' + decode_time +' ms.';
+  } else {
+    result = "WASM module not finished loading! Please retry";
+  }
+  // display timing result
+  var speed_result = document.getElementById('timing');
+  if (speed_result != null) {
+    speed_result.innerHTML = '<p>'+ result + '</p>';
+  }
+}
+
+function loadfile(filename, canvas_id) {
+  var xhr = new XMLHttpRequest();
+  xhr.open('GET', filename);
+  xhr.responseType = 'arraybuffer';
+  xhr.onreadystatechange = function() {
+    if (xhr.readyState == 4 && xhr.status == 200) {
+      var webp_data = new Uint8Array(xhr.response);
+      decode(webp_data, canvas_id);
+    }
+  };
+  xhr.send();
+}
+  </script>
+</head>
+
+<body>
+  <p>
+    <strong>WebP demo using Web-Assembly</strong> -
+  </p>
+  <p>
+    WASM version of the WebP decoder, using libwebp compiled with
+    <a href="https://github.com/kripken/emscripten/wiki">Emscripten</a>.
+  </p>
+  <p id="image_buttons">
+    <input type="button" value="test image!"
+           onclick="loadfile('./test_webp_wasm.webp', 'output_canvas')">
+  </p>
+  <p id="timing">Timing: N/A</p>
+  <canvas id="output_canvas">Your browser does not support canvas</canvas>
+</body>
+</html>
diff --git a/webp_js/test_webp_js.webp b/webp_js/test_webp_js.webp
new file mode 100644
index 0000000..f798f55
--- /dev/null
+++ b/webp_js/test_webp_js.webp
Binary files differ
diff --git a/webp_js/test_webp_wasm.webp b/webp_js/test_webp_wasm.webp
new file mode 100644
index 0000000..f798f55
--- /dev/null
+++ b/webp_js/test_webp_wasm.webp
Binary files differ
diff --git a/xcframeworkbuild.sh b/xcframeworkbuild.sh
new file mode 100755
index 0000000..8d484c2
--- /dev/null
+++ b/xcframeworkbuild.sh
@@ -0,0 +1,255 @@
+#!/bin/bash
+#
+# This script generates 'WebP.xcframework', 'WebPDecoder.xcframework',
+# 'WebPDemux.xcframework' and 'WebPMux.xcframework'.
+# An iOS, Mac or Mac Catalyst app can decode WebP images by including
+# 'WebPDecoder.xcframework' and both encode and decode WebP images by including
+# 'WebP.xcframework'.
+#
+# Run ./xcframeworkbuild.sh to generate the frameworks under the current
+# directory (the previous build will be erased if it exists).
+#
+
+set -e
+
+# Set these variables based on the desired minimum deployment target.
+readonly IOS_MIN_VERSION=6.0
+readonly MACOSX_MIN_VERSION=10.15
+readonly MACOSX_CATALYST_MIN_VERSION=14.0
+
+# Extract Xcode version.
+readonly XCODE=$(xcodebuild -version | grep Xcode | cut -d " " -f2)
+if [[ -z "${XCODE}" ]] || [[ "${XCODE%%.*}" -lt 11 ]]; then
+  echo "Xcode 11.0 or higher is required!"
+  exit 1
+fi
+
+# Extract the latest SDK version from the final field of the form: iphoneosX.Y
+# / macosxX.Y
+readonly SDK=($(
+  xcodebuild -showsdks \
+    | grep iphoneos | sort | tail -n 1 | awk '{print substr($NF, 9)}'
+  xcodebuild -showsdks \
+    | grep macosx | sort | tail -n 1 | awk '{print substr($NF, 7)}'
+))
+readonly IOS=0
+readonly MACOS=1
+readonly IOS_SIMULATOR=2
+readonly MACOS_CATALYST=3
+readonly NUM_PLATFORMS=4
+
+readonly OLDPATH=${PATH}
+
+# Names should be of the form '<platform>-[<variant>-]<architecture>'.
+PLATFORMS[$IOS]="iPhoneOS-armv7 iPhoneOS-armv7s iPhoneOS-arm64"
+PLATFORMS[$IOS_SIMULATOR]="iPhoneSimulator-i386 iPhoneSimulator-x86_64"
+PLATFORMS[$MACOS]="MacOSX-x86_64"
+PLATFORMS[$MACOS_CATALYST]="MacOSX-Catalyst-x86_64"
+if [[ "${XCODE%%.*}" -ge 12 ]]; then
+  PLATFORMS[$MACOS]+=" MacOSX-arm64"
+  PLATFORMS[$MACOS_CATALYST]+=" MacOSX-Catalyst-arm64"
+  PLATFORMS[$IOS_SIMULATOR]+=" iPhoneSimulator-arm64"
+elif [[ "${XCODE%%.*}" -eq 11 ]]; then
+  cat << EOF
+WARNING: Xcode 12.0 or higher is required to build targets for
+WARNING: Apple Silicon (arm64). The XCFrameworks generated with Xcode 11 will
+WARNING: contain libraries for MacOS & Catalyst supporting x86_64 only.
+WARNING: The build will continue in 5 seconds...
+EOF
+  sleep 5
+else
+  echo "Xcode 11.0 or higher is required!"
+  exit 1
+fi
+readonly PLATFORMS
+readonly SRCDIR=$(dirname $0)
+readonly TOPDIR=$(pwd)
+readonly BUILDDIR="${TOPDIR}/xcframeworkbuild"
+readonly TARGETDIR="${TOPDIR}/WebP.xcframework"
+readonly DECTARGETDIR="${TOPDIR}/WebPDecoder.xcframework"
+readonly MUXTARGETDIR="${TOPDIR}/WebPMux.xcframework"
+readonly DEMUXTARGETDIR="${TOPDIR}/WebPDemux.xcframework"
+readonly DEVELOPER=$(xcode-select --print-path)
+readonly DEVROOT="${DEVELOPER}/Toolchains/XcodeDefault.xctoolchain"
+readonly PLATFORMSROOT="${DEVELOPER}/Platforms"
+readonly LIPO=$(xcrun -sdk iphoneos${SDK[$IOS]} -find lipo)
+
+if [[ -z "${SDK[$IOS]}" ]] || [[ ${SDK[$IOS]%%.*} -lt 8 ]]; then
+  echo "iOS SDK version 8.0 or higher is required!"
+  exit 1
+fi
+
+#######################################
+# Moves Headers/*.h to Headers/<framework>/
+#
+# Places framework headers in a subdirectory to avoid Xcode errors when using
+# multiple frameworks:
+#   error: Multiple commands produce
+#     '.../Build/Products/Debug-iphoneos/include/types.h'
+# Arguments:
+#   $1 - path to framework
+#######################################
+update_headers_path() {
+  local framework_name="$(basename ${1%.xcframework})"
+  local subdir
+  for d in $(find "$1" -path "*/Headers"); do
+    subdir="$d/$framework_name"
+    mkdir "$subdir"
+    mv "$d/"*.h "$subdir"
+  done
+}
+
+echo "Xcode Version: ${XCODE}"
+echo "iOS SDK Version: ${SDK[$IOS]}"
+echo "MacOS SDK Version: ${SDK[$MACOS]}"
+
+if [[ -e "${BUILDDIR}" || -e "${TARGETDIR}" || -e "${DECTARGETDIR}" \
+      || -e "${MUXTARGETDIR}" || -e "${DEMUXTARGETDIR}" ]]; then
+  cat << EOF
+WARNING: The following directories will be deleted:
+WARNING:   ${BUILDDIR}
+WARNING:   ${TARGETDIR}
+WARNING:   ${DECTARGETDIR}
+WARNING:   ${MUXTARGETDIR}
+WARNING:   ${DEMUXTARGETDIR}
+WARNING: The build will continue in 5 seconds...
+EOF
+  sleep 5
+fi
+rm -rf ${BUILDDIR} ${TARGETDIR} ${DECTARGETDIR} \
+  ${MUXTARGETDIR} ${DEMUXTARGETDIR}
+
+if [[ ! -e ${SRCDIR}/configure ]]; then
+  if ! (cd ${SRCDIR} && sh autogen.sh); then
+    cat << EOF
+Error creating configure script!
+This script requires the autoconf/automake and libtool to build. MacPorts or
+Homebrew can be used to obtain these:
+https://www.macports.org/install.php
+https://brew.sh/
+EOF
+    exit 1
+  fi
+fi
+
+for (( i = 0; i < $NUM_PLATFORMS; ++i )); do
+  LIBLIST=()
+  DECLIBLIST=()
+  MUXLIBLIST=()
+  DEMUXLIBLIST=()
+
+  for PLATFORM in ${PLATFORMS[$i]}; do
+    ROOTDIR="${BUILDDIR}/${PLATFORM}"
+    mkdir -p "${ROOTDIR}"
+
+    ARCH="${PLATFORM##*-}"
+    case "${PLATFORM}" in
+      iPhone*)
+        sdk="${SDK[$IOS]}"
+        ;;
+      MacOS*)
+        sdk="${SDK[$MACOS]}"
+        ;;
+      *)
+        echo "Unrecognized platform: ${PLATFORM}!"
+        exit 1
+        ;;
+    esac
+
+    SDKROOT="${PLATFORMSROOT}/${PLATFORM%%-*}.platform/"
+    SDKROOT+="Developer/SDKs/${PLATFORM%%-*}${sdk}.sdk/"
+    CFLAGS="-pipe -isysroot ${SDKROOT} -O3 -DNDEBUG"
+    case "${PLATFORM}" in
+      iPhone*)
+        CFLAGS+=" -fembed-bitcode"
+        CFLAGS+=" -target ${ARCH}-apple-ios${IOS_MIN_VERSION}"
+        [[ "${PLATFORM}" == *Simulator* ]] && CFLAGS+="-simulator"
+        ;;
+      MacOSX-Catalyst*)
+        CFLAGS+=" -target"
+        CFLAGS+=" ${ARCH}-apple-ios${MACOSX_CATALYST_MIN_VERSION}-macabi"
+        ;;
+      MacOSX*)
+        CFLAGS+=" -mmacosx-version-min=${MACOSX_MIN_VERSION}"
+        ;;
+    esac
+
+    set -x
+    export PATH="${DEVROOT}/usr/bin:${OLDPATH}"
+    ${SRCDIR}/configure --host=${ARCH/arm64/aarch64}-apple-darwin \
+      --build=$(${SRCDIR}/config.guess) \
+      --prefix=${ROOTDIR} \
+      --disable-shared --enable-static \
+      --enable-libwebpdecoder --enable-swap-16bit-csp \
+      --enable-libwebpmux \
+      CC="clang -arch ${ARCH}" \
+      CFLAGS="${CFLAGS}"
+    set +x
+
+    # Build only the libraries, skip the examples.
+    make V=0 -C sharpyuv
+    make V=0 -C src install
+
+    LIBLIST+=("${ROOTDIR}/lib/libwebp.a")
+    DECLIBLIST+=("${ROOTDIR}/lib/libwebpdecoder.a")
+    MUXLIBLIST+=("${ROOTDIR}/lib/libwebpmux.a")
+    DEMUXLIBLIST+=("${ROOTDIR}/lib/libwebpdemux.a")
+    # xcodebuild requires a directory for the -headers option, these will match
+    # for all builds.
+    make -C src install-data DESTDIR="${ROOTDIR}/lib-headers"
+    make -C src install-commonHEADERS DESTDIR="${ROOTDIR}/dec-headers"
+    make -C src/demux install-data DESTDIR="${ROOTDIR}/demux-headers"
+    make -C src/mux install-data DESTDIR="${ROOTDIR}/mux-headers"
+    LIB_HEADERS="${ROOTDIR}/lib-headers/${ROOTDIR}/include/webp"
+    DEC_HEADERS="${ROOTDIR}/dec-headers/${ROOTDIR}/include/webp"
+    DEMUX_HEADERS="${ROOTDIR}/demux-headers/${ROOTDIR}/include/webp"
+    MUX_HEADERS="${ROOTDIR}/mux-headers/${ROOTDIR}/include/webp"
+
+    make distclean
+
+    export PATH=${OLDPATH}
+  done
+
+  [[ -z "${LIBLIST[@]}" ]] && continue
+
+  # Create a temporary target directory for each <platform>[-<variant>].
+  target_dir="${BUILDDIR}/${PLATFORMS[$i]}"
+  target_dir="${target_dir%% *}"
+  target_dir="${target_dir%-*}"
+  target_lib="${target_dir}/$(basename ${LIBLIST[0]})"
+  target_declib="${target_dir}/$(basename ${DECLIBLIST[0]})"
+  target_demuxlib="${target_dir}/$(basename ${DEMUXLIBLIST[0]})"
+  target_muxlib="${target_dir}/$(basename ${MUXLIBLIST[0]})"
+
+  mkdir -p "${target_dir}"
+  ${LIPO} -create ${LIBLIST[@]} -output "${target_lib}"
+  ${LIPO} -create ${DECLIBLIST[@]} -output "${target_declib}"
+  ${LIPO} -create ${DEMUXLIBLIST[@]} -output "${target_demuxlib}"
+  ${LIPO} -create ${MUXLIBLIST[@]} -output "${target_muxlib}"
+  FAT_LIBLIST+=(-library "${target_lib}" -headers "${LIB_HEADERS}")
+  FAT_DECLIBLIST+=(-library "${target_declib}" -headers "${DEC_HEADERS}")
+  FAT_DEMUXLIBLIST+=(-library "${target_demuxlib}" -headers "${DEMUX_HEADERS}")
+  FAT_MUXLIBLIST+=(-library "${target_muxlib}" -headers "${MUX_HEADERS}")
+done
+
+# lipo will not put archives with the same architecture (e.g., x86_64
+# iPhoneSimulator & MacOS) in the same fat output file. xcodebuild
+# -create-xcframework requires universal archives to avoid e.g.:
+#   Both ios-x86_64-maccatalyst and ios-arm64-maccatalyst represent two
+#   equivalent library definitions
+set -x
+xcodebuild -create-xcframework "${FAT_LIBLIST[@]}" \
+  -output ${TARGETDIR}
+xcodebuild -create-xcframework "${FAT_DECLIBLIST[@]}" \
+  -output ${DECTARGETDIR}
+xcodebuild -create-xcframework "${FAT_DEMUXLIBLIST[@]}" \
+  -output ${DEMUXTARGETDIR}
+xcodebuild -create-xcframework "${FAT_MUXLIBLIST[@]}" \
+  -output ${MUXTARGETDIR}
+update_headers_path "${TARGETDIR}"
+update_headers_path "${DECTARGETDIR}"
+update_headers_path "${DEMUXTARGETDIR}"
+update_headers_path "${MUXTARGETDIR}"
+set +x
+
+echo  "SUCCESS"