[
  {
    "path": ".gitignore",
    "content": "bin/\nobj/\n/bazel-*\n\n# Visual Studio 2015 cache/options directory\n.vs/\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n"
  },
  {
    "path": ".travis.sh",
    "content": "#!/bin/bash\n\ncase \"$1\" in\n    \"install\")\n\tcase \"${TRAVIS_OS_NAME}\" in\n\t    \"linux\")\n\t\t;;\n\t    \"osx\")\n\t\tbrew update\n\t\tbrew install netpbm\n\t\t;;\n\tesac\n\tcase \"${BUILD_SYSTEM}\" in\n\t    \"bazel\")\n\t\tcase \"${TRAVIS_OS_NAME}\" in\n\t\t    \"linux\")\n\t\t\tsudo apt-get remove openjdk-9-jdk oracle-java9-installer # Conflicts with Bazel.\n\t\t\twget https://github.com/bazelbuild/bazel/releases/download/1.1.0/bazel_1.1.0-linux-x86_64.deb\n\t\t\techo '138b47ffd54924e3c0264c65d31d3927803fb9025db4d5b18107df79ee3bda95  bazel_1.1.0-linux-x86_64.deb' | sha256sum -c --strict || exit 1\n\t\t\tsudo dpkg -i bazel_1.1.0-linux-x86_64.deb\n\t\t\t;;\n\t\t    \"osx\")\n\t\t\tbrew cask install homebrew/cask-versions/adoptopenjdk8\n\t\t\tbrew install bazel\n\t\t\t;;\n\t\tesac\n\t\t;;\n\t    \"make\")\n\t\t;;\n\tesac\n\t;;\n    \"script\")\n\tcase \"${BUILD_SYSTEM}\" in\n\t    \"bazel\")\n\t\tbazel build -c opt ...:all\n\t\tGUETZLI_BIN=bazel-bin/guetzli\n\t\t;;\n\t    \"make\")\n\t\tmake\n\t\tGUETZLI_BIN=bin/Release/guetzli\n\t\t;;\n\tesac\n\ttests/smoke_test.sh $GUETZLI_BIN\n\t;;\nesac\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: cpp\nsudo: false\nmatrix:\n  include:\n    - os: linux\n      dist: trusty\n      sudo: required\n      env: BUILD_SYSTEM=bazel\n      addons:\n        apt:\n          sources:\n            - ubuntu-toolchain-r-test\n          packages:\n            - libjpeg-progs\n            - netpbm # For pngtopnm in smoke_test.sh.\n            - wget\n\n    - os: osx\n      env: BUILD_SYSTEM=bazel\n\n    - os: linux\n      dist: trusty\n      env: BUILD_SYSTEM=make\n      addons:\n        apt:\n          sources:\n            - ubuntu-toolchain-r-test\n          packages:\n            - libjpeg-progs\n            - libpng-dev\n            - netpbm # For pngtopnm in smoke_test.sh.\n            - pkg-config\n            - wget\n\n    - os: osx\n      env: BUILD_SYSTEM=make\n\n\ninstall:\n- ./.travis.sh install\nscript:\n- ./.travis.sh script\n"
  },
  {
    "path": "BUILD",
    "content": "# Description:\n#   Bazel build file for Guetzli.\n\ncc_library(\n    name = \"guetzli_lib\",\n    srcs = glob(\n        [\n            \"guetzli/*.h\",\n            \"guetzli/*.cc\",\n            \"guetzli/*.inc\",\n        ],\n        exclude = [\"guetzli/guetzli.cc\"],\n    ),\n    copts = [ \"-Wno-sign-compare\" ],\n    deps = [\n        \"@butteraugli//:butteraugli_lib\",\n    ],\n)\n\ncc_binary(\n    name = \"guetzli\",\n    srcs = [\"guetzli/guetzli.cc\"],\n    deps = [\n        \":guetzli_lib\",\n        \"@png_archive//:png\",\n    ],\n)\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "Want to contribute bugfixes or non-output-modifying changes (e.g. speedups)?\nGreat! First, read this page (including the small print at the end). Want to\ncontribute changes that modify the generated JPEG file? Talk to us first.\n\n### Before you contribute\nBefore we can use your code, you must sign the\n[Google Individual Contributor License Agreement](https://cla.developers.google.com/about/google-individual)\n(CLA), which you can do online. The CLA is necessary mainly because you own the\ncopyright to your changes, even after your contribution becomes part of our\ncodebase, so we need your permission to use and distribute your code. We also\nneed to be sure of various other things—for instance that you'll tell us if you\nknow that your code infringes on other people's patents. You don't have to sign\nthe CLA until after you've submitted your code for review and a member has\napproved it, but you must do it before we can put your code into our codebase.\nBefore you start working on a larger contribution, you should get in touch with\nus first through the issue tracker with your idea so that we can help out and\npossibly guide you. Coordinating up front makes it much easier to avoid\nfrustration later on.\n\n### Code reviews\nAll submissions, including submissions by project members, require review. We\nuse Github pull requests for this purpose.\n\n### The small print\nContributions made by corporations are covered by a different agreement than\nthe one above, the\n[Software Grant and Corporate Contributor License Agreement](https://cla.developers.google.com/about/google-corporate).\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "Makefile",
    "content": "# GNU Make workspace makefile autogenerated by Premake\n\nifndef config\n  config=release\nendif\n\nifndef verbose\n  SILENT = @\nendif\n\nifeq ($(config),release)\n  guetzli_static_config = release\n  guetzli_config = release\nendif\nifeq ($(config),debug)\n  guetzli_static_config = debug\n  guetzli_config = debug\nendif\n\nPROJECTS := guetzli_static guetzli\n\n.PHONY: all clean help $(PROJECTS) \n\nall: $(PROJECTS)\n\nguetzli_static:\nifneq (,$(guetzli_static_config))\n\t@echo \"==== Building guetzli_static ($(guetzli_static_config)) ====\"\n\t@${MAKE} --no-print-directory -C . -f guetzli_static.make config=$(guetzli_static_config)\nendif\n\nguetzli:\nifneq (,$(guetzli_config))\n\t@echo \"==== Building guetzli ($(guetzli_config)) ====\"\n\t@${MAKE} --no-print-directory -C . -f guetzli.make config=$(guetzli_config)\nendif\n\nclean:\n\t@${MAKE} --no-print-directory -C . -f guetzli_static.make clean\n\t@${MAKE} --no-print-directory -C . -f guetzli.make clean\n\nhelp:\n\t@echo \"Usage: make [config=name] [target]\"\n\t@echo \"\"\n\t@echo \"CONFIGURATIONS:\"\n\t@echo \"  release\"\n\t@echo \"  debug\"\n\t@echo \"\"\n\t@echo \"TARGETS:\"\n\t@echo \"   all (default)\"\n\t@echo \"   clean\"\n\t@echo \"   guetzli_static\"\n\t@echo \"   guetzli\"\n\t@echo \"\"\n\t@echo \"For more information, see http://industriousone.com/premake/quick-start\""
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\"><img src=\"https://cloud.githubusercontent.com/assets/203457/24553916/1f3f88b6-162c-11e7-990a-731b2560f15c.png\" alt=\"Guetzli\" width=\"64\"></p>\n\n# Introduction\n\nGuetzli is a JPEG encoder that aims for excellent compression density at high\nvisual quality. Guetzli-generated images are typically 20-30% smaller than\nimages of equivalent quality generated by libjpeg. Guetzli generates only\nsequential (nonprogressive) JPEGs due to faster decompression speeds they offer.\n\n[![Build Status](https://travis-ci.org/google/guetzli.svg?branch=master)](https://travis-ci.org/google/guetzli)\n\n# Building\n\n## On POSIX systems\n\n1.  Get a copy of the source code, either by cloning this repository, or by\n    downloading an\n    [archive](https://github.com/google/guetzli/archive/master.zip) and\n    unpacking it.\n2.  Install [libpng](http://www.libpng.org/pub/png/libpng.html).\n    If using your operating system\n    package manager, install development versions of the packages if the\n    distinction exists.\n    *   On Ubuntu, do `apt-get install libpng-dev`.\n    *   On Fedora, do `dnf install libpng-devel`. \n    *   On Arch Linux, do `pacman -S libpng`.\n    *   On Alpine Linux, do `apk add libpng-dev`.\n3.  Run `make` and expect the binary to be created in `bin/Release/guetzli`.\n\n## On Windows\n\n1.  Get a copy of the source code, either by cloning this repository, or by\n    downloading an\n    [archive](https://github.com/google/guetzli/archive/master.zip) and\n    unpacking it.\n2.  Install [Visual Studio 2015](https://www.visualstudio.com) and\n    [vcpkg](https://github.com/Microsoft/vcpkg)\n3.  Install `libpng` using vcpkg: `.\\vcpkg install libpng`.\n4.  Cause the installed packages to be available system-wide: `.\\vcpkg integrate\n    install`. If you prefer not to do this, refer to [vcpkg's\n    documentation](https://github.com/Microsoft/vcpkg/blob/master/docs/EXAMPLES.md#example-1-2).\n5.  Open the Visual Studio project enclosed in the repository and build it.\n\n## On macOS\n\nTo install using [Homebrew](https://brew.sh/):\n1. Install [Homebrew](https://brew.sh/)\n2. `brew install guetzli`\n\nTo install using the repository:\n1.  Get a copy of the source code, either by cloning this repository, or by\n    downloading an\n    [archive](https://github.com/google/guetzli/archive/master.zip) and\n    unpacking it.\n2.  Install [Homebrew](https://brew.sh/) or [MacPorts](https://www.macports.org/)\n3.  Install `libpng`\n    *   Using [Homebrew](https://brew.sh/): `brew install libpng`.\n    *   Using [MacPorts](https://www.macports.org/): `port install libpng` (You may need to use `sudo`).\n4.  Run the following command to build the binary in `bin/Release/guetzli`.\n    *   If you installed using [Homebrew](https://brew.sh/) simply use `make`\n    *   If you installed using [MacPorts](https://www.macports.org/) use `CFLAGS='-I/opt/local/include' LDFLAGS='-L/opt/local/lib' make`\n\n## With Bazel\n\nThere's also a [Bazel](https://bazel.build) build configuration provided. If you\nhave Bazel installed, you can also compile Guetzli by running `bazel build -c opt //:guetzli`.\n\n# Using\n\n**Note:** Guetzli uses a large amount of memory. You should provide 300MB of\nmemory per 1MPix of the input image.\n\n**Note:** Guetzli uses a significant amount of CPU time. You should count on\nusing about 1 minute of CPU per 1 MPix of input image.\n\n**Note:** Guetzli assumes that input is in **sRGB profile** with a **gamma of\n2.2**. Guetzli will ignore any color-profile metadata in the image.\n\nTo try out Guetzli you need to [build](#building) or\n[download](https://github.com/google/guetzli/releases) the Guetzli binary. The\nbinary reads a PNG or JPEG image and creates an optimized JPEG image:\n\n```bash\nguetzli [--quality Q] [--verbose] original.png output.jpg\nguetzli [--quality Q] [--verbose] original.jpg output.jpg\n```\n\nNote that Guetzli is designed to work on high quality images. You should always\nprefer providing uncompressed input images (e.g. that haven't been already\ncompressed with any JPEG encoders, including Guetzli). While it will work on other\nimages too, results will be poorer. You can try compressing an enclosed [sample\nhigh quality\nimage](https://github.com/google/guetzli/releases/download/v0/bees.png).\n\nYou can pass a `--quality Q` parameter to set quality in units equivalent to\nlibjpeg quality. You can also pass a `--verbose` flag to see a trace of encoding\nattempts made.\n\nPlease note that JPEG images do not support alpha channel (transparency). If the\ninput is a PNG with an alpha channel, it will be overlaid on black background\nbefore encoding.\n"
  },
  {
    "path": "WORKSPACE",
    "content": "# Description:\n#   Bazel workspace file for Guetzli.\n\nworkspace(name = \"guetzli\")\n\nload(\"@bazel_tools//tools/build_defs/repo:http.bzl\", \"http_archive\")\n\nhttp_archive(\n    name = \"png_archive\",\n    build_file = \"png.BUILD\",\n    sha256 = \"a941dc09ca00148fe7aaf4ecdd6a67579c293678ed1e1cf633b5ffc02f4f8cf7\",\n    strip_prefix = \"libpng-1.2.57\",\n    url = \"http://github.com/glennrp/libpng/archive/v1.2.57.zip\",\n)\n\nhttp_archive(\n    name = \"zlib_archive\",\n    build_file = \"zlib.BUILD\",\n    sha256 = \"8d7e9f698ce48787b6e1c67e6bff79e487303e66077e25cb9784ac8835978017\",\n    strip_prefix = \"zlib-1.2.10\",\n    url = \"http://zlib.net/fossils/zlib-1.2.10.tar.gz\",\n)\n\nlocal_repository(\n    name = \"butteraugli\",\n    path = \"third_party/butteraugli/\",\n)\n"
  },
  {
    "path": "appveyor.yml",
    "content": "version: '1.0.1#{build}'\n\nshallow_clone: true\n\nos:\n  - Visual Studio 2015\n\nenvironment:\n  matrix:\n  - TOOLSET: vs2015\n\ninstall:\n  - ps: Start-FileDownload 'https://github.com/premake/premake-core/releases/download/v5.0.0-alpha11/premake-5.0.0-alpha11-windows.zip' 'premake.zip'\n  - 7z x premake.zip\n  - premake5.exe %TOOLSET%\n  - git clone https://github.com/Microsoft/vcpkg\n  - md vcpkg\\downloads\\nuget-3.5.0\n  - appveyor DownloadFile https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -FileName %appveyor_build_folder%\\vcpkg\\downloads\\nuget-3.5.0\\nuget.exe\n  - appveyor DownloadFile https://cmake.org/files/v3.8/cmake-3.8.0-rc1-win32-x86.zip -FileName %appveyor_build_folder%\\vcpkg\\downloads\\cmake-3.8.0-rc1-win32-x86.zip\n  - 7z x %appveyor_build_folder%\\vcpkg\\downloads\\cmake-3.8.0-rc1-win32-x86.zip\n  - cd vcpkg\n  - powershell -exec bypass -File scripts\\bootstrap.ps1\n  - vcpkg integrate install\n  - vcpkg install libpng\n  - cd ..\n\nconfiguration:\n  - Debug\n  - Release\n\nbuild:\n  project: guetzli.sln\n"
  },
  {
    "path": "external/png.BUILD",
    "content": "# Description:\n#   libpng is the official PNG reference library.\n\nlicenses([\"notice\"])  # BSD/MIT-like license\n\ncc_library(\n    name = \"png\",\n    srcs = [\n        \"png.c\",\n        \"pngerror.c\",\n        \"pngget.c\",\n        \"pngmem.c\",\n        \"pngpread.c\",\n        \"pngread.c\",\n        \"pngrio.c\",\n        \"pngrtran.c\",\n        \"pngrutil.c\",\n        \"pngset.c\",\n        \"pngtrans.c\",\n        \"pngwio.c\",\n        \"pngwrite.c\",\n        \"pngwtran.c\",\n        \"pngwutil.c\",\n    ],\n    hdrs = [\n        \"png.h\",\n        \"pngconf.h\",\n    ],\n    includes = [\".\"],\n    linkopts = [\"-lm\"],\n    visibility = [\"//visibility:public\"],\n    deps = [\"@zlib_archive//:zlib\"],\n)\n"
  },
  {
    "path": "external/zlib.BUILD",
    "content": "package(default_visibility = [\"//visibility:public\"])\n\nlicenses([\"notice\"])  # BSD/MIT-like license (for zlib)\n\ncc_library(\n    name = \"zlib\",\n    srcs = [\n        \"adler32.c\",\n        \"compress.c\",\n        \"crc32.c\",\n        \"crc32.h\",\n        \"deflate.c\",\n        \"deflate.h\",\n        \"gzclose.c\",\n        \"gzguts.h\",\n        \"gzlib.c\",\n        \"gzread.c\",\n        \"gzwrite.c\",\n        \"infback.c\",\n        \"inffast.c\",\n        \"inffast.h\",\n        \"inffixed.h\",\n        \"inflate.c\",\n        \"inflate.h\",\n        \"inftrees.c\",\n        \"inftrees.h\",\n        \"trees.c\",\n        \"trees.h\",\n        \"uncompr.c\",\n        \"zconf.h\",\n        \"zutil.c\",\n        \"zutil.h\",\n    ],\n    hdrs = [\"zlib.h\"],\n    includes = [\".\"],\n)\n"
  },
  {
    "path": "fuzz_target.cc",
    "content": "#include <stdint.h>\n#include \"guetzli/jpeg_data.h\"\n#include \"guetzli/jpeg_data_reader.h\"\n#include \"guetzli/processor.h\"\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {\n  std::string jpeg_data(reinterpret_cast<const char*>(data), size);\n\n  // Ignore large images, to prevent timeouts.\n  guetzli::JPEGData jpg_header;\n  if (!guetzli::ReadJpeg(data, size, guetzli::JPEG_READ_HEADER, &jpg_header)) {\n    return 0;\n  }\n  static constexpr int kMaxPixels = 10000;\n  if (static_cast<int64_t>(jpg_header.width) * jpg_header.height > kMaxPixels) {\n    return 0;\n  }\n\n  // TODO(robryk): Use nondefault parameters.\n  guetzli::Params params;\n  std::string jpeg_out;\n  (void)guetzli::Process(params, nullptr, jpeg_data, &jpeg_out);\n  // TODO(robryk): Verify output distance if Process() succeeded.\n  return 0;\n}\n\n\n\n\n"
  },
  {
    "path": "guetzli/butteraugli_comparator.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"guetzli/butteraugli_comparator.h\"\n\n#include <algorithm>\n\n#include \"guetzli/debug_print.h\"\n#include \"guetzli/gamma_correct.h\"\n#include \"guetzli/score.h\"\n\nnamespace guetzli {\n\nnamespace {\nusing ::butteraugli::ImageF;\nusing ::butteraugli::CreatePlanes;\nusing ::butteraugli::PlanesFromPacked;\nusing ::butteraugli::PackedFromPlanes;\n\nstd::vector<ImageF> LinearRgb(const size_t xsize, const size_t ysize,\n                              const std::vector<uint8_t>& rgb) {\n  const double* lut = Srgb8ToLinearTable();\n  std::vector<ImageF> planes = CreatePlanes<float>(xsize, ysize, 3);\n  for (int c = 0; c < 3; ++c) {\n    for (size_t y = 0; y < ysize; ++y) {\n      const uint8_t* const BUTTERAUGLI_RESTRICT row_in = &rgb[3 * xsize * y];\n      float* const BUTTERAUGLI_RESTRICT row_out = planes[c].Row(y);\n      for (size_t x = 0; x < xsize; ++x) {\n        row_out[x] = lut[row_in[3 * x + c]];\n      }\n    }\n  }\n  return planes;\n}\n\n}  // namespace\n\nButteraugliComparator::ButteraugliComparator(const int width, const int height,\n                                             const std::vector<uint8_t>* rgb,\n                                             const float target_distance,\n                                             ProcessStats* stats)\n    : width_(width),\n      height_(height),\n      target_distance_(target_distance),\n      rgb_orig_(*rgb),\n      comparator_(LinearRgb(width_, height_, *rgb)),\n      distance_(0.0),\n      stats_(stats) {}\n\nvoid ButteraugliComparator::Compare(const OutputImage& img) {\n  std::vector<ImageF> rgb0 =\n      ::butteraugli::OpsinDynamicsImage(LinearRgb(width_, height_, rgb_orig_));\n  std::vector<std::vector<float> > rgb(3, std::vector<float>(width_ * height_));\n  img.ToLinearRGB(&rgb);\n  const std::vector<ImageF> rgb_planes = PlanesFromPacked(width_, height_, rgb);\n  std::vector<float>(width_ * height_).swap(distmap_);\n  ImageF distmap;\n  comparator_.Diffmap(rgb_planes, distmap);\n  CopyToPacked(distmap, &distmap_);\n  distance_ = ::butteraugli::ButteraugliScoreFromDiffmap(distmap);\n  GUETZLI_LOG(stats_, \" BA[100.00%%] D[%6.4f]\", distance_);\n}\n\nnamespace {\n\n// To change this to n, add the relevant FFTn function and kFFTnMapIndexTable.\nconstexpr size_t kBlockEdge = 8;\nconstexpr size_t kBlockSize = kBlockEdge * kBlockEdge;\nconstexpr size_t kBlockEdgeHalf = kBlockEdge / 2;\nconstexpr size_t kBlockHalf = kBlockEdge * kBlockEdgeHalf;\n\n// Contrast sensitivity related weights.\n// Contrast sensitivity matrix is a model of the contrast sensitivity of the\n// human eye. The contrast sensitivity function, also known as contrast\n// sensitivity curve, is a basic psychovisual function and it roughly means\n// that highest frequences have less impact than middle and low frequences.\n//\n// The order of coefficients here is slightly confusing.\n// It is a creative order that benefits from the mirroring of the fft.\nstatic const double *GetContrastSensitivityMatrix() {\n  static double csf8x8[kBlockHalf + kBlockEdgeHalf + 1] = {\n    0.0,  // no 8x8 dc, enough 'dc' is calculated from low/mid freq images.\n    0.0,\n    0.0,\n    0.0,\n    0.3831134973,\n    0.676303603859,\n    1.1550451483,\n    8,\n    8,\n    0.692062533689,\n    0.847511538605,\n    0.498250875965,\n    0.36198671102,\n    0.308982169883,\n    0.1312701920435,\n    4.71274312228,\n    1.1550451483,\n    0.847511538605,\n    4.71274312228,\n    0.991205724152,\n    1.30229591239,\n    0.627264168628,\n    0.4,\n    0.1312701920435,\n    0.676303603859,\n    0.498250875965,\n    0.991205724152,\n    0.5,\n    0.3831134973,\n    0.349686450518,\n    0.627264168628,\n    0.308982169883,\n    0.3831134973,\n    0.36198671102,\n    1.30229591239,\n    0.3831134973,\n    0.323078800177,\n  };\n  return &csf8x8[0];\n}\n\nstruct Complex {\n  double real;\n  double imag;\n};\n\ninline double abssq(const Complex& c) {\n  return c.real * c.real + c.imag * c.imag;\n}\n\nstatic void TransposeBlock(Complex data[kBlockSize]) {\n  for (int i = 0; i < kBlockEdge; i++) {\n    for (int j = 0; j < i; j++) {\n      std::swap(data[kBlockEdge * i + j], data[kBlockEdge * j + i]);\n    }\n  }\n}\n\n//  D. J. Bernstein's Fast Fourier Transform algorithm on 4 elements.\ninline void FFT4(Complex* a) {\n  double t1, t2, t3, t4, t5, t6, t7, t8;\n  t5 = a[2].real;\n  t1 = a[0].real - t5;\n  t7 = a[3].real;\n  t5 += a[0].real;\n  t3 = a[1].real - t7;\n  t7 += a[1].real;\n  t8 = t5 + t7;\n  a[0].real = t8;\n  t5 -= t7;\n  a[1].real = t5;\n  t6 = a[2].imag;\n  t2 = a[0].imag - t6;\n  t6 += a[0].imag;\n  t5 = a[3].imag;\n  a[2].imag = t2 + t3;\n  t2 -= t3;\n  a[3].imag = t2;\n  t4 = a[1].imag - t5;\n  a[3].real = t1 + t4;\n  t1 -= t4;\n  a[2].real = t1;\n  t5 += a[1].imag;\n  a[0].imag = t6 + t5;\n  t6 -= t5;\n  a[1].imag = t6;\n}\n\nconst double kSqrtHalf = 0.70710678118654752440084436210484903;\n\n//  D. J. Bernstein's Fast Fourier Transform algorithm on 8 elements.\nvoid FFT8(Complex* a) {\n  double t1, t2, t3, t4, t5, t6, t7, t8;\n\n  t7 = a[4].imag;\n  t4 = a[0].imag - t7;\n  t7 += a[0].imag;\n  a[0].imag = t7;\n\n  t8 = a[6].real;\n  t5 = a[2].real - t8;\n  t8 += a[2].real;\n  a[2].real = t8;\n\n  t7 = a[6].imag;\n  a[6].imag = t4 - t5;\n  t4 += t5;\n  a[4].imag = t4;\n\n  t6 = a[2].imag - t7;\n  t7 += a[2].imag;\n  a[2].imag = t7;\n\n  t8 = a[4].real;\n  t3 = a[0].real - t8;\n  t8 += a[0].real;\n  a[0].real = t8;\n\n  a[4].real = t3 - t6;\n  t3 += t6;\n  a[6].real = t3;\n\n  t7 = a[5].real;\n  t3 = a[1].real - t7;\n  t7 += a[1].real;\n  a[1].real = t7;\n\n  t8 = a[7].imag;\n  t6 = a[3].imag - t8;\n  t8 += a[3].imag;\n  a[3].imag = t8;\n  t1 = t3 - t6;\n  t3 += t6;\n\n  t7 = a[5].imag;\n  t4 = a[1].imag - t7;\n  t7 += a[1].imag;\n  a[1].imag = t7;\n\n  t8 = a[7].real;\n  t5 = a[3].real - t8;\n  t8 += a[3].real;\n  a[3].real = t8;\n\n  t2 = t4 - t5;\n  t4 += t5;\n\n  t6 = t1 - t4;\n  t8 = kSqrtHalf;\n  t6 *= t8;\n  a[5].real = a[4].real - t6;\n  t1 += t4;\n  t1 *= t8;\n  a[5].imag = a[4].imag - t1;\n  t6 += a[4].real;\n  a[4].real = t6;\n  t1 += a[4].imag;\n  a[4].imag = t1;\n\n  t5 = t2 - t3;\n  t5 *= t8;\n  a[7].imag = a[6].imag - t5;\n  t2 += t3;\n  t2 *= t8;\n  a[7].real = a[6].real - t2;\n  t2 += a[6].real;\n  a[6].real = t2;\n  t5 += a[6].imag;\n  a[6].imag = t5;\n\n  FFT4(a);\n\n  // Reorder to the correct output order.\n  // TODO(szabadka): Modify the above computation so that this is not needed.\n  Complex tmp = a[2];\n  a[2] = a[3];\n  a[3] = a[5];\n  a[5] = a[7];\n  a[7] = a[4];\n  a[4] = a[1];\n  a[1] = a[6];\n  a[6] = tmp;\n}\n\n// Same as FFT8, but all inputs are real.\n// TODO(szabadka): Since this does not need to be in-place, maybe there is a\n// faster FFT than this one, which is derived from DJB's in-place complex FFT.\nvoid RealFFT8(const double* in, Complex* out) {\n  double t1, t2, t3, t5, t6, t7, t8;\n  t8 = in[6];\n  t5 = in[2] - t8;\n  t8 += in[2];\n  out[2].real = t8;\n  out[6].imag = -t5;\n  out[4].imag = t5;\n  t8 = in[4];\n  t3 = in[0] - t8;\n  t8 += in[0];\n  out[0].real = t8;\n  out[4].real = t3;\n  out[6].real = t3;\n  t7 = in[5];\n  t3 = in[1] - t7;\n  t7 += in[1];\n  out[1].real = t7;\n  t8 = in[7];\n  t5 = in[3] - t8;\n  t8 += in[3];\n  out[3].real = t8;\n  t2 = -t5;\n  t6 = t3 - t5;\n  t8 = kSqrtHalf;\n  t6 *= t8;\n  out[5].real = out[4].real - t6;\n  t1 = t3 + t5;\n  t1 *= t8;\n  out[5].imag = out[4].imag - t1;\n  t6 += out[4].real;\n  out[4].real = t6;\n  t1 += out[4].imag;\n  out[4].imag = t1;\n  t5 = t2 - t3;\n  t5 *= t8;\n  out[7].imag = out[6].imag - t5;\n  t2 += t3;\n  t2 *= t8;\n  out[7].real = out[6].real - t2;\n  t2 += out[6].real;\n  out[6].real = t2;\n  t5 += out[6].imag;\n  out[6].imag = t5;\n  t5 = out[2].real;\n  t1 = out[0].real - t5;\n  t7 = out[3].real;\n  t5 += out[0].real;\n  t3 = out[1].real - t7;\n  t7 += out[1].real;\n  t8 = t5 + t7;\n  out[0].real = t8;\n  t5 -= t7;\n  out[1].real = t5;\n  out[2].imag = t3;\n  out[3].imag = -t3;\n  out[3].real = t1;\n  out[2].real = t1;\n  out[0].imag = 0;\n  out[1].imag = 0;\n\n  // Reorder to the correct output order.\n  // TODO(szabadka): Modify the above computation so that this is not needed.\n  Complex tmp = out[2];\n  out[2] = out[3];\n  out[3] = out[5];\n  out[5] = out[7];\n  out[7] = out[4];\n  out[4] = out[1];\n  out[1] = out[6];\n  out[6] = tmp;\n}\n\n// Fills in block[kBlockEdgeHalf..(kBlockHalf+kBlockEdgeHalf)], and leaves the\n// rest unmodified.\nvoid ButteraugliFFTSquared(double block[kBlockSize]) {\n  double global_mul = 0.000064;\n  Complex block_c[kBlockSize];\n  assert(kBlockEdge == 8);\n  for (int y = 0; y < kBlockEdge; ++y) {\n    RealFFT8(block + y * kBlockEdge, block_c + y * kBlockEdge);\n  }\n  TransposeBlock(block_c);\n  double r0[kBlockEdge];\n  double r1[kBlockEdge];\n  for (int x = 0; x < kBlockEdge; ++x) {\n    r0[x] = block_c[x].real;\n    r1[x] = block_c[kBlockHalf + x].real;\n  }\n  RealFFT8(r0, block_c);\n  RealFFT8(r1, block_c + kBlockHalf);\n  for (int y = 1; y < kBlockEdgeHalf; ++y) {\n    FFT8(block_c + y * kBlockEdge);\n  }\n  for (int i = kBlockEdgeHalf; i < kBlockHalf + kBlockEdgeHalf + 1; ++i) {\n    block[i] = abssq(block_c[i]);\n    block[i] *= global_mul;\n  }\n}\n\nvoid ButteraugliBlockDiff(double xyb0[3 * kBlockSize],\n                          double xyb1[3 * kBlockSize],\n                          double* diff_xyb) {\n  const double *csf8x8 = GetContrastSensitivityMatrix();\n  double avgdiff_xyb[3] = {0.0};\n  for (int i = 0; i < 3 * kBlockSize; ++i) {\n    avgdiff_xyb[i / kBlockSize] += xyb0[i] - xyb1[i];\n  }\n  for (int c = 0; c < 3; ++c) {\n    double avgdiff = avgdiff_xyb[c] / kBlockSize;\n    diff_xyb[c] += 4.0 * avgdiff * avgdiff;\n  }\n  double x_diff[kBlockSize];\n  double y_diff[kBlockSize];\n  double b_diff[kBlockSize];\n  for (int i = 0; i < kBlockSize; ++i) {\n    x_diff[i] = (xyb0[i] - xyb1[i]);\n    y_diff[i] = (xyb0[kBlockSize + i] - xyb1[kBlockSize + i]);\n    b_diff[i] = (xyb0[2 * kBlockSize + i] - xyb1[2 * kBlockSize + i]);\n  }\n  ButteraugliFFTSquared(x_diff);\n  ButteraugliFFTSquared(y_diff);\n  ButteraugliFFTSquared(b_diff);\n  for (size_t i = kBlockEdgeHalf; i < kBlockHalf + kBlockEdgeHalf + 1; ++i) {\n    double d = csf8x8[i];\n    diff_xyb[0] += d * x_diff[i];\n    diff_xyb[1] += d * y_diff[i];\n    diff_xyb[2] += d * b_diff[i];\n  }\n}\n\n}  // namespace\n\nvoid ButteraugliComparator::StartBlockComparisons() {\n  std::vector<ImageF> dummy(3);\n  std::vector<ImageF> rgb0 =\n      ::butteraugli::OpsinDynamicsImage(LinearRgb(width_, height_, rgb_orig_));\n  ::butteraugli::Mask(rgb0, rgb0,\n                      &mask_xyz_, &dummy);\n}\n\nvoid ButteraugliComparator::FinishBlockComparisons() {\n  mask_xyz_.clear();\n}\n\nvoid ButteraugliComparator::SwitchBlock(int block_x, int block_y,\n                                        int factor_x, int factor_y) {\n  block_x_ = block_x;\n  block_y_ = block_y;\n  factor_x_ = factor_x;\n  factor_y_ = factor_y;\n  per_block_pregamma_.resize(factor_x_ * factor_y_);\n  const double* lut = Srgb8ToLinearTable();\n  for (int off_y = 0, bx = 0; off_y < factor_y_; ++off_y) {\n    for (int off_x = 0; off_x < factor_x_; ++off_x, ++bx) {\n      per_block_pregamma_[bx].resize(3, std::vector<float>(kDCTBlockSize));\n      int block_xx = block_x_ * factor_x_ + off_x;\n      int block_yy = block_y_ * factor_y_ + off_y;\n      for (int iy = 0, i = 0; iy < 8; ++iy) {\n        for (int ix = 0; ix < 8; ++ix, ++i) {\n          int x = std::min(8 * block_xx + ix, width_ - 1);\n          int y = std::min(8 * block_yy + iy, height_ - 1);\n          int px = y * width_ + x;\n          for (int c = 0; c < 3; ++c) {\n            per_block_pregamma_[bx][c][i] = lut[rgb_orig_[3 * px + c]];\n          }\n        }\n      }\n      per_block_pregamma_[bx] =\n          ::butteraugli::PackedFromPlanes(::butteraugli::OpsinDynamicsImage(\n              ::butteraugli::PlanesFromPacked(8, 8, per_block_pregamma_[bx])));\n    }\n  }\n}\n\ndouble ButteraugliComparator::CompareBlock(const OutputImage& img,\n                                           int off_x, int off_y) const {\n  int block_x = block_x_ * factor_x_ + off_x;\n  int block_y = block_y_ * factor_y_ + off_y;\n  int xmin = 8 * block_x;\n  int ymin = 8 * block_y;\n  int block_ix = off_y * factor_x_ + off_x;\n  const std::vector<std::vector<float> >& rgb0 =\n      per_block_pregamma_[block_ix];\n\n  std::vector<std::vector<float> > rgb1(3, std::vector<float>(kDCTBlockSize));\n  img.ToLinearRGB(xmin, ymin, 8, 8, &rgb1);\n  rgb1 = ::butteraugli::PackedFromPlanes(::butteraugli::OpsinDynamicsImage(\n      ::butteraugli::PlanesFromPacked(8, 8, rgb1)));\n\n  double b0[3 * kDCTBlockSize];\n  double b1[3 * kDCTBlockSize];\n  for (int c = 0; c < 3; ++c) {\n    for (int ix = 0; ix < kDCTBlockSize; ++ix) {\n      b0[c * kDCTBlockSize + ix] = rgb0[c][ix];\n      b1[c * kDCTBlockSize + ix] = rgb1[c][ix];\n    }\n  }\n  double diff_xyz[3] = { 0.0 };\n  ButteraugliBlockDiff(b0, b1, diff_xyz);\n\n  double diff = 0.0;\n  for (int c = 0; c < 3; ++c) {\n    diff += diff_xyz[c] * mask_xyz_[c].Row(ymin)[xmin];\n  }\n  return sqrt(diff);\n}\n\nfloat ButteraugliComparator::BlockErrorLimit() const {\n  return target_distance_;\n}\n\nvoid ButteraugliComparator::ComputeBlockErrorAdjustmentWeights(\n      int direction,\n      int max_block_dist,\n      double target_mul,\n      int factor_x, int factor_y,\n      const std::vector<float>& distmap,\n      std::vector<float>* block_weight) {\n  const double target_distance = target_distance_ * target_mul;\n  const int sizex = 8 * factor_x;\n  const int sizey = 8 * factor_y;\n  const int block_width = (width_ + sizex - 1) / sizex;\n  const int block_height = (height_ + sizey - 1) / sizey;\n  std::vector<float> max_dist_per_block(block_width * block_height);\n  for (int block_y = 0; block_y < block_height; ++block_y) {\n    for (int block_x = 0; block_x < block_width; ++block_x) {\n      int block_ix = block_y * block_width + block_x;\n      int x_max = std::min(width_, sizex * (block_x + 1));\n      int y_max = std::min(height_, sizey * (block_y + 1));\n      float max_dist = 0.0;\n      for (int y = sizey * block_y; y < y_max; ++y) {\n        for (int x = sizex * block_x; x < x_max; ++x) {\n          max_dist = std::max(max_dist, distmap[y * width_ + x]);\n        }\n      }\n      max_dist_per_block[block_ix] = max_dist;\n    }\n  }\n  for (int block_y = 0; block_y < block_height; ++block_y) {\n    for (int block_x = 0; block_x < block_width; ++block_x) {\n      int block_ix = block_y * block_width + block_x;\n      float max_local_dist = static_cast<float>(target_distance);\n      int x_min = std::max(0, block_x - max_block_dist);\n      int y_min = std::max(0, block_y - max_block_dist);\n      int x_max = std::min(block_width, block_x + 1 + max_block_dist);\n      int y_max = std::min(block_height, block_y + 1 + max_block_dist);\n      for (int y = y_min; y < y_max; ++y) {\n        for (int x = x_min; x < x_max; ++x) {\n          max_local_dist =\n              std::max(max_local_dist, max_dist_per_block[y * block_width + x]);\n        }\n      }\n      if (direction > 0) {\n        if (max_dist_per_block[block_ix] <= target_distance &&\n            max_local_dist <= 1.1 * target_distance) {\n          (*block_weight)[block_ix] = 1.0;\n        }\n      } else {\n        constexpr double kLocalMaxWeight = 0.5;\n        if (max_dist_per_block[block_ix] <=\n            (1 - kLocalMaxWeight) * target_distance +\n            kLocalMaxWeight * max_local_dist) {\n          continue;\n        }\n        for (int y = y_min; y < y_max; ++y) {\n          for (int x = x_min; x < x_max; ++x) {\n            int d = std::max(std::abs(y - block_y), std::abs(x - block_x));\n            int ix = y * block_width + x;\n            (*block_weight)[ix] = std::max<float>(\n                (*block_weight)[ix], 1.0f / (d + 1.0f));\n          }\n        }\n      }\n    }\n  }\n}\n\ndouble ButteraugliComparator::ScoreOutputSize(int size) const {\n  return ScoreJPEG(distance_, size, target_distance_);\n}\n\n\n}  // namespace guetzli\n"
  },
  {
    "path": "guetzli/butteraugli_comparator.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef GUETZLI_BUTTERAUGLI_COMPARATOR_H_\n#define GUETZLI_BUTTERAUGLI_COMPARATOR_H_\n\n#include <vector>\n\n#include \"butteraugli/butteraugli.h\"\n#include \"guetzli/comparator.h\"\n#include \"guetzli/jpeg_data.h\"\n#include \"guetzli/output_image.h\"\n#include \"guetzli/stats.h\"\n\nnamespace guetzli {\n\nconstexpr int kButteraugliStep = 3;\n\nclass ButteraugliComparator : public Comparator {\n public:\n  ButteraugliComparator(const int width, const int height,\n                        const std::vector<uint8_t>* rgb,\n                        const float target_distance, ProcessStats* stats);\n\n  void Compare(const OutputImage& img) override;\n\n  void StartBlockComparisons() override;\n  void FinishBlockComparisons() override;\n\n  void SwitchBlock(int block_x, int block_y,\n                   int factor_x, int factor_y) override;\n\n  double CompareBlock(const OutputImage& img,\n                      int off_x, int off_y) const override;\n\n  double ScoreOutputSize(int size) const override;\n\n  bool DistanceOK(double target_mul) const override {\n    return distance_ <= target_mul * target_distance_;\n  }\n\n  const std::vector<float> distmap() const override { return distmap_; }\n  float distmap_aggregate() const override { return distance_; }\n\n  float BlockErrorLimit() const override;\n\n  void ComputeBlockErrorAdjustmentWeights(\n      int direction, int max_block_dist, double target_mul, int factor_x,\n      int factor_y, const std::vector<float>& distmap,\n      std::vector<float>* block_weight) override;\n\n private:\n  const int width_;\n  const int height_;\n  const float target_distance_;\n  const std::vector<uint8_t>& rgb_orig_;\n  int block_x_;\n  int block_y_;\n  int factor_x_;\n  int factor_y_;\n  std::vector<::butteraugli::ImageF> mask_xyz_;\n  std::vector<std::vector<std::vector<float>>> per_block_pregamma_;\n  ::butteraugli::ButteraugliComparator comparator_;\n  float distance_;\n  std::vector<float> distmap_;\n  ProcessStats* stats_;\n};\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_BUTTERAUGLI_COMPARATOR_H_\n"
  },
  {
    "path": "guetzli/color_transform.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef GUETZLI_COLOR_TRANSFORM_H_\n#define GUETZLI_COLOR_TRANSFORM_H_\n\nnamespace guetzli {\n\nstatic const int kCrToRedTable[256] = {\n  -179, -178, -177, -175, -174, -172, -171, -170, -168, -167, -165, -164,\n  -163, -161, -160, -158, -157, -156, -154, -153, -151, -150, -149, -147,\n  -146, -144, -143, -142, -140, -139, -137, -136, -135, -133, -132, -130,\n  -129, -128, -126, -125, -123, -122, -121, -119, -118, -116, -115, -114,\n  -112, -111, -109, -108, -107, -105, -104, -102, -101, -100,  -98,  -97,\n   -95,  -94,  -93,  -91,  -90,  -88,  -87,  -86,  -84,  -83,  -81,  -80,\n   -79,  -77,  -76,  -74,  -73,  -72,  -70,  -69,  -67,  -66,  -64,  -63,\n   -62,  -60,  -59,  -57,  -56,  -55,  -53,  -52,  -50,  -49,  -48,  -46,\n   -45,  -43,  -42,  -41,  -39,  -38,  -36,  -35,  -34,  -32,  -31,  -29,\n   -28,  -27,  -25,  -24,  -22,  -21,  -20,  -18,  -17,  -15,  -14,  -13,\n   -11,  -10,   -8,   -7,   -6,   -4,   -3,   -1,    0,    1,    3,    4,\n     6,    7,    8,   10,   11,   13,   14,   15,   17,   18,   20,   21,\n    22,   24,   25,   27,   28,   29,   31,   32,   34,   35,   36,   38,\n    39,   41,   42,   43,   45,   46,   48,   49,   50,   52,   53,   55,\n    56,   57,   59,   60,   62,   63,   64,   66,   67,   69,   70,   72,\n    73,   74,   76,   77,   79,   80,   81,   83,   84,   86,   87,   88,\n    90,   91,   93,   94,   95,   97,   98,  100,  101,  102,  104,  105,\n   107,  108,  109,  111,  112,  114,  115,  116,  118,  119,  121,  122,\n   123,  125,  126,  128,  129,  130,  132,  133,  135,  136,  137,  139,\n   140,  142,  143,  144,  146,  147,  149,  150,  151,  153,  154,  156,\n   157,  158,  160,  161,  163,  164,  165,  167,  168,  170,  171,  172,\n   174,  175,  177,  178\n};\n\nstatic const int kCbToBlueTable[256] = {\n  -227, -225, -223, -222, -220, -218, -216, -214, -213, -211, -209, -207,\n  -206, -204, -202, -200, -198, -197, -195, -193, -191, -190, -188, -186,\n  -184, -183, -181, -179, -177, -175, -174, -172, -170, -168, -167, -165,\n  -163, -161, -159, -158, -156, -154, -152, -151, -149, -147, -145, -144,\n  -142, -140, -138, -136, -135, -133, -131, -129, -128, -126, -124, -122,\n  -120, -119, -117, -115, -113, -112, -110, -108, -106, -105, -103, -101,\n   -99,  -97,  -96,  -94,  -92,  -90,  -89,  -87,  -85,  -83,  -82,  -80,\n   -78,  -76,  -74,  -73,  -71,  -69,  -67,  -66,  -64,  -62,  -60,  -58,\n   -57,  -55,  -53,  -51,  -50,  -48,  -46,  -44,  -43,  -41,  -39,  -37,\n   -35,  -34,  -32,  -30,  -28,  -27,  -25,  -23,  -21,  -19,  -18,  -16,\n   -14,  -12,  -11,   -9,   -7,   -5,   -4,   -2,    0,    2,    4,    5,\n     7,    9,   11,   12,   14,   16,   18,   19,   21,   23,   25,   27,\n    28,   30,   32,   34,   35,   37,   39,   41,   43,   44,   46,   48,\n    50,   51,   53,   55,   57,   58,   60,   62,   64,   66,   67,   69,\n    71,   73,   74,   76,   78,   80,   82,   83,   85,   87,   89,   90,\n    92,   94,   96,   97,   99,  101,  103,  105,  106,  108,  110,  112,\n   113,  115,  117,  119,  120,  122,  124,  126,  128,  129,  131,  133,\n   135,  136,  138,  140,  142,  144,  145,  147,  149,  151,  152,  154,\n   156,  158,  159,  161,  163,  165,  167,  168,  170,  172,  174,  175,\n   177,  179,  181,  183,  184,  186,  188,  190,  191,  193,  195,  197,\n   198,  200,  202,  204,  206,  207,  209,  211,  213,  214,  216,  218,\n   220,  222,  223,  225,\n};\n\nstatic const int kCrToGreenTable[256] = {\n  5990656,  5943854,  5897052,  5850250,  5803448,  5756646,  5709844,  5663042,\n  5616240,  5569438,  5522636,  5475834,  5429032,  5382230,  5335428,  5288626,\n  5241824,  5195022,  5148220,  5101418,  5054616,  5007814,  4961012,  4914210,\n  4867408,  4820606,  4773804,  4727002,  4680200,  4633398,  4586596,  4539794,\n  4492992,  4446190,  4399388,  4352586,  4305784,  4258982,  4212180,  4165378,\n  4118576,  4071774,  4024972,  3978170,  3931368,  3884566,  3837764,  3790962,\n  3744160,  3697358,  3650556,  3603754,  3556952,  3510150,  3463348,  3416546,\n  3369744,  3322942,  3276140,  3229338,  3182536,  3135734,  3088932,  3042130,\n  2995328,  2948526,  2901724,  2854922,  2808120,  2761318,  2714516,  2667714,\n  2620912,  2574110,  2527308,  2480506,  2433704,  2386902,  2340100,  2293298,\n  2246496,  2199694,  2152892,  2106090,  2059288,  2012486,  1965684,  1918882,\n  1872080,  1825278,  1778476,  1731674,  1684872,  1638070,  1591268,  1544466,\n  1497664,  1450862,  1404060,  1357258,  1310456,  1263654,  1216852,  1170050,\n  1123248,  1076446,  1029644,   982842,   936040,   889238,   842436,   795634,\n   748832,   702030,   655228,   608426,   561624,   514822,   468020,   421218,\n   374416,   327614,   280812,   234010,   187208,   140406,    93604,    46802,\n        0,   -46802,   -93604,  -140406,  -187208,  -234010,  -280812,  -327614,\n  -374416,  -421218,  -468020,  -514822,  -561624,  -608426,  -655228,  -702030,\n  -748832,  -795634,  -842436,  -889238,  -936040,  -982842, -1029644, -1076446,\n -1123248, -1170050, -1216852, -1263654, -1310456, -1357258, -1404060, -1450862,\n -1497664, -1544466, -1591268, -1638070, -1684872, -1731674, -1778476, -1825278,\n -1872080, -1918882, -1965684, -2012486, -2059288, -2106090, -2152892, -2199694,\n -2246496, -2293298, -2340100, -2386902, -2433704, -2480506, -2527308, -2574110,\n -2620912, -2667714, -2714516, -2761318, -2808120, -2854922, -2901724, -2948526,\n -2995328, -3042130, -3088932, -3135734, -3182536, -3229338, -3276140, -3322942,\n -3369744, -3416546, -3463348, -3510150, -3556952, -3603754, -3650556, -3697358,\n -3744160, -3790962, -3837764, -3884566, -3931368, -3978170, -4024972, -4071774,\n -4118576, -4165378, -4212180, -4258982, -4305784, -4352586, -4399388, -4446190,\n -4492992, -4539794, -4586596, -4633398, -4680200, -4727002, -4773804, -4820606,\n -4867408, -4914210, -4961012, -5007814, -5054616, -5101418, -5148220, -5195022,\n -5241824, -5288626, -5335428, -5382230, -5429032, -5475834, -5522636, -5569438,\n -5616240, -5663042, -5709844, -5756646, -5803448, -5850250, -5897052, -5943854,\n};\n\nstatic const int kCbToGreenTable[256] = {\n  2919680,  2897126,  2874572,  2852018,  2829464,  2806910,  2784356,  2761802,\n  2739248,  2716694,  2694140,  2671586,  2649032,  2626478,  2603924,  2581370,\n  2558816,  2536262,  2513708,  2491154,  2468600,  2446046,  2423492,  2400938,\n  2378384,  2355830,  2333276,  2310722,  2288168,  2265614,  2243060,  2220506,\n  2197952,  2175398,  2152844,  2130290,  2107736,  2085182,  2062628,  2040074,\n  2017520,  1994966,  1972412,  1949858,  1927304,  1904750,  1882196,  1859642,\n  1837088,  1814534,  1791980,  1769426,  1746872,  1724318,  1701764,  1679210,\n  1656656,  1634102,  1611548,  1588994,  1566440,  1543886,  1521332,  1498778,\n  1476224,  1453670,  1431116,  1408562,  1386008,  1363454,  1340900,  1318346,\n  1295792,  1273238,  1250684,  1228130,  1205576,  1183022,  1160468,  1137914,\n  1115360,  1092806,  1070252,  1047698,  1025144,  1002590,   980036,   957482,\n   934928,   912374,   889820,   867266,   844712,   822158,   799604,   777050,\n   754496,   731942,   709388,   686834,   664280,   641726,   619172,   596618,\n   574064,   551510,   528956,   506402,   483848,   461294,   438740,   416186,\n   393632,   371078,   348524,   325970,   303416,   280862,   258308,   235754,\n   213200,   190646,   168092,   145538,   122984,   100430,    77876,    55322,\n    32768,    10214,   -12340,   -34894,   -57448,   -80002,  -102556,  -125110,\n  -147664,  -170218,  -192772,  -215326,  -237880,  -260434,  -282988,  -305542,\n  -328096,  -350650,  -373204,  -395758,  -418312,  -440866,  -463420,  -485974,\n  -508528,  -531082,  -553636,  -576190,  -598744,  -621298,  -643852,  -666406,\n  -688960,  -711514,  -734068,  -756622,  -779176,  -801730,  -824284,  -846838,\n  -869392,  -891946,  -914500,  -937054,  -959608,  -982162, -1004716, -1027270,\n -1049824, -1072378, -1094932, -1117486, -1140040, -1162594, -1185148, -1207702,\n -1230256, -1252810, -1275364, -1297918, -1320472, -1343026, -1365580, -1388134,\n -1410688, -1433242, -1455796, -1478350, -1500904, -1523458, -1546012, -1568566,\n -1591120, -1613674, -1636228, -1658782, -1681336, -1703890, -1726444, -1748998,\n -1771552, -1794106, -1816660, -1839214, -1861768, -1884322, -1906876, -1929430,\n -1951984, -1974538, -1997092, -2019646, -2042200, -2064754, -2087308, -2109862,\n -2132416, -2154970, -2177524, -2200078, -2222632, -2245186, -2267740, -2290294,\n -2312848, -2335402, -2357956, -2380510, -2403064, -2425618, -2448172, -2470726,\n -2493280, -2515834, -2538388, -2560942, -2583496, -2606050, -2628604, -2651158,\n -2673712, -2696266, -2718820, -2741374, -2763928, -2786482, -2809036, -2831590,\n};\n\nstatic const uint8_t kRangeLimitLut[4 * 256] = {\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,\n  0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15,\n  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,\n  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,\n  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,\n  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,\n  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,\n  96,  97,  98,  99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,\n 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,\n 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,\n 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,\n 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,\n 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,\n 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,\n 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,\n 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,\n 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n};\n\nstatic const uint8_t* kRangeLimit = kRangeLimitLut + 384;\n\ninline void ColorTransformYCbCrToRGB(uint8_t* pixel) {\n  int y  = pixel[0];\n  int cb = pixel[1];\n  int cr = pixel[2];\n  pixel[0] = kRangeLimit[y + kCrToRedTable[cr]];\n  pixel[1] = kRangeLimit[y +\n                         ((kCrToGreenTable[cr] + kCbToGreenTable[cb]) >> 16)];\n  pixel[2] = kRangeLimit[y + kCbToBlueTable[cb]];\n}\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_COLOR_TRANSFORM_H_\n"
  },
  {
    "path": "guetzli/comparator.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef GUETZLI_COMPARATOR_H_\n#define GUETZLI_COMPARATOR_H_\n\n#include <vector>\n\n#include \"guetzli/output_image.h\"\n#include \"guetzli/stats.h\"\n\nnamespace guetzli {\n\n// Represents a baseline image, a comparison metric and an image acceptance\n// criteria based on this metric.\nclass Comparator {\n public:\n  Comparator() {}\n  virtual ~Comparator() {}\n\n  // Compares img with the baseline image and saves the resulting distance map\n  // inside the object. The provided image must have the same dimensions as the\n  // baseline image.\n  virtual void Compare(const OutputImage& img) = 0;\n\n  // Must be called before any CompareBlock() calls can be called.\n  virtual void StartBlockComparisons() = 0;\n  // No more CompareBlock() calls can be called after this.\n  virtual void FinishBlockComparisons() = 0;\n\n  // Sets the coordinates of the current macro-block for the purpose of\n  // CompareBlock() calls.\n  virtual void SwitchBlock(int block_x, int block_y,\n                           int factor_x, int factor_y) = 0;\n\n  // Compares the 8x8 block with offsets (off_x, off_y) within the current\n  // macro-block of the baseline image with the same block of img and returns\n  // the resulting per-block distance. The interpretation of the returned\n  // distance depends on the comparator used.\n  virtual double CompareBlock(const OutputImage& img,\n                              int off_x, int off_y) const = 0;\n\n  // Returns the combined score of the output image in the last Compare() call\n  // (or the baseline image, if Compare() was not called yet), based on output\n  // size and the similarity metric.\n  virtual double ScoreOutputSize(int size) const = 0;\n\n  // Returns true if the argument of the last Compare() call (or the baseline\n  // image, if Compare() was not called yet) meets the image acceptance\n  // criteria. The target_mul modifies the acceptance criteria used in this call\n  // the following way:\n  //    = 1.0 : the original acceptance criteria is used,\n  //    < 1.0 : a more strict acceptance criteria is used,\n  //    > 1.0 : a less strict acceptance criteria is used.\n  virtual bool DistanceOK(double target_mul) const = 0;\n\n  // Returns the distance map between the baseline image and the image in the\n  // last Compare() call (or the baseline image, if Compare() was not called\n  // yet).\n  // The dimensions of the distance map are the same as the baseline image.\n  // The interpretation of the distance values depend on the comparator used.\n  virtual const std::vector<float> distmap() const = 0;\n\n  // Returns an aggregate distance or similarity value between the baseline\n  // image and the image in the last Compare() call (or the baseline image, if\n  // Compare() was not called yet).\n  // The interpretation of this aggregate value depends on the comparator used.\n  virtual float distmap_aggregate() const = 0;\n\n  // Returns a heuristic cutoff on block errors in the sense that we won't\n  // consider distortions where a block error is greater than this.\n  virtual float BlockErrorLimit() const = 0;\n  // Given the search direction (+1 for upwards and -1 for downwards) and the\n  // current distance map, fills in *block_weight image with the relative block\n  // error adjustment weights.\n  // The target_mul param has the same semantics as in DistanceOK().\n  // Note that this is essentially a static function in the sense that it does\n  // not depend on the last Compare() call.\n  virtual void ComputeBlockErrorAdjustmentWeights(\n      int direction, int max_block_dist, double target_mul, int factor_x,\n      int factor_y, const std::vector<float>& distmap,\n      std::vector<float>* block_weight) = 0;\n};\n\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_COMPARATOR_H_\n"
  },
  {
    "path": "guetzli/dct_double.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"guetzli/dct_double.h\"\n\n#include <algorithm>\n#include <cmath>\n\nnamespace guetzli {\n\nnamespace {\n\n// kDCTMatrix[8*u+x] = 0.5*alpha(u)*cos((2*x+1)*u*M_PI/16),\n// where alpha(0) = 1/sqrt(2) and alpha(u) = 1 for u > 0.\nstatic const double kDCTMatrix[64] = {\n  0.3535533906,  0.3535533906,  0.3535533906,  0.3535533906,\n  0.3535533906,  0.3535533906,  0.3535533906,  0.3535533906,\n  0.4903926402,  0.4157348062,  0.2777851165,  0.0975451610,\n -0.0975451610, -0.2777851165, -0.4157348062, -0.4903926402,\n  0.4619397663,  0.1913417162, -0.1913417162, -0.4619397663,\n -0.4619397663, -0.1913417162,  0.1913417162,  0.4619397663,\n  0.4157348062, -0.0975451610, -0.4903926402, -0.2777851165,\n  0.2777851165,  0.4903926402,  0.0975451610, -0.4157348062,\n  0.3535533906, -0.3535533906, -0.3535533906,  0.3535533906,\n  0.3535533906, -0.3535533906, -0.3535533906,  0.3535533906,\n  0.2777851165, -0.4903926402,  0.0975451610,  0.4157348062,\n -0.4157348062, -0.0975451610,  0.4903926402, -0.2777851165,\n  0.1913417162, -0.4619397663,  0.4619397663, -0.1913417162,\n -0.1913417162,  0.4619397663, -0.4619397663,  0.1913417162,\n  0.0975451610, -0.2777851165,  0.4157348062, -0.4903926402,\n  0.4903926402, -0.4157348062,  0.2777851165, -0.0975451610,\n};\n\nvoid DCT1d(const double* in, int stride, double* out) {\n  for (int x = 0; x < 8; ++x) {\n    out[x * stride] = 0.0;\n    for (int u = 0; u < 8; ++u) {\n      out[x * stride] += kDCTMatrix[8 * x + u] * in[u * stride];\n    }\n  }\n}\n\nvoid IDCT1d(const double* in, int stride, double* out) {\n  for (int x = 0; x < 8; ++x) {\n    out[x * stride] = 0.0;\n    for (int u = 0; u < 8; ++u) {\n      out[x * stride] += kDCTMatrix[8 * u + x] * in[u * stride];\n    }\n  }\n}\n\ntypedef void (*Transform1d)(const double* in, int stride, double* out);\n\nvoid TransformBlock(double block[64], Transform1d f) {\n  double tmp[64];\n  for (int x = 0; x < 8; ++x) {\n    f(&block[x], 8, &tmp[x]);\n  }\n  for (int y = 0; y < 8; ++y) {\n    f(&tmp[8 * y], 1, &block[8 * y]);\n  }\n}\n\n}  // namespace\n\nvoid ComputeBlockDCTDouble(double block[64]) {\n  TransformBlock(block, DCT1d);\n}\n\nvoid ComputeBlockIDCTDouble(double block[64]) {\n  TransformBlock(block, IDCT1d);\n}\n\n}  // namespace guetzli\n"
  },
  {
    "path": "guetzli/dct_double.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef GUETZLI_DCT_DOUBLE_H_\n#define GUETZLI_DCT_DOUBLE_H_\n\nnamespace guetzli {\n\n// Performs in-place floating point 8x8 DCT on block[0..63].\n// Note that the DCT used here is the DCT-2 with the first term multiplied by\n// 1/sqrt(2) and the result scaled by 1/2.\nvoid ComputeBlockDCTDouble(double block[64]);\n\n// Performs in-place floating point 8x8 inverse DCT on block[0..63].\nvoid ComputeBlockIDCTDouble(double block[64]);\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_DCT_DOUBLE_H_\n"
  },
  {
    "path": "guetzli/debug_print.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"guetzli/debug_print.h\"\n\nnamespace guetzli {\n\nvoid PrintDebug(ProcessStats* stats, std::string s) {\n  if (stats->debug_output) {\n    stats->debug_output->append(s);\n  }\n  if (stats->debug_output_file) {\n    fprintf(stats->debug_output_file, \"%s\", s.c_str());\n  }\n}\n\n}  // namespace guetzli\n"
  },
  {
    "path": "guetzli/debug_print.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef GUETZLI_DEBUG_PRINT_H_\n#define GUETZLI_DEBUG_PRINT_H_\n\n#include \"guetzli/stats.h\"\n\nnamespace guetzli {\n\nvoid PrintDebug(ProcessStats* stats, std::string s);\n\n}  // namespace guetzli\n\n#define GUETZLI_LOG(stats, ...)                                    \\\n  do {                                                             \\\n    char debug_string[1024];                                       \\\n    int res = snprintf(debug_string, sizeof(debug_string),         \\\n                       __VA_ARGS__);                               \\\n    assert(res > 0 && \"expected successful printing\");             \\\n    (void)res;                                                     \\\n    debug_string[sizeof(debug_string) - 1] = '\\0';                 \\\n    ::guetzli::PrintDebug(                      \\\n         stats, std::string(debug_string));        \\\n  } while (0)\n#define GUETZLI_LOG_QUANT(stats, q)                    \\\n  for (int y = 0; y < 8; ++y) {                        \\\n    for (int c = 0; c < 3; ++c) {                      \\\n      for (int x = 0; x < 8; ++x)                      \\\n        GUETZLI_LOG(stats, \" %2d\", (q)[c][8 * y + x]); \\\n      GUETZLI_LOG(stats, \"   \");                       \\\n    }                                                  \\\n    GUETZLI_LOG(stats, \"\\n\");                          \\\n  }\n\n#endif  // GUETZLI_DEBUG_PRINT_H_\n"
  },
  {
    "path": "guetzli/entropy_encode.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Entropy encoding (Huffman) utilities.\n\n#include \"guetzli/entropy_encode.h\"\n\n#include <assert.h>\n#include <algorithm>\n\nnamespace guetzli {\n\nbool SetDepth(int p0, HuffmanTree *pool, uint8_t *depth, int max_depth) {\n  int stack[17];\n  int level = 0;\n  int p = p0;\n  assert(max_depth <= 16);\n  stack[0] = -1;\n  while (true) {\n    if (pool[p].index_left_ >= 0) {\n      level++;\n      if (level > max_depth) return false;\n      stack[level] = pool[p].index_right_or_value_;\n      p = pool[p].index_left_;\n      continue;\n    } else {\n      depth[pool[p].index_right_or_value_] = static_cast<uint8_t>(level);\n    }\n    while (level >= 0 && stack[level] == -1) level--;\n    if (level < 0) return true;\n    p = stack[level];\n    stack[level] = -1;\n  }\n}\n\n// Sort the root nodes, least popular first.\nstatic inline bool SortHuffmanTree(const HuffmanTree& v0,\n                                   const HuffmanTree& v1) {\n  if (v0.total_count_ != v1.total_count_) {\n    return v0.total_count_ < v1.total_count_;\n  }\n  return v0.index_right_or_value_ > v1.index_right_or_value_;\n}\n\n// This function will create a Huffman tree.\n//\n// The catch here is that the tree cannot be arbitrarily deep.\n// Brotli specifies a maximum depth of 15 bits for \"code trees\"\n// and 7 bits for \"code length code trees.\"\n//\n// count_limit is the value that is to be faked as the minimum value\n// and this minimum value is raised until the tree matches the\n// maximum length requirement.\n//\n// This algorithm is not of excellent performance for very long data blocks,\n// especially when population counts are longer than 2**tree_limit, but\n// we are not planning to use this with extremely long blocks.\n//\n// See http://en.wikipedia.org/wiki/Huffman_coding\nvoid CreateHuffmanTree(const uint32_t *data,\n                       const size_t length,\n                       const int tree_limit,\n                       HuffmanTree* tree,\n                       uint8_t *depth) {\n  // For block sizes below 64 kB, we never need to do a second iteration\n  // of this loop. Probably all of our block sizes will be smaller than\n  // that, so this loop is mostly of academic interest. If we actually\n  // would need this, we would be better off with the Katajainen algorithm.\n  for (uint32_t count_limit = 1; ; count_limit *= 2) {\n    size_t n = 0;\n    for (size_t i = length; i != 0;) {\n      --i;\n      if (data[i]) {\n        const uint32_t count = std::max<uint32_t>(data[i], count_limit);\n        tree[n++] = HuffmanTree(count, -1, static_cast<int16_t>(i));\n      }\n    }\n\n    if (n == 1) {\n      depth[tree[0].index_right_or_value_] = 1;      // Only one element.\n      break;\n    }\n\n    std::sort(tree, tree + n, SortHuffmanTree);\n\n    // The nodes are:\n    // [0, n): the sorted leaf nodes that we start with.\n    // [n]: we add a sentinel here.\n    // [n + 1, 2n): new parent nodes are added here, starting from\n    //              (n+1). These are naturally in ascending order.\n    // [2n]: we add a sentinel at the end as well.\n    // There will be (2n+1) elements at the end.\n    const HuffmanTree sentinel(~static_cast<uint32_t>(0), -1, -1);\n    tree[n] = sentinel;\n    tree[n + 1] = sentinel;\n\n    size_t i = 0;      // Points to the next leaf node.\n    size_t j = n + 1;  // Points to the next non-leaf node.\n    for (size_t k = n - 1; k != 0; --k) {\n      size_t left, right;\n      if (tree[i].total_count_ <= tree[j].total_count_) {\n        left = i;\n        ++i;\n      } else {\n        left = j;\n        ++j;\n      }\n      if (tree[i].total_count_ <= tree[j].total_count_) {\n        right = i;\n        ++i;\n      } else {\n        right = j;\n        ++j;\n      }\n\n      // The sentinel node becomes the parent node.\n      size_t j_end = 2 * n - k;\n      tree[j_end].total_count_ =\n          tree[left].total_count_ + tree[right].total_count_;\n      tree[j_end].index_left_ = static_cast<int16_t>(left);\n      tree[j_end].index_right_or_value_ = static_cast<int16_t>(right);\n\n      // Add back the last sentinel node.\n      tree[j_end + 1] = sentinel;\n    }\n    if (SetDepth(static_cast<int>(2 * n - 1), &tree[0], depth, tree_limit)) {\n      /* We need to pack the Huffman tree in tree_limit bits. If this was not\n         successful, add fake entities to the lowest values and retry. */\n      break;\n    }\n  }\n}\n\n}  // namespace guetzli\n"
  },
  {
    "path": "guetzli/entropy_encode.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Entropy encoding (Huffman) utilities.\n\n#ifndef GUETZLI_ENTROPY_ENCODE_H_\n#define GUETZLI_ENTROPY_ENCODE_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\nnamespace guetzli {\n\n// A node of a Huffman tree.\nstruct HuffmanTree {\n  HuffmanTree() {}\n  HuffmanTree(uint32_t count, int16_t left, int16_t right)\n      : total_count_(count),\n        index_left_(left),\n        index_right_or_value_(right) {\n  }\n  uint32_t total_count_;\n  int16_t index_left_;\n  int16_t index_right_or_value_;\n};\n\nbool SetDepth(int p, HuffmanTree *pool, uint8_t *depth, int max_depth);\n\n// This function will create a Huffman tree.\n//\n// The (data,length) contains the population counts.\n// The tree_limit is the maximum bit depth of the Huffman codes.\n//\n// The depth contains the tree, i.e., how many bits are used for\n// the symbol.\n//\n// The actual Huffman tree is constructed in the tree[] array, which has to\n// be at least 2 * length + 1 long.\n//\n// See http://en.wikipedia.org/wiki/Huffman_coding\nvoid CreateHuffmanTree(const uint32_t *data,\n                       const size_t length,\n                       const int tree_limit,\n                       HuffmanTree* tree,\n                       uint8_t *depth);\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_ENTROPY_ENCODE_H_\n"
  },
  {
    "path": "guetzli/fast_log.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef GUETZLI_FAST_LOG_H_\n#define GUETZLI_FAST_LOG_H_\n\n#include <math.h>\n\nnamespace guetzli {\n\ninline int Log2FloorNonZero(uint32_t n) {\n#ifdef __GNUC__\n  return 31 ^ __builtin_clz(n);\n#else\n  unsigned int result = 0;\n  while (n >>= 1) result++;\n  return result;\n#endif\n}\n\ninline int Log2Floor(uint32_t n) {\n  return n == 0 ? -1 : Log2FloorNonZero(n);\n}\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_FAST_LOG_H_\n"
  },
  {
    "path": "guetzli/fdct.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Integer implementation of the Discrete Cosine Transform (DCT)\n//\n// Note! DCT output is kept scaled by 16, to retain maximum 16bit precision\n\n#include \"guetzli/fdct.h\"\n\nnamespace guetzli {\n\nnamespace {\n\n///////////////////////////////////////////////////////////////////////////////\n// Cosine table: C(k) = cos(k.pi/16)/sqrt(2), k = 1..7 using 15 bits signed\nconst coeff_t kTable04[7] = { 22725, 21407, 19266, 16384, 12873,  8867, 4520 };\n// rows #1 and #7 are pre-multiplied by 2.C(1) before the 2nd pass.\n// This multiply is merged in the table of constants used during 1st pass:\nconst coeff_t kTable17[7] = { 31521, 29692, 26722, 22725, 17855, 12299, 6270 };\n// rows #2 and #6 are pre-multiplied by 2.C(2):\nconst coeff_t kTable26[7] = { 29692, 27969, 25172, 21407, 16819, 11585, 5906 };\n// rows #3 and #5 are pre-multiplied by 2.C(3):\nconst coeff_t kTable35[7] = { 26722, 25172, 22654, 19266, 15137, 10426, 5315 };\n\n///////////////////////////////////////////////////////////////////////////////\n// Constants (15bit precision) and C macros for IDCT vertical pass\n\n#define kTan1   (13036)   // = tan(pi/16)\n#define kTan2   (27146)   // = tan(2.pi/16) = sqrt(2) - 1.\n#define kTan3m1 (-21746)  // = tan(3.pi/16) - 1\n#define k2Sqrt2 (23170)   // = 1 / 2.sqrt(2)\n\n  // performs: {a,b} <- {a-b, a+b}, without saturation\n#define BUTTERFLY(a, b) do {   \\\n  SUB((a), (b));               \\\n  ADD((b), (b));               \\\n  ADD((b), (a));               \\\n} while (0)\n\n///////////////////////////////////////////////////////////////////////////////\n// Constants for DCT horizontal pass\n\n// Note about the CORRECT_LSB macro:\n// using 16bit fixed-point constants, we often compute products like:\n// p = (A*x + B*y + 32768) >> 16 by adding two sub-terms q = (A*x) >> 16\n// and r = (B*y) >> 16 together. Statistically, we have p = q + r + 1\n// in 3/4 of the cases. This can be easily seen from the relation:\n//   (a + b + 1) >> 1 = (a >> 1) + (b >> 1) + ((a|b)&1)\n// The approximation we are doing is replacing ((a|b)&1) by 1.\n// In practice, this is a slightly more involved because the constants A and B\n// have also been rounded compared to their exact floating point value.\n// However, all in all the correction is quite small, and CORRECT_LSB can\n// be defined empty if needed.\n\n#define COLUMN_DCT8(in) do { \\\n  LOAD(m0, (in)[0 * 8]);     \\\n  LOAD(m2, (in)[2 * 8]);     \\\n  LOAD(m7, (in)[7 * 8]);     \\\n  LOAD(m5, (in)[5 * 8]);     \\\n                             \\\n  BUTTERFLY(m0, m7);         \\\n  BUTTERFLY(m2, m5);         \\\n                             \\\n  LOAD(m3, (in)[3 * 8]);     \\\n  LOAD(m4, (in)[4 * 8]);     \\\n  BUTTERFLY(m3, m4);         \\\n                             \\\n  LOAD(m6, (in)[6 * 8]);     \\\n  LOAD(m1, (in)[1 * 8]);     \\\n  BUTTERFLY(m1, m6);         \\\n  BUTTERFLY(m7, m4);         \\\n  BUTTERFLY(m6, m5);         \\\n                             \\\n  /* RowIdct() needs 15bits fixed-point input, when the output from   */ \\\n  /* ColumnIdct() would be 12bits. We are better doing the shift by 3 */ \\\n  /* now instead of in RowIdct(), because we have some multiplies to  */ \\\n  /* perform, that can take advantage of the extra 3bits precision.   */ \\\n  LSHIFT(m4, 3);             \\\n  LSHIFT(m5, 3);             \\\n  BUTTERFLY(m4, m5);         \\\n  STORE16((in)[0 * 8], m5);  \\\n  STORE16((in)[4 * 8], m4);  \\\n                             \\\n  LSHIFT(m7, 3);             \\\n  LSHIFT(m6, 3);             \\\n  LSHIFT(m3, 3);             \\\n  LSHIFT(m0, 3);             \\\n                             \\\n  LOAD_CST(m4, kTan2);       \\\n  m5 = m4;                   \\\n  MULT(m4, m7);              \\\n  MULT(m5, m6);              \\\n  SUB(m4, m6);               \\\n  ADD(m5, m7);               \\\n  STORE16((in)[2 * 8], m5);  \\\n  STORE16((in)[6 * 8], m4);  \\\n                             \\\n  /* We should be multiplying m6 by C4 = 1/sqrt(2) here, but we only have */ \\\n  /* the k2Sqrt2 = 1/(2.sqrt(2)) constant that fits into 15bits. So we    */ \\\n  /* shift by 4 instead of 3 to compensate for the additional 1/2 factor. */ \\\n  LOAD_CST(m6, k2Sqrt2);     \\\n  LSHIFT(m2, 3 + 1);         \\\n  LSHIFT(m1, 3 + 1);         \\\n  BUTTERFLY(m1, m2);         \\\n  MULT(m2, m6);              \\\n  MULT(m1, m6);              \\\n  BUTTERFLY(m3, m1);         \\\n  BUTTERFLY(m0, m2);         \\\n                             \\\n  LOAD_CST(m4, kTan3m1);     \\\n  LOAD_CST(m5, kTan1);       \\\n  m7 = m3;                   \\\n  m6 = m1;                   \\\n  MULT(m3, m4);              \\\n  MULT(m1, m5);              \\\n                             \\\n  ADD(m3, m7);               \\\n  ADD(m1, m2);               \\\n  CORRECT_LSB(m1);           \\\n  CORRECT_LSB(m3);           \\\n  MULT(m4, m0);              \\\n  MULT(m5, m2);              \\\n  ADD(m4, m0);               \\\n  SUB(m0, m3);               \\\n  ADD(m7, m4);               \\\n  SUB(m5, m6);               \\\n                             \\\n  STORE16((in)[1 * 8], m1);  \\\n  STORE16((in)[3 * 8], m0);  \\\n  STORE16((in)[5 * 8], m7);  \\\n  STORE16((in)[7 * 8], m5);  \\\n} while (0)\n\n\n// these are the macro required by COLUMN_*\n#define LOAD_CST(dst, src) (dst) = (src)\n#define LOAD(dst, src) (dst) = (src)\n#define MULT(a, b)  (a) = (((a) * (b)) >> 16)\n#define ADD(a, b)   (a) = (a) + (b)\n#define SUB(a, b)   (a) = (a) - (b)\n#define LSHIFT(a, n) (a) = ((a) << (n))\n#define STORE16(a, b) (a) = (b)\n#define CORRECT_LSB(a) (a) += 1\n\n// DCT vertical pass\n\ninline void ColumnDct(coeff_t* in) {\n  for (int i = 0; i < 8; ++i) {\n    int m0, m1, m2, m3, m4, m5, m6, m7;\n    COLUMN_DCT8(in + i);\n  }\n}\n\n// DCT horizontal pass\n\n// We don't really need to round before descaling, since we\n// still have 4 bits of precision left as final scaled output.\n#define DESCALE(a)  static_cast<coeff_t>((a) >> 16)\n\nvoid RowDct(coeff_t* in, const coeff_t* table) {\n  // The Fourier transform is an unitary operator, so we're basically\n  // doing the transpose of RowIdct()\n  const int a0 = in[0] + in[7];\n  const int b0 = in[0] - in[7];\n  const int a1 = in[1] + in[6];\n  const int b1 = in[1] - in[6];\n  const int a2 = in[2] + in[5];\n  const int b2 = in[2] - in[5];\n  const int a3 = in[3] + in[4];\n  const int b3 = in[3] - in[4];\n\n  // even part\n  const int C2 = table[1];\n  const int C4 = table[3];\n  const int C6 = table[5];\n  const int c0 = a0 + a3;\n  const int c1 = a0 - a3;\n  const int c2 = a1 + a2;\n  const int c3 = a1 - a2;\n\n  in[0] = DESCALE(C4 * (c0 + c2));\n  in[4] = DESCALE(C4 * (c0 - c2));\n  in[2] = DESCALE(C2 * c1 + C6 * c3);\n  in[6] = DESCALE(C6 * c1 - C2 * c3);\n\n  // odd part\n  const int C1 = table[0];\n  const int C3 = table[2];\n  const int C5 = table[4];\n  const int C7 = table[6];\n  in[1] = DESCALE(C1 * b0 + C3 * b1 + C5 * b2 + C7 * b3);\n  in[3] = DESCALE(C3 * b0 - C7 * b1 - C1 * b2 - C5 * b3);\n  in[5] = DESCALE(C5 * b0 - C1 * b1 + C7 * b2 + C3 * b3);\n  in[7] = DESCALE(C7 * b0 - C5 * b1 + C3 * b2 - C1 * b3);\n}\n#undef DESCALE\n#undef LOAD_CST\n#undef LOAD\n#undef MULT\n#undef ADD\n#undef SUB\n#undef LSHIFT\n#undef STORE16\n#undef CORRECT_LSB\n#undef kTan1\n#undef kTan2\n#undef kTan3m1\n#undef k2Sqrt2\n#undef BUTTERFLY\n#undef COLUMN_DCT8\n\n}  // namespace\n\n///////////////////////////////////////////////////////////////////////////////\n// visible FDCT callable functions\n\nvoid ComputeBlockDCT(coeff_t* coeffs) {\n  ColumnDct(coeffs);\n  RowDct(coeffs + 0 * 8, kTable04);\n  RowDct(coeffs + 1 * 8, kTable17);\n  RowDct(coeffs + 2 * 8, kTable26);\n  RowDct(coeffs + 3 * 8, kTable35);\n  RowDct(coeffs + 4 * 8, kTable04);\n  RowDct(coeffs + 5 * 8, kTable35);\n  RowDct(coeffs + 6 * 8, kTable26);\n  RowDct(coeffs + 7 * 8, kTable17);\n}\n\n}  // namespace guetzli\n"
  },
  {
    "path": "guetzli/fdct.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef GUETZLI_FDCT_H_\n#define GUETZLI_FDCT_H_\n\n#include \"guetzli/jpeg_data.h\"\n\nnamespace guetzli {\n\n// Computes the DCT (Discrete Cosine Transform) of the 8x8 array in 'block',\n// scaled up by a factor of 16. The values in 'block' are laid out row-by-row\n// and the result is written to the same memory area.\nvoid ComputeBlockDCT(coeff_t* block);\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_FDCT_H_\n"
  },
  {
    "path": "guetzli/gamma_correct.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"guetzli/gamma_correct.h\"\n\n#include <cmath>\n\nnamespace guetzli {\n\nconst double* NewSrgb8ToLinearTable() {\n  double* table = new double[256];\n  int i = 0;\n  for (; i < 11; ++i) {\n    table[i] = i / 12.92;\n  }\n  for (; i < 256; ++i) {\n    table[i] = 255.0 * std::pow(((i / 255.0) + 0.055) / 1.055, 2.4);\n  }\n  return table;\n}\n\nconst double* Srgb8ToLinearTable() {\n  static const double* const kSrgb8ToLinearTable = NewSrgb8ToLinearTable();\n  return kSrgb8ToLinearTable;\n}\n\n}  // namespace guetzli\n"
  },
  {
    "path": "guetzli/gamma_correct.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef GUETZLI_GAMMA_CORRECT_H_\n#define GUETZLI_GAMMA_CORRECT_H_\n\nnamespace guetzli {\n\nconst double* Srgb8ToLinearTable();\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_GAMMA_CORRECT_H_\n"
  },
  {
    "path": "guetzli/guetzli.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <algorithm>\n#include <cstdio>\n#include <cstdlib>\n#include <exception>\n#include <memory>\n#include <string>\n#include <sstream>\n#include <string.h>\n#include \"png.h\"\n#include \"guetzli/jpeg_data.h\"\n#include \"guetzli/jpeg_data_reader.h\"\n#include \"guetzli/processor.h\"\n#include \"guetzli/quality.h\"\n#include \"guetzli/stats.h\"\n\nnamespace {\n\nconstexpr int kDefaultJPEGQuality = 95;\n\n// An upper estimate of memory usage of Guetzli. The bound is\n// max(kLowerMemusaeMB * 1<<20, pixel_count * kBytesPerPixel)\nconstexpr int kBytesPerPixel = 350;\nconstexpr int kLowestMemusageMB = 100; // in MB\n\nconstexpr int kDefaultMemlimitMB = 6000; // in MB\n\ninline uint8_t BlendOnBlack(const uint8_t val, const uint8_t alpha) {\n  return (static_cast<int>(val) * static_cast<int>(alpha) + 128) / 255;\n}\n\nbool ReadPNG(const std::string& data, int* xsize, int* ysize,\n             std::vector<uint8_t>* rgb) {\n  png_structp png_ptr =\n      png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);\n  if (!png_ptr) {\n    return false;\n  }\n\n  png_infop info_ptr = png_create_info_struct(png_ptr);\n  if (!info_ptr) {\n    png_destroy_read_struct(&png_ptr, nullptr, nullptr);\n    return false;\n  }\n\n  if (setjmp(png_jmpbuf(png_ptr)) != 0) {\n    // Ok we are here because of the setjmp.\n    png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);\n    return false;\n  }\n\n  std::istringstream memstream(data, std::ios::in | std::ios::binary);\n  png_set_read_fn(png_ptr, static_cast<void*>(&memstream), [](png_structp png_ptr, png_bytep outBytes, png_size_t byteCountToRead) {\n    std::istringstream& memstream = *static_cast<std::istringstream*>(png_get_io_ptr(png_ptr));\n    \n    memstream.read(reinterpret_cast<char*>(outBytes), byteCountToRead);\n\n    if (memstream.eof()) png_error(png_ptr, \"unexpected end of data\");\n    if (memstream.fail()) png_error(png_ptr, \"read from memory error\");\n  });\n\n  // The png_transforms flags are as follows:\n  // packing == convert 1,2,4 bit images,\n  // strip == 16 -> 8 bits / channel,\n  // shift == use sBIT dynamics, and\n  // expand == palettes -> rgb, grayscale -> 8 bit images, tRNS -> alpha.\n  const unsigned int png_transforms =\n      PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_16;\n\n  png_read_png(png_ptr, info_ptr, png_transforms, nullptr);\n\n  png_bytep* row_pointers = png_get_rows(png_ptr, info_ptr);\n\n  *xsize = png_get_image_width(png_ptr, info_ptr);\n  *ysize = png_get_image_height(png_ptr, info_ptr);\n  rgb->resize(3 * (*xsize) * (*ysize));\n\n  const int components = png_get_channels(png_ptr, info_ptr);\n  switch (components) {\n    case 1: {\n      // GRAYSCALE\n      for (int y = 0; y < *ysize; ++y) {\n        const uint8_t* row_in = row_pointers[y];\n        uint8_t* row_out = &(*rgb)[3 * y * (*xsize)];\n        for (int x = 0; x < *xsize; ++x) {\n          const uint8_t gray = row_in[x];\n          row_out[3 * x + 0] = gray;\n          row_out[3 * x + 1] = gray;\n          row_out[3 * x + 2] = gray;\n        }\n      }\n      break;\n    }\n    case 2: {\n      // GRAYSCALE + ALPHA\n      for (int y = 0; y < *ysize; ++y) {\n        const uint8_t* row_in = row_pointers[y];\n        uint8_t* row_out = &(*rgb)[3 * y * (*xsize)];\n        for (int x = 0; x < *xsize; ++x) {\n          const uint8_t gray = BlendOnBlack(row_in[2 * x], row_in[2 * x + 1]);\n          row_out[3 * x + 0] = gray;\n          row_out[3 * x + 1] = gray;\n          row_out[3 * x + 2] = gray;\n        }\n      }\n      break;\n    }\n    case 3: {\n      // RGB\n      for (int y = 0; y < *ysize; ++y) {\n        const uint8_t* row_in = row_pointers[y];\n        uint8_t* row_out = &(*rgb)[3 * y * (*xsize)];\n        memcpy(row_out, row_in, 3 * (*xsize));\n      }\n      break;\n    }\n    case 4: {\n      // RGBA\n      for (int y = 0; y < *ysize; ++y) {\n        const uint8_t* row_in = row_pointers[y];\n        uint8_t* row_out = &(*rgb)[3 * y * (*xsize)];\n        for (int x = 0; x < *xsize; ++x) {\n          const uint8_t alpha = row_in[4 * x + 3];\n          row_out[3 * x + 0] = BlendOnBlack(row_in[4 * x + 0], alpha);\n          row_out[3 * x + 1] = BlendOnBlack(row_in[4 * x + 1], alpha);\n          row_out[3 * x + 2] = BlendOnBlack(row_in[4 * x + 2], alpha);\n        }\n      }\n      break;\n    }\n    default:\n      png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);\n      return false;\n  }\n  png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);\n  return true;\n}\n\nstd::string ReadFileOrDie(const char* filename) {\n  bool read_from_stdin = strncmp(filename, \"-\", 2) == 0;\n\n  FILE* f = read_from_stdin ? stdin : fopen(filename, \"rb\");\n  if (!f) {\n    perror(\"Can't open input file\");\n    exit(1);\n  }\n\n  std::string result;\n  off_t buffer_size = 8192;\n\n  if (fseek(f, 0, SEEK_END) == 0) {\n    buffer_size = std::max<off_t>(ftell(f), 1);\n    if (fseek(f, 0, SEEK_SET) != 0) {\n      perror(\"fseek\");\n      exit(1);\n    }\n  } else if (ferror(f)) {\n    perror(\"fseek\");\n    exit(1);\n  }\n\n  std::unique_ptr<char[]> buf(new char[buffer_size]);\n  while (!feof(f)) {\n    size_t read_bytes = fread(buf.get(), sizeof(char), buffer_size, f);\n    if (ferror(f)) {\n      perror(\"fread\");\n      exit(1);\n    }\n    result.append(buf.get(), read_bytes);\n  }\n\n  fclose(f);\n  return result;\n}\n\nvoid WriteFileOrDie(const char* filename, const std::string& contents) {\n  bool write_to_stdout = strncmp(filename, \"-\", 2) == 0;\n\n  FILE* f = write_to_stdout ? stdout : fopen(filename, \"wb\");\n  if (!f) {\n    perror(\"Can't open output file for writing\");\n    exit(1);\n  }\n  if (fwrite(contents.data(), 1, contents.size(), f) != contents.size()) {\n    perror(\"fwrite\");\n    exit(1);\n  }\n  if (fclose(f) < 0) {\n    perror(\"fclose\");\n    exit(1);\n  }\n}\n\nvoid TerminateHandler() {\n  fprintf(stderr, \"Unhandled exception. Most likely insufficient memory available.\\n\"\n          \"Make sure that there is 300MB/MPix of memory available.\\n\");\n  exit(1);\n}\n\nvoid Usage() {\n  fprintf(stderr,\n      \"Guetzli JPEG compressor. Usage: \\n\"\n      \"guetzli [flags] input_filename output_filename\\n\"\n      \"\\n\"\n      \"Flags:\\n\"\n      \"  --verbose    - Print a verbose trace of all attempts to standard output.\\n\"\n      \"  --quality Q  - Visual quality to aim for, expressed as a JPEG quality value.\\n\"\n      \"                 Default value is %d.\\n\"\n      \"  --memlimit M - Memory limit in MB. Guetzli will fail if unable to stay under\\n\"\n      \"                 the limit. Default limit is %d MB.\\n\"\n      \"  --nomemlimit - Do not limit memory usage.\\n\", kDefaultJPEGQuality, kDefaultMemlimitMB);\n  exit(1);\n}\n\n}  // namespace\n\nint main(int argc, char** argv) {\n  std::set_terminate(TerminateHandler);\n\n  int verbose = 0;\n  int quality = kDefaultJPEGQuality;\n  int memlimit_mb = kDefaultMemlimitMB;\n\n  int opt_idx = 1;\n  for(;opt_idx < argc;opt_idx++) {\n    if (strnlen(argv[opt_idx], 2) < 2 || argv[opt_idx][0] != '-' || argv[opt_idx][1] != '-')\n      break;\n    if (!strcmp(argv[opt_idx], \"--verbose\")) {\n      verbose = 1;\n    } else if (!strcmp(argv[opt_idx], \"--quality\")) {\n      opt_idx++;\n      if (opt_idx >= argc)\n        Usage();\n      quality = atoi(argv[opt_idx]);\n    } else if (!strcmp(argv[opt_idx], \"--memlimit\")) {\n      opt_idx++;\n      if (opt_idx >= argc)\n        Usage();\n      memlimit_mb = atoi(argv[opt_idx]);\n    } else if (!strcmp(argv[opt_idx], \"--nomemlimit\")) {\n      memlimit_mb = -1;\n    } else if (!strcmp(argv[opt_idx], \"--\")) {\n      opt_idx++;\n      break;\n    } else {\n      fprintf(stderr, \"Unknown commandline flag: %s\\n\", argv[opt_idx]);\n      Usage();\n    }\n  }\n\n  if (argc - opt_idx != 2) {\n    Usage();\n  }\n\n  std::string in_data = ReadFileOrDie(argv[opt_idx]);\n  std::string out_data;\n\n  guetzli::Params params;\n  params.butteraugli_target = static_cast<float>(\n      guetzli::ButteraugliScoreForQuality(quality));\n\n  guetzli::ProcessStats stats;\n\n  if (verbose) {\n    stats.debug_output_file = stderr;\n  }\n\n  static const unsigned char kPNGMagicBytes[] = {\n      0x89, 'P', 'N', 'G', '\\r', '\\n', 0x1a, '\\n',\n  };\n  if (in_data.size() >= 8 &&\n      memcmp(in_data.data(), kPNGMagicBytes, sizeof(kPNGMagicBytes)) == 0) {\n    int xsize, ysize;\n    std::vector<uint8_t> rgb;\n    if (!ReadPNG(in_data, &xsize, &ysize, &rgb)) {\n      fprintf(stderr, \"Error reading PNG data from input file\\n\");\n      return 1;\n    }\n    double pixels = static_cast<double>(xsize) * ysize;\n    if (memlimit_mb != -1\n        && (pixels * kBytesPerPixel / (1 << 20) > memlimit_mb\n            || memlimit_mb < kLowestMemusageMB)) {\n      fprintf(stderr, \"Memory limit would be exceeded. Failing.\\n\");\n      return 1;\n    }\n    if (!guetzli::Process(params, &stats, rgb, xsize, ysize, &out_data)) {\n      fprintf(stderr, \"Guetzli processing failed\\n\");\n      return 1;\n    }\n  } else {\n    guetzli::JPEGData jpg_header;\n    if (!guetzli::ReadJpeg(in_data, guetzli::JPEG_READ_HEADER, &jpg_header)) {\n      fprintf(stderr, \"Error reading JPG data from input file\\n\");\n      return 1;\n    }\n    double pixels = static_cast<double>(jpg_header.width) * jpg_header.height;\n    if (memlimit_mb != -1\n        && (pixels * kBytesPerPixel / (1 << 20) > memlimit_mb\n            || memlimit_mb < kLowestMemusageMB)) {\n      fprintf(stderr, \"Memory limit would be exceeded. Failing.\\n\");\n      return 1;\n    }\n    if (!guetzli::Process(params, &stats, in_data, &out_data)) {\n      fprintf(stderr, \"Guetzli processing failed\\n\");\n      return 1;\n    }\n  }\n\n  WriteFileOrDie(argv[opt_idx + 1], out_data);\n  return 0;\n}\n"
  },
  {
    "path": "guetzli/idct.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Integer implementation of the Inverse Discrete Cosine Transform (IDCT).\n\n#include \"guetzli/idct.h\"\n\n#include <algorithm>\n#include <math.h>\n\nnamespace guetzli {\n\n// kIDCTMatrix[8*x+u] = alpha(u)*cos((2*x+1)*u*M_PI/16)*sqrt(2), with fixed 13\n// bit precision, where alpha(0) = 1/sqrt(2) and alpha(u) = 1 for u > 0.\n// Some coefficients are off by +-1 to mimick libjpeg's behaviour.\nstatic const int kIDCTMatrix[kDCTBlockSize] = {\n  8192,  11363,  10703,   9633,   8192,   6437,   4433,   2260,\n  8192,   9633,   4433,  -2259,  -8192, -11362, -10704,  -6436,\n  8192,   6437,  -4433, -11362,  -8192,   2261,  10704,   9633,\n  8192,   2260, -10703,  -6436,   8192,   9633,  -4433, -11363,\n  8192,  -2260, -10703,   6436,   8192,  -9633,  -4433,  11363,\n  8192,  -6437,  -4433,  11362,  -8192,  -2261,  10704,  -9633,\n  8192,  -9633,   4433,   2259,  -8192,  11362, -10704,   6436,\n  8192, -11363,  10703,  -9633,   8192,  -6437,   4433,  -2260,\n};\n\n// Computes out[x] = sum{kIDCTMatrix[8*x+u]*in[u*stride]; for u in [0..7]}\ninline void Compute1dIDCT(const coeff_t* in, const int stride, int out[8]) {\n  int tmp0, tmp1, tmp2, tmp3, tmp4;\n\n  tmp1 = kIDCTMatrix[0] * in[0];\n  out[0] = out[1] = out[2] = out[3] = out[4] = out[5] = out[6] = out[7] = tmp1;\n\n  tmp0 = in[stride];\n  tmp1 = kIDCTMatrix[ 1] * tmp0;\n  tmp2 = kIDCTMatrix[ 9] * tmp0;\n  tmp3 = kIDCTMatrix[17] * tmp0;\n  tmp4 = kIDCTMatrix[25] * tmp0;\n  out[0] += tmp1;\n  out[1] += tmp2;\n  out[2] += tmp3;\n  out[3] += tmp4;\n  out[4] -= tmp4;\n  out[5] -= tmp3;\n  out[6] -= tmp2;\n  out[7] -= tmp1;\n\n  tmp0 = in[2 * stride];\n  tmp1 = kIDCTMatrix[ 2] * tmp0;\n  tmp2 = kIDCTMatrix[10] * tmp0;\n  out[0] += tmp1;\n  out[1] += tmp2;\n  out[2] -= tmp2;\n  out[3] -= tmp1;\n  out[4] -= tmp1;\n  out[5] -= tmp2;\n  out[6] += tmp2;\n  out[7] += tmp1;\n\n  tmp0 = in[3 * stride];\n  tmp1 = kIDCTMatrix[ 3] * tmp0;\n  tmp2 = kIDCTMatrix[11] * tmp0;\n  tmp3 = kIDCTMatrix[19] * tmp0;\n  tmp4 = kIDCTMatrix[27] * tmp0;\n  out[0] += tmp1;\n  out[1] += tmp2;\n  out[2] += tmp3;\n  out[3] += tmp4;\n  out[4] -= tmp4;\n  out[5] -= tmp3;\n  out[6] -= tmp2;\n  out[7] -= tmp1;\n\n  tmp0 = in[4 * stride];\n  tmp1 = kIDCTMatrix[ 4] * tmp0;\n  out[0] += tmp1;\n  out[1] -= tmp1;\n  out[2] -= tmp1;\n  out[3] += tmp1;\n  out[4] += tmp1;\n  out[5] -= tmp1;\n  out[6] -= tmp1;\n  out[7] += tmp1;\n\n  tmp0 = in[5 * stride];\n  tmp1 = kIDCTMatrix[ 5] * tmp0;\n  tmp2 = kIDCTMatrix[13] * tmp0;\n  tmp3 = kIDCTMatrix[21] * tmp0;\n  tmp4 = kIDCTMatrix[29] * tmp0;\n  out[0] += tmp1;\n  out[1] += tmp2;\n  out[2] += tmp3;\n  out[3] += tmp4;\n  out[4] -= tmp4;\n  out[5] -= tmp3;\n  out[6] -= tmp2;\n  out[7] -= tmp1;\n\n  tmp0 = in[6 * stride];\n  tmp1 = kIDCTMatrix[ 6] * tmp0;\n  tmp2 = kIDCTMatrix[14] * tmp0;\n  out[0] += tmp1;\n  out[1] += tmp2;\n  out[2] -= tmp2;\n  out[3] -= tmp1;\n  out[4] -= tmp1;\n  out[5] -= tmp2;\n  out[6] += tmp2;\n  out[7] += tmp1;\n\n  tmp0 = in[7 * stride];\n  tmp1 = kIDCTMatrix[ 7] * tmp0;\n  tmp2 = kIDCTMatrix[15] * tmp0;\n  tmp3 = kIDCTMatrix[23] * tmp0;\n  tmp4 = kIDCTMatrix[31] * tmp0;\n  out[0] += tmp1;\n  out[1] += tmp2;\n  out[2] += tmp3;\n  out[3] += tmp4;\n  out[4] -= tmp4;\n  out[5] -= tmp3;\n  out[6] -= tmp2;\n  out[7] -= tmp1;\n}\n\nvoid ComputeBlockIDCT(const coeff_t* block, uint8_t* out) {\n  coeff_t colidcts[kDCTBlockSize];\n  const int kColScale = 11;\n  const int kColRound = 1 << (kColScale - 1);\n  for (int x = 0; x < 8; ++x) {\n    int colbuf[8] = { 0 };\n    Compute1dIDCT(&block[x], 8, colbuf);\n    for (int y = 0; y < 8; ++y) {\n      colidcts[8 * y + x] = (colbuf[y] + kColRound) >> kColScale;\n    }\n  }\n  const int kRowScale = 18;\n  const int kRowRound = 257 << (kRowScale - 1);  // includes offset by 128\n  for (int y = 0; y < 8; ++y) {\n    const int rowidx = 8 * y;\n    int rowbuf[8] = { 0 };\n    Compute1dIDCT(&colidcts[rowidx], 1, rowbuf);\n    for (int x = 0; x < 8; ++x) {\n      out[rowidx + x] =\n          std::max(0, std::min(255, (rowbuf[x] + kRowRound) >> kRowScale));\n    }\n  }\n}\n\n}  // namespace guetzli\n"
  },
  {
    "path": "guetzli/idct.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef GUETZLI_IDCT_H_\n#define GUETZLI_IDCT_H_\n\n#include \"guetzli/jpeg_data.h\"\n\nnamespace guetzli {\n\n// Fills in 'result' with the inverse DCT of 'block'.\n// The arguments 'block' and 'result' point to 8x8 arrays that are arranged in\n// a row-by-row memory layout.\nvoid ComputeBlockIDCT(const coeff_t* block, uint8_t* result);\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_IDCT_H_\n"
  },
  {
    "path": "guetzli/jpeg_bit_writer.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef GUETZLI_JPEG_BIT_WRITER_H_\n#define GUETZLI_JPEG_BIT_WRITER_H_\n\n#include <stdint.h>\n#include <memory>\n\nnamespace guetzli {\n\n// Returns non-zero if and only if x has a zero byte, i.e. one of\n// x & 0xff, x & 0xff00, ..., x & 0xff00000000000000 is zero.\ninline uint64_t HasZeroByte(uint64_t x) {\n  return (x - 0x0101010101010101ULL) & ~x & 0x8080808080808080ULL;\n}\n\n// Handles the packing of bits into output bytes.\nstruct BitWriter {\n  explicit BitWriter(size_t length) : len(length),\n                                      data(new uint8_t[len]),\n                                      pos(0),\n                                      put_buffer(0),\n                                      put_bits(64),\n                                      overflow(false) {}\n\n  void WriteBits(int nbits, uint64_t bits) {\n    put_bits -= nbits;\n    put_buffer |= (bits << put_bits);\n    if (put_bits <= 16) {\n      // At this point we are ready to emit the most significant 6 bytes of\n      // put_buffer_ to the output.\n      // The JPEG format requires that after every 0xff byte in the entropy\n      // coded section, there is a zero byte, therefore we first check if any of\n      // the 6 most significant bytes of put_buffer_ is 0xff.\n      if (HasZeroByte(~put_buffer | 0xffff)) {\n        // We have a 0xff byte somewhere, examine each byte and append a zero\n        // byte if necessary.\n        EmitByte((put_buffer >> 56) & 0xff);\n        EmitByte((put_buffer >> 48) & 0xff);\n        EmitByte((put_buffer >> 40) & 0xff);\n        EmitByte((put_buffer >> 32) & 0xff);\n        EmitByte((put_buffer >> 24) & 0xff);\n        EmitByte((put_buffer >> 16) & 0xff);\n      } else if (pos + 6 < len) {\n        // We don't have any 0xff bytes, output all 6 bytes without checking.\n        data[pos] = (put_buffer >> 56) & 0xff;\n        data[pos + 1] = (put_buffer >> 48) & 0xff;\n        data[pos + 2] = (put_buffer >> 40) & 0xff;\n        data[pos + 3] = (put_buffer >> 32) & 0xff;\n        data[pos + 4] = (put_buffer >> 24) & 0xff;\n        data[pos + 5] = (put_buffer >> 16) & 0xff;\n        pos += 6;\n      } else {\n        overflow = true;\n      }\n      put_buffer <<= 48;\n      put_bits += 48;\n    }\n  }\n\n  // Writes the given byte to the output, writes an extra zero if byte is 0xff.\n  void EmitByte(int byte) {\n    if (pos < len) {\n      data[pos++] = byte;\n    } else {\n      overflow = true;\n    }\n    if (byte == 0xff) {\n      EmitByte(0);\n    }\n  }\n\n  void JumpToByteBoundary() {\n    while (put_bits <= 56) {\n      int c = (put_buffer >> 56) & 0xff;\n      EmitByte(c);\n      put_buffer <<= 8;\n      put_bits += 8;\n    }\n    if (put_bits < 64) {\n      int padmask = 0xff >> (64 - put_bits);\n      int c = ((put_buffer >> 56) & ~padmask) | padmask;\n      EmitByte(c);\n    }\n    put_buffer = 0;\n    put_bits = 64;\n  }\n\n  size_t len;\n  std::unique_ptr<uint8_t[]> data;\n  size_t pos;\n  uint64_t put_buffer;\n  int put_bits;\n  bool overflow;\n};\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_JPEG_BIT_WRITER_H_\n"
  },
  {
    "path": "guetzli/jpeg_data.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"guetzli/jpeg_data.h\"\n\n#include <assert.h>\n#include <string.h>\n\nnamespace guetzli {\n\nbool JPEGData::Is420() const {\n  return (components.size() == 3 &&\n          max_h_samp_factor == 2 &&\n          max_v_samp_factor == 2 &&\n          components[0].h_samp_factor == 2 &&\n          components[0].v_samp_factor == 2 &&\n          components[1].h_samp_factor == 1 &&\n          components[1].v_samp_factor == 1 &&\n          components[2].h_samp_factor == 1 &&\n          components[2].v_samp_factor == 1);\n}\n\nbool JPEGData::Is444() const {\n  return (components.size() == 3 &&\n          max_h_samp_factor == 1 &&\n          max_v_samp_factor == 1 &&\n          components[0].h_samp_factor == 1 &&\n          components[0].v_samp_factor == 1 &&\n          components[1].h_samp_factor == 1 &&\n          components[1].v_samp_factor == 1 &&\n          components[2].h_samp_factor == 1 &&\n          components[2].v_samp_factor == 1);\n}\n\nvoid InitJPEGDataForYUV444(int w, int h, JPEGData* jpg) {\n  jpg->width = w;\n  jpg->height = h;\n  jpg->max_h_samp_factor = 1;\n  jpg->max_v_samp_factor = 1;\n  jpg->MCU_rows = (h + 7) >> 3;\n  jpg->MCU_cols = (w + 7) >> 3;\n  jpg->quant.resize(3);\n  jpg->components.resize(3);\n  for (int i = 0; i < 3; ++i) {\n    JPEGComponent* c = &jpg->components[i];\n    c->id = i;\n    c->h_samp_factor = 1;\n    c->v_samp_factor = 1;\n    c->quant_idx = i;\n    c->width_in_blocks = jpg->MCU_cols;\n    c->height_in_blocks = jpg->MCU_rows;\n    c->num_blocks = c->width_in_blocks * c->height_in_blocks;\n    c->coeffs.resize(c->num_blocks * kDCTBlockSize);\n  }\n}\n\nvoid SaveQuantTables(const int q[3][kDCTBlockSize], JPEGData* jpg) {\n  const size_t kTableSize = kDCTBlockSize * sizeof(q[0][0]);\n  jpg->quant.clear();\n  int num_tables = 0;\n  for (size_t i = 0; i < jpg->components.size(); ++i) {\n    JPEGComponent* comp = &jpg->components[i];\n    // Check if we have this quant table already.\n    bool found = false;\n    for (int j = 0; j < num_tables; ++j) {\n      if (memcmp(&q[i][0], &jpg->quant[j].values[0], kTableSize) == 0) {\n        comp->quant_idx = j;\n        found = true;\n        break;\n      }\n    }\n    if (!found) {\n      JPEGQuantTable table;\n      memcpy(&table.values[0], &q[i][0], kTableSize);\n      table.precision = 0;\n      for (int k = 0; k < kDCTBlockSize; ++k) {\n        assert(table.values[k] >= 0);\n        assert(table.values[k] < (1 << 16));\n        if (table.values[k] > 0xff) {\n          table.precision = 1;\n        }\n      }\n      table.index = num_tables;\n      comp->quant_idx = num_tables;\n      jpg->quant.push_back(table);\n      ++num_tables;\n    }\n  }\n}\n\n}  // namespace guetzli\n"
  },
  {
    "path": "guetzli/jpeg_data.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Data structures that represent the contents of a jpeg file.\n\n#ifndef GUETZLI_JPEG_DATA_H_\n#define GUETZLI_JPEG_DATA_H_\n\n#include <stddef.h>\n#include <stdint.h>\n#include <string>\n#include <vector>\n\n#include \"guetzli/jpeg_error.h\"\n\nnamespace guetzli {\n\nstatic const int kDCTBlockSize = 64;\nstatic const int kMaxComponents = 4;\nstatic const int kMaxQuantTables = 4;\nstatic const int kMaxHuffmanTables = 4;\nstatic const int kJpegHuffmanMaxBitLength = 16;\nstatic const int kJpegHuffmanAlphabetSize = 256;\nstatic const int kJpegDCAlphabetSize = 12;\nstatic const int kMaxDHTMarkers = 512;\n\nstatic const uint8_t kDefaultQuantMatrix[2][64] = {\n  { 16,  11,  10,  16,  24,  40,  51,  61,\n    12,  12,  14,  19,  26,  58,  60,  55,\n    14,  13,  16,  24,  40,  57,  69,  56,\n    14,  17,  22,  29,  51,  87,  80,  62,\n    18,  22,  37,  56,  68, 109, 103,  77,\n    24,  35,  55,  64,  81, 104, 113,  92,\n    49,  64,  78,  87, 103, 121, 120, 101,\n    72,  92,  95,  98, 112, 100, 103,  99 },\n  { 17,  18,  24,  47,  99,  99,  99,  99,\n    18,  21,  26,  66,  99,  99,  99,  99,\n    24,  26,  56,  99,  99,  99,  99,  99,\n    47,  66,  99,  99,  99,  99,  99,  99,\n    99,  99,  99,  99,  99,  99,  99,  99,\n    99,  99,  99,  99,  99,  99,  99,  99,\n    99,  99,  99,  99,  99,  99,  99,  99,\n    99,  99,  99,  99,  99,  99,  99,  99 }\n};\n\nconst int kJPEGNaturalOrder[80] = {\n  0,   1,  8, 16,  9,  2,  3, 10,\n  17, 24, 32, 25, 18, 11,  4,  5,\n  12, 19, 26, 33, 40, 48, 41, 34,\n  27, 20, 13,  6,  7, 14, 21, 28,\n  35, 42, 49, 56, 57, 50, 43, 36,\n  29, 22, 15, 23, 30, 37, 44, 51,\n  58, 59, 52, 45, 38, 31, 39, 46,\n  53, 60, 61, 54, 47, 55, 62, 63,\n  // extra entries for safety in decoder\n  63, 63, 63, 63, 63, 63, 63, 63,\n  63, 63, 63, 63, 63, 63, 63, 63\n};\n\nconst int kJPEGZigZagOrder[64] = {\n  0,   1,  5,  6, 14, 15, 27, 28,\n  2,   4,  7, 13, 16, 26, 29, 42,\n  3,   8, 12, 17, 25, 30, 41, 43,\n  9,  11, 18, 24, 31, 40, 44, 53,\n  10, 19, 23, 32, 39, 45, 52, 54,\n  20, 22, 33, 38, 46, 51, 55, 60,\n  21, 34, 37, 47, 50, 56, 59, 61,\n  35, 36, 48, 49, 57, 58, 62, 63\n};\n\n// Quantization values for an 8x8 pixel block.\nstruct JPEGQuantTable {\n  JPEGQuantTable() : values(kDCTBlockSize), precision(0),\n                     index(0), is_last(true) {}\n\n  std::vector<int> values;\n  int precision;\n  // The index of this quantization table as it was parsed from the input JPEG.\n  // Each DQT marker segment contains an 'index' field, and we save this index\n  // here. Valid values are 0 to 3.\n  int index;\n  // Set to true if this table is the last one within its marker segment.\n  bool is_last;\n};\n\n// Huffman code and decoding lookup table used for DC and AC coefficients.\nstruct JPEGHuffmanCode {\n  JPEGHuffmanCode() : counts(kJpegHuffmanMaxBitLength + 1),\n                      values(kJpegHuffmanAlphabetSize + 1),\n                      slot_id(0),\n                      is_last(true) {}\n\n  // Bit length histogram.\n  std::vector<int> counts;\n  // Symbol values sorted by increasing bit lengths.\n  std::vector<int> values;\n  // The index of the Huffman code in the current set of Huffman codes. For AC\n  // component Huffman codes, 0x10 is added to the index.\n  int slot_id;\n  // Set to true if this Huffman code is the last one within its marker segment.\n  bool is_last;\n};\n\n// Huffman table indexes used for one component of one scan.\nstruct JPEGComponentScanInfo {\n  int comp_idx;\n  int dc_tbl_idx;\n  int ac_tbl_idx;\n};\n\n// Contains information that is used in one scan.\nstruct JPEGScanInfo {\n  // Parameters used for progressive scans (named the same way as in the spec):\n  //   Ss : Start of spectral band in zig-zag sequence.\n  //   Se : End of spectral band in zig-zag sequence.\n  //   Ah : Successive approximation bit position, high.\n  //   Al : Successive approximation bit position, low.\n  int Ss;\n  int Se;\n  int Ah;\n  int Al;\n  std::vector<JPEGComponentScanInfo> components;\n};\n\ntypedef int16_t coeff_t;\n\n// Represents one component of a jpeg file.\nstruct JPEGComponent {\n  JPEGComponent() : id(0),\n                    h_samp_factor(1),\n                    v_samp_factor(1),\n                    quant_idx(0),\n                    width_in_blocks(0),\n                    height_in_blocks(0) {}\n\n  // One-byte id of the component.\n  int id;\n  // Horizontal and vertical sampling factors.\n  // In interleaved mode, each minimal coded unit (MCU) has\n  // h_samp_factor x v_samp_factor DCT blocks from this component.\n  int h_samp_factor;\n  int v_samp_factor;\n  // The index of the quantization table used for this component.\n  size_t quant_idx;\n  // The dimensions of the component measured in 8x8 blocks.\n  int width_in_blocks;\n  int height_in_blocks;\n  int num_blocks;\n  // The DCT coefficients of this component, laid out block-by-block, divided\n  // through the quantization matrix values.\n  std::vector<coeff_t> coeffs;\n};\n\n// Represents a parsed jpeg file.\nstruct JPEGData {\n  JPEGData() : width(0),\n               height(0),\n               version(0),\n               max_h_samp_factor(1),\n               max_v_samp_factor(1),\n               MCU_rows(0),\n               MCU_cols(0),\n               restart_interval(0),\n               original_jpg(NULL),\n               original_jpg_size(0),\n               error(JPEG_OK) {}\n\n  bool Is420() const;\n  bool Is444() const;\n\n  int width;\n  int height;\n  int version;\n  int max_h_samp_factor;\n  int max_v_samp_factor;\n  int MCU_rows;\n  int MCU_cols;\n  int restart_interval;\n  std::vector<std::string> app_data;\n  std::vector<std::string> com_data;\n  std::vector<JPEGQuantTable> quant;\n  std::vector<JPEGHuffmanCode> huffman_code;\n  std::vector<JPEGComponent> components;\n  std::vector<JPEGScanInfo> scan_info;\n  std::vector<uint8_t> marker_order;\n  std::vector<std::string> inter_marker_data;\n  std::string tail_data;\n  const uint8_t* original_jpg;\n  size_t original_jpg_size;\n  JPEGReadError error;\n};\n\nvoid InitJPEGDataForYUV444(int w, int h, JPEGData* jpg);\nvoid SaveQuantTables(const int q[3][kDCTBlockSize], JPEGData* jpg);\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_JPEG_DATA_H_\n"
  },
  {
    "path": "guetzli/jpeg_data_decoder.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"guetzli/jpeg_data_decoder.h\"\n\n#include \"guetzli/output_image.h\"\n\nnamespace guetzli {\n\n// Mimic libjpeg's heuristics to guess jpeg color space.\n// Requires that the jpg has 3 components.\nbool HasYCbCrColorSpace(const JPEGData& jpg) {\n  bool has_Adobe_marker = false;\n  uint8_t Adobe_transform = 0;\n  for (const std::string& app : jpg.app_data) {\n    if (static_cast<uint8_t>(app[0]) == 0xe0) {\n      return true;\n    } else if (static_cast<uint8_t>(app[0]) == 0xee && app.size() >= 15) {\n      has_Adobe_marker = true;\n      Adobe_transform = app[14];\n    }\n  }\n  if (has_Adobe_marker) {\n    return (Adobe_transform != 0);\n  }\n  const int cid0 = jpg.components[0].id;\n  const int cid1 = jpg.components[1].id;\n  const int cid2 = jpg.components[2].id;\n  return (cid0 != 'R' || cid1 != 'G' || cid2 != 'B');\n}\n\nstd::vector<uint8_t> DecodeJpegToRGB(const JPEGData& jpg) {\n  if (jpg.components.size() == 1 ||\n      (jpg.components.size() == 3 &&\n       HasYCbCrColorSpace(jpg) && (jpg.Is420() || jpg.Is444()))) {\n    OutputImage img(jpg.width, jpg.height);\n    img.CopyFromJpegData(jpg);\n    return img.ToSRGB();\n  }\n  return std::vector<uint8_t>();\n}\n\n}  // namespace guetzli\n"
  },
  {
    "path": "guetzli/jpeg_data_decoder.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Library to decode jpeg coefficients into an RGB image.\n\n#ifndef GUETZLI_JPEG_DATA_DECODER_H_\n#define GUETZLI_JPEG_DATA_DECODER_H_\n\n#include <stdint.h>\n\n#include \"guetzli/jpeg_data.h\"\n\nnamespace guetzli {\n\n// Decodes the parsed jpeg coefficients into an RGB image.\n// There can be only either 1 or 3 image components, in either case, an RGB\n// output image will be generated.\n// Only YUV420 and YUV444 sampling factors are supported.\n// Vector will be empty if a decoding error occurred.\nstd::vector<uint8_t> DecodeJpegToRGB(const JPEGData& jpg);\n\n// Mimic libjpeg's heuristics to guess jpeg color space.\n// Requires that the jpg has 3 components.\nbool HasYCbCrColorSpace(const JPEGData& jpg);\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_JPEG_DATA_DECODER_H_\n"
  },
  {
    "path": "guetzli/jpeg_data_encoder.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"guetzli/jpeg_data_encoder.h\"\n\n#include <algorithm>\n#include <string.h>\n\n#include \"guetzli/fdct.h\"\n\nnamespace guetzli {\n\nnamespace {\n\nstatic const int kIQuantBits = 16;\n// Output of the DCT is upscaled by 16.\nstatic const int kDCTBits = kIQuantBits + 4;\nstatic const int kBias = 0x80 << (kDCTBits - 8);\n\nvoid Quantize(coeff_t* v, int iquant) {\n  *v = (*v * iquant + kBias) >> kDCTBits;\n}\n\n// Single pixel rgb to 16-bit yuv conversion.\n// The returned yuv values are signed integers in the\n// range [-128, 127] inclusive.\ninline static void RGBToYUV16(const uint8_t* const rgb,\n                              coeff_t *out) {\n  enum { FRAC = 16, HALF = 1 << (FRAC - 1) };\n  const int r = rgb[0];\n  const int g = rgb[1];\n  const int b = rgb[2];\n  out[0] = (19595 * r  + 38469 * g +  7471 * b - (128 << 16) + HALF) >> FRAC;\n  out[64] = (-11059 * r - 21709 * g + 32768 * b + HALF - 1) >> FRAC;\n  out[128] = (32768 * r  - 27439 * g -  5329 * b + HALF - 1) >> FRAC;\n}\n\n}  // namespace\n\nvoid AddApp0Data(JPEGData* jpg) {\n  const unsigned char kApp0Data[] = {\n      0xe0, 0x00, 0x10,              // APP0\n      0x4a, 0x46, 0x49, 0x46, 0x00,  // 'JFIF'\n      0x01, 0x01,                    // v1.01\n      0x00, 0x00, 0x01, 0x00, 0x01,  // aspect ratio = 1:1\n      0x00, 0x00                     // thumbnail width/height\n  };\n  jpg->app_data.push_back(\n      std::string(reinterpret_cast<const char*>(kApp0Data),\n                                 sizeof(kApp0Data)));\n}\n\nbool EncodeRGBToJpeg(const std::vector<uint8_t>& rgb, int w, int h,\n                     const int* quant, JPEGData* jpg) {\n  if (w < 0 || w >= 1 << 16 || h < 0 || h >= 1 << 16 ||\n      rgb.size() != 3 * w * h) {\n    return false;\n  }\n  InitJPEGDataForYUV444(w, h, jpg);\n  AddApp0Data(jpg);\n\n  int iquant[3 * kDCTBlockSize];\n  int idx = 0;\n  for (int i = 0; i < 3; ++i) {\n    for (int j = 0; j < kDCTBlockSize; ++j) {\n      int v = quant[idx];\n      jpg->quant[i].values[j] = v;\n      iquant[idx++] = ((1 << kIQuantBits) + 1) / v;\n    }\n  }\n\n  // Compute YUV444 DCT coefficients.\n  int block_ix = 0;\n  for (int block_y = 0; block_y < jpg->MCU_rows; ++block_y) {\n    for (int block_x = 0; block_x < jpg->MCU_cols; ++block_x) {\n      coeff_t block[3 * kDCTBlockSize];\n      // RGB->YUV transform.\n      for (int iy = 0; iy < 8; ++iy) {\n        for (int ix = 0; ix < 8; ++ix) {\n          int y = std::min(h - 1, 8 * block_y + iy);\n          int x = std::min(w - 1, 8 * block_x + ix);\n          int p = y * w + x;\n          RGBToYUV16(&rgb[3 * p], &block[8 * iy + ix]);\n        }\n      }\n      // DCT\n      for (int i = 0; i < 3; ++i) {\n        ComputeBlockDCT(&block[i * kDCTBlockSize]);\n      }\n      // Quantization\n      for (int i = 0; i < 3 * 64; ++i) {\n        Quantize(&block[i], iquant[i]);\n      }\n      // Copy the resulting coefficients to *jpg.\n      for (int i = 0; i < 3; ++i) {\n        memcpy(&jpg->components[i].coeffs[block_ix * kDCTBlockSize],\n               &block[i * kDCTBlockSize], kDCTBlockSize * sizeof(block[0]));\n      }\n      ++block_ix;\n    }\n  }\n\n  return true;\n}\n\nbool EncodeRGBToJpeg(const std::vector<uint8_t>& rgb, int w, int h,\n                     JPEGData* jpg) {\n  static const int quant[3 * kDCTBlockSize] = {\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n  };\n  return EncodeRGBToJpeg(rgb, w, h, quant, jpg);\n}\n\n}  // namespace guetzli\n"
  },
  {
    "path": "guetzli/jpeg_data_encoder.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef GUETZLI_JPEG_DATA_ENCODER_H_\n#define GUETZLI_JPEG_DATA_ENCODER_H_\n\n#include <stdint.h>\n\n#include \"guetzli/jpeg_data.h\"\n\nnamespace guetzli {\n\n\n// Adds APP0 header data.\nvoid AddApp0Data(JPEGData* jpg);\n\n// Creates a JPEG from the rgb pixel data. Returns true on success.\nbool EncodeRGBToJpeg(const std::vector<uint8_t>& rgb, int w, int h,\n                     JPEGData* jpg);\n\n// Creates a JPEG from the rgb pixel data. Returns true on success. The given\n// quantization table must have 3 * kDCTBlockSize values.\nbool EncodeRGBToJpeg(const std::vector<uint8_t>& rgb, int w, int h,\n                     const int* quant, JPEGData* jpg);\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_JPEG_DATA_ENCODER_H_\n"
  },
  {
    "path": "guetzli/jpeg_data_reader.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"guetzli/jpeg_data_reader.h\"\n\n#include <algorithm>\n#include <stdio.h>\n#include <string.h>\n\n#include \"guetzli/jpeg_huffman_decode.h\"\n\nnamespace guetzli {\n\nnamespace {\n\n// Macros for commonly used error conditions.\n\n#define VERIFY_LEN(n)                                                   \\\n  if (*pos + (n) > len) {                                               \\\n    fprintf(stderr, \"Unexpected end of input: pos=%d need=%d len=%d\\n\", \\\n            static_cast<int>(*pos), static_cast<int>(n),                \\\n            static_cast<int>(len));                                     \\\n    jpg->error = JPEG_UNEXPECTED_EOF;                                   \\\n    return false;                                                       \\\n  }\n\n#define VERIFY_INPUT(var, low, high, code)                              \\\n  if (var < low || var > high) {                                        \\\n    fprintf(stderr, \"Invalid %s: %d\\n\", #var, static_cast<int>(var));   \\\n    jpg->error = JPEG_INVALID_ ## code;                                 \\\n        return false;                                                   \\\n  }\n\n#define VERIFY_MARKER_END()                                             \\\n  if (start_pos + marker_len != *pos) {                                 \\\n    fprintf(stderr, \"Invalid marker length: declared=%d actual=%d\\n\",   \\\n            static_cast<int>(marker_len),                               \\\n            static_cast<int>(*pos - start_pos));                        \\\n    jpg->error = JPEG_WRONG_MARKER_SIZE;                                \\\n    return false;                                                       \\\n  }\n\n#define EXPECT_MARKER() \\\n  if (pos + 2 > len || data[pos] != 0xff) {                             \\\n    fprintf(stderr, \"Marker byte (0xff) expected, found: %d \"           \\\n            \"pos=%d len=%d\\n\",                                          \\\n            (pos < len ? data[pos] : 0), static_cast<int>(pos),         \\\n            static_cast<int>(len));                                     \\\n    jpg->error = JPEG_MARKER_BYTE_NOT_FOUND;                            \\\n    return false;                                                       \\\n  }\n\ninline int SignedLeftshift(int v, int s) {\n  return (v >= 0) ? (v << s) : -((-v) << s);\n}\n\n// Returns ceil(a/b).\ninline int DivCeil(int a, int b) {\n  return (a + b - 1) / b;\n}\n\ninline int ReadUint8(const uint8_t* data, size_t* pos) {\n  return data[(*pos)++];\n}\n\ninline int ReadUint16(const uint8_t* data, size_t* pos) {\n  int v = (data[*pos] << 8) + data[*pos + 1];\n  *pos += 2;\n  return v;\n}\n\n// Reads the Start of Frame (SOF) marker segment and fills in *jpg with the\n// parsed data.\nbool ProcessSOF(const uint8_t* data, const size_t len,\n                JpegReadMode mode, size_t* pos, JPEGData* jpg) {\n  if (jpg->width != 0) {\n    fprintf(stderr, \"Duplicate SOF marker.\\n\");\n    jpg->error = JPEG_DUPLICATE_SOF;\n    return false;\n  }\n  const size_t start_pos = *pos;\n  VERIFY_LEN(8);\n  size_t marker_len = ReadUint16(data, pos);\n  int precision = ReadUint8(data, pos);\n  int height = ReadUint16(data, pos);\n  int width = ReadUint16(data, pos);\n  int num_components = ReadUint8(data, pos);\n  VERIFY_INPUT(precision, 8, 8, PRECISION);\n  VERIFY_INPUT(height, 1, 65535, HEIGHT);\n  VERIFY_INPUT(width, 1, 65535, WIDTH);\n  VERIFY_INPUT(num_components, 1, kMaxComponents, NUMCOMP);\n  VERIFY_LEN(3 * num_components);\n  jpg->height = height;\n  jpg->width = width;\n  jpg->components.resize(num_components);\n\n  // Read sampling factors and quant table index for each component.\n  std::vector<bool> ids_seen(256, false);\n  for (size_t i = 0; i < jpg->components.size(); ++i) {\n    const int id = ReadUint8(data, pos);\n    if (ids_seen[id]) {   // (cf. section B.2.2, syntax of Ci)\n      fprintf(stderr, \"Duplicate ID %d in SOF.\\n\", id);\n      jpg->error = JPEG_DUPLICATE_COMPONENT_ID;\n      return false;\n    }\n    ids_seen[id] = true;\n    jpg->components[i].id = id;\n    int factor = ReadUint8(data, pos);\n    int h_samp_factor = factor >> 4;\n    int v_samp_factor = factor & 0xf;\n    VERIFY_INPUT(h_samp_factor, 1, 15, SAMP_FACTOR);\n    VERIFY_INPUT(v_samp_factor, 1, 15, SAMP_FACTOR);\n    jpg->components[i].h_samp_factor = h_samp_factor;\n    jpg->components[i].v_samp_factor = v_samp_factor;\n    jpg->components[i].quant_idx = ReadUint8(data, pos);\n    jpg->max_h_samp_factor = std::max(jpg->max_h_samp_factor, h_samp_factor);\n    jpg->max_v_samp_factor = std::max(jpg->max_v_samp_factor, v_samp_factor);\n  }\n\n  // We have checked above that none of the sampling factors are 0, so the max\n  // sampling factors can not be 0.\n  jpg->MCU_rows = DivCeil(jpg->height, jpg->max_v_samp_factor * 8);\n  jpg->MCU_cols = DivCeil(jpg->width, jpg->max_h_samp_factor * 8);\n  // Compute the block dimensions for each component.\n  if (mode == JPEG_READ_ALL) {\n    for (size_t i = 0; i < jpg->components.size(); ++i) {\n      JPEGComponent* c = &jpg->components[i];\n      if (jpg->max_h_samp_factor % c->h_samp_factor != 0 ||\n          jpg->max_v_samp_factor % c->v_samp_factor != 0) {\n        fprintf(stderr, \"Non-integral subsampling ratios.\\n\");\n        jpg->error = JPEG_INVALID_SAMPLING_FACTORS;\n        return false;\n      }\n      c->width_in_blocks = jpg->MCU_cols * c->h_samp_factor;\n      c->height_in_blocks = jpg->MCU_rows * c->v_samp_factor;\n      const uint64_t num_blocks =\n          static_cast<uint64_t>(c->width_in_blocks) * c->height_in_blocks;\n      if (num_blocks > (1ull << 21)) {\n        // Refuse to allocate more than 1 GB of memory for the coefficients,\n        // that is 2M blocks x 64 coeffs x 2 bytes per coeff x max 4 components.\n        // TODO(user) Add this limit to a GuetzliParams struct.\n        fprintf(stderr, \"Image too large.\\n\");\n        jpg->error = JPEG_IMAGE_TOO_LARGE;\n        return false;\n      }\n      c->num_blocks = static_cast<int>(num_blocks);\n      c->coeffs.resize(c->num_blocks * kDCTBlockSize);\n    }\n  }\n  VERIFY_MARKER_END();\n  return true;\n}\n\n// Reads the Start of Scan (SOS) marker segment and fills in *scan_info with the\n// parsed data.\nbool ProcessSOS(const uint8_t* data, const size_t len, size_t* pos,\n                JPEGData* jpg) {\n  const size_t start_pos = *pos;\n  VERIFY_LEN(3);\n  size_t marker_len = ReadUint16(data, pos);\n  int comps_in_scan = ReadUint8(data, pos);\n  VERIFY_INPUT(comps_in_scan, 1, static_cast<int>(jpg->components.size()),\n               COMPS_IN_SCAN);\n\n  JPEGScanInfo scan_info;\n  scan_info.components.resize(comps_in_scan);\n  VERIFY_LEN(2 * comps_in_scan);\n  std::vector<bool> ids_seen(256, false);\n  for (int i = 0; i < comps_in_scan; ++i) {\n    int id = ReadUint8(data, pos);\n    if (ids_seen[id]) {   // (cf. section B.2.3, regarding CSj)\n      fprintf(stderr, \"Duplicate ID %d in SOS.\\n\", id);\n      jpg->error = JPEG_DUPLICATE_COMPONENT_ID;\n      return false;\n    }\n    ids_seen[id] = true;\n    bool found_index = false;\n    for (size_t j = 0; j < jpg->components.size(); ++j) {\n      if (jpg->components[j].id == id) {\n        scan_info.components[i].comp_idx = j;\n        found_index = true;\n      }\n    }\n    if (!found_index) {\n      fprintf(stderr, \"SOS marker: Could not find component with id %d\\n\", id);\n      jpg->error = JPEG_COMPONENT_NOT_FOUND;\n      return false;\n    }\n    int c = ReadUint8(data, pos);\n    int dc_tbl_idx = c >> 4;\n    int ac_tbl_idx = c & 0xf;\n    VERIFY_INPUT(dc_tbl_idx, 0, 3, HUFFMAN_INDEX);\n    VERIFY_INPUT(ac_tbl_idx, 0, 3, HUFFMAN_INDEX);\n    scan_info.components[i].dc_tbl_idx = dc_tbl_idx;\n    scan_info.components[i].ac_tbl_idx = ac_tbl_idx;\n  }\n  VERIFY_LEN(3);\n  scan_info.Ss = ReadUint8(data, pos);\n  scan_info.Se = ReadUint8(data, pos);\n  VERIFY_INPUT(scan_info.Ss, 0, 63, START_OF_SCAN);\n  VERIFY_INPUT(scan_info.Se, scan_info.Ss, 63, END_OF_SCAN);\n  int c = ReadUint8(data, pos);\n  scan_info.Ah = c >> 4;\n  scan_info.Al = c & 0xf;\n  // Check that all the Huffman tables needed for this scan are defined.\n  for (int i = 0; i < comps_in_scan; ++i) {\n    bool found_dc_table = false;\n    bool found_ac_table = false;\n    for (size_t j = 0; j < jpg->huffman_code.size(); ++j) {\n      int slot_id = jpg->huffman_code[j].slot_id;\n      if (slot_id == scan_info.components[i].dc_tbl_idx) {\n        found_dc_table = true;\n      } else if (slot_id == scan_info.components[i].ac_tbl_idx + 16) {\n        found_ac_table = true;\n      }\n    }\n    if (scan_info.Ss == 0 && !found_dc_table) {\n      fprintf(stderr, \"SOS marker: Could not find DC Huffman table with index \"\n              \"%d\\n\", scan_info.components[i].dc_tbl_idx);\n      jpg->error = JPEG_HUFFMAN_TABLE_NOT_FOUND;\n      return false;\n    }\n    if (scan_info.Se > 0 && !found_ac_table) {\n      fprintf(stderr, \"SOS marker: Could not find AC Huffman table with index \"\n              \"%d\\n\", scan_info.components[i].ac_tbl_idx);\n      jpg->error = JPEG_HUFFMAN_TABLE_NOT_FOUND;\n      return false;\n    }\n  }\n  jpg->scan_info.push_back(scan_info);\n  VERIFY_MARKER_END();\n  return true;\n}\n\n// Reads the Define Huffman Table (DHT) marker segment and fills in *jpg with\n// the parsed data. Builds the Huffman decoding table in either dc_huff_lut or\n// ac_huff_lut, depending on the type and solt_id of Huffman code being read.\nbool ProcessDHT(const uint8_t* data, const size_t len,\n                JpegReadMode mode,\n                std::vector<HuffmanTableEntry>* dc_huff_lut,\n                std::vector<HuffmanTableEntry>* ac_huff_lut,\n                size_t* pos,\n                JPEGData* jpg) {\n  const size_t start_pos = *pos;\n  VERIFY_LEN(2);\n  size_t marker_len = ReadUint16(data, pos);\n  if (marker_len == 2) {\n    fprintf(stderr, \"DHT marker: no Huffman table found\\n\");\n    jpg->error = JPEG_EMPTY_DHT;\n    return false;\n  }\n  while (*pos < start_pos + marker_len) {\n    VERIFY_LEN(1 + kJpegHuffmanMaxBitLength);\n    JPEGHuffmanCode huff;\n    huff.slot_id = ReadUint8(data, pos);\n    int huffman_index = huff.slot_id;\n    int is_ac_table = (huff.slot_id & 0x10) != 0;\n    HuffmanTableEntry* huff_lut;\n    if (is_ac_table) {\n      huffman_index -= 0x10;\n      VERIFY_INPUT(huffman_index, 0, 3, HUFFMAN_INDEX);\n      huff_lut = &(*ac_huff_lut)[huffman_index * kJpegHuffmanLutSize];\n    } else {\n      VERIFY_INPUT(huffman_index, 0, 3, HUFFMAN_INDEX);\n      huff_lut = &(*dc_huff_lut)[huffman_index * kJpegHuffmanLutSize];\n    }\n    huff.counts[0] = 0;\n    int total_count = 0;\n    int space = 1 << kJpegHuffmanMaxBitLength;\n    int max_depth = 1;\n    for (int i = 1; i <= kJpegHuffmanMaxBitLength; ++i) {\n      int count = ReadUint8(data, pos);\n      if (count != 0) {\n        max_depth = i;\n      }\n      huff.counts[i] = count;\n      total_count += count;\n      space -= count * (1 << (kJpegHuffmanMaxBitLength - i));\n    }\n    if (is_ac_table) {\n      VERIFY_INPUT(total_count, 0, kJpegHuffmanAlphabetSize, HUFFMAN_CODE);\n    } else {\n      VERIFY_INPUT(total_count, 0, kJpegDCAlphabetSize, HUFFMAN_CODE);\n    }\n    VERIFY_LEN(total_count);\n    std::vector<bool> values_seen(256, false);\n    for (int i = 0; i < total_count; ++i) {\n      uint8_t value = ReadUint8(data, pos);\n      if (!is_ac_table) {\n        VERIFY_INPUT(value, 0, kJpegDCAlphabetSize - 1, HUFFMAN_CODE);\n      }\n      if (values_seen[value]) {\n        fprintf(stderr, \"Duplicate Huffman code value %d\\n\", value);\n        jpg->error = JPEG_INVALID_HUFFMAN_CODE;\n        return false;\n      }\n      values_seen[value] = true;\n      huff.values[i] = value;\n    }\n    // Add an invalid symbol that will have the all 1 code.\n    ++huff.counts[max_depth];\n    huff.values[total_count] = kJpegHuffmanAlphabetSize;\n    space -= (1 << (kJpegHuffmanMaxBitLength - max_depth));\n    if (space < 0) {\n      fprintf(stderr, \"Invalid Huffman code lengths.\\n\");\n      jpg->error = JPEG_INVALID_HUFFMAN_CODE;\n      return false;\n    } else if (space > 0 && huff_lut[0].value != 0xffff) {\n      // Re-initialize the values to an invalid symbol so that we can recognize\n      // it when reading the bit stream using a Huffman code with space > 0.\n      for (int i = 0; i < kJpegHuffmanLutSize; ++i) {\n        huff_lut[i].bits = 0;\n        huff_lut[i].value = 0xffff;\n      }\n    }\n    huff.is_last = (*pos == start_pos + marker_len);\n    if (mode == JPEG_READ_ALL &&\n        !BuildJpegHuffmanTable(&huff.counts[0], &huff.values[0], huff_lut)) {\n      fprintf(stderr, \"Failed to build Huffman table.\\n\");\n      jpg->error = JPEG_INVALID_HUFFMAN_CODE;\n      return false;\n    }\n    jpg->huffman_code.push_back(huff);\n  }\n  VERIFY_MARKER_END();\n  return true;\n}\n\n// Reads the Define Quantization Table (DQT) marker segment and fills in *jpg\n// with the parsed data.\nbool ProcessDQT(const uint8_t* data, const size_t len, size_t* pos,\n                JPEGData* jpg) {\n  const size_t start_pos = *pos;\n  VERIFY_LEN(2);\n  size_t marker_len = ReadUint16(data, pos);\n  if (marker_len == 2) {\n    fprintf(stderr, \"DQT marker: no quantization table found\\n\");\n    jpg->error = JPEG_EMPTY_DQT;\n    return false;\n  }\n  while (*pos < start_pos + marker_len && jpg->quant.size() < kMaxQuantTables) {\n    VERIFY_LEN(1);\n    int quant_table_index = ReadUint8(data, pos);\n    int quant_table_precision = quant_table_index >> 4;\n    quant_table_index &= 0xf;\n    VERIFY_INPUT(quant_table_index, 0, 3, QUANT_TBL_INDEX);\n    VERIFY_LEN((quant_table_precision ? 2 : 1) * kDCTBlockSize);\n    JPEGQuantTable table;\n    table.index = quant_table_index;\n    table.precision = quant_table_precision;\n    for (int i = 0; i < kDCTBlockSize; ++i) {\n      int quant_val = quant_table_precision ?\n          ReadUint16(data, pos) :\n          ReadUint8(data, pos);\n      VERIFY_INPUT(quant_val, 1, 65535, QUANT_VAL);\n      table.values[kJPEGNaturalOrder[i]] = quant_val;\n    }\n    table.is_last = (*pos == start_pos + marker_len);\n    jpg->quant.push_back(table);\n  }\n  VERIFY_MARKER_END();\n  return true;\n}\n\n// Reads the DRI marker and saved the restart interval into *jpg.\nbool ProcessDRI(const uint8_t* data, const size_t len, size_t* pos,\n                JPEGData* jpg) {\n  if (jpg->restart_interval > 0) {\n    fprintf(stderr, \"Duplicate DRI marker.\\n\");\n    jpg->error = JPEG_DUPLICATE_DRI;\n    return false;\n  }\n  const size_t start_pos = *pos;\n  VERIFY_LEN(4);\n  size_t marker_len = ReadUint16(data, pos);\n  int restart_interval = ReadUint16(data, pos);\n  jpg->restart_interval = restart_interval;\n  VERIFY_MARKER_END();\n  return true;\n}\n\n// Saves the APP marker segment as a string to *jpg.\nbool ProcessAPP(const uint8_t* data, const size_t len, size_t* pos,\n                JPEGData* jpg) {\n  VERIFY_LEN(2);\n  size_t marker_len = ReadUint16(data, pos);\n  VERIFY_INPUT(marker_len, 2, 65535, MARKER_LEN);\n  VERIFY_LEN(marker_len - 2);\n  // Save the marker type together with the app data.\n  std::string app_str(reinterpret_cast<const char*>(\n      &data[*pos - 3]), marker_len + 1);\n  *pos += marker_len - 2;\n  jpg->app_data.push_back(app_str);\n  return true;\n}\n\n// Saves the COM marker segment as a string to *jpg.\nbool ProcessCOM(const uint8_t* data, const size_t len, size_t* pos,\n                JPEGData* jpg) {\n  VERIFY_LEN(2);\n  size_t marker_len = ReadUint16(data, pos);\n  VERIFY_INPUT(marker_len, 2, 65535, MARKER_LEN);\n  VERIFY_LEN(marker_len - 2);\n  std::string com_str(reinterpret_cast<const char*>(\n      &data[*pos - 2]), marker_len);\n  *pos += marker_len - 2;\n  jpg->com_data.push_back(com_str);\n  return true;\n}\n\n// Helper structure to read bits from the entropy coded data segment.\nstruct BitReaderState {\n  BitReaderState(const uint8_t* data, const size_t len, size_t pos)\n      : data_(data), len_(len) {\n    Reset(pos);\n  }\n\n  void Reset(size_t pos) {\n    pos_ = pos;\n    val_ = 0;\n    bits_left_ = 0;\n    next_marker_pos_ = len_ - 2;\n    FillBitWindow();\n  }\n\n  // Returns the next byte and skips the 0xff/0x00 escape sequences.\n  uint8_t GetNextByte() {\n    if (pos_ >= next_marker_pos_) {\n      ++pos_;\n      return 0;\n    }\n    uint8_t c = data_[pos_++];\n    if (c == 0xff) {\n      uint8_t escape = data_[pos_];\n      if (escape == 0) {\n        ++pos_;\n      } else {\n        // 0xff was followed by a non-zero byte, which means that we found the\n        // start of the next marker segment.\n        next_marker_pos_ = pos_ - 1;\n      }\n    }\n    return c;\n  }\n\n  void FillBitWindow() {\n    if (bits_left_ <= 16) {\n      while (bits_left_ <= 56) {\n        val_ <<= 8;\n        val_ |= (uint64_t)GetNextByte();\n        bits_left_ += 8;\n      }\n    }\n  }\n\n  int ReadBits(int nbits) {\n    FillBitWindow();\n    uint64_t val = (val_ >> (bits_left_ - nbits)) & ((1ULL << nbits) - 1);\n    bits_left_ -= nbits;\n    return val;\n  }\n\n  // Sets *pos to the next stream position where parsing should continue.\n  // Returns false if the stream ended too early.\n  bool FinishStream(size_t* pos) {\n    // Give back some bytes that we did not use.\n    int unused_bytes_left = bits_left_ >> 3;\n    while (unused_bytes_left-- > 0) {\n      --pos_;\n      // If we give back a 0 byte, we need to check if it was a 0xff/0x00 escape\n      // sequence, and if yes, we need to give back one more byte.\n      if (pos_ < next_marker_pos_ &&\n          data_[pos_] == 0 && data_[pos_ - 1] == 0xff) {\n        --pos_;\n      }\n    }\n    if (pos_ > next_marker_pos_) {\n      // Data ran out before the scan was complete.\n      fprintf(stderr, \"Unexpected end of scan.\\n\");\n      return false;\n    }\n    *pos = pos_;\n    return true;\n  }\n\n  const uint8_t* data_;\n  const size_t len_;\n  size_t pos_;\n  uint64_t val_;\n  int bits_left_;\n  size_t next_marker_pos_;\n};\n\n// Returns the next Huffman-coded symbol.\nint ReadSymbol(const HuffmanTableEntry* table, BitReaderState* br) {\n  int nbits;\n  br->FillBitWindow();\n  int val = (br->val_ >> (br->bits_left_ - 8)) & 0xff;\n  table += val;\n  nbits = table->bits - 8;\n  if (nbits > 0) {\n    br->bits_left_ -= 8;\n    table += table->value;\n    val = (br->val_ >> (br->bits_left_ - nbits)) & ((1 << nbits) - 1);\n    table += val;\n  }\n  br->bits_left_ -= table->bits;\n  return table->value;\n}\n\n// Returns the DC diff or AC value for extra bits value x and prefix code s.\n// See Tables F.1 and F.2 of the spec.\nint HuffExtend(int x, int s) {\n  return (x < (1 << (s - 1)) ? x - (1 << s) + 1 : x);\n}\n\n// Decodes one 8x8 block of DCT coefficients from the bit stream.\nbool DecodeDCTBlock(const HuffmanTableEntry* dc_huff,\n                    const HuffmanTableEntry* ac_huff,\n                    int Ss, int Se, int Al,\n                    int* eobrun,\n                    BitReaderState* br,\n                    JPEGData* jpg,\n                    coeff_t* last_dc_coeff,\n                    coeff_t* coeffs) {\n  int s;\n  int r;\n  bool eobrun_allowed = Ss > 0;\n  if (Ss == 0) {\n    s = ReadSymbol(dc_huff, br);\n    if (s >= kJpegDCAlphabetSize) {\n      fprintf(stderr, \"Invalid Huffman symbol %d for DC coefficient.\\n\", s);\n      jpg->error = JPEG_INVALID_SYMBOL;\n      return false;\n    }\n    if (s > 0) {\n      r = br->ReadBits(s);\n      s = HuffExtend(r, s);\n    }\n    s += *last_dc_coeff;\n    const int dc_coeff = SignedLeftshift(s, Al);\n    coeffs[0] = dc_coeff;\n    if (dc_coeff != coeffs[0]) {\n      fprintf(stderr, \"Invalid DC coefficient %d\\n\", dc_coeff);\n      jpg->error = JPEG_NON_REPRESENTABLE_DC_COEFF;\n      return false;\n    }\n    *last_dc_coeff = s;\n    ++Ss;\n  }\n  if (Ss > Se) {\n    return true;\n  }\n  if (*eobrun > 0) {\n    --(*eobrun);\n    return true;\n  }\n  for (int k = Ss; k <= Se; k++) {\n    s = ReadSymbol(ac_huff, br);\n    if (s >= kJpegHuffmanAlphabetSize) {\n      fprintf(stderr, \"Invalid Huffman symbol %d for AC coefficient %d\\n\",\n              s, k);\n      jpg->error = JPEG_INVALID_SYMBOL;\n      return false;\n    }\n    r = s >> 4;\n    s &= 15;\n    if (s > 0) {\n      k += r;\n      if (k > Se) {\n        fprintf(stderr, \"Out-of-band coefficient %d band was %d-%d\\n\",\n                k, Ss, Se);\n        jpg->error = JPEG_OUT_OF_BAND_COEFF;\n        return false;\n      }\n      if (s + Al >= kJpegDCAlphabetSize) {\n        fprintf(stderr, \"Out of range AC coefficient value: s=%d Al=%d k=%d\\n\",\n                s, Al, k);\n        jpg->error = JPEG_NON_REPRESENTABLE_AC_COEFF;\n        return false;\n      }\n      r = br->ReadBits(s);\n      s = HuffExtend(r, s);\n      coeffs[kJPEGNaturalOrder[k]] = SignedLeftshift(s, Al);\n    } else if (r == 15) {\n      k += 15;\n    } else {\n      *eobrun = 1 << r;\n      if (r > 0) {\n        if (!eobrun_allowed) {\n          fprintf(stderr, \"End-of-block run crossing DC coeff.\\n\");\n          jpg->error = JPEG_EOB_RUN_TOO_LONG;\n          return false;\n        }\n        *eobrun += br->ReadBits(r);\n      }\n      break;\n    }\n  }\n  --(*eobrun);\n  return true;\n}\n\nbool RefineDCTBlock(const HuffmanTableEntry* ac_huff,\n                    int Ss, int Se, int Al,\n                    int* eobrun,\n                    BitReaderState* br,\n                    JPEGData* jpg,\n                    coeff_t* coeffs) {\n  bool eobrun_allowed = Ss > 0;\n  if (Ss == 0) {\n    int s = br->ReadBits(1);\n    coeff_t dc_coeff = coeffs[0];\n    dc_coeff |= s << Al;\n    coeffs[0] = dc_coeff;\n    ++Ss;\n  }\n  if (Ss > Se) {\n    return true;\n  }\n  int p1 = 1 << Al;\n  int m1 = -(1 << Al);\n  int k = Ss;\n  int r;\n  int s;\n  bool in_zero_run = false;\n  if (*eobrun <= 0) {\n    for (; k <= Se; k++) {\n      s = ReadSymbol(ac_huff, br);\n      if (s >= kJpegHuffmanAlphabetSize) {\n        fprintf(stderr, \"Invalid Huffman symbol %d for AC coefficient %d\\n\",\n                s, k);\n        jpg->error = JPEG_INVALID_SYMBOL;\n        return false;\n      }\n      r = s >> 4;\n      s &= 15;\n      if (s) {\n        if (s != 1) {\n          fprintf(stderr, \"Invalid Huffman symbol %d for AC coefficient %d\\n\",\n                  s, k);\n          jpg->error = JPEG_INVALID_SYMBOL;\n          return false;\n        }\n        s = br->ReadBits(1) ? p1 : m1;\n        in_zero_run = false;\n      } else {\n        if (r != 15) {\n          *eobrun = 1 << r;\n          if (r > 0) {\n            if (!eobrun_allowed) {\n              fprintf(stderr, \"End-of-block run crossing DC coeff.\\n\");\n              jpg->error = JPEG_EOB_RUN_TOO_LONG;\n              return false;\n            }\n            *eobrun += br->ReadBits(r);\n          }\n          break;\n        }\n        in_zero_run = true;\n      }\n      do {\n        coeff_t thiscoef = coeffs[kJPEGNaturalOrder[k]];\n        if (thiscoef != 0) {\n          if (br->ReadBits(1)) {\n            if ((thiscoef & p1) == 0) {\n              if (thiscoef >= 0) {\n                thiscoef += p1;\n              } else {\n                thiscoef += m1;\n              }\n            }\n          }\n          coeffs[kJPEGNaturalOrder[k]] = thiscoef;\n        } else {\n          if (--r < 0) {\n            break;\n          }\n        }\n        k++;\n      } while (k <= Se);\n      if (s) {\n        if (k > Se) {\n          fprintf(stderr, \"Out-of-band coefficient %d band was %d-%d\\n\",\n                  k, Ss, Se);\n          jpg->error = JPEG_OUT_OF_BAND_COEFF;\n          return false;\n        }\n        coeffs[kJPEGNaturalOrder[k]] = s;\n      }\n    }\n  }\n  if (in_zero_run) {\n    fprintf(stderr, \"Extra zero run before end-of-block.\\n\");\n    jpg->error = JPEG_EXTRA_ZERO_RUN;\n    return false;\n  }\n  if (*eobrun > 0) {\n    for (; k <= Se; k++) {\n      coeff_t thiscoef = coeffs[kJPEGNaturalOrder[k]];\n      if (thiscoef != 0) {\n        if (br->ReadBits(1)) {\n          if ((thiscoef & p1) == 0) {\n            if (thiscoef >= 0) {\n              thiscoef += p1;\n            } else {\n              thiscoef += m1;\n            }\n          }\n        }\n        coeffs[kJPEGNaturalOrder[k]] = thiscoef;\n      }\n    }\n  }\n  --(*eobrun);\n  return true;\n}\n\nbool ProcessRestart(const uint8_t* data, const size_t len,\n                    int* next_restart_marker, BitReaderState* br,\n                    JPEGData* jpg) {\n  size_t pos = 0;\n  if (!br->FinishStream(&pos)) {\n    jpg->error = JPEG_INVALID_SCAN;\n    return false;\n  }\n  int expected_marker = 0xd0 + *next_restart_marker;\n  EXPECT_MARKER();\n  int marker = data[pos + 1];\n  if (marker != expected_marker) {\n    fprintf(stderr, \"Did not find expected restart marker %d actual=%d\\n\",\n            expected_marker, marker);\n    jpg->error = JPEG_WRONG_RESTART_MARKER;\n    return false;\n  }\n  br->Reset(pos + 2);\n  *next_restart_marker += 1;\n  *next_restart_marker &= 0x7;\n  return true;\n}\n\nbool ProcessScan(const uint8_t* data, const size_t len,\n                 const std::vector<HuffmanTableEntry>& dc_huff_lut,\n                 const std::vector<HuffmanTableEntry>& ac_huff_lut,\n                 uint16_t scan_progression[kMaxComponents][kDCTBlockSize],\n                 bool is_progressive,\n                 size_t* pos,\n                 JPEGData* jpg) {\n  if (!ProcessSOS(data, len, pos, jpg)) {\n    return false;\n  }\n  JPEGScanInfo* scan_info = &jpg->scan_info.back();\n  bool is_interleaved = (scan_info->components.size() > 1);\n  int MCUs_per_row;\n  int MCU_rows;\n  if (is_interleaved) {\n    MCUs_per_row = jpg->MCU_cols;\n    MCU_rows = jpg->MCU_rows;\n  } else {\n    const JPEGComponent& c = jpg->components[scan_info->components[0].comp_idx];\n    MCUs_per_row =\n        DivCeil(jpg->width * c.h_samp_factor, 8 * jpg->max_h_samp_factor);\n    MCU_rows =\n        DivCeil(jpg->height * c.v_samp_factor, 8 * jpg->max_v_samp_factor);\n  }\n  coeff_t last_dc_coeff[kMaxComponents] = {0};\n  BitReaderState br(data, len, *pos);\n  int restarts_to_go = jpg->restart_interval;\n  int next_restart_marker = 0;\n  int eobrun = -1;\n  int block_scan_index = 0;\n  const int Al = is_progressive ? scan_info->Al : 0;\n  const int Ah = is_progressive ? scan_info->Ah : 0;\n  const int Ss = is_progressive ? scan_info->Ss : 0;\n  const int Se = is_progressive ? scan_info->Se : 63;\n  const uint16_t scan_bitmask = Ah == 0 ? (0xffff << Al) : (1u << Al);\n  const uint16_t refinement_bitmask = (1 << Al) - 1;\n  for (size_t i = 0; i < scan_info->components.size(); ++i) {\n    int comp_idx = scan_info->components[i].comp_idx;\n    for (int k = Ss; k <= Se; ++k) {\n      if (scan_progression[comp_idx][k] & scan_bitmask) {\n        fprintf(stderr, \"Overlapping scans: component=%d k=%d prev_mask=%d \"\n                \"cur_mask=%d\\n\", comp_idx, k, scan_progression[i][k],\n                scan_bitmask);\n        jpg->error = JPEG_OVERLAPPING_SCANS;\n        return false;\n      }\n      if (scan_progression[comp_idx][k] & refinement_bitmask) {\n        fprintf(stderr, \"Invalid scan order, a more refined scan was already \"\n                \"done: component=%d k=%d prev_mask=%d cur_mask=%d\\n\", comp_idx,\n                k, scan_progression[i][k], scan_bitmask);\n        jpg->error = JPEG_INVALID_SCAN_ORDER;\n        return false;\n      }\n      scan_progression[comp_idx][k] |= scan_bitmask;\n    }\n  }\n  if (Al > 10) {\n    fprintf(stderr, \"Scan parameter Al=%d is not supported in guetzli.\\n\", Al);\n    jpg->error = JPEG_NON_REPRESENTABLE_AC_COEFF;\n    return false;\n  }\n  for (int mcu_y = 0; mcu_y < MCU_rows; ++mcu_y) {\n    for (int mcu_x = 0; mcu_x < MCUs_per_row; ++mcu_x) {\n      // Handle the restart intervals.\n      if (jpg->restart_interval > 0) {\n        if (restarts_to_go == 0) {\n          if (ProcessRestart(data, len,\n                             &next_restart_marker, &br, jpg)) {\n            restarts_to_go = jpg->restart_interval;\n            memset(last_dc_coeff, 0, sizeof(last_dc_coeff));\n            if (eobrun > 0) {\n              fprintf(stderr, \"End-of-block run too long.\\n\");\n              jpg->error = JPEG_EOB_RUN_TOO_LONG;\n              return false;\n            }\n            eobrun = -1;   // fresh start\n          } else {\n            return false;\n          }\n        }\n        --restarts_to_go;\n      }\n      // Decode one MCU.\n      for (size_t i = 0; i < scan_info->components.size(); ++i) {\n        JPEGComponentScanInfo* si = &scan_info->components[i];\n        JPEGComponent* c = &jpg->components[si->comp_idx];\n        const HuffmanTableEntry* dc_lut =\n            &dc_huff_lut[si->dc_tbl_idx * kJpegHuffmanLutSize];\n        const HuffmanTableEntry* ac_lut =\n            &ac_huff_lut[si->ac_tbl_idx * kJpegHuffmanLutSize];\n        int nblocks_y = is_interleaved ? c->v_samp_factor : 1;\n        int nblocks_x = is_interleaved ? c->h_samp_factor : 1;\n        for (int iy = 0; iy < nblocks_y; ++iy) {\n          for (int ix = 0; ix < nblocks_x; ++ix) {\n            int block_y = mcu_y * nblocks_y + iy;\n            int block_x = mcu_x * nblocks_x + ix;\n            int block_idx = block_y * c->width_in_blocks + block_x;\n            coeff_t* coeffs = &c->coeffs[block_idx * kDCTBlockSize];\n            if (Ah == 0) {\n              if (!DecodeDCTBlock(dc_lut, ac_lut, Ss, Se, Al, &eobrun, &br, jpg,\n                                  &last_dc_coeff[si->comp_idx], coeffs)) {\n                return false;\n              }\n            } else {\n              if (!RefineDCTBlock(ac_lut, Ss, Se, Al,\n                                  &eobrun, &br, jpg, coeffs)) {\n                return false;\n              }\n            }\n            ++block_scan_index;\n          }\n        }\n      }\n    }\n  }\n  if (eobrun > 0) {\n    fprintf(stderr, \"End-of-block run too long.\\n\");\n    jpg->error = JPEG_EOB_RUN_TOO_LONG;\n    return false;\n  }\n  if (!br.FinishStream(pos)) {\n    jpg->error = JPEG_INVALID_SCAN;\n    return false;\n  }\n  if (*pos > len) {\n    fprintf(stderr, \"Unexpected end of file during scan. pos=%d len=%d\\n\",\n            static_cast<int>(*pos), static_cast<int>(len));\n    jpg->error = JPEG_UNEXPECTED_EOF;\n    return false;\n  }\n  return true;\n}\n\n// Changes the quant_idx field of the components to refer to the index of the\n// quant table in the jpg->quant array.\nbool FixupIndexes(JPEGData* jpg) {\n  for (size_t i = 0; i < jpg->components.size(); ++i) {\n    JPEGComponent* c = &jpg->components[i];\n    bool found_index = false;\n    for (size_t j = 0; j < jpg->quant.size(); ++j) {\n      if (jpg->quant[j].index == c->quant_idx) {\n        c->quant_idx = j;\n        found_index = true;\n        break;\n      }\n    }\n    if (!found_index) {\n      fprintf(stderr, \"Quantization table with index %zd not found\\n\",\n              c->quant_idx);\n      jpg->error = JPEG_QUANT_TABLE_NOT_FOUND;\n      return false;\n    }\n  }\n  return true;\n}\n\nsize_t FindNextMarker(const uint8_t* data, const size_t len, size_t pos) {\n  // kIsValidMarker[i] == 1 means (0xc0 + i) is a valid marker.\n  static const uint8_t kIsValidMarker[] = {\n    1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n    1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0,\n    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,\n    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,\n  };\n  size_t num_skipped = 0;\n  while (pos + 1 < len &&\n         (data[pos] != 0xff || data[pos + 1] < 0xc0 ||\n          !kIsValidMarker[data[pos + 1] - 0xc0])) {\n    ++pos;\n    ++num_skipped;\n  }\n  return num_skipped;\n}\n\n}  // namespace\n\nbool ReadJpeg(const uint8_t* data, const size_t len, JpegReadMode mode,\n              JPEGData* jpg) {\n  size_t pos = 0;\n  // Check SOI marker.\n  EXPECT_MARKER();\n  int marker = data[pos + 1];\n  pos += 2;\n  if (marker != 0xd8) {\n    fprintf(stderr, \"Did not find expected SOI marker, actual=%d\\n\", marker);\n    jpg->error = JPEG_SOI_NOT_FOUND;\n    return false;\n  }\n  int lut_size = kMaxHuffmanTables * kJpegHuffmanLutSize;\n  std::vector<HuffmanTableEntry> dc_huff_lut(lut_size);\n  std::vector<HuffmanTableEntry> ac_huff_lut(lut_size);\n  bool found_sof = false;\n  uint16_t scan_progression[kMaxComponents][kDCTBlockSize] = { { 0 } };\n\n  bool is_progressive = false;   // default\n  do {\n    // Read next marker.\n    size_t num_skipped = FindNextMarker(data, len, pos);\n    if (num_skipped > 0) {\n      // Add a fake marker to indicate arbitrary in-between-markers data.\n      jpg->marker_order.push_back(0xff);\n      jpg->inter_marker_data.push_back(\n          std::string(reinterpret_cast<const char*>(&data[pos]),\n                                      num_skipped));\n      pos += num_skipped;\n    }\n    EXPECT_MARKER();\n    marker = data[pos + 1];\n    pos += 2;\n    bool ok = true;\n    switch (marker) {\n      case 0xc0:\n      case 0xc1:\n      case 0xc2:\n        is_progressive = (marker == 0xc2);\n        ok = ProcessSOF(data, len, mode, &pos, jpg);\n        found_sof = true;\n        break;\n      case 0xc4:\n        ok = ProcessDHT(data, len, mode, &dc_huff_lut, &ac_huff_lut, &pos, jpg);\n        break;\n      case 0xd0:\n      case 0xd1:\n      case 0xd2:\n      case 0xd3:\n      case 0xd4:\n      case 0xd5:\n      case 0xd6:\n      case 0xd7:\n        // RST markers do not have any data.\n        break;\n      case 0xd9:\n        // Found end marker.\n        break;\n      case 0xda:\n        if (mode == JPEG_READ_ALL) {\n          ok = ProcessScan(data, len, dc_huff_lut, ac_huff_lut,\n                           scan_progression, is_progressive, &pos, jpg);\n        }\n        break;\n      case 0xdb:\n        ok = ProcessDQT(data, len, &pos, jpg);\n        break;\n      case 0xdd:\n        ok = ProcessDRI(data, len, &pos, jpg);\n        break;\n      case 0xe0:\n      case 0xe1:\n      case 0xe2:\n      case 0xe3:\n      case 0xe4:\n      case 0xe5:\n      case 0xe6:\n      case 0xe7:\n      case 0xe8:\n      case 0xe9:\n      case 0xea:\n      case 0xeb:\n      case 0xec:\n      case 0xed:\n      case 0xee:\n      case 0xef:\n        if (mode != JPEG_READ_TABLES) {\n          ok = ProcessAPP(data, len, &pos, jpg);\n        }\n        break;\n      case 0xfe:\n        if (mode != JPEG_READ_TABLES) {\n          ok = ProcessCOM(data, len, &pos, jpg);\n        }\n        break;\n      default:\n        fprintf(stderr, \"Unsupported marker: %d pos=%d len=%d\\n\",\n                marker, static_cast<int>(pos), static_cast<int>(len));\n        jpg->error = JPEG_UNSUPPORTED_MARKER;\n        ok = false;\n        break;\n    }\n    if (!ok) {\n      return false;\n    }\n    jpg->marker_order.push_back(marker);\n    if (mode == JPEG_READ_HEADER && found_sof) {\n      break;\n    }\n  } while (marker != 0xd9);\n\n  if (!found_sof) {\n    fprintf(stderr, \"Missing SOF marker.\\n\");\n    jpg->error = JPEG_SOF_NOT_FOUND;\n    return false;\n  }\n\n  // Supplemental checks.\n  if (mode == JPEG_READ_ALL) {\n    if (pos < len) {\n      jpg->tail_data.assign(reinterpret_cast<const char*>(&data[pos]),\n                            len - pos);\n    }\n    if (!FixupIndexes(jpg)) {\n      return false;\n    }\n    if (jpg->huffman_code.size() == 0) {\n      // Section B.2.4.2: \"If a table has never been defined for a particular\n      // destination, then when this destination is specified in a scan header,\n      // the results are unpredictable.\"\n      fprintf(stderr, \"Need at least one Huffman code table.\\n\");\n      jpg->error = JPEG_HUFFMAN_TABLE_ERROR;\n      return false;\n    }\n    if (jpg->huffman_code.size() >= kMaxDHTMarkers) {\n      fprintf(stderr, \"Too many Huffman tables.\\n\");\n      jpg->error = JPEG_HUFFMAN_TABLE_ERROR;\n      return false;\n    }\n  }\n  return true;\n}\n\nbool ReadJpeg(const std::string& data, JpegReadMode mode,\n              JPEGData* jpg) {\n  return ReadJpeg(reinterpret_cast<const uint8_t*>(data.data()),\n                  static_cast<const size_t>(data.size()),\n                  mode, jpg);\n}\n\n}  // namespace guetzli\n"
  },
  {
    "path": "guetzli/jpeg_data_reader.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Functions for reading a jpeg byte stream into a JPEGData object.\n\n#ifndef GUETZLI_JPEG_DATA_READER_H_\n#define GUETZLI_JPEG_DATA_READER_H_\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <string>\n\n#include \"guetzli/jpeg_data.h\"\n\nnamespace guetzli {\n\nenum JpegReadMode {\n  JPEG_READ_HEADER,   // only basic headers\n  JPEG_READ_TABLES,   // headers and tables (quant, Huffman, ...)\n  JPEG_READ_ALL,      // everything\n};\n\n// Parses the jpeg stream contained in data[*pos ... len) and fills in *jpg with\n// the parsed information.\n// If mode is JPEG_READ_HEADER, it fills in only the image dimensions in *jpg.\n// Returns false if the data is not valid jpeg, or if it contains an unsupported\n// jpeg feature.\nbool ReadJpeg(const uint8_t* data, const size_t len, JpegReadMode mode,\n              JPEGData* jpg);\n// string variant\nbool ReadJpeg(const std::string& data, JpegReadMode mode,\n              JPEGData* jpg);\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_JPEG_DATA_READER_H_\n"
  },
  {
    "path": "guetzli/jpeg_data_writer.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"guetzli/jpeg_data_writer.h\"\n\n#include <assert.h>\n#include <cstdlib>\n#include <string.h>\n\n#include \"guetzli/entropy_encode.h\"\n#include \"guetzli/fast_log.h\"\n#include \"guetzli/jpeg_bit_writer.h\"\n\nnamespace guetzli {\n\nnamespace {\n\nstatic const int kJpegPrecision = 8;\n\n// Writes len bytes from buf, using the out callback.\ninline bool JPEGWrite(JPEGOutput out, const uint8_t* buf, size_t len) {\n  static const size_t kBlockSize = 1u << 30;\n  size_t pos = 0;\n  while (len - pos > kBlockSize) {\n    if (!out.Write(buf + pos, kBlockSize)) {\n      return false;\n    }\n    pos += kBlockSize;\n  }\n  return out.Write(buf + pos, len - pos);\n}\n\n// Writes a string using the out callback.\ninline bool JPEGWrite(JPEGOutput out, const std::string& s) {\n  const uint8_t* data = reinterpret_cast<const uint8_t*>(&s[0]);\n  return JPEGWrite(out, data, s.size());\n}\n\nbool EncodeMetadata(const JPEGData& jpg, bool strip_metadata, JPEGOutput out) {\n  if (strip_metadata) {\n    const uint8_t kApp0Data[] = {\n      0xff, 0xe0, 0x00, 0x10,        // APP0\n      0x4a, 0x46, 0x49, 0x46, 0x00,  // 'JFIF'\n      0x01, 0x01,                    // v1.01\n      0x00, 0x00, 0x01, 0x00, 0x01,  // aspect ratio = 1:1\n      0x00, 0x00                     // thumbnail width/height\n    };\n    return JPEGWrite(out, kApp0Data, sizeof(kApp0Data));\n  }\n  bool ok = true;\n  for (size_t i = 0; i < jpg.app_data.size(); ++i) {\n    uint8_t data[1] = { 0xff };\n    ok = ok && JPEGWrite(out, data, sizeof(data));\n    ok = ok && JPEGWrite(out, jpg.app_data[i]);\n  }\n  for (size_t i = 0; i < jpg.com_data.size(); ++i) {\n    uint8_t data[2] = { 0xff, 0xfe };\n    ok = ok && JPEGWrite(out, data, sizeof(data));\n    ok = ok && JPEGWrite(out, jpg.com_data[i]);\n  }\n  return ok;\n}\n\nbool EncodeDQT(const std::vector<JPEGQuantTable>& quant, JPEGOutput out) {\n  int marker_len = 2;\n  for (size_t i = 0; i < quant.size(); ++i) {\n    marker_len += 1 + (quant[i].precision ? 2 : 1) * kDCTBlockSize;\n  }\n  std::vector<uint8_t> data(marker_len + 2);\n  size_t pos = 0;\n  data[pos++] = 0xff;\n  data[pos++] = 0xdb;\n  data[pos++] = marker_len >> 8;\n  data[pos++] = marker_len & 0xff;\n  for (size_t i = 0; i < quant.size(); ++i) {\n    const JPEGQuantTable& table = quant[i];\n    data[pos++] = (table.precision << 4) + table.index;\n    for (int k = 0; k < kDCTBlockSize; ++k) {\n      int val = table.values[kJPEGNaturalOrder[k]];\n      if (table.precision) {\n        data[pos++] = val >> 8;\n      }\n      data[pos++] = val & 0xff;\n    }\n  }\n  return JPEGWrite(out, &data[0], pos);\n}\n\nbool EncodeSOF(const JPEGData& jpg, JPEGOutput out) {\n  const size_t ncomps = jpg.components.size();\n  const size_t marker_len = 8 + 3 * ncomps;\n  std::vector<uint8_t> data(marker_len + 2);\n  size_t pos = 0;\n  data[pos++] = 0xff;\n  data[pos++] = 0xc1;\n  data[pos++] = static_cast<uint8_t>(marker_len >> 8);\n  data[pos++] = marker_len & 0xff;\n  data[pos++] = kJpegPrecision;\n  data[pos++] = jpg.height >> 8;\n  data[pos++] = jpg.height & 0xff;\n  data[pos++] = jpg.width >> 8;\n  data[pos++] = jpg.width & 0xff;\n  data[pos++] = static_cast<uint8_t>(ncomps);\n  for (size_t i = 0; i < ncomps; ++i) {\n    data[pos++] = jpg.components[i].id;\n    data[pos++] = ((jpg.components[i].h_samp_factor << 4) |\n                      (jpg.components[i].v_samp_factor));\n    const size_t quant_idx = jpg.components[i].quant_idx;\n    if (quant_idx >= jpg.quant.size()) {\n      return false;\n    }\n    data[pos++] = jpg.quant[quant_idx].index;\n  }\n  return JPEGWrite(out, &data[0], pos);\n}\n\n// Builds a JPEG-style huffman code from the given bit depths.\nvoid BuildHuffmanCode(uint8_t* depth, int* counts, int* values) {\n  for (int i = 0; i < JpegHistogram::kSize; ++i) {\n    if (depth[i] > 0) {\n      ++counts[depth[i]];\n    }\n  }\n  int offset[kJpegHuffmanMaxBitLength + 1] = { 0 };\n  for (int i = 1; i <= kJpegHuffmanMaxBitLength; ++i) {\n    offset[i] = offset[i - 1] + counts[i - 1];\n  }\n  for (int i = 0; i < JpegHistogram::kSize; ++i) {\n    if (depth[i] > 0) {\n      values[offset[depth[i]]++] = i;\n    }\n  }\n}\n\nvoid BuildHuffmanCodeTable(const int* counts, const int* values,\n                           HuffmanCodeTable* table) {\n  int huffcode[256];\n  int huffsize[256];\n  int p = 0;\n  for (int l = 1; l <= kJpegHuffmanMaxBitLength; ++l) {\n    int i = counts[l];\n    while (i--) huffsize[p++] = l;\n  }\n\n  if (p == 0)\n    return;\n\n  huffsize[p - 1] = 0;\n  int lastp = p - 1;\n\n  int code = 0;\n  int si = huffsize[0];\n  p = 0;\n  while (huffsize[p]) {\n    while ((huffsize[p]) == si) {\n      huffcode[p++] = code;\n      code++;\n    }\n    code <<= 1;\n    si++;\n  }\n  for (p = 0; p < lastp; p++) {\n    int i = values[p];\n    table->depth[i] = huffsize[p];\n    table->code[i] = huffcode[p];\n  }\n}\n\n}  // namespace\n\n// Updates ac_histogram with the counts of the AC symbols that will be added by\n// a sequential jpeg encoder for this block. Every symbol is counted twice so\n// that we can add a fake symbol at the end with count 1 to be the last (least\n// frequent) symbol with the all 1 code.\nvoid UpdateACHistogramForDCTBlock(const coeff_t* coeffs,\n                                  JpegHistogram* ac_histogram) {\n  int r = 0;\n  for (int k = 1; k < 64; ++k) {\n    coeff_t coeff = coeffs[kJPEGNaturalOrder[k]];\n    if (coeff == 0) {\n      r++;\n      continue;\n    }\n    while (r > 15) {\n      ac_histogram->Add(0xf0);\n      r -= 16;\n    }\n    int nbits = Log2FloorNonZero(std::abs(coeff)) + 1;\n    int symbol = (r << 4) + nbits;\n    ac_histogram->Add(symbol);\n    r = 0;\n  }\n  if (r > 0) {\n    ac_histogram->Add(0);\n  }\n}\n\nsize_t HistogramHeaderCost(const JpegHistogram& histo) {\n  size_t header_bits = 17 * 8;\n  for (int i = 0; i + 1 < JpegHistogram::kSize; ++i) {\n    if (histo.counts[i] > 0) {\n      header_bits += 8;\n    }\n  }\n  return header_bits;\n}\n\nsize_t HistogramEntropyCost(const JpegHistogram& histo,\n                            const uint8_t depths[256]) {\n  size_t bits = 0;\n  for (int i = 0; i + 1 < JpegHistogram::kSize; ++i) {\n    // JpegHistogram::Add() counts every symbol twice, so we have to divide by\n    // two here.\n    bits += (histo.counts[i] / 2) * (depths[i] + (i & 0xf));\n  }\n  // Estimate escape byte rate to be 0.75/256.\n  bits += (bits * 3 + 512) >> 10;\n  return bits;\n}\n\nvoid BuildDCHistograms(const JPEGData& jpg, JpegHistogram* histo) {\n  for (size_t i = 0; i < jpg.components.size(); ++i) {\n    const JPEGComponent& c = jpg.components[i];\n    JpegHistogram* dc_histogram = &histo[i];\n    coeff_t last_dc_coeff = 0;\n    for (int mcu_y = 0; mcu_y < jpg.MCU_rows; ++mcu_y) {\n      for (int mcu_x = 0; mcu_x < jpg.MCU_cols; ++mcu_x) {\n        for (int iy = 0; iy < c.v_samp_factor; ++iy) {\n          for (int ix = 0; ix < c.h_samp_factor; ++ix) {\n            int block_y = mcu_y * c.v_samp_factor + iy;\n            int block_x = mcu_x * c.h_samp_factor + ix;\n            int block_idx = block_y * c.width_in_blocks + block_x;\n            coeff_t dc_coeff = c.coeffs[block_idx << 6];\n            int diff = std::abs(dc_coeff - last_dc_coeff);\n            int nbits = Log2Floor(diff) + 1;\n            dc_histogram->Add(nbits);\n            last_dc_coeff = dc_coeff;\n          }\n        }\n      }\n    }\n  }\n}\n\nvoid BuildACHistograms(const JPEGData& jpg, JpegHistogram* histo) {\n  for (size_t i = 0; i < jpg.components.size(); ++i) {\n    const JPEGComponent& c = jpg.components[i];\n    JpegHistogram* ac_histogram = &histo[i];\n    for (size_t j = 0; j < c.coeffs.size(); j += kDCTBlockSize) {\n      UpdateACHistogramForDCTBlock(&c.coeffs[j], ac_histogram);\n    }\n  }\n}\n\n// Size of everything except the Huffman codes and the entropy coded data.\nsize_t JpegHeaderSize(const JPEGData& jpg, bool strip_metadata) {\n  size_t num_bytes = 0;\n  num_bytes += 2;  // SOI\n  if (strip_metadata) {\n    num_bytes += 18;  // APP0\n  } else {\n    for (size_t i = 0; i < jpg.app_data.size(); ++i) {\n      num_bytes += 1 + jpg.app_data[i].size();\n    }\n    for (size_t i = 0; i < jpg.com_data.size(); ++i) {\n      num_bytes += 2 + jpg.com_data[i].size();\n    }\n  }\n  // DQT\n  num_bytes += 4;\n  for (size_t i = 0; i < jpg.quant.size(); ++i) {\n    num_bytes += 1 + (jpg.quant[i].precision ? 2 : 1) * kDCTBlockSize;\n  }\n  num_bytes += 10 + 3 * jpg.components.size();  // SOF\n  num_bytes += 4;  // DHT (w/o actual Huffman code data)\n  num_bytes += 8 + 2 * jpg.components.size();  // SOS\n  num_bytes += 2;  // EOI\n  num_bytes += jpg.tail_data.size();\n  return num_bytes;\n}\n\nsize_t ClusterHistograms(JpegHistogram* histo, size_t* num,\n                         int* histo_indexes, uint8_t* depth) {\n  memset(depth, 0, *num * JpegHistogram::kSize);\n  size_t costs[kMaxComponents];\n  for (size_t i = 0; i < *num; ++i) {\n    histo_indexes[i] = i;\n    std::vector<HuffmanTree> tree(2 * JpegHistogram::kSize + 1);\n    CreateHuffmanTree(histo[i].counts, JpegHistogram::kSize,\n                      kJpegHuffmanMaxBitLength, &tree[0],\n                      &depth[i * JpegHistogram::kSize]);\n    costs[i] = (HistogramHeaderCost(histo[i]) +\n                HistogramEntropyCost(histo[i],\n                                     &depth[i * JpegHistogram::kSize]));\n  }\n  const size_t orig_num = *num;\n  while (*num > 1) {\n    size_t last = *num - 1;\n    size_t second_last = *num - 2;\n    JpegHistogram combined(histo[last]);\n    combined.AddHistogram(histo[second_last]);\n    std::vector<HuffmanTree> tree(2 * JpegHistogram::kSize + 1);\n    uint8_t depth_combined[JpegHistogram::kSize] = { 0 };\n    CreateHuffmanTree(combined.counts, JpegHistogram::kSize,\n                      kJpegHuffmanMaxBitLength, &tree[0], depth_combined);\n    size_t cost_combined = (HistogramHeaderCost(combined) +\n                            HistogramEntropyCost(combined, depth_combined));\n    if (cost_combined < costs[last] + costs[second_last]) {\n      histo[second_last] = combined;\n      histo[last] = JpegHistogram();\n      costs[second_last] = cost_combined;\n      memcpy(&depth[second_last * JpegHistogram::kSize], depth_combined,\n             sizeof(depth_combined));\n      for (size_t i = 0; i < orig_num; ++i) {\n        if (histo_indexes[i] == last) {\n          histo_indexes[i] = second_last;\n        }\n      }\n      --(*num);\n    } else {\n      break;\n    }\n  }\n  size_t total_cost = 0;\n  for (size_t i = 0; i < *num; ++i) {\n    total_cost += costs[i];\n  }\n  return (total_cost + 7) / 8;\n}\n\nsize_t EstimateJpegDataSize(const int num_components,\n                            const std::vector<JpegHistogram>& histograms) {\n  assert(histograms.size() == 2 * num_components);\n  std::vector<JpegHistogram> clustered = histograms;\n  size_t num_dc = num_components;\n  size_t num_ac = num_components;\n  int indexes[kMaxComponents];\n  uint8_t depth[kMaxComponents * JpegHistogram::kSize];\n  return (ClusterHistograms(&clustered[0], &num_dc, indexes, depth) +\n          ClusterHistograms(&clustered[num_components], &num_ac, indexes,\n                            depth));\n}\n\nnamespace {\n\n// Writes DHT and SOS marker segments to out and fills in DC/AC Huffman tables\n// for each component of the image.\nbool BuildAndEncodeHuffmanCodes(const JPEGData& jpg, JPEGOutput out,\n                                std::vector<HuffmanCodeTable>* dc_huff_tables,\n                                std::vector<HuffmanCodeTable>* ac_huff_tables) {\n  const int ncomps = jpg.components.size();\n  dc_huff_tables->resize(ncomps);\n  ac_huff_tables->resize(ncomps);\n\n  // Build separate DC histograms for each component.\n  std::vector<JpegHistogram> histograms(ncomps);\n  BuildDCHistograms(jpg, &histograms[0]);\n\n  // Cluster DC histograms.\n  size_t num_dc_histo = ncomps;\n  int dc_histo_indexes[kMaxComponents];\n  std::vector<uint8_t> depths(ncomps * JpegHistogram::kSize);\n  ClusterHistograms(&histograms[0], &num_dc_histo, dc_histo_indexes,\n                    &depths[0]);\n\n  // Build separate AC histograms for each component.\n  histograms.resize(num_dc_histo + ncomps);\n  depths.resize((num_dc_histo + ncomps) * JpegHistogram::kSize);\n  BuildACHistograms(jpg, &histograms[num_dc_histo]);\n\n  // Cluster AC histograms.\n  size_t num_ac_histo = ncomps;\n  int ac_histo_indexes[kMaxComponents];\n  ClusterHistograms(&histograms[num_dc_histo], &num_ac_histo, ac_histo_indexes,\n                    &depths[num_dc_histo * JpegHistogram::kSize]);\n\n  // Compute DHT and SOS marker data sizes and start emitting DHT marker.\n  int num_histo = num_dc_histo + num_ac_histo;\n  histograms.resize(num_histo);\n  int total_count = 0;\n  for (size_t i = 0; i < histograms.size(); ++i) {\n    total_count += histograms[i].NumSymbols();\n  }\n  const size_t dht_marker_len =\n      2 + num_histo * (kJpegHuffmanMaxBitLength + 1) + total_count;\n  const size_t sos_marker_len = 6 + 2 * ncomps;\n  std::vector<uint8_t> data(dht_marker_len + sos_marker_len + 4);\n  size_t pos = 0;\n  data[pos++] = 0xff;\n  data[pos++] = 0xc4;\n  data[pos++] = static_cast<uint8_t>(dht_marker_len >> 8);\n  data[pos++] = dht_marker_len & 0xff;\n\n  // Compute Huffman codes for each histograms.\n  for (int i = 0; i < num_histo; ++i) {\n    const bool is_dc = static_cast<size_t>(i) < num_dc_histo;\n    const int idx = is_dc ? i : i - num_dc_histo;\n    int counts[kJpegHuffmanMaxBitLength + 1] = { 0 };\n    int values[JpegHistogram::kSize] = { 0 };\n    BuildHuffmanCode(&depths[i * JpegHistogram::kSize], counts, values);\n    HuffmanCodeTable table;\n    for (int j = 0; j < 256; ++j) table.depth[j] = 255;\n    BuildHuffmanCodeTable(counts, values, &table);\n    for (int c = 0; c < ncomps; ++c) {\n      if (is_dc) {\n        if (dc_histo_indexes[c] == idx) (*dc_huff_tables)[c] = table;\n      } else {\n        if (ac_histo_indexes[c] == idx) (*ac_huff_tables)[c] = table;\n      }\n    }\n    int max_length = kJpegHuffmanMaxBitLength;\n    while (max_length > 0 && counts[max_length] == 0) --max_length;\n    --counts[max_length];\n    int total_count = 0;\n    for (int j = 0; j <= max_length; ++j) total_count += counts[j];\n    data[pos++] = is_dc ? i : static_cast<uint8_t>(i - num_dc_histo + 0x10);\n    for (size_t j = 1; j <= kJpegHuffmanMaxBitLength; ++j) {\n      data[pos++] = counts[j];\n    }\n    for (int j = 0; j < total_count; ++j) {\n      data[pos++] = values[j];\n    }\n  }\n\n  // Emit SOS marker data.\n  data[pos++] = 0xff;\n  data[pos++] = 0xda;\n  data[pos++] = static_cast<uint8_t>(sos_marker_len >> 8);\n  data[pos++] = sos_marker_len & 0xff;\n  data[pos++] = ncomps;\n  for (int i = 0; i < ncomps; ++i) {\n    data[pos++] = jpg.components[i].id;\n    data[pos++] = (dc_histo_indexes[i] << 4) | ac_histo_indexes[i];\n  }\n  data[pos++] = 0;\n  data[pos++] = 63;\n  data[pos++] = 0;\n  assert(pos == data.size());\n  return JPEGWrite(out, &data[0], data.size());\n}\n\nvoid EncodeDCTBlockSequential(const coeff_t* coeffs,\n                              const HuffmanCodeTable& dc_huff,\n                              const HuffmanCodeTable& ac_huff,\n                              coeff_t* last_dc_coeff,\n                              BitWriter* bw) {\n  coeff_t temp2;\n  coeff_t temp;\n  temp2 = coeffs[0];\n  temp = temp2 - *last_dc_coeff;\n  *last_dc_coeff = temp2;\n  temp2 = temp;\n  if (temp < 0) {\n    temp = -temp;\n    temp2--;\n  }\n  int nbits = Log2Floor(temp) + 1;\n  bw->WriteBits(dc_huff.depth[nbits], dc_huff.code[nbits]);\n  if (nbits > 0) {\n    bw->WriteBits(nbits, temp2 & ((1 << nbits) - 1));\n  }\n  int r = 0;\n  for (int k = 1; k < 64; ++k) {\n    if ((temp = coeffs[kJPEGNaturalOrder[k]]) == 0) {\n      r++;\n      continue;\n    }\n    if (temp < 0) {\n      temp = -temp;\n      temp2 = ~temp;\n    } else {\n      temp2 = temp;\n    }\n    while (r > 15) {\n      bw->WriteBits(ac_huff.depth[0xf0], ac_huff.code[0xf0]);\n      r -= 16;\n    }\n    int nbits = Log2FloorNonZero(temp) + 1;\n    int symbol = (r << 4) + nbits;\n    bw->WriteBits(ac_huff.depth[symbol], ac_huff.code[symbol]);\n    bw->WriteBits(nbits, temp2 & ((1 << nbits) - 1));\n    r = 0;\n  }\n  if (r > 0) {\n    bw->WriteBits(ac_huff.depth[0], ac_huff.code[0]);\n  }\n}\n\nbool EncodeScan(const JPEGData& jpg,\n                const std::vector<HuffmanCodeTable>& dc_huff_table,\n                const std::vector<HuffmanCodeTable>& ac_huff_table,\n                JPEGOutput out) {\n  coeff_t last_dc_coeff[kMaxComponents] = { 0 };\n  BitWriter bw(1 << 17);\n  for (int mcu_y = 0; mcu_y < jpg.MCU_rows; ++mcu_y) {\n    for (int mcu_x = 0; mcu_x < jpg.MCU_cols; ++mcu_x) {\n      // Encode one MCU\n      for (size_t i = 0; i < jpg.components.size(); ++i) {\n        const JPEGComponent& c = jpg.components[i];\n        int nblocks_y = c.v_samp_factor;\n        int nblocks_x = c.h_samp_factor;\n        for (int iy = 0; iy < nblocks_y; ++iy) {\n          for (int ix = 0; ix < nblocks_x; ++ix) {\n            int block_y = mcu_y * nblocks_y + iy;\n            int block_x = mcu_x * nblocks_x + ix;\n            int block_idx = block_y * c.width_in_blocks + block_x;\n            const coeff_t* coeffs = &c.coeffs[block_idx << 6];\n            EncodeDCTBlockSequential(coeffs, dc_huff_table[i], ac_huff_table[i],\n                                     &last_dc_coeff[i], &bw);\n          }\n        }\n      }\n      if (bw.pos > (1 << 16)) {\n        if (!JPEGWrite(out, bw.data.get(), bw.pos)) {\n          return false;\n        }\n        bw.pos = 0;\n      }\n    }\n  }\n  bw.JumpToByteBoundary();\n  return !bw.overflow && JPEGWrite(out, bw.data.get(), bw.pos);\n}\n\n}  // namespace\n\nbool WriteJpeg(const JPEGData& jpg, bool strip_metadata, JPEGOutput out) {\n  static const uint8_t kSOIMarker[2] = { 0xff, 0xd8 };\n  static const uint8_t kEOIMarker[2] = { 0xff, 0xd9 };\n  std::vector<HuffmanCodeTable> dc_codes;\n  std::vector<HuffmanCodeTable> ac_codes;\n  return (JPEGWrite(out, kSOIMarker, sizeof(kSOIMarker)) &&\n          EncodeMetadata(jpg, strip_metadata, out) &&\n          EncodeDQT(jpg.quant, out) &&\n          EncodeSOF(jpg, out) &&\n          BuildAndEncodeHuffmanCodes(jpg, out, &dc_codes, &ac_codes) &&\n          EncodeScan(jpg, dc_codes, ac_codes, out) &&\n          JPEGWrite(out, kEOIMarker, sizeof(kEOIMarker)) &&\n          (strip_metadata || JPEGWrite(out, jpg.tail_data)));\n}\n\nint NullOut(void* data, const uint8_t* buf, size_t count) {\n  return count;\n}\n\nvoid BuildSequentialHuffmanCodes(\n    const JPEGData& jpg,\n    std::vector<HuffmanCodeTable>* dc_huffman_code_tables,\n    std::vector<HuffmanCodeTable>* ac_huffman_code_tables) {\n  JPEGOutput out(NullOut, nullptr);\n  BuildAndEncodeHuffmanCodes(jpg, out, dc_huffman_code_tables,\n                             ac_huffman_code_tables);\n}\n\n}  // namespace guetzli\n"
  },
  {
    "path": "guetzli/jpeg_data_writer.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Functions for writing a JPEGData object into a jpeg byte stream.\n\n#ifndef GUETZLI_JPEG_DATA_WRITER_H_\n#define GUETZLI_JPEG_DATA_WRITER_H_\n\n#include <stdint.h>\n#include <string.h>\n#include <vector>\n\n#include \"guetzli/jpeg_data.h\"\n\nnamespace guetzli {\n\n// Function pointer type used to write len bytes into buf. Returns the\n// number of bytes written or -1 on error.\ntypedef int (*JPEGOutputHook)(void* data, const uint8_t* buf, size_t len);\n\n// Output callback function with associated data.\nstruct JPEGOutput {\n  JPEGOutput(JPEGOutputHook cb, void* data) : cb(cb), data(data) {}\n  bool Write(const uint8_t* buf, size_t len) const {\n    return (len == 0) || (cb(data, buf, len) == len);\n  }\n private:\n  JPEGOutputHook cb;\n  void* data;\n};\n\nbool WriteJpeg(const JPEGData& jpg, bool strip_metadata, JPEGOutput out);\n\nstruct HuffmanCodeTable {\n  uint8_t depth[256];\n  int code[256];\n};\n\nvoid BuildSequentialHuffmanCodes(\n    const JPEGData& jpg, std::vector<HuffmanCodeTable>* dc_huffman_code_tables,\n    std::vector<HuffmanCodeTable>* ac_huffman_code_tables);\n\nstruct JpegHistogram {\n  static const int kSize = kJpegHuffmanAlphabetSize + 1;\n\n  JpegHistogram() { Clear(); }\n  void Clear() {\n    memset(counts, 0, sizeof(counts));\n    counts[kSize - 1] = 1;\n  }\n  void Add(int symbol) {\n    counts[symbol] += 2;\n  }\n  void Add(int symbol, int weight) {\n    counts[symbol] += 2 * weight;\n  }\n  void AddHistogram(const JpegHistogram& other) {\n    for (int i = 0; i + 1 < kSize; ++i) {\n      counts[i] += other.counts[i];\n    }\n    counts[kSize - 1] = 1;\n  }\n  int NumSymbols() const {\n    int n = 0;\n    for (int i = 0; i + 1 < kSize; ++i) {\n      n += (counts[i] > 0 ? 1 : 0);\n    }\n    return n;\n  }\n\n  uint32_t counts[kSize];\n};\n\nvoid BuildDCHistograms(const JPEGData& jpg, JpegHistogram* histo);\nvoid BuildACHistograms(const JPEGData& jpg, JpegHistogram* histo);\nsize_t JpegHeaderSize(const JPEGData& jpg, bool strip_metadata);\nsize_t EstimateJpegDataSize(const int num_components,\n                            const std::vector<JpegHistogram>& histograms);\n\nsize_t HistogramEntropyCost(const JpegHistogram& histo,\n                            const uint8_t depths[256]);\nsize_t HistogramHeaderCost(const JpegHistogram& histo);\n\nvoid UpdateACHistogramForDCTBlock(const coeff_t* coeffs,\n                                  JpegHistogram* ac_histogram);\n\nsize_t ClusterHistograms(JpegHistogram* histo, size_t* num, int* histo_indexes,\n                         uint8_t* depths);\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_JPEG_DATA_WRITER_H_\n"
  },
  {
    "path": "guetzli/jpeg_error.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Definition of error codes for parsing jpeg files.\n\n#ifndef GUETZLI_JPEG_ERROR_H_\n#define GUETZLI_JPEG_ERROR_H_\n\nnamespace guetzli {\n\nenum JPEGReadError {\n  JPEG_OK = 0,\n  JPEG_SOI_NOT_FOUND,\n  JPEG_SOF_NOT_FOUND,\n  JPEG_UNEXPECTED_EOF,\n  JPEG_MARKER_BYTE_NOT_FOUND,\n  JPEG_UNSUPPORTED_MARKER,\n  JPEG_WRONG_MARKER_SIZE,\n  JPEG_INVALID_PRECISION,\n  JPEG_INVALID_WIDTH,\n  JPEG_INVALID_HEIGHT,\n  JPEG_INVALID_NUMCOMP,\n  JPEG_INVALID_SAMP_FACTOR,\n  JPEG_INVALID_START_OF_SCAN,\n  JPEG_INVALID_END_OF_SCAN,\n  JPEG_INVALID_SCAN_BIT_POSITION,\n  JPEG_INVALID_COMPS_IN_SCAN,\n  JPEG_INVALID_HUFFMAN_INDEX,\n  JPEG_INVALID_QUANT_TBL_INDEX,\n  JPEG_INVALID_QUANT_VAL,\n  JPEG_INVALID_MARKER_LEN,\n  JPEG_INVALID_SAMPLING_FACTORS,\n  JPEG_INVALID_HUFFMAN_CODE,\n  JPEG_INVALID_SYMBOL,\n  JPEG_NON_REPRESENTABLE_DC_COEFF,\n  JPEG_NON_REPRESENTABLE_AC_COEFF,\n  JPEG_INVALID_SCAN,\n  JPEG_OVERLAPPING_SCANS,\n  JPEG_INVALID_SCAN_ORDER,\n  JPEG_EXTRA_ZERO_RUN,\n  JPEG_DUPLICATE_DRI,\n  JPEG_DUPLICATE_SOF,\n  JPEG_WRONG_RESTART_MARKER,\n  JPEG_DUPLICATE_COMPONENT_ID,\n  JPEG_COMPONENT_NOT_FOUND,\n  JPEG_HUFFMAN_TABLE_NOT_FOUND,\n  JPEG_HUFFMAN_TABLE_ERROR,\n  JPEG_QUANT_TABLE_NOT_FOUND,\n  JPEG_EMPTY_DHT,\n  JPEG_EMPTY_DQT,\n  JPEG_OUT_OF_BAND_COEFF,\n  JPEG_EOB_RUN_TOO_LONG,\n  JPEG_IMAGE_TOO_LARGE,\n};\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_JPEG_ERROR_H_\n"
  },
  {
    "path": "guetzli/jpeg_huffman_decode.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"guetzli/jpeg_huffman_decode.h\"\n\n#include <assert.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"guetzli/jpeg_data.h\"\n\nnamespace guetzli {\n\n// Returns the table width of the next 2nd level table, count is the histogram\n// of bit lengths for the remaining symbols, len is the code length of the next\n// processed symbol.\nstatic inline int NextTableBitSize(const int* count, int len) {\n  int left = 1 << (len - kJpegHuffmanRootTableBits);\n  while (len < kJpegHuffmanMaxBitLength) {\n    left -= count[len];\n    if (left <= 0) break;\n    ++len;\n    left <<= 1;\n  }\n  return len - kJpegHuffmanRootTableBits;\n}\n\nint BuildJpegHuffmanTable(const int* count_in, const int* symbols,\n                          HuffmanTableEntry* lut) {\n  HuffmanTableEntry code;    // current table entry\n  HuffmanTableEntry* table;  // next available space in table\n  int len;         // current code length\n  int idx;         // symbol index\n  int key;         // prefix code\n  int reps;        // number of replicate key values in current table\n  int low;         // low bits for current root entry\n  int table_bits;  // key length of current table\n  int table_size;  // size of current table\n  int total_size;  // sum of root table size and 2nd level table sizes\n\n  // Make a local copy of the input bit length histogram.\n  int count[kJpegHuffmanMaxBitLength + 1] = { 0 };\n  int total_count = 0;\n  for (len = 1; len <= kJpegHuffmanMaxBitLength; ++len) {\n    count[len] = count_in[len];\n    total_count += count[len];\n  }\n\n  table = lut;\n  table_bits = kJpegHuffmanRootTableBits;\n  table_size = 1 << table_bits;\n  total_size = table_size;\n\n  // Special case code with only one value.\n  if (total_count == 1) {\n    code.bits = 0;\n    code.value = symbols[0];\n    for (key = 0; key < total_size; ++key) {\n      table[key] = code;\n    }\n    return total_size;\n  }\n\n  // Fill in root table.\n  key = 0;\n  idx = 0;\n  for (len = 1; len <= kJpegHuffmanRootTableBits; ++len) {\n    for (; count[len] > 0; --count[len]) {\n      code.bits = len;\n      code.value = symbols[idx++];\n      reps = 1 << (kJpegHuffmanRootTableBits - len);\n      while (reps--) {\n        table[key++] = code;\n      }\n    }\n  }\n\n  // Fill in 2nd level tables and add pointers to root table.\n  table += table_size;\n  table_size = 0;\n  low = 0;\n  for (len = kJpegHuffmanRootTableBits + 1;\n       len <= kJpegHuffmanMaxBitLength; ++len) {\n    for (; count[len] > 0; --count[len]) {\n      // Start a new sub-table if the previous one is full.\n      if (low >= table_size) {\n        table += table_size;\n        table_bits = NextTableBitSize(count, len);\n        table_size = 1 << table_bits;\n        total_size += table_size;\n        low = 0;\n        lut[key].bits = table_bits + kJpegHuffmanRootTableBits;\n        lut[key].value = (table - lut) - key;\n        ++key;\n      }\n      code.bits = len - kJpegHuffmanRootTableBits;\n      code.value = symbols[idx++];\n      reps = 1 << (table_bits - code.bits);\n      while (reps--) {\n        table[low++] = code;\n      }\n    }\n  }\n\n  return total_size;\n}\n\n}  // namespace guetzli\n"
  },
  {
    "path": "guetzli/jpeg_huffman_decode.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Utility function for building a Huffman lookup table for the jpeg decoder.\n\n#ifndef GUETZLI_JPEG_HUFFMAN_DECODE_H_\n#define GUETZLI_JPEG_HUFFMAN_DECODE_H_\n\n#include <inttypes.h>\n\nnamespace guetzli {\n\nstatic const int kJpegHuffmanRootTableBits = 8;\n// Maximum huffman lookup table size.\n// According to zlib/examples/enough.c, 758 entries are always enough for\n// an alphabet of 257 symbols (256 + 1 special symbol for the all 1s code) and\n// max bit length 16 if the root table has 8 bits.\nstatic const int kJpegHuffmanLutSize = 758;\n\nstruct HuffmanTableEntry {\n  // Initialize the value to an invalid symbol so that we can recognize it\n  // when reading the bit stream using a Huffman code with space > 0.\n  HuffmanTableEntry() : bits(0), value(0xffff) {}\n\n  uint8_t bits;     // number of bits used for this symbol\n  uint16_t value;   // symbol value or table offset\n};\n\n// Builds jpeg-style Huffman lookup table from the given symbols.\n// The symbols are in order of increasing bit lengths. The number of symbols\n// with bit length n is given in counts[n] for each n >= 1.\n// Returns the size of the lookup table.\nint BuildJpegHuffmanTable(const int* counts, const int* symbols,\n                          HuffmanTableEntry* lut);\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_JPEG_HUFFMAN_DECODE_H_\n"
  },
  {
    "path": "guetzli/order.inc",
    "content": "// Automatically generated by guetzli/order:update_c_code\n\nstatic const float csf[192] = {\n  0.0f,\n  1.71014f,\n  0.298711f,\n  0.233709f,\n  0.223126f,\n  0.207072f,\n  0.192775f,\n  0.161201f,\n  2.05807f,\n  0.222927f,\n  0.203406f,\n  0.188465f,\n  0.184668f,\n  0.169993f,\n  0.159142f,\n  0.130155f,\n  0.430518f,\n  0.204939f,\n  0.206655f,\n  0.192231f,\n  0.182941f,\n  0.169455f,\n  0.157599f,\n  0.127153f,\n  0.234757f,\n  0.191098f,\n  0.192698f,\n  0.17425f,\n  0.166503f,\n  0.142154f,\n  0.126182f,\n  0.104196f,\n  0.226117f,\n  0.185373f,\n  0.183825f,\n  0.166643f,\n  0.159414f,\n  0.12636f,\n  0.108696f,\n  0.0911974f,\n  0.207463f,\n  0.171517f,\n  0.170124f,\n  0.141582f,\n  0.126213f,\n  0.103627f,\n  0.0882436f,\n  0.0751848f,\n  0.196436f,\n  0.161947f,\n  0.159271f,\n  0.126938f,\n  0.109125f,\n  0.0878027f,\n  0.0749842f,\n  0.0633859f,\n  0.165232f,\n  0.132905f,\n  0.128679f,\n  0.105766f,\n  0.0906087f,\n  0.0751544f,\n  0.0641187f,\n  0.0529921f,\n  0.0f,\n  0.147235f,\n  0.11264f,\n  0.0757892f,\n  0.0493929f,\n  0.0280663f,\n  0.0075012f,\n  -0.000945567f,\n  0.149251f,\n  0.0964806f,\n  0.0786224f,\n  0.05206f,\n  0.0292758f,\n  0.00353094f,\n  -0.00277912f,\n  -0.00404481f,\n  0.115551f,\n  0.0793142f,\n  0.0623735f,\n  0.0405019f,\n  0.0152656f,\n  -0.00145742f,\n  -0.00370369f,\n  -0.00375106f,\n  0.0791547f,\n  0.0537506f,\n  0.0413634f,\n  0.0193486f,\n  0.000609066f,\n  -0.00510923f,\n  -0.0046452f,\n  -0.00385187f,\n  0.0544534f,\n  0.0334066f,\n  0.0153899f,\n  0.000539088f,\n  -0.00356085f,\n  -0.00535661f,\n  -0.00429145f,\n  -0.00343131f,\n  0.0356439f,\n  0.00865645f,\n  0.00165229f,\n  -0.00425931f,\n  -0.00507324f,\n  -0.00459083f,\n  -0.003703f,\n  -0.00310327f,\n  0.0121926f,\n  -0.0009259f,\n  -0.00330991f,\n  -0.00499378f,\n  -0.00437381f,\n  -0.00377427f,\n  -0.00311731f,\n  -0.00255125f,\n  -0.000320593f,\n  -0.00426043f,\n  -0.00416549f,\n  -0.00419364f,\n  -0.00365418f,\n  -0.00317499f,\n  -0.00255932f,\n  -0.00217917f,\n  0.0f,\n  0.143471f,\n  0.124336f,\n  0.0947465f,\n  0.0814066f,\n  0.0686776f,\n  0.0588122f,\n  0.0374415f,\n  0.146315f,\n  0.105334f,\n  0.0949415f,\n  0.0784241f,\n  0.0689064f,\n  0.0588304f,\n  0.0495961f,\n  0.0202342f,\n  0.123818f,\n  0.0952654f,\n  0.0860556f,\n  0.0724158f,\n  0.0628307f,\n  0.0529965f,\n  0.0353941f,\n  0.00815821f,\n  0.097054f,\n  0.080422f,\n  0.0731085f,\n  0.0636154f,\n  0.055606f,\n  0.0384127f,\n  0.0142879f,\n  0.00105195f,\n  0.0849312f,\n  0.071115f,\n  0.0631183f,\n  0.0552972f,\n  0.0369221f,\n  0.00798314f,\n  0.000716374f,\n  -0.00200948f,\n  0.0722298f,\n  0.0599559f,\n  0.054841f,\n  0.0387529f,\n  0.0107262f,\n  0.000355315f,\n  -0.00244803f,\n  -0.00335222f,\n  0.0635335f,\n  0.0514196f,\n  0.0406309f,\n  0.0125833f,\n  0.00151305f,\n  -0.00140269f,\n  -0.00362547f,\n  -0.00337649f,\n  0.0472024f,\n  0.0198725f,\n  0.0113437f,\n  0.00266305f,\n  -0.00137183f,\n  -0.00354158f,\n  -0.00341292f,\n  -0.00290074f\n};\n\nstatic const float bias[192] = {\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0f,\n  0.0\n};\n"
  },
  {
    "path": "guetzli/output_image.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"guetzli/output_image.h\"\n\n#include <algorithm>\n#include <assert.h>\n#include <stdio.h>\n#include <string.h>\n#include <cmath>\n#include <cstdlib>\n\n#include \"guetzli/idct.h\"\n#include \"guetzli/color_transform.h\"\n#include \"guetzli/dct_double.h\"\n#include \"guetzli/gamma_correct.h\"\n#include \"guetzli/preprocess_downsample.h\"\n#include \"guetzli/quantize.h\"\n\nnamespace guetzli {\n\nOutputImageComponent::OutputImageComponent(int w, int h)\n    : width_(w), height_(h) {\n  Reset(1, 1);\n}\n\nvoid OutputImageComponent::Reset(int factor_x, int factor_y) {\n  factor_x_ = factor_x;\n  factor_y_ = factor_y;\n  width_in_blocks_ = (width_ + 8 * factor_x_ - 1) / (8 * factor_x_);\n  height_in_blocks_ = (height_ + 8 * factor_y_ - 1) / (8 * factor_y_);\n  num_blocks_ = width_in_blocks_ * height_in_blocks_;\n  coeffs_ = std::vector<coeff_t>(num_blocks_ * kDCTBlockSize);\n  pixels_ = std::vector<uint16_t>(width_ * height_, 128 << 4);\n  for (int i = 0; i < kDCTBlockSize; ++i) quant_[i] = 1;\n}\n\nbool OutputImageComponent::IsAllZero() const {\n  int numcoeffs = num_blocks_ * kDCTBlockSize;\n  for (int i = 0; i < numcoeffs; ++i) {\n    if (coeffs_[i] != 0) return false;\n  }\n  return true;\n}\n\nvoid OutputImageComponent::GetCoeffBlock(int block_x, int block_y,\n                                         coeff_t block[kDCTBlockSize]) const {\n  assert(block_x < width_in_blocks_);\n  assert(block_y < height_in_blocks_);\n  int offset = (block_y * width_in_blocks_ + block_x) * kDCTBlockSize;\n  memcpy(block, &coeffs_[offset], kDCTBlockSize * sizeof(coeffs_[0]));\n}\n\nvoid OutputImageComponent::ToPixels(int xmin, int ymin, int xsize, int ysize,\n                                    uint8_t* out, int stride) const {\n  assert(xmin >= 0);\n  assert(ymin >= 0);\n  assert(xmin < width_);\n  assert(ymin < height_);\n  const int yend1 = ymin + ysize;\n  const int yend0 = std::min(yend1, height_);\n  int y = ymin;\n  for (; y < yend0; ++y) {\n    const int xend1 = xmin + xsize;\n    const int xend0 = std::min(xend1, width_);\n    int x = xmin;\n    int px = y * width_ + xmin;\n    for (; x < xend0; ++x, ++px, out += stride) {\n      *out = static_cast<uint8_t>((pixels_[px] + 8 - (x & 1)) >> 4);\n    }\n    const int offset = -stride;\n    for (; x < xend1; ++x) {\n      *out = out[offset];\n      out += stride;\n    }\n  }\n  for (; y < yend1; ++y) {\n    const int offset = -stride * xsize;\n    for (int x = 0; x < xsize; ++x) {\n      *out = out[offset];\n      out += stride;\n    }\n  }\n}\n\nvoid OutputImageComponent::ToFloatPixels(float* out, int stride) const {\n  assert(factor_x_ == 1);\n  assert(factor_y_ == 1);\n  for (int block_y = 0; block_y < height_in_blocks_; ++block_y) {\n    for (int block_x = 0; block_x < width_in_blocks_; ++block_x) {\n      coeff_t block[kDCTBlockSize];\n      GetCoeffBlock(block_x, block_y, block);\n      double blockd[kDCTBlockSize];\n      for (int k = 0; k < kDCTBlockSize; ++k) {\n        blockd[k] = block[k];\n      }\n      ComputeBlockIDCTDouble(blockd);\n      for (int iy = 0; iy < 8; ++iy) {\n        for (int ix = 0; ix < 8; ++ix) {\n          int y = block_y * 8 + iy;\n          int x = block_x * 8 + ix;\n          if (y >= height_ || x >= width_) continue;\n          out[(y * width_ + x) * stride] = static_cast<float>(blockd[8 * iy + ix] + 128.0);\n        }\n      }\n    }\n  }\n}\n\nvoid OutputImageComponent::SetCoeffBlock(int block_x, int block_y,\n                                         const coeff_t block[kDCTBlockSize]) {\n  assert(block_x < width_in_blocks_);\n  assert(block_y < height_in_blocks_);\n  int offset = (block_y * width_in_blocks_ + block_x) * kDCTBlockSize;\n  memcpy(&coeffs_[offset], block, kDCTBlockSize * sizeof(coeffs_[0]));\n  uint8_t idct[kDCTBlockSize];\n  ComputeBlockIDCT(&coeffs_[offset], idct);\n  UpdatePixelsForBlock(block_x, block_y, idct);\n}\n\nvoid OutputImageComponent::UpdatePixelsForBlock(\n    int block_x, int block_y, const uint8_t idct[kDCTBlockSize]) {\n  if (factor_x_ == 1 && factor_y_ == 1) {\n    for (int iy = 0; iy < 8; ++iy) {\n      for (int ix = 0; ix < 8; ++ix) {\n        int x = 8 * block_x + ix;\n        int y = 8 * block_y + iy;\n        if (x >= width_ || y >= height_) continue;\n        int p = y * width_ + x;\n        pixels_[p] = idct[8 * iy + ix] << 4;\n      }\n    }\n  } else if (factor_x_ == 2 && factor_y_ == 2) {\n    // Fill in the 10x10 pixel area in the subsampled image that will be the\n    // basis of the upsampling. This area is enough to hold the 3x3 kernel of\n    // the fancy upsampler around each pixel.\n    static const int kSubsampledEdgeSize = 10;\n    uint16_t subsampled[kSubsampledEdgeSize * kSubsampledEdgeSize];\n    for (int j = 0; j < kSubsampledEdgeSize; ++j) {\n      // The order we fill in the rows is:\n      //   8 rows intersecting the block, row below, row above\n      const int y0 = block_y * 16 + (j < 9 ? j * 2 : -2);\n      for (int i = 0; i < kSubsampledEdgeSize; ++i) {\n        // The order we fill in each row is:\n        //   8 pixels within the block, left edge, right edge\n        const int ix = ((j < 9 ? (j + 1) * kSubsampledEdgeSize : 0) +\n                        (i < 9 ? i + 1 : 0));\n        const int x0 = block_x * 16 + (i < 9 ? i * 2 : -2);\n        if (x0 < 0) {\n          subsampled[ix] = subsampled[ix + 1];\n        } else if (y0 < 0) {\n          subsampled[ix] = subsampled[ix + kSubsampledEdgeSize];\n        } else if (x0 >= width_) {\n          subsampled[ix] = subsampled[ix - 1];\n        } else if (y0 >= height_) {\n          subsampled[ix] = subsampled[ix - kSubsampledEdgeSize];\n        } else if (i < 8 && j < 8) {\n          subsampled[ix] = idct[j * 8 + i] << 4;\n        } else {\n          // Reconstruct the subsampled pixels around the edge of the current\n          // block by computing the inverse of the fancy upsampler.\n          const int y1 = std::max(y0 - 1, 0);\n          const int x1 = std::max(x0 - 1, 0);\n          subsampled[ix] = (pixels_[y0 * width_ + x0] * 9 +\n                            pixels_[y1 * width_ + x1] +\n                            pixels_[y0 * width_ + x1] * -3 +\n                            pixels_[y1 * width_ + x0] * -3) >> 2;\n        }\n      }\n    }\n\n    // Determine area to update.\n    int xmin = std::max(block_x * 16 - 1, 0);\n    int xmax = std::min(block_x * 16 + 16, width_ - 1);\n    int ymin = std::max(block_y * 16 - 1, 0);\n    int ymax = std::min(block_y * 16 + 16, height_ - 1);\n\n    // Apply the fancy upsampler on the subsampled block.\n    for (int y = ymin; y <= ymax; ++y) {\n      const int y0 = ((y & ~1) / 2 - block_y * 8 + 1) * kSubsampledEdgeSize;\n      const int dy = ((y & 1) * 2 - 1) * kSubsampledEdgeSize;\n      uint16_t* rowptr = &pixels_[y * width_];\n      for (int x = xmin; x <= xmax; ++x) {\n        const int x0 = (x & ~1) / 2 - block_x * 8 + 1;\n        const int dx = (x & 1) * 2 - 1;\n        const int ix = x0 + y0;\n        rowptr[x] = (subsampled[ix] * 9 + subsampled[ix + dy] * 3 +\n                     subsampled[ix + dx] * 3 + subsampled[ix + dx + dy]) >> 4;\n      }\n    }\n  } else {\n    printf(\"Sampling ratio not supported: factor_x = %d factor_y = %d\\n\",\n           factor_x_, factor_y_);\n    exit(1);\n  }\n}\n\nvoid OutputImageComponent::CopyFromJpegComponent(const JPEGComponent& comp,\n                                                 int factor_x, int factor_y,\n                                                 const int* quant) {\n  Reset(factor_x, factor_y);\n  assert(width_in_blocks_ <= comp.width_in_blocks);\n  assert(height_in_blocks_ <= comp.height_in_blocks);\n  const size_t src_row_size = comp.width_in_blocks * kDCTBlockSize;\n  for (int block_y = 0; block_y < height_in_blocks_; ++block_y) {\n    const coeff_t* src_coeffs = &comp.coeffs[block_y * src_row_size];\n    for (int block_x = 0; block_x < width_in_blocks_; ++block_x) {\n      coeff_t block[kDCTBlockSize];\n      for (int i = 0; i < kDCTBlockSize; ++i) {\n        block[i] = src_coeffs[i] * quant[i];\n      }\n      SetCoeffBlock(block_x, block_y, block);\n      src_coeffs += kDCTBlockSize;\n    }\n  }\n  memcpy(quant_, quant, sizeof(quant_));\n}\n\nvoid OutputImageComponent::ApplyGlobalQuantization(const int q[kDCTBlockSize]) {\n  for (int block_y = 0; block_y < height_in_blocks_; ++block_y) {\n    for (int block_x = 0; block_x < width_in_blocks_; ++block_x) {\n      coeff_t block[kDCTBlockSize];\n      GetCoeffBlock(block_x, block_y, block);\n      if (QuantizeBlock(block, q)) {\n        SetCoeffBlock(block_x, block_y, block);\n      }\n    }\n  }\n  memcpy(quant_, q, sizeof(quant_));\n}\n\nOutputImage::OutputImage(int w, int h)\n    : width_(w),\n      height_(h),\n      components_(3, OutputImageComponent(w, h)) {}\n\nvoid OutputImage::CopyFromJpegData(const JPEGData& jpg) {\n  for (size_t i = 0; i < jpg.components.size(); ++i) {\n    const JPEGComponent& comp = jpg.components[i];\n    assert(jpg.max_h_samp_factor % comp.h_samp_factor == 0);\n    assert(jpg.max_v_samp_factor % comp.v_samp_factor == 0);\n    int factor_x = jpg.max_h_samp_factor / comp.h_samp_factor;\n    int factor_y = jpg.max_v_samp_factor / comp.v_samp_factor;\n    assert(comp.quant_idx < jpg.quant.size());\n    components_[i].CopyFromJpegComponent(comp, factor_x, factor_y,\n                                         &jpg.quant[comp.quant_idx].values[0]);\n  }\n}\n\nnamespace {\n\nvoid SetDownsampledCoefficients(const std::vector<float>& pixels,\n                                int factor_x, int factor_y,\n                                OutputImageComponent* comp) {\n  assert(pixels.size() == comp->width() * comp->height());\n  comp->Reset(factor_x, factor_y);\n  for (int block_y = 0; block_y < comp->height_in_blocks(); ++block_y) {\n    for (int block_x = 0; block_x < comp->width_in_blocks(); ++block_x) {\n      double blockd[kDCTBlockSize];\n      int x0 = 8 * block_x * factor_x;\n      int y0 = 8 * block_y * factor_y;\n      assert(x0 < comp->width());\n      assert(y0 < comp->height());\n      for (int iy = 0; iy < 8; ++iy) {\n        for (int ix = 0; ix < 8; ++ix) {\n          float avg = 0.0;\n          for (int j = 0; j < factor_y; ++j) {\n            for (int i = 0; i < factor_x; ++i) {\n              int x = std::min(x0 + ix * factor_x + i, comp->width() - 1);\n              int y = std::min(y0 + iy * factor_y + j, comp->height() - 1);\n              avg += pixels[y * comp->width() + x];\n            }\n          }\n          avg /= factor_x * factor_y;\n          blockd[iy * 8 + ix] = avg;\n        }\n      }\n      ComputeBlockDCTDouble(blockd);\n      blockd[0] -= 1024.0;\n      coeff_t block[kDCTBlockSize];\n      for (int k = 0; k < kDCTBlockSize; ++k) {\n        block[k] = static_cast<coeff_t>(std::round(blockd[k]));\n      }\n      comp->SetCoeffBlock(block_x, block_y, block);\n    }\n  }\n}\n\n}  // namespace\n\nvoid OutputImage::Downsample(const DownsampleConfig& cfg) {\n  if (components_[1].IsAllZero() && components_[2].IsAllZero()) {\n    // If the image is already grayscale, nothing to do.\n    return;\n  }\n  if (cfg.use_silver_screen &&\n      cfg.u_factor_x == 2 && cfg.u_factor_y == 2 &&\n      cfg.v_factor_x == 2 && cfg.v_factor_y == 2) {\n    std::vector<uint8_t> rgb = ToSRGB();\n    std::vector<std::vector<float> > yuv = RGBToYUV420(rgb, width_, height_);\n    SetDownsampledCoefficients(yuv[0], 1, 1, &components_[0]);\n    SetDownsampledCoefficients(yuv[1], 2, 2, &components_[1]);\n    SetDownsampledCoefficients(yuv[2], 2, 2, &components_[2]);\n    return;\n  }\n  // Get the floating-point precision YUV array represented by the set of\n  // DCT coefficients.\n  std::vector<std::vector<float> > yuv(3, std::vector<float>(width_ * height_));\n  for (int c = 0; c < 3; ++c) {\n    components_[c].ToFloatPixels(&yuv[c][0], 1);\n  }\n\n  yuv = PreProcessChannel(width_, height_, 2, 1.3f, 0.5f,\n                          cfg.u_sharpen, cfg.u_blur, yuv);\n  yuv = PreProcessChannel(width_, height_, 1, 1.3f, 0.5f,\n                          cfg.v_sharpen, cfg.v_blur, yuv);\n\n  // Do the actual downsampling (averaging) and forward-DCT.\n  if (cfg.u_factor_x != 1 || cfg.u_factor_y != 1) {\n    SetDownsampledCoefficients(yuv[1], cfg.u_factor_x, cfg.u_factor_y,\n                               &components_[1]);\n  }\n  if (cfg.v_factor_x != 1 || cfg.v_factor_y != 1) {\n    SetDownsampledCoefficients(yuv[2], cfg.v_factor_x, cfg.v_factor_y,\n                               &components_[2]);\n  }\n}\n\nvoid OutputImage::ApplyGlobalQuantization(const int q[3][kDCTBlockSize]) {\n  for (int c = 0; c < 3; ++c) {\n    components_[c].ApplyGlobalQuantization(&q[c][0]);\n  }\n}\n\nvoid OutputImage::SaveToJpegData(JPEGData* jpg) const {\n  assert(components_[0].factor_x() == 1);\n  assert(components_[0].factor_y() == 1);\n  jpg->width = width_;\n  jpg->height = height_;\n  jpg->max_h_samp_factor = 1;\n  jpg->max_v_samp_factor = 1;\n  jpg->MCU_cols = components_[0].width_in_blocks();\n  jpg->MCU_rows = components_[0].height_in_blocks();\n  int ncomp = components_[1].IsAllZero() && components_[2].IsAllZero() ? 1 : 3;\n  for (int i = 1; i < ncomp; ++i) {\n    jpg->max_h_samp_factor = std::max(jpg->max_h_samp_factor,\n                                      components_[i].factor_x());\n    jpg->max_v_samp_factor = std::max(jpg->max_h_samp_factor,\n                                      components_[i].factor_y());\n    jpg->MCU_cols = std::min(jpg->MCU_cols, components_[i].width_in_blocks());\n    jpg->MCU_rows = std::min(jpg->MCU_rows, components_[i].height_in_blocks());\n  }\n  jpg->components.resize(ncomp);\n  int q[3][kDCTBlockSize];\n  for (int c = 0; c < 3; ++c) {\n    memcpy(&q[c][0], components_[c].quant(), kDCTBlockSize * sizeof(q[0][0]));\n  }\n  for (int c = 0; c < ncomp; ++c) {\n    JPEGComponent* comp = &jpg->components[c];\n    assert(jpg->max_h_samp_factor % components_[c].factor_x() == 0);\n    assert(jpg->max_v_samp_factor % components_[c].factor_y() == 0);\n    comp->id = c;\n    comp->h_samp_factor = jpg->max_h_samp_factor / components_[c].factor_x();\n    comp->v_samp_factor = jpg->max_v_samp_factor / components_[c].factor_y();\n    comp->width_in_blocks = jpg->MCU_cols * comp->h_samp_factor;\n    comp->height_in_blocks = jpg->MCU_rows * comp->v_samp_factor;\n    comp->num_blocks = comp->width_in_blocks * comp->height_in_blocks;\n    comp->coeffs.resize(kDCTBlockSize * comp->num_blocks);\n\n    int last_dc = 0;\n    const coeff_t* src_coeffs = components_[c].coeffs();\n    coeff_t* dest_coeffs = &comp->coeffs[0];\n    for (int block_y = 0; block_y < comp->height_in_blocks; ++block_y) {\n      for (int block_x = 0; block_x < comp->width_in_blocks; ++block_x) {\n        if (block_y >= components_[c].height_in_blocks() ||\n            block_x >= components_[c].width_in_blocks()) {\n          dest_coeffs[0] = last_dc;\n          for (int k = 1; k < kDCTBlockSize; ++k) {\n            dest_coeffs[k] = 0;\n          }\n        } else {\n          for (int k = 0; k < kDCTBlockSize; ++k) {\n            const int quant = q[c][k];\n            int coeff = src_coeffs[k];\n            assert(coeff % quant == 0);\n            dest_coeffs[k] = coeff / quant;\n          }\n          src_coeffs += kDCTBlockSize;\n        }\n        last_dc = dest_coeffs[0];\n        dest_coeffs += kDCTBlockSize;\n      }\n    }\n  }\n  SaveQuantTables(q, jpg);\n}\n\nstd::vector<uint8_t> OutputImage::ToSRGB(int xmin, int ymin,\n                                         int xsize, int ysize) const {\n  std::vector<uint8_t> rgb(xsize * ysize * 3);\n  for (int c = 0; c < 3; ++c) {\n    components_[c].ToPixels(xmin, ymin, xsize, ysize, &rgb[c], 3);\n  }\n  for (size_t p = 0; p < rgb.size(); p += 3) {\n    ColorTransformYCbCrToRGB(&rgb[p]);\n  }\n  return rgb;\n}\n\nstd::vector<uint8_t> OutputImage::ToSRGB() const {\n  return ToSRGB(0, 0, width_, height_);\n}\n\nvoid OutputImage::ToLinearRGB(int xmin, int ymin, int xsize, int ysize,\n                              std::vector<std::vector<float> >* rgb) const {\n  const double* lut = Srgb8ToLinearTable();\n  std::vector<uint8_t> rgb_pixels = ToSRGB(xmin, ymin, xsize, ysize);\n  for (int p = 0; p < xsize * ysize; ++p) {\n    for (int i = 0; i < 3; ++i) {\n      (*rgb)[i][p] = static_cast<float>(lut[rgb_pixels[3 * p + i]]);\n    }\n  }\n}\n\nvoid OutputImage::ToLinearRGB(std::vector<std::vector<float> >* rgb) const {\n  ToLinearRGB(0, 0, width_, height_, rgb);\n}\n\nstd::string OutputImage::FrameTypeStr() const {\n  char buf[128];\n  int len = snprintf(buf, sizeof(buf), \"f%d%d%d%d%d%d\",\n                     component(0).factor_x(), component(0).factor_y(),\n                     component(1).factor_x(), component(1).factor_y(),\n                     component(2).factor_x(), component(2).factor_y());\n  return std::string(buf, len);\n}\n\n}  // namespace guetzli\n"
  },
  {
    "path": "guetzli/output_image.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef GUETZLI_OUTPUT_IMAGE_H_\n#define GUETZLI_OUTPUT_IMAGE_H_\n\n#include <stdint.h>\n#include <vector>\n\n#include \"guetzli/jpeg_data.h\"\n\nnamespace guetzli {\n\nclass OutputImageComponent {\n public:\n  OutputImageComponent(int w, int h);\n\n  void Reset(int factor_x, int factor_y);\n\n  int width() const { return width_; }\n  int height() const { return height_; }\n  int factor_x() const { return factor_x_; }\n  int factor_y() const { return factor_y_; }\n  int width_in_blocks() const { return width_in_blocks_; }\n  int height_in_blocks() const { return height_in_blocks_; }\n  const coeff_t* coeffs() const { return &coeffs_[0]; }\n  const int* quant() const { return &quant_[0]; }\n  bool IsAllZero() const;\n\n  // Fills in block[] with the 8x8 coefficient block with block coordinates\n  // (block_x, block_y).\n  // NOTE: If the component is 2x2 subsampled, this corresponds to the 16x16\n  // pixel area with upper-left corner (16 * block_x, 16 * block_y).\n  void GetCoeffBlock(int block_x, int block_y,\n                     coeff_t block[kDCTBlockSize]) const;\n\n  // Fills in out[] array with the 8-bit pixel view of this component cropped\n  // to the specified window. The window's upper-left corner, (xmin, ymin) must\n  // be within the image, but the window may extend past the image. In that\n  // case the edge pixels are duplicated.\n  void ToPixels(int xmin, int ymin, int xsize, int ysize,\n                uint8_t* out, int stride) const;\n\n  // Fills in out[] array with the floating-point precision pixel view of the\n  // component.\n  // REQUIRES: factor_x() == 1 and factor_y() == 1.\n  void ToFloatPixels(float* out, int stride) const;\n\n  // Sets the 8x8 coefficient block with block coordinates (block_x, block_y)\n  // to block[].\n  // NOTE: If the component is 2x2 subsampled, this corresponds to the 16x16\n  // pixel area with upper-left corner (16 * block_x, 16 * block_y).\n  // REQUIRES: block[k] % quant()[k] == 0 for each coefficient index k.\n  void SetCoeffBlock(int block_x, int block_y,\n                     const coeff_t block[kDCTBlockSize]);\n\n  // Requires that comp is not downsampled.\n  void CopyFromJpegComponent(const JPEGComponent& comp,\n                             int factor_x, int factor_y,\n                             const int* quant);\n\n  void ApplyGlobalQuantization(const int q[kDCTBlockSize]);\n\n private:\n  void UpdatePixelsForBlock(int block_x, int block_y,\n                            const uint8_t idct[kDCTBlockSize]);\n\n  const int width_;\n  const int height_;\n  int factor_x_;\n  int factor_y_;\n  int width_in_blocks_;\n  int height_in_blocks_;\n  int num_blocks_;\n  std::vector<coeff_t> coeffs_;\n  std::vector<uint16_t> pixels_;\n  // Same as last argument of ApplyGlobalQuantization() (default is all 1s).\n  int quant_[kDCTBlockSize];\n};\n\nclass OutputImage {\n public:\n  OutputImage(int w, int h);\n\n  int width() const { return width_; }\n  int height() const { return height_; }\n\n  OutputImageComponent& component(int c) { return components_[c]; }\n  const OutputImageComponent& component(int c) const { return components_[c]; }\n\n  // Requires that jpg is in YUV444 format.\n  void CopyFromJpegData(const JPEGData& jpg);\n\n  void ApplyGlobalQuantization(const int q[3][kDCTBlockSize]);\n\n  // If sharpen or blur are enabled, preprocesses image before downsampling U or\n  // V to improve butteraugli score and/or reduce file size.\n  // u_sharpen: sharpen the u channel in red areas to improve score (not as\n  // effective as v_sharpen, blue is not so important)\n  // u_blur: blur the u channel in some areas to reduce file size\n  // v_sharpen: sharpen the v channel in red areas to improve score\n  // v_blur: blur the v channel in some areas to reduce file size\n  struct DownsampleConfig {\n    // Default is YUV420.\n    DownsampleConfig() : u_factor_x(2), u_factor_y(2),\n                         v_factor_x(2), v_factor_y(2),\n                         u_sharpen(true), u_blur(true),\n                         v_sharpen(true), v_blur(true),\n                         use_silver_screen(false) {}\n    int u_factor_x;\n    int u_factor_y;\n    int v_factor_x;\n    int v_factor_y;\n    bool u_sharpen;\n    bool u_blur;\n    bool v_sharpen;\n    bool v_blur;\n    bool use_silver_screen;\n  };\n\n  void Downsample(const DownsampleConfig& cfg);\n\n  void SaveToJpegData(JPEGData* jpg) const;\n\n  std::vector<uint8_t> ToSRGB() const;\n\n  std::vector<uint8_t> ToSRGB(int xmin, int ymin, int xsize, int ysize) const;\n\n  void ToLinearRGB(std::vector<std::vector<float> >* rgb) const;\n\n  void ToLinearRGB(int xmin, int ymin, int xsize, int ysize,\n                   std::vector<std::vector<float> >* rgb) const;\n\n  std::string FrameTypeStr() const;\n\n private:\n  const int width_;\n  const int height_;\n  std::vector<OutputImageComponent> components_;\n};\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_OUTPUT_IMAGE_H_\n"
  },
  {
    "path": "guetzli/preprocess_downsample.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"guetzli/preprocess_downsample.h\"\n\n#include <algorithm>\n#include <assert.h>\n#include <string.h>\n#include <cmath>\n\nusing std::size_t;\n\nnamespace {\n\n// convolve with size*size kernel\nstd::vector<float> Convolve2D(const std::vector<float>& image, int w, int h,\n                              const double* kernel, int size) {\n  auto result = image;\n  int size2 = size / 2;\n  for (size_t i = 0; i < image.size(); i++) {\n    int x = i % w;\n    int y = i / w;\n    // Avoid non-normalized results at boundary by skipping edges.\n    if (x < size2 || x + size - size2 - 1 >= w\n        || y < size2 || y + size - size2 - 1 >= h) {\n      continue;\n    }\n    float v = 0;\n    for (int j = 0; j < size * size; j++) {\n      int x2 = x + j % size - size2;\n      int y2 = y + j / size - size2;\n      v += static_cast<float>(kernel[j]) * image[y2 * w + x2];\n    }\n    result[i] = v;\n  }\n  return result;\n}\n\n// convolve horizontally and vertically with 1D kernel\nstd::vector<float> Convolve2X(const std::vector<float>& image, int w, int h,\n                              const double* kernel, int size, double mul) {\n  auto temp = image;\n  int size2 = size / 2;\n  for (size_t i = 0; i < image.size(); i++) {\n    int x = i % w;\n    int y = i / w;\n    // Avoid non-normalized results at boundary by skipping edges.\n    if (x < size2 || x + size - size2 - 1 >= w) continue;\n    float v = 0;\n    for (int j = 0; j < size; j++) {\n      int x2 = x + j - size2;\n      v += static_cast<float>(kernel[j]) * image[y * w + x2];\n    }\n    temp[i] = v * static_cast<float>(mul);\n  }\n  auto result = temp;\n  for (size_t i = 0; i < temp.size(); i++) {\n    int x = i % w;\n    int y = i / w;\n    // Avoid non-normalized results at boundary by skipping edges.\n    if (y < size2 || y + size - size2 - 1 >= h) continue;\n    float v = 0;\n    for (int j = 0; j < size; j++) {\n      int y2 = y + j - size2;\n      v += static_cast<float>(kernel[j]) * temp[y2 * w + x];\n    }\n    result[i] = v * static_cast<float>(mul);\n  }\n  return result;\n}\n\ndouble Normal(double x, double sigma) {\n  static const double kInvSqrt2Pi = 0.3989422804014327;\n  return std::exp(-x * x / (2 * sigma * sigma)) * kInvSqrt2Pi / sigma;\n}\n\nstd::vector<float> Sharpen(const std::vector<float>& image, int w, int h,\n                           float sigma, float amount) {\n  // This is only made for small sigma, e.g. 1.3.\n  std::vector<double> kernel(5);\n  for (size_t i = 0; i < kernel.size(); i++) {\n    kernel[i] = Normal(1.0 * i - kernel.size() / 2, sigma);\n  }\n\n  double sum = 0;\n  for (size_t i = 0; i < kernel.size(); i++) sum += kernel[i];\n  const double mul = 1.0 / sum;\n\n  std::vector<float> result =\n      Convolve2X(image, w, h, kernel.data(), kernel.size(), mul);\n  for (size_t i = 0; i < image.size(); i++) {\n    result[i] = image[i] + (image[i] - result[i]) * amount;\n  }\n  return result;\n}\n\nvoid Erode(int w, int h, std::vector<bool>* image) {\n  std::vector<bool> temp = *image;\n  for (int y = 1; y + 1 < h; y++) {\n    for (int x = 1; x + 1 < w; x++) {\n      size_t index = y * w + x;\n      if (!(temp[index] && temp[index - 1] && temp[index + 1]\n          && temp[index - w] && temp[index + w])) {\n        (*image)[index] = 0;\n      }\n    }\n  }\n}\n\nvoid Dilate(int w, int h, std::vector<bool>* image) {\n  std::vector<bool> temp = *image;\n  for (int y = 1; y + 1 < h; y++) {\n    for (int x = 1; x + 1 < w; x++) {\n      size_t index = y * w + x;\n      if (temp[index] || temp[index - 1] || temp[index + 1]\n          || temp[index - w] || temp[index + w]) {\n        (*image)[index] = 1;\n      }\n    }\n  }\n}\n\nstd::vector<float> Blur(const std::vector<float>& image, int w, int h) {\n    // This is only made for small sigma, e.g. 1.3.\n    static const double kSigma = 1.3;\n    std::vector<double> kernel(5);\n    for (size_t i = 0; i < kernel.size(); i++) {\n      kernel[i] = Normal(1.0 * i - kernel.size() / 2, kSigma);\n    }\n\n    double sum = 0;\n    for (size_t i = 0; i < kernel.size(); i++) sum += kernel[i];\n    const double mul = 1.0 / sum;\n\n    return Convolve2X(image, w, h, kernel.data(), kernel.size(), mul);\n}\n\n}  // namespace\n\nnamespace guetzli {\n\n// Do the sharpening to the v channel, but only in areas where it will help\n// channel should be 2 for v sharpening, or 1 for less effective u sharpening\nstd::vector<std::vector<float>> PreProcessChannel(\n    int w, int h, int channel, float sigma, float amount, bool blur,\n    bool sharpen, const std::vector<std::vector<float>>& image) {\n  if (!blur && !sharpen) return image;\n\n  // Bring in range 0.0-1.0 for Y, -0.5 - 0.5 for U and V\n  auto yuv = image;\n  for (size_t i = 0; i < yuv[0].size(); i++) {\n    yuv[0][i] /= 255.0;\n    yuv[1][i] = yuv[1][i] / 255.0f - 0.5f;\n    yuv[2][i] = yuv[2][i] / 255.0f - 0.5f;\n  }\n\n  // Map of areas where the image is not too bright to apply the effect.\n  std::vector<bool> darkmap(image[0].size(), false);\n  for (int y = 0; y < h; y++) {\n    for (int x = 0; x < w; x++) {\n      size_t index = y * w + x;\n      float y = yuv[0][index];\n      float u = yuv[1][index];\n      float v = yuv[2][index];\n\n      float r = y + 1.402f * v;\n      float g = y - 0.34414f * u - 0.71414f * v;\n      float b = y + 1.772f * u;\n\n      // Parameters tuned to avoid sharpening in too bright areas, where the\n      // effect makes it worse instead of better.\n      if (channel == 2 && g < 0.85 && b < 0.85 && r < 0.9) {\n        darkmap[index] = true;\n      }\n      if (channel == 1 && r < 0.85 && g < 0.85 && b < 0.9) {\n        darkmap[index] = true;\n      }\n    }\n  }\n\n  Erode(w, h, &darkmap);\n  Erode(w, h, &darkmap);\n  Erode(w, h, &darkmap);\n\n  // Map of areas where the image is red enough (blue in case of u channel).\n  std::vector<bool> redmap(image[0].size(), false);\n  for (int y = 0; y < h; y++) {\n    for (int x = 0; x < w; x++) {\n      size_t index = y * w + x;\n      float u = yuv[1][index];\n      float v = yuv[2][index];\n\n      // Parameters tuned to allow only colors on which sharpening is useful.\n      if (channel == 2 && 2.116 * v > -0.34414 * u + 0.2\n          && 1.402 * v > 1.772 * u + 0.2) {\n        redmap[index] = true;\n      }\n      if (channel == 1 && v < 1.263 * u - 0.1 && u > -0.33741 * v) {\n        redmap[index] = true;\n      }\n    }\n  }\n\n  Dilate(w, h, &redmap);\n  Dilate(w, h, &redmap);\n  Dilate(w, h, &redmap);\n\n  // Map of areas where to allow sharpening by combining red and dark areas\n  std::vector<bool> sharpenmap(image[0].size(), 0);\n  for (int y = 0; y < h; y++) {\n    for (int x = 0; x < w; x++) {\n      size_t index = y * w + x;\n      sharpenmap[index] = redmap[index] && darkmap[index];\n    }\n  }\n\n  // Threshold for where considered an edge.\n  const double threshold = (channel == 2 ? 0.02 : 1.0) * 127.5;\n\n  static const double kEdgeMatrix[9] = {\n    0, -1, 0,\n    -1, 4, -1,\n    0, -1, 0\n  };\n\n  // Map of areas where to allow blurring, only where it is not too sharp\n  std::vector<bool> blurmap(image[0].size(), false);\n  std::vector<float> edge = Convolve2D(yuv[channel], w, h, kEdgeMatrix, 3);\n  for (int y = 0; y < h; y++) {\n    for (int x = 0; x < w; x++) {\n      size_t index = y * w + x;\n      float u = yuv[1][index];\n      float v = yuv[2][index];\n      if (sharpenmap[index]) continue;\n      if (!darkmap[index]) continue;\n      if (fabs(edge[index]) < threshold && v < -0.162 * u) {\n        blurmap[index] = true;\n      }\n    }\n  }\n  Erode(w, h, &blurmap);\n  Erode(w, h, &blurmap);\n\n  // Choose sharpened, blurred or original per pixel\n  std::vector<float> sharpened = Sharpen(yuv[channel], w, h, sigma, amount);\n  std::vector<float> blurred = Blur(yuv[channel], w, h);\n  for (int y = 0; y < h; y++) {\n    for (int x = 0; x < w; x++) {\n      size_t index = y * w + x;\n\n      if (sharpenmap[index]) {\n        if (sharpen) yuv[channel][index] = sharpened[index];\n      } else if (blurmap[index]) {\n        if (blur) yuv[channel][index] = blurred[index];\n      }\n    }\n  }\n\n  // Bring back to range 0-255\n  for (size_t i = 0; i < yuv[0].size(); i++) {\n    yuv[0][i] *= 255.0;\n    yuv[1][i] = (yuv[1][i] + 0.5f) * 255.0f;\n    yuv[2][i] = (yuv[2][i] + 0.5f) * 255.0f;\n  }\n  return yuv;\n}\n\nnamespace {\n\ninline float Clip(float val) {\n  return std::max(0.0f, std::min(255.0f, val));\n}\n\ninline float RGBToY(float r, float g, float b) {\n  return 0.299f * r + 0.587f * g + 0.114f * b;\n}\n\ninline float RGBToU(float r, float g, float b) {\n  return -0.16874f * r - 0.33126f * g + 0.5f * b + 128.0f;\n}\n\ninline float RGBToV(float r, float g, float b) {\n  return 0.5f * r - 0.41869f * g - 0.08131f * b + 128.0f;\n}\n\ninline float YUVToR(float y, float u, float v) {\n  return y + 1.402f * (v - 128.0f);\n}\n\ninline float YUVToG(float y, float u, float v) {\n  return y - 0.344136f * (u - 128.0f) - 0.714136f * (v - 128.0f);\n}\n\ninline float YUVToB(float y, float u, float v) {\n  return y + 1.772f * (u - 128.0f);\n}\n\n// TODO(user) Use SRGB->linear conversion and a lookup-table.\ninline float GammaToLinear(float x) {\n  return static_cast<float>(std::pow(x / 255.0f, 2.2));\n}\n\n// TODO(user) Use linear->SRGB conversion and a lookup-table.\ninline float LinearToGamma(float x) {\n  return 255.0 * std::pow(x, 1.0 / 2.2);\n}\n\nstd::vector<float> LinearlyAveragedLuma(const std::vector<float>& rgb) {\n  assert(rgb.size() % 3 == 0);\n  std::vector<float> y(rgb.size() / 3);\n  for (size_t i = 0, p = 0; p < rgb.size(); ++i, p += 3) {\n    y[i] = LinearToGamma(RGBToY(GammaToLinear(rgb[p + 0]),\n                                GammaToLinear(rgb[p + 1]),\n                                GammaToLinear(rgb[p + 2])));\n  }\n  return y;\n}\n\nstd::vector<float> LinearlyDownsample2x2(const std::vector<float>& rgb_in,\n                                         const int width, const int height) {\n  assert(rgb_in.size() == 3 * width * height);\n  int w = (width + 1) / 2;\n  int h = (height + 1) / 2;\n  std::vector<float> rgb_out(3 * w * h);\n  for (int y = 0, p = 0; y < h; ++y) {\n    for (int x = 0; x < w; ++x) {\n      for (int i = 0; i < 3; ++i, ++p) {\n        rgb_out[p] = 0.0;\n        for (int iy = 0; iy < 2; ++iy) {\n          for (int ix = 0; ix < 2; ++ix) {\n            int yy = std::min(height - 1, 2 * y + iy);\n            int xx = std::min(width - 1, 2 * x + ix);\n            rgb_out[p] += GammaToLinear(rgb_in[3 * (yy * width + xx) + i]);\n          }\n        }\n        rgb_out[p] = LinearToGamma(0.25f * rgb_out[p]);\n      }\n    }\n  }\n  return rgb_out;\n}\n\nstd::vector<std::vector<float> > RGBToYUV(const std::vector<float>& rgb) {\n  std::vector<std::vector<float> > yuv(3, std::vector<float>(rgb.size() / 3));\n  for (size_t i = 0, p = 0; p < rgb.size(); ++i, p += 3) {\n    const float r = rgb[p + 0];\n    const float g = rgb[p + 1];\n    const float b = rgb[p + 2];\n    yuv[0][i] = RGBToY(r, g, b);\n    yuv[1][i] = RGBToU(r, g, b);\n    yuv[2][i] = RGBToV(r, g, b);\n  }\n  return yuv;\n}\n\nstd::vector<float> YUVToRGB(const std::vector<std::vector<float> >& yuv) {\n  std::vector<float> rgb(3 * yuv[0].size());\n  for (size_t i = 0, p = 0; p < rgb.size(); ++i, p += 3) {\n    const float y = yuv[0][i];\n    const float u = yuv[1][i];\n    const float v = yuv[2][i];\n    rgb[p + 0] = Clip(YUVToR(y, u, v));\n    rgb[p + 1] = Clip(YUVToG(y, u, v));\n    rgb[p + 2] = Clip(YUVToB(y, u, v));\n  }\n  return rgb;\n}\n\n// Upsamples img_in with a box-filter, and returns an image with output\n// dimensions width x height.\nstd::vector<float> Upsample2x2(const std::vector<float>& img_in,\n                               const int width, const int height) {\n  int w = (width + 1) / 2;\n  int h = (height + 1) / 2;\n  assert(img_in.size() == w * h);\n  std::vector<float> img_out(width * height);\n  for (int y = 0, p = 0; y < h; ++y) {\n    for (int x = 0; x < w; ++x, ++p) {\n      for (int iy = 0; iy < 2; ++iy) {\n        for (int ix = 0; ix < 2; ++ix) {\n          int yy = std::min(height - 1, 2 * y + iy);\n          int xx = std::min(width - 1, 2 * x + ix);\n          img_out[yy * width + xx] = img_in[p];\n        }\n      }\n    }\n  }\n  return img_out;\n}\n\n// Apply the \"fancy upsample\" filter used by libjpeg.\nstd::vector<float> Blur(const std::vector<float>& img,\n                        const int width, const int height) {\n  std::vector<float> img_out(width * height);\n  for (int y0 = 0; y0 < height; y0 += 2) {\n    for (int x0 = 0; x0 < width; x0 += 2) {\n      for (int iy = 0; iy < 2 && y0 + iy < height; ++iy) {\n        for (int ix = 0; ix < 2 && x0 + ix < width; ++ix) {\n          int dy = 4 * iy - 2;\n          int dx = 4 * ix - 2;\n          int x1 = std::min(width - 1, std::max(0, x0 + dx));\n          int y1 = std::min(height - 1, std::max(0, y0 + dy));\n          img_out[(y0 + iy) * width + x0 + ix] =\n              (9.0f * img[y0 * width + x0] +\n               3.0f * img[y0 * width + x1] +\n               3.0f * img[y1 * width + x0] +\n               1.0f * img[y1 * width + x1]) / 16.0f;\n        }\n      }\n    }\n  }\n  return img_out;\n}\n\nstd::vector<float> YUV420ToRGB(const std::vector<std::vector<float> >& yuv420,\n                               const int width, const int height) {\n  std::vector<std::vector<float> > yuv;\n  yuv.push_back(yuv420[0]);\n  std::vector<float> u = Upsample2x2(yuv420[1], width, height);\n  std::vector<float> v = Upsample2x2(yuv420[2], width, height);\n  yuv.push_back(Blur(u, width, height));\n  yuv.push_back(Blur(v, width, height));\n  return YUVToRGB(yuv);\n}\n\nvoid UpdateGuess(const std::vector<float>& target,\n                 const std::vector<float>& reconstructed,\n                 std::vector<float>* guess) {\n  assert(reconstructed.size() == guess->size());\n  assert(target.size() == guess->size());\n  for (size_t i = 0; i < guess->size(); ++i) {\n    // TODO(user): Evaluate using a decaying constant here.\n    (*guess)[i] = Clip((*guess)[i] - (reconstructed[i] - target[i]));\n  }\n}\n\n}  // namespace\n\nstd::vector<std::vector<float> > RGBToYUV420(\n    const std::vector<uint8_t>& rgb_in, const int width, const int height) {\n  std::vector<float> rgbf(rgb_in.size());\n  for (size_t i = 0; i < rgb_in.size(); ++i) {\n    rgbf[i] = static_cast<float>(rgb_in[i]);\n  }\n  std::vector<float> y_target = LinearlyAveragedLuma(rgbf);\n  std::vector<std::vector<float> > yuv_target =\n      RGBToYUV(LinearlyDownsample2x2(rgbf, width, height));\n  std::vector<std::vector<float> > yuv_guess = yuv_target;\n  yuv_guess[0] = Upsample2x2(yuv_guess[0], width, height);\n  // TODO(user): Stop early if the error is small enough.\n  for (int iter = 0; iter < 20; ++iter) {\n    std::vector<float> rgb_rec = YUV420ToRGB(yuv_guess, width, height);\n    std::vector<float> y_rec = LinearlyAveragedLuma(rgb_rec);\n    std::vector<std::vector<float> > yuv_rec =\n        RGBToYUV(LinearlyDownsample2x2(rgb_rec, width, height));\n    UpdateGuess(y_target, y_rec, &yuv_guess[0]);\n    UpdateGuess(yuv_target[1], yuv_rec[1], &yuv_guess[1]);\n    UpdateGuess(yuv_target[2], yuv_rec[2], &yuv_guess[2]);\n  }\n  yuv_guess[1] = Upsample2x2(yuv_guess[1], width, height);\n  yuv_guess[2] = Upsample2x2(yuv_guess[2], width, height);\n  return yuv_guess;\n}\n\n}  // namespace guetzli\n"
  },
  {
    "path": "guetzli/preprocess_downsample.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Preprocesses U and V channel for better results after downsampling.\n\n#ifndef GUETZLI_PREPROCESS_DOWNSAMPLE_H_\n#define GUETZLI_PREPROCESS_DOWNSAMPLE_H_\n\n#include <stdint.h>\n#include <vector>\n\nnamespace guetzli {\n\n// Preprocesses the u (1) or v (2) channel of the given YUV image (range 0-255).\nstd::vector<std::vector<float>> PreProcessChannel(\n    int w, int h, int channel, float sigma, float amount, bool blur,\n    bool sharpen, const std::vector<std::vector<float>>& image);\n\n// Gamma-compensated chroma subsampling.\n// Returns Y, U, V image planes, each with width x height dimensions, but the\n// U and V planes are composed of 2x2 blocks with the same values.\nstd::vector<std::vector<float> > RGBToYUV420(\n    const std::vector<uint8_t>& rgb_in, const int width, const int height);\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_PREPROCESS_DOWNSAMPLE_H_\n"
  },
  {
    "path": "guetzli/processor.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"guetzli/processor.h\"\n\n#include <algorithm>\n#include <set>\n#include <string.h>\n#include <vector>\n\n#include \"guetzli/butteraugli_comparator.h\"\n#include \"guetzli/comparator.h\"\n#include \"guetzli/debug_print.h\"\n#include \"guetzli/fast_log.h\"\n#include \"guetzli/jpeg_data_decoder.h\"\n#include \"guetzli/jpeg_data_encoder.h\"\n#include \"guetzli/jpeg_data_reader.h\"\n#include \"guetzli/jpeg_data_writer.h\"\n#include \"guetzli/output_image.h\"\n#include \"guetzli/quantize.h\"\n\nnamespace guetzli {\n\nnamespace {\n\nstatic const size_t kBlockSize = 3 * kDCTBlockSize;\n\nstruct CoeffData {\n  int idx;\n  float block_err;\n};\nstruct QuantData {\n  int q[3][kDCTBlockSize];\n  size_t jpg_size;\n  bool dist_ok;\n};\nclass Processor {\n public:\n  bool ProcessJpegData(const Params& params, const JPEGData& jpg_in,\n                       Comparator* comparator, GuetzliOutput* out,\n                       ProcessStats* stats);\n\n private:\n  void SelectFrequencyMasking(const JPEGData& jpg, OutputImage* img,\n                              const uint8_t comp_mask, const double target_mul,\n                              bool stop_early);\n  void ComputeBlockZeroingOrder(\n      const coeff_t block[kBlockSize], const coeff_t orig_block[kBlockSize],\n      const int block_x, const int block_y, const int factor_x,\n      const int factor_y, const uint8_t comp_mask, OutputImage* img,\n      std::vector<CoeffData>* output_order);\n  bool SelectQuantMatrix(const JPEGData& jpg_in, const bool downsample,\n                         int best_q[3][kDCTBlockSize],\n                         OutputImage* img);\n  QuantData TryQuantMatrix(const JPEGData& jpg_in,\n                           const float target_mul,\n                           int q[3][kDCTBlockSize],\n                           OutputImage* img);\n  void MaybeOutput(const std::string& encoded_jpg);\n  void DownsampleImage(OutputImage* img);\n  void OutputJpeg(const JPEGData& in, std::string* out);\n\n  Params params_;\n  Comparator* comparator_;\n  GuetzliOutput* final_output_;\n  ProcessStats* stats_;\n};\n\nvoid RemoveOriginalQuantization(JPEGData* jpg, int q_in[3][kDCTBlockSize]) {\n  for (int i = 0; i < 3; ++i) {\n    JPEGComponent& c = jpg->components[i];\n    const int* q = &jpg->quant[c.quant_idx].values[0];\n    memcpy(&q_in[i][0], q, kDCTBlockSize * sizeof(q[0]));\n    for (size_t j = 0; j < c.coeffs.size(); ++j) {\n      c.coeffs[j] *= q[j % kDCTBlockSize];\n    }\n  }\n  int q[3][kDCTBlockSize];\n  for (int i = 0; i < 3; ++i)\n    for (int j = 0; j < kDCTBlockSize; ++j) q[i][j] = 1;\n  SaveQuantTables(q, jpg);\n}\n\nvoid Processor::DownsampleImage(OutputImage* img) {\n  if (img->component(1).factor_x() > 1 || img->component(1).factor_y() > 1) {\n    return;\n  }\n  OutputImage::DownsampleConfig cfg;\n  cfg.use_silver_screen = params_.use_silver_screen;\n  img->Downsample(cfg);\n}\n\nbool CheckJpegSanity(const JPEGData& jpg) {\n  const int kMaxComponent = 1 << 12;\n  for (const JPEGComponent& comp : jpg.components) {\n    const JPEGQuantTable& quant_table = jpg.quant[comp.quant_idx];\n    for (int i = 0; i < comp.coeffs.size(); i++) {\n      coeff_t coeff = comp.coeffs[i];\n      int quant = quant_table.values[i % kDCTBlockSize];\n      if (std::abs(static_cast<int64_t>(coeff) * quant) > kMaxComponent) {\n        return false;\n      }\n    }\n  }\n  return true;\n}\n\n}  // namespace\n\nint GuetzliStringOut(void* data, const uint8_t* buf, size_t count) {\n  std::string* sink =\n      reinterpret_cast<std::string*>(data);\n  sink->append(reinterpret_cast<const char*>(buf), count);\n  return count;\n}\n\nvoid Processor::OutputJpeg(const JPEGData& jpg,\n                           std::string* out) {\n  out->clear();\n  JPEGOutput output(GuetzliStringOut, out);\n  if (!WriteJpeg(jpg, params_.clear_metadata, output)) {\n    assert(0);\n  }\n}\n\nvoid Processor::MaybeOutput(const std::string& encoded_jpg) {\n  double score = comparator_->ScoreOutputSize(encoded_jpg.size());\n  GUETZLI_LOG(stats_, \" Score[%.4f]\", score);\n  if (score < final_output_->score || final_output_->score < 0) {\n    final_output_->jpeg_data = encoded_jpg;\n    final_output_->score = score;\n    GUETZLI_LOG(stats_, \" (*)\");\n  }\n  GUETZLI_LOG(stats_, \"\\n\");\n}\n\nbool CompareQuantData(const QuantData& a, const QuantData& b) {\n  if (a.dist_ok && !b.dist_ok) return true;\n  if (!a.dist_ok && b.dist_ok) return false;\n  return a.jpg_size < b.jpg_size;\n}\n\n// Compares a[0..kBlockSize) and b[0..kBlockSize) vectors, and returns\n//   0 : if they are equal\n//  -1 : if a is everywhere <= than b and in at least one coordinate <\n//   1 : if a is everywhere >= than b and in at least one coordinate >\n//   2 : if a and b are uncomparable (some coordinate smaller and some greater)\nint CompareQuantMatrices(const int* a, const int* b) {\n  int i = 0;\n  while (i < kBlockSize && a[i] == b[i]) ++i;\n  if (i == kBlockSize) {\n    return 0;\n  }\n  if (a[i] < b[i]) {\n    for (++i; i < kBlockSize; ++i) {\n      if (a[i] > b[i]) return 2;\n    }\n    return -1;\n  } else {\n    for (++i; i < kBlockSize; ++i) {\n      if (a[i] < b[i]) return 2;\n    }\n    return 1;\n  }\n}\n\ndouble ContrastSensitivity(int k) {\n  return 1.0 / (1.0 + kJPEGZigZagOrder[k] / 2.0);\n}\n\ndouble QuantMatrixHeuristicScore(const int q[3][kDCTBlockSize]) {\n  double score = 0.0;\n  for (int c = 0; c < 3; ++c) {\n    for (int k = 0; k < kDCTBlockSize; ++k) {\n      score += 0.5 * (q[c][k] - 1.0) * ContrastSensitivity(k);\n    }\n  }\n  return score;\n}\n\nclass QuantMatrixGenerator {\n public:\n  QuantMatrixGenerator(bool downsample, ProcessStats* stats)\n      : downsample_(downsample), hscore_a_(-1.0), hscore_b_(-1.0),\n        total_csf_(0.0), stats_(stats) {\n    for (int k = 0; k < kDCTBlockSize; ++k) {\n      total_csf_ += 3.0 * ContrastSensitivity(k);\n    }\n  }\n\n  bool GetNext(int q[3][kDCTBlockSize]) {\n    // This loop should terminate by return. This 1000 iteration limit is just a\n    // precaution.\n    for (int iter = 0; iter < 1000; iter++) {\n      double hscore;\n      if (hscore_b_ == -1.0) {\n        if (hscore_a_ == -1.0) {\n          hscore = downsample_ ? 0.0 : total_csf_;\n        } else {\n          if (hscore_a_ < 5.0 * total_csf_) {\n            hscore = hscore_a_ + total_csf_;\n          } else {\n            hscore = 2 * (hscore_a_ + total_csf_);\n          }\n        }\n        if (hscore > 100 * total_csf_) {\n          // We could not find a quantization matrix that creates enough\n          // butteraugli error. This can happen if all dct coefficients are\n          // close to zero in the original image.\n          return false;\n        }\n      } else if (hscore_b_ == 0.0) {\n        return false;\n      } else if (hscore_a_ == -1.0) {\n        hscore = 0.0;\n      } else {\n        int lower_q[3][kDCTBlockSize];\n        int upper_q[3][kDCTBlockSize];\n        constexpr double kEps = 0.05;\n        GetQuantMatrixWithHeuristicScore(\n            (1 - kEps) * hscore_a_ + kEps * 0.5 * (hscore_a_ + hscore_b_),\n            lower_q);\n        GetQuantMatrixWithHeuristicScore(\n            (1 - kEps) * hscore_b_ + kEps * 0.5 * (hscore_a_ + hscore_b_),\n            upper_q);\n        if (CompareQuantMatrices(&lower_q[0][0], &upper_q[0][0]) == 0)\n          return false;\n        hscore = (hscore_a_ + hscore_b_) * 0.5;\n      }\n      GetQuantMatrixWithHeuristicScore(hscore, q);\n      bool retry = false;\n      for (size_t i = 0; i < quants_.size(); ++i) {\n        if (CompareQuantMatrices(&q[0][0], &quants_[i].q[0][0]) == 0) {\n          if (quants_[i].dist_ok) {\n            hscore_a_ = hscore;\n          } else {\n            hscore_b_ = hscore;\n          }\n          retry = true;\n          break;\n        }\n      }\n      if (!retry) return true;\n    }\n    return false;\n  }\n\n  void Add(const QuantData& data) {\n    quants_.push_back(data);\n    double hscore = QuantMatrixHeuristicScore(data.q);\n    if (data.dist_ok) {\n      hscore_a_ = std::max(hscore_a_, hscore);\n    } else {\n      hscore_b_ = hscore_b_ == -1.0 ? hscore : std::min(hscore_b_, hscore);\n    }\n  }\n\n private:\n  void GetQuantMatrixWithHeuristicScore(double score,\n                                        int q[3][kDCTBlockSize]) const {\n    int level = static_cast<int>(score / total_csf_);\n    score -= level * total_csf_;\n    for (int k = kDCTBlockSize - 1; k >= 0; --k) {\n      for (int c = 0; c < 3; ++c) {\n        q[c][kJPEGNaturalOrder[k]] = 2 * level + (score > 0.0 ? 3 : 1);\n      }\n      score -= 3.0 * ContrastSensitivity(kJPEGNaturalOrder[k]);\n    }\n  }\n\n  const bool downsample_;\n  // Lower bound for quant matrix heuristic score used in binary search.\n  double hscore_a_;\n  // Upper bound for quant matrix heuristic score used in binary search, or 0.0\n  // if no upper bound is found yet.\n  double hscore_b_;\n  // Cached value of the sum of all ContrastSensitivity() values over all\n  // quant matrix elements.\n  double total_csf_;\n  std::vector<QuantData> quants_;\n\n  ProcessStats* stats_;\n};\n\nQuantData Processor::TryQuantMatrix(const JPEGData& jpg_in,\n                                    const float target_mul,\n                                    int q[3][kDCTBlockSize],\n                                    OutputImage* img) {\n  QuantData data;\n  memcpy(data.q, q, sizeof(data.q));\n  img->CopyFromJpegData(jpg_in);\n  img->ApplyGlobalQuantization(data.q);\n  std::string encoded_jpg;\n  {\n    JPEGData jpg_out = jpg_in;\n    img->SaveToJpegData(&jpg_out);\n    OutputJpeg(jpg_out, &encoded_jpg);\n  }\n  GUETZLI_LOG(stats_, \"Iter %2d: %s quantization matrix:\\n\",\n              stats_->counters[kNumItersCnt] + 1,\n              img->FrameTypeStr().c_str());\n  GUETZLI_LOG_QUANT(stats_, q);\n  GUETZLI_LOG(stats_, \"Iter %2d: %s GQ[%5.2f] Out[%7zd]\",\n              stats_->counters[kNumItersCnt] + 1,\n              img->FrameTypeStr().c_str(),\n              QuantMatrixHeuristicScore(q), encoded_jpg.size());\n  ++stats_->counters[kNumItersCnt];\n  comparator_->Compare(*img);\n  data.dist_ok = comparator_->DistanceOK(target_mul);\n  data.jpg_size = encoded_jpg.size();\n  MaybeOutput(encoded_jpg);\n  return data;\n}\n\nbool Processor::SelectQuantMatrix(const JPEGData& jpg_in, const bool downsample,\n                                  int best_q[3][kDCTBlockSize],\n                                  OutputImage* img) {\n  QuantMatrixGenerator qgen(downsample, stats_);\n  // Don't try to go up to exactly the target distance when selecting a\n  // quantization matrix, since we will need some slack to do the frequency\n  // masking later.\n  const float target_mul_high = 0.97f;\n  const float target_mul_low = 0.95f;\n\n  QuantData best = TryQuantMatrix(jpg_in, target_mul_high, best_q, img);\n  for (;;) {\n    int q_next[3][kDCTBlockSize];\n    if (!qgen.GetNext(q_next)) {\n      break;\n    }\n\n    QuantData data = TryQuantMatrix(jpg_in, target_mul_high, q_next, img);\n    qgen.Add(data);\n    if (CompareQuantData(data, best)) {\n      best = data;\n      if (data.dist_ok && !comparator_->DistanceOK(target_mul_low)) {\n        break;\n      }\n    }\n  }\n\n  memcpy(&best_q[0][0], &best.q[0][0], kBlockSize * sizeof(best_q[0][0]));\n  GUETZLI_LOG(stats_, \"\\n%s selected quantization matrix:\\n\",\n              downsample ? \"YUV420\" : \"YUV444\");\n  GUETZLI_LOG_QUANT(stats_, best_q);\n  return best.dist_ok;\n}\n\n\n// REQUIRES: block[c*64...(c*64+63)] is all zero if (comp_mask & (1<<c)) == 0.\nvoid Processor::ComputeBlockZeroingOrder(\n    const coeff_t block[kBlockSize], const coeff_t orig_block[kBlockSize],\n    const int block_x, const int block_y, const int factor_x,\n    const int factor_y, const uint8_t comp_mask, OutputImage* img,\n    std::vector<CoeffData>* output_order) {\n  static const uint8_t oldCsf[kDCTBlockSize] = {\n      10, 10, 20, 40, 60, 70, 80, 90,\n      10, 20, 30, 60, 70, 80, 90, 90,\n      20, 30, 60, 70, 80, 90, 90, 90,\n      40, 60, 70, 80, 90, 90, 90, 90,\n      60, 70, 80, 90, 90, 90, 90, 90,\n      70, 80, 90, 90, 90, 90, 90, 90,\n      80, 90, 90, 90, 90, 90, 90, 90,\n      90, 90, 90, 90, 90, 90, 90, 90,\n  };\n  static const double kWeight[3] = { 1.0, 0.22, 0.20 };\n#include \"guetzli/order.inc\"\n  std::vector<std::pair<int, float> > input_order;\n  for (int c = 0; c < 3; ++c) {\n    if (!(comp_mask & (1 << c))) continue;\n    for (int k = 1; k < kDCTBlockSize; ++k) {\n      int idx = c * kDCTBlockSize + k;\n      if (block[idx] != 0) {\n        float score;\n        if (params_.new_zeroing_model) {\n          score = std::abs(orig_block[idx]) * csf[idx] + bias[idx];\n        } else {\n          score = static_cast<float>((std::abs(orig_block[idx]) - kJPEGZigZagOrder[k] / 64.0) *\n                  kWeight[c] / oldCsf[k]);\n        }\n        input_order.push_back(std::make_pair(idx, score));\n      }\n    }\n  }\n  std::sort(input_order.begin(), input_order.end(),\n            [](const std::pair<int, float>& a, const std::pair<int, float>& b) {\n              return a.second < b.second; });\n  coeff_t processed_block[kBlockSize];\n  memcpy(processed_block, block, sizeof(processed_block));\n  comparator_->SwitchBlock(block_x, block_y, factor_x, factor_y);\n  while (!input_order.empty()) {\n    float best_err = 1e17f;\n    int best_i = 0;\n    for (size_t i = 0; i < std::min<size_t>(params_.zeroing_greedy_lookahead,\n                                         input_order.size());\n         ++i) {\n      coeff_t candidate_block[kBlockSize];\n      memcpy(candidate_block, processed_block, sizeof(candidate_block));\n      const int idx = input_order[i].first;\n      candidate_block[idx] = 0;\n      for (int c = 0; c < 3; ++c) {\n        if (comp_mask & (1 << c)) {\n          img->component(c).SetCoeffBlock(\n              block_x, block_y, &candidate_block[c * kDCTBlockSize]);\n        }\n      }\n      float max_err = 0;\n      for (int iy = 0; iy < factor_y; ++iy) {\n        for (int ix = 0; ix < factor_x; ++ix) {\n          int block_xx = block_x * factor_x + ix;\n          int block_yy = block_y * factor_y + iy;\n          if (8 * block_xx < img->width() && 8 * block_yy < img->height()) {\n            float err = static_cast<float>(comparator_->CompareBlock(*img, ix, iy));\n            max_err = std::max(max_err, err);\n          }\n        }\n      }\n      if (max_err < best_err) {\n        best_err = max_err;\n        best_i = i;\n      }\n    }\n    int idx = input_order[best_i].first;\n    processed_block[idx] = 0;\n    input_order.erase(input_order.begin() + best_i);\n    output_order->push_back({idx, best_err});\n    for (int c = 0; c < 3; ++c) {\n      if (comp_mask & (1 << c)) {\n        img->component(c).SetCoeffBlock(\n            block_x, block_y, &processed_block[c * kDCTBlockSize]);\n      }\n    }\n  }\n  // Make the block error values monotonic.\n  float min_err = 1e10;\n  for (int i = output_order->size() - 1; i >= 0; --i) {\n    min_err = std::min(min_err, (*output_order)[i].block_err);\n    (*output_order)[i].block_err = min_err;\n  }\n  // Cut off at the block error limit.\n  size_t num = 0;\n  while (num < output_order->size() &&\n         (*output_order)[num].block_err <= comparator_->BlockErrorLimit()) {\n    ++num;\n  }\n  output_order->resize(num);\n  // Restore *img to the same state as it was at the start of this function.\n  for (int c = 0; c < 3; ++c) {\n    if (comp_mask & (1 << c)) {\n      img->component(c).SetCoeffBlock(\n          block_x, block_y, &block[c * kDCTBlockSize]);\n    }\n  }\n}\n\nnamespace {\n\nvoid UpdateACHistogram(const int weight,\n                       const coeff_t* coeffs,\n                       const int* q,\n                       JpegHistogram* ac_histogram) {\n  int r = 0;\n  for (int k = 1; k < 64; ++k) {\n    const int k_nat = kJPEGNaturalOrder[k];\n    coeff_t coeff = coeffs[k_nat];\n    if (coeff == 0) {\n      r++;\n      continue;\n    }\n    while (r > 15) {\n      ac_histogram->Add(0xf0, weight);\n      r -= 16;\n    }\n    int nbits = Log2FloorNonZero(std::abs(coeff / q[k_nat])) + 1;\n    int symbol = (r << 4) + nbits;\n    ac_histogram->Add(symbol, weight);\n    r = 0;\n  }\n  if (r > 0) {\n    ac_histogram->Add(0, weight);\n  }\n}\n\nsize_t ComputeEntropyCodes(const std::vector<JpegHistogram>& histograms,\n                           std::vector<uint8_t>* depths) {\n  std::vector<JpegHistogram> clustered = histograms;\n  size_t num = histograms.size();\n  std::vector<int> indexes(histograms.size());\n  std::vector<uint8_t> clustered_depths(\n      histograms.size() * JpegHistogram::kSize);\n  ClusterHistograms(&clustered[0], &num, &indexes[0], &clustered_depths[0]);\n  depths->resize(clustered_depths.size());\n  for (size_t i = 0; i < histograms.size(); ++i) {\n    memcpy(&(*depths)[i * JpegHistogram::kSize],\n           &clustered_depths[indexes[i] * JpegHistogram::kSize],\n           JpegHistogram::kSize);\n  }\n  size_t histogram_size = 0;\n  for (size_t i = 0; i < num; ++i) {\n    histogram_size += HistogramHeaderCost(clustered[i]) / 8;\n  }\n  return histogram_size;\n}\n\nsize_t EntropyCodedDataSize(const std::vector<JpegHistogram>& histograms,\n                            const std::vector<uint8_t>& depths) {\n  size_t numbits = 0;\n  for (size_t i = 0; i < histograms.size(); ++i) {\n    numbits += HistogramEntropyCost(\n        histograms[i], &depths[i * JpegHistogram::kSize]);\n  }\n  return (numbits + 7) / 8;\n}\n\nsize_t EstimateDCSize(const JPEGData& jpg) {\n  std::vector<JpegHistogram> histograms(jpg.components.size());\n  BuildDCHistograms(jpg, &histograms[0]);\n  size_t num = histograms.size();\n  std::vector<int> indexes(num);\n  std::vector<uint8_t> depths(num * JpegHistogram::kSize);\n  return ClusterHistograms(&histograms[0], &num, &indexes[0], &depths[0]);\n}\n\n}  // namespace\n\nvoid Processor::SelectFrequencyMasking(const JPEGData& jpg, OutputImage* img,\n                                       const uint8_t comp_mask,\n                                       const double target_mul,\n                                       bool stop_early) {\n  const int width = img->width();\n  const int height = img->height();\n  const int ncomp = jpg.components.size();\n  const int last_c = Log2FloorNonZero(comp_mask);\n  if (static_cast<size_t>(last_c) >= jpg.components.size()) return;\n  const int factor_x = img->component(last_c).factor_x();\n  const int factor_y = img->component(last_c).factor_y();\n  const int block_width = (width + 8 * factor_x - 1) / (8 * factor_x);\n  const int block_height = (height + 8 * factor_y - 1) / (8 * factor_y);\n  const int num_blocks = block_width * block_height;\n\n  std::vector<int> candidate_coeff_offsets(num_blocks + 1);\n  std::vector<uint8_t> candidate_coeffs;\n  std::vector<float> candidate_coeff_errors;\n  candidate_coeffs.reserve(60 * num_blocks);\n  candidate_coeff_errors.reserve(60 * num_blocks);\n  std::vector<CoeffData> block_order;\n  block_order.reserve(3 * kDCTBlockSize);\n  comparator_->StartBlockComparisons();\n  for (int block_y = 0, block_ix = 0; block_y < block_height; ++block_y) {\n    for (int block_x = 0; block_x < block_width; ++block_x, ++block_ix) {\n      coeff_t block[kBlockSize] = { 0 };\n      coeff_t orig_block[kBlockSize] = { 0 };\n      for (int c = 0; c < 3; ++c) {\n        if (comp_mask & (1 << c)) {\n          assert(img->component(c).factor_x() == factor_x);\n          assert(img->component(c).factor_y() == factor_y);\n          img->component(c).GetCoeffBlock(block_x, block_y,\n                                          &block[c * kDCTBlockSize]);\n          const JPEGComponent& comp = jpg.components[c];\n          int jpg_block_ix = block_y * comp.width_in_blocks + block_x;\n          memcpy(&orig_block[c * kDCTBlockSize],\n                 &comp.coeffs[jpg_block_ix * kDCTBlockSize],\n                 kDCTBlockSize * sizeof(orig_block[0]));\n        }\n      }\n      block_order.clear();\n      ComputeBlockZeroingOrder(block, orig_block, block_x, block_y, factor_x,\n                               factor_y, comp_mask, img, &block_order);\n      candidate_coeff_offsets[block_ix] = candidate_coeffs.size();\n      for (size_t i = 0; i < block_order.size(); ++i) {\n        candidate_coeffs.push_back(block_order[i].idx);\n        candidate_coeff_errors.push_back(block_order[i].block_err);\n      }\n    }\n  }\n  comparator_->FinishBlockComparisons();\n  candidate_coeff_offsets[num_blocks] = candidate_coeffs.size();\n\n  std::vector<JpegHistogram> ac_histograms(ncomp);\n  int jpg_header_size, dc_size;\n  {\n    JPEGData jpg_out = jpg;\n    img->SaveToJpegData(&jpg_out);\n    jpg_header_size = JpegHeaderSize(jpg_out, params_.clear_metadata);\n    dc_size = EstimateDCSize(jpg_out);\n    BuildACHistograms(jpg_out, &ac_histograms[0]);\n  }\n  std::vector<uint8_t> ac_depths;\n  int ac_histogram_size = ComputeEntropyCodes(ac_histograms, &ac_depths);\n  int base_size = jpg_header_size + dc_size + ac_histogram_size +\n      EntropyCodedDataSize(ac_histograms, ac_depths);\n  int prev_size = base_size;\n\n  std::vector<float> max_block_error(num_blocks);\n  std::vector<int> last_indexes(num_blocks);\n\n  bool first_up_iter = true;\n  for (int direction : {1, -1}) {\n    for (;;) {\n      if (stop_early && direction == -1) {\n        if (prev_size > 1.01 * final_output_->jpeg_data.size()) {\n          // If we are down-adjusting the error, the output size will only keep\n          // increasing.\n          // TODO(user): Do this check always by comparing only the size\n          // of the currently processed components.\n          break;\n        }\n      }\n      std::vector<std::pair<int, float> > global_order;\n      int blocks_to_change;\n      std::vector<float> block_weight;\n      for (int rblock = 1; rblock <= 4; ++rblock) {\n        block_weight = std::vector<float>(num_blocks);\n        std::vector<float> distmap(width * height);\n        if (!first_up_iter) {\n          distmap = comparator_->distmap();\n        }\n        comparator_->ComputeBlockErrorAdjustmentWeights(\n            direction, rblock, target_mul, factor_x, factor_y, distmap,\n            &block_weight);\n        global_order.clear();\n        blocks_to_change = 0;\n        for (int block_y = 0, block_ix = 0; block_y < block_height; ++block_y) {\n          for (int block_x = 0; block_x < block_width; ++block_x, ++block_ix) {\n            const int last_index = last_indexes[block_ix];\n            const int offset = candidate_coeff_offsets[block_ix];\n            const int num_candidates =\n                candidate_coeff_offsets[block_ix + 1] - offset;\n            const float* candidate_errors = &candidate_coeff_errors[offset];\n            const float max_err = max_block_error[block_ix];\n            if (block_weight[block_ix] == 0) {\n              continue;\n            }\n            if (direction > 0) {\n              for (size_t i = last_index; i < num_candidates; ++i) {\n                float val = ((candidate_errors[i] - max_err) /\n                             block_weight[block_ix]);\n                global_order.push_back(std::make_pair(block_ix, val));\n              }\n              blocks_to_change += (last_index < num_candidates ? 1 : 0);\n            } else {\n              for (int i = last_index - 1; i >= 0; --i) {\n                float val = ((max_err - candidate_errors[i]) /\n                             block_weight[block_ix]);\n                global_order.push_back(std::make_pair(block_ix, val));\n              }\n              blocks_to_change += (last_index > 0 ? 1 : 0);\n            }\n          }\n        }\n        if (!global_order.empty()) {\n          // If we found something to adjust with the current block adjustment\n          // radius, we can stop and adjust the blocks we have.\n          break;\n        }\n      }\n\n      if (global_order.empty()) {\n        break;\n      }\n\n      std::sort(global_order.begin(), global_order.end(),\n                [](const std::pair<int, float>& a,\n                   const std::pair<int, float>& b) {\n                  return a.second < b.second; });\n\n      double rel_size_delta = direction > 0 ? 0.01 : 0.0005;\n      if (direction > 0 && comparator_->DistanceOK(1.0)) {\n        rel_size_delta = 0.05;\n      }\n      double min_size_delta = base_size * rel_size_delta;\n\n      float coeffs_to_change_per_block =\n          direction > 0 ? 2.0f : factor_x * factor_y * 0.2f;\n      int min_coeffs_to_change = coeffs_to_change_per_block * blocks_to_change;\n\n      if (first_up_iter) {\n        const float limit = 0.75f * comparator_->BlockErrorLimit();\n        auto it = std::partition_point(global_order.begin(), global_order.end(),\n                                       [=](const std::pair<int, float>& a) {\n                                         return a.second < limit; });\n        min_coeffs_to_change = std::max<int>(min_coeffs_to_change,\n                                             it - global_order.begin());\n        first_up_iter = false;\n      }\n\n      std::set<int> changed_blocks;\n      float val_threshold = 0.0;\n      int changed_coeffs = 0;\n      int est_jpg_size = prev_size;\n      for (size_t i = 0; i < global_order.size(); ++i) {\n        const int block_ix = global_order[i].first;\n        const int block_x = block_ix % block_width;\n        const int block_y = block_ix / block_width;\n        const int last_idx = last_indexes[block_ix];\n        const int offset = candidate_coeff_offsets[block_ix];\n        const uint8_t* candidates = &candidate_coeffs[offset];\n        const int idx = candidates[last_idx + std::min(direction, 0)];\n        const int c = idx / kDCTBlockSize;\n        const int k = idx % kDCTBlockSize;\n        const int* quant = img->component(c).quant();\n        const JPEGComponent& comp = jpg.components[c];\n        const int jpg_block_ix = block_y * comp.width_in_blocks + block_x;\n        const int newval = direction > 0 ? 0 : Quantize(\n            comp.coeffs[jpg_block_ix * kDCTBlockSize + k], quant[k]);\n        coeff_t block[kDCTBlockSize] = { 0 };\n        img->component(c).GetCoeffBlock(block_x, block_y, block);\n        UpdateACHistogram(-1, block, quant, &ac_histograms[c]);\n        double sum_of_hf = 0;\n        for (int ii = 3; ii < 64; ++ii) {\n          if ((ii & 7) < 3 && ii < 3 * 8) continue;\n          sum_of_hf += std::abs(comp.coeffs[jpg_block_ix * kDCTBlockSize + ii]);\n        }\n        int limit = sum_of_hf < 60 ? 4 : 8;\n        bool precious =\n            (k == 1 || k == 8) &&\n            std::abs(comp.coeffs[jpg_block_ix * kDCTBlockSize + k]) >= limit;\n        if (!precious || newval != 0) {\n          block[k] = newval;\n        }\n        UpdateACHistogram(1, block, quant, &ac_histograms[c]);\n        img->component(c).SetCoeffBlock(block_x, block_y, block);\n        last_indexes[block_ix] += direction;\n        changed_blocks.insert(block_ix);\n        val_threshold = global_order[i].second;\n        ++changed_coeffs;\n        static const int kEntropyCodeUpdateFreq = 10;\n        if (i % kEntropyCodeUpdateFreq == 0) {\n          ac_histogram_size = ComputeEntropyCodes(ac_histograms, &ac_depths);\n        }\n        est_jpg_size = jpg_header_size + dc_size + ac_histogram_size +\n            EntropyCodedDataSize(ac_histograms, ac_depths);\n        if (changed_coeffs > min_coeffs_to_change &&\n            std::abs(est_jpg_size - prev_size) > min_size_delta) {\n          break;\n        }\n      }\n      size_t global_order_size = global_order.size();\n      std::vector<std::pair<int, float>>().swap(global_order);\n\n      for (int i = 0; i < num_blocks; ++i) {\n        max_block_error[i] += block_weight[i] * val_threshold * direction;\n      }\n\n      ++stats_->counters[kNumItersCnt];\n      ++stats_->counters[direction > 0 ? kNumItersUpCnt : kNumItersDownCnt];\n      std::string encoded_jpg;\n      {\n        JPEGData jpg_out = jpg;\n        img->SaveToJpegData(&jpg_out);\n        OutputJpeg(jpg_out, &encoded_jpg);\n      }\n      GUETZLI_LOG(stats_,\n                  \"Iter %2d: %s(%d) %s Coeffs[%d/%zd] \"\n                  \"Blocks[%zd/%d/%d] ValThres[%.4f] Out[%7zd] EstErr[%.2f%%]\",\n                  stats_->counters[kNumItersCnt], img->FrameTypeStr().c_str(),\n                  comp_mask, direction > 0 ? \"up\" : \"down\", changed_coeffs,\n                  global_order_size, changed_blocks.size(),\n                  blocks_to_change, num_blocks, val_threshold,\n                  encoded_jpg.size(),\n                  100.0 - (100.0 * est_jpg_size) / encoded_jpg.size());\n      comparator_->Compare(*img);\n      MaybeOutput(encoded_jpg);\n      prev_size = est_jpg_size;\n    }\n  }\n}\n\nbool IsGrayscale(const JPEGData& jpg) {\n  for (int c = 1; c < 3; ++c) {\n    const JPEGComponent& comp = jpg.components[c];\n    for (size_t i = 0; i < comp.coeffs.size(); ++i) {\n      if (comp.coeffs[i] != 0) return false;\n    }\n  }\n  return true;\n}\n\nbool Processor::ProcessJpegData(const Params& params, const JPEGData& jpg_in,\n                                Comparator* comparator, GuetzliOutput* out,\n                                ProcessStats* stats) {\n  params_ = params;\n  comparator_ = comparator;\n  final_output_ = out;\n  stats_ = stats;\n\n  if (params.butteraugli_target > 2.0f) {\n    fprintf(stderr,\n            \"Guetzli should be called with quality >= 84, otherwise the\\n\"\n            \"output will have noticeable artifacts. If you want to\\n\"\n            \"proceed anyway, please edit the source code.\\n\");\n    return false;\n  }\n  if (jpg_in.components.size() != 3 || !HasYCbCrColorSpace(jpg_in)) {\n    fprintf(stderr, \"Only YUV color space input jpeg is supported\\n\");\n    return false;\n  }\n  bool input_is_420;\n  if (jpg_in.Is444()) {\n    input_is_420 = false;\n  } else if (jpg_in.Is420()) {\n    input_is_420 = true;\n  } else {\n    fprintf(stderr, \"Unsupported sampling factors:\");\n    for (size_t i = 0; i < jpg_in.components.size(); ++i) {\n      fprintf(stderr, \" %dx%d\", jpg_in.components[i].h_samp_factor,\n              jpg_in.components[i].v_samp_factor);\n    }\n    fprintf(stderr, \"\\n\");\n    return false;\n  }\n  int q_in[3][kDCTBlockSize];\n  // Output the original image, in case we do not manage to create anything\n  // with a good enough quality.\n  std::string encoded_jpg;\n  OutputJpeg(jpg_in, &encoded_jpg);\n  final_output_->score = -1;\n  GUETZLI_LOG(stats, \"Original Out[%7zd]\", encoded_jpg.size());\n  if (comparator_ == nullptr) {\n    GUETZLI_LOG(stats, \" <image too small for Butteraugli>\\n\");\n    final_output_->jpeg_data = encoded_jpg;\n    final_output_->score = encoded_jpg.size();\n    // Butteraugli doesn't work with images this small.\n    return true;\n  }\n  {\n    JPEGData jpg = jpg_in;\n    RemoveOriginalQuantization(&jpg, q_in);\n    OutputImage img(jpg.width, jpg.height);\n    img.CopyFromJpegData(jpg);\n    comparator_->Compare(img);\n  }\n  MaybeOutput(encoded_jpg);\n  int try_420 = (input_is_420 || params_.force_420 ||\n                 (params_.try_420 && !IsGrayscale(jpg_in))) ? 1 : 0;\n  int force_420 = (input_is_420 || params_.force_420) ? 1 : 0;\n  for (int downsample = force_420; downsample <= try_420; ++downsample) {\n    JPEGData jpg = jpg_in;\n    RemoveOriginalQuantization(&jpg, q_in);\n    OutputImage img(jpg.width, jpg.height);\n    img.CopyFromJpegData(jpg);\n    if (downsample) {\n      DownsampleImage(&img);\n      img.SaveToJpegData(&jpg);\n    }\n    int best_q[3][kDCTBlockSize];\n    memcpy(best_q, q_in, sizeof(best_q));\n    if (!SelectQuantMatrix(jpg, downsample != 0, best_q, &img)) {\n      for (int c = 0; c < 3; ++c) {\n        for (int i = 0; i < kDCTBlockSize; ++i) {\n          best_q[c][i] = 1;\n        }\n      }\n    }\n    img.CopyFromJpegData(jpg);\n    img.ApplyGlobalQuantization(best_q);\n\n    if (!downsample) {\n      SelectFrequencyMasking(jpg, &img, 7, 1.0, false);\n    } else {\n      const float ymul = jpg.components.size() == 1 ? 1.0f : 0.97f;\n      SelectFrequencyMasking(jpg, &img, 1, ymul, false);\n      SelectFrequencyMasking(jpg, &img, 6, 1.0, true);\n    }\n  }\n\n  return true;\n}\n\nbool ProcessJpegData(const Params& params, const JPEGData& jpg_in,\n                     Comparator* comparator, GuetzliOutput* out,\n                     ProcessStats* stats) {\n  Processor processor;\n  return processor.ProcessJpegData(params, jpg_in, comparator, out, stats);\n}\n\nbool Process(const Params& params, ProcessStats* stats,\n             const std::string& data,\n             std::string* jpg_out) {\n  JPEGData jpg;\n  if (!ReadJpeg(data, JPEG_READ_ALL, &jpg)) {\n    fprintf(stderr, \"Can't read jpg data from input file\\n\");\n    return false;\n  }\n  if (!CheckJpegSanity(jpg)) {\n    fprintf(stderr, \"Unsupported input JPEG (unexpectedly large coefficient \"\n            \"values).\\n\");\n    return false;\n  }\n  std::vector<uint8_t> rgb = DecodeJpegToRGB(jpg);\n  if (rgb.empty()) {\n    fprintf(stderr, \"Unsupported input JPEG file (e.g. unsupported \"\n            \"downsampling mode).\\nPlease provide the input image as \"\n            \"a PNG file.\\n\");\n    return false;\n  }\n  GuetzliOutput out;\n  ProcessStats dummy_stats;\n  if (stats == nullptr) {\n    stats = &dummy_stats;\n  }\n  std::unique_ptr<ButteraugliComparator> comparator;\n  if (jpg.width >= 32 && jpg.height >= 32) {\n    comparator.reset(\n        new ButteraugliComparator(jpg.width, jpg.height, &rgb,\n                                  params.butteraugli_target, stats));\n  }\n  bool ok = ProcessJpegData(params, jpg, comparator.get(), &out, stats);\n  *jpg_out = out.jpeg_data;\n  return ok;\n}\n\nbool Process(const Params& params, ProcessStats* stats,\n             const std::vector<uint8_t>& rgb, int w, int h,\n             std::string* jpg_out) {\n  JPEGData jpg;\n  if (!EncodeRGBToJpeg(rgb, w, h, &jpg)) {\n    fprintf(stderr, \"Could not create jpg data from rgb pixels\\n\");\n    return false;\n  }\n  GuetzliOutput out;\n  ProcessStats dummy_stats;\n  if (stats == nullptr) {\n    stats = &dummy_stats;\n  }\n  std::unique_ptr<ButteraugliComparator> comparator;\n  if (jpg.width >= 32 && jpg.height >= 32) {\n    comparator.reset(\n        new ButteraugliComparator(jpg.width, jpg.height, &rgb,\n                                  params.butteraugli_target, stats));\n  }\n  bool ok = ProcessJpegData(params, jpg, comparator.get(), &out, stats);\n  *jpg_out = out.jpeg_data;\n  return ok;\n}\n\n}  // namespace guetzli\n"
  },
  {
    "path": "guetzli/processor.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef GUETZLI_PROCESSOR_H_\n#define GUETZLI_PROCESSOR_H_\n\n#include <string>\n#include <vector>\n\n#include \"guetzli/comparator.h\"\n#include \"guetzli/jpeg_data.h\"\n#include \"guetzli/stats.h\"\n\nnamespace guetzli {\n\nstruct Params {\n  float butteraugli_target = 1.0;\n  bool clear_metadata = true;\n  bool try_420 = false;\n  bool force_420 = false;\n  bool use_silver_screen = false;\n  int zeroing_greedy_lookahead = 3;\n  bool new_zeroing_model = true;\n};\n\nbool Process(const Params& params, ProcessStats* stats,\n             const std::string& in_data,\n             std::string* out_data);\n\nstruct GuetzliOutput {\n  std::string jpeg_data;\n  double score;\n};\n\nbool ProcessJpegData(const Params& params, const JPEGData& jpg_in,\n                     Comparator* comparator, GuetzliOutput* out,\n                     ProcessStats* stats);\n\n// Sets *out to a jpeg encoded string that will decode to an image that is\n// visually indistinguishable from the input rgb image.\nbool Process(const Params& params, ProcessStats* stats,\n             const std::vector<uint8_t>& rgb, int w, int h,\n             std::string* out);\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_PROCESSOR_H_\n"
  },
  {
    "path": "guetzli/quality.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"guetzli/quality.h\"\n\nnamespace guetzli {\n\nnamespace {\n\nconstexpr int kLowestQuality = 70;\nconstexpr int kHighestQuality = 110;\n\n// Butteraugli scores that correspond to JPEG quality levels, starting at\n// kLowestQuality. They were computed by taking median BA scores of JPEGs\n// generated using libjpeg-turbo at given quality from a set of PNGs.\n// The scores above quality level 100 are just linearly decreased so that score\n// for 110 is 90% of the score for 100.\nconst double kScoreForQuality[] = {\n  2.810761,  // 70\n  2.729300,\n  2.689687,\n  2.636811,\n  2.547863,\n  2.525400,\n  2.473416,\n  2.366133,\n  2.338078,\n  2.318654,\n  2.201674,  // 80\n  2.145517,\n  2.087322,\n  2.009328,\n  1.945456,\n  1.900112,\n  1.805701,\n  1.750194,\n  1.644175,\n  1.562165,\n  1.473608,  // 90\n  1.382021,\n  1.294298,\n  1.185402,\n  1.066781,\n  0.971769,  // 95\n  0.852901,\n  0.724544,\n  0.611302,\n  0.443185,\n  0.211578,  // 100\n  0.209462,\n  0.207346,\n  0.205230,\n  0.203114,\n  0.200999,  // 105\n  0.198883,\n  0.196767,\n  0.194651,\n  0.192535,\n  0.190420,  // 110\n  0.190420,\n};\n\n}  // namespace\n\ndouble ButteraugliScoreForQuality(double quality) {\n  if (quality < kLowestQuality) quality = kLowestQuality;\n  if (quality > kHighestQuality) quality = kHighestQuality;\n  int index = static_cast<int>(quality);\n  double mix = quality - index;\n  return kScoreForQuality[index - kLowestQuality] * (1 - mix) +\n      kScoreForQuality[index - kLowestQuality + 1] * mix;\n}\n\n}  // namespace guetzli\n"
  },
  {
    "path": "guetzli/quality.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef GUETZLI_QUALITY_H_\n#define GUETZLI_QUALITY_H_\n\nnamespace guetzli {\n\ndouble ButteraugliScoreForQuality(double quality);\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_QUALITY_H_\n"
  },
  {
    "path": "guetzli/quantize.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"guetzli/quantize.h\"\n\nnamespace guetzli {\n\nbool QuantizeBlock(coeff_t block[kDCTBlockSize],\n                   const int q[kDCTBlockSize]) {\n  bool changed = false;\n  for (int k = 0; k < kDCTBlockSize; ++k) {\n    coeff_t coeff = Quantize(block[k], q[k]);\n    changed = changed || (coeff != block[k]);\n    block[k] = coeff;\n  }\n  return changed;\n}\n\n}  // namespace guetzli\n"
  },
  {
    "path": "guetzli/quantize.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef GUETZLI_QUANTIZE_H_\n#define GUETZLI_QUANTIZE_H_\n\n#include \"guetzli/jpeg_data.h\"\n\nnamespace guetzli {\n\ninline coeff_t Quantize(coeff_t raw_coeff, int quant) {\n  const int r = raw_coeff % quant;\n  const coeff_t delta =\n      2 * r > quant ? quant - r : (-2) * r > quant ? -quant - r : -r;\n  return raw_coeff + delta;\n}\n\nbool QuantizeBlock(coeff_t block[kDCTBlockSize], const int q[kDCTBlockSize]);\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_QUANTIZE_H_\n"
  },
  {
    "path": "guetzli/score.cc",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"guetzli/score.h\"\n\n#include <cmath>\n\nnamespace guetzli {\n\ndouble ScoreJPEG(double butteraugli_distance, int size,\n                 double butteraugli_target) {\n  constexpr double kScale = 50;\n  constexpr double kMaxExponent = 10;\n  constexpr double kLargeSize = 1e30;\n  // TODO(user): The score should also depend on distance below target (and be\n  // smooth).\n  double diff = butteraugli_distance - butteraugli_target;\n  if (diff <= 0.0) {\n    return size;\n  } else {\n    double exponent = kScale * diff;\n    if (exponent > kMaxExponent) {\n      return kLargeSize * std::exp(kMaxExponent) * diff + size;\n    } else {\n      return std::exp(exponent) * size;\n    }\n  }\n}\n\n}  // namespace guetzli\n"
  },
  {
    "path": "guetzli/score.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef GUETZLI_SCORE_H_\n#define GUETZLI_SCORE_H_\n\n#include <vector>\n\nnamespace guetzli {\n\ndouble ScoreJPEG(double butteraugli_distance, int size,\n                 double butteraugli_target);\n\n}  // namespace guetzli\n#endif  // GUETZLI_SCORE_H_\n"
  },
  {
    "path": "guetzli/stats.h",
    "content": "/*\n * Copyright 2016 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef GUETZLI_STATS_H_\n#define GUETZLI_STATS_H_\n\n#include <cstdio>\n#include <map>\n#include <string>\n#include <utility>\n#include <vector>\n\n\nnamespace guetzli {\n\nstatic const char* const  kNumItersCnt = \"number of iterations\";\nstatic const char* const kNumItersUpCnt = \"number of iterations up\";\nstatic const char* const kNumItersDownCnt = \"number of iterations down\";\n\nstruct ProcessStats {\n  ProcessStats() {}\n  std::map<std::string, int> counters;\n  std::string* debug_output = nullptr;\n  FILE* debug_output_file = nullptr;\n\n  std::string filename;\n};\n\n}  // namespace guetzli\n\n#endif  // GUETZLI_STATS_H_\n"
  },
  {
    "path": "guetzli.make",
    "content": "# GNU Make project makefile autogenerated by Premake\n\nifndef config\n  config=release\nendif\n\nifndef verbose\n  SILENT = @\nendif\n\n.PHONY: clean prebuild prelink\n\nifeq ($(config),release)\n  RESCOMP = windres\n  TARGETDIR = bin/Release\n  TARGET = $(TARGETDIR)/guetzli\n  OBJDIR = obj/Release/guetzli\n  DEFINES +=\n  INCLUDES += -I. -Ithird_party/butteraugli\n  FORCE_INCLUDE +=\n  ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)\n  ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -O3 -g `pkg-config --cflags libpng || libpng-config --cflags`\n  ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -O3 -g -std=c++11 `pkg-config --cflags libpng || libpng-config --cflags`\n  ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)\n  LIBS +=\n  LDDEPS +=\n  ALL_LDFLAGS += $(LDFLAGS) `pkg-config --libs libpng || libpng-config --ldflags`\n  LINKCMD = $(CXX) -o \"$@\" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)\n  define PREBUILDCMDS\n  endef\n  define PRELINKCMDS\n  endef\n  define POSTBUILDCMDS\n  endef\nall: $(TARGETDIR) $(OBJDIR) prebuild prelink $(TARGET)\n\t@:\n\nendif\n\nifeq ($(config),debug)\n  RESCOMP = windres\n  TARGETDIR = bin/Debug\n  TARGET = $(TARGETDIR)/guetzli\n  OBJDIR = obj/Debug/guetzli\n  DEFINES +=\n  INCLUDES += -I. -Ithird_party/butteraugli\n  FORCE_INCLUDE +=\n  ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)\n  ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -g `pkg-config --cflags libpng || libpng-config --cflags`\n  ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -g -std=c++11 `pkg-config --cflags libpng || libpng-config --cflags`\n  ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)\n  LIBS +=\n  LDDEPS +=\n  ALL_LDFLAGS += $(LDFLAGS) `pkg-config --libs libpng || libpng-config --ldflags`\n  LINKCMD = $(CXX) -o \"$@\" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)\n  define PREBUILDCMDS\n  endef\n  define PRELINKCMDS\n  endef\n  define POSTBUILDCMDS\n  endef\nall: $(TARGETDIR) $(OBJDIR) prebuild prelink $(TARGET)\n\t@:\n\nendif\n\nOBJECTS := \\\n\t$(OBJDIR)/butteraugli_comparator.o \\\n\t$(OBJDIR)/dct_double.o \\\n\t$(OBJDIR)/debug_print.o \\\n\t$(OBJDIR)/entropy_encode.o \\\n\t$(OBJDIR)/fdct.o \\\n\t$(OBJDIR)/gamma_correct.o \\\n\t$(OBJDIR)/guetzli.o \\\n\t$(OBJDIR)/idct.o \\\n\t$(OBJDIR)/jpeg_data.o \\\n\t$(OBJDIR)/jpeg_data_decoder.o \\\n\t$(OBJDIR)/jpeg_data_encoder.o \\\n\t$(OBJDIR)/jpeg_data_reader.o \\\n\t$(OBJDIR)/jpeg_data_writer.o \\\n\t$(OBJDIR)/jpeg_huffman_decode.o \\\n\t$(OBJDIR)/output_image.o \\\n\t$(OBJDIR)/preprocess_downsample.o \\\n\t$(OBJDIR)/processor.o \\\n\t$(OBJDIR)/quality.o \\\n\t$(OBJDIR)/quantize.o \\\n\t$(OBJDIR)/score.o \\\n\t$(OBJDIR)/butteraugli.o \\\n\nRESOURCES := \\\n\nCUSTOMFILES := \\\n\nSHELLTYPE := msdos\nifeq (,$(ComSpec)$(COMSPEC))\n  SHELLTYPE := posix\nendif\nifeq (/bin,$(findstring /bin,$(SHELL)))\n  SHELLTYPE := posix\nendif\n\n$(TARGET): $(GCH) ${CUSTOMFILES} $(OBJECTS) $(LDDEPS) $(RESOURCES)\n\t@echo Linking guetzli\n\t$(SILENT) $(LINKCMD)\n\t$(POSTBUILDCMDS)\n\n$(TARGETDIR):\n\t@echo Creating $(TARGETDIR)\nifeq (posix,$(SHELLTYPE))\n\t$(SILENT) mkdir -p $(TARGETDIR)\nelse\n\t$(SILENT) mkdir $(subst /,\\\\,$(TARGETDIR))\nendif\n\n$(OBJDIR):\n\t@echo Creating $(OBJDIR)\nifeq (posix,$(SHELLTYPE))\n\t$(SILENT) mkdir -p $(OBJDIR)\nelse\n\t$(SILENT) mkdir $(subst /,\\\\,$(OBJDIR))\nendif\n\nclean:\n\t@echo Cleaning guetzli\nifeq (posix,$(SHELLTYPE))\n\t$(SILENT) rm -f  $(TARGET)\n\t$(SILENT) rm -rf $(OBJDIR)\nelse\n\t$(SILENT) if exist $(subst /,\\\\,$(TARGET)) del $(subst /,\\\\,$(TARGET))\n\t$(SILENT) if exist $(subst /,\\\\,$(OBJDIR)) rmdir /s /q $(subst /,\\\\,$(OBJDIR))\nendif\n\nprebuild:\n\t$(PREBUILDCMDS)\n\nprelink:\n\t$(PRELINKCMDS)\n\nifneq (,$(PCH))\n$(OBJECTS): $(GCH) $(PCH)\n$(GCH): $(PCH)\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) -x c++-header $(ALL_CXXFLAGS) -o \"$@\" -MF \"$(@:%.gch=%.d)\" -c \"$<\"\nendif\n\n$(OBJDIR)/butteraugli_comparator.o: guetzli/butteraugli_comparator.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/dct_double.o: guetzli/dct_double.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/debug_print.o: guetzli/debug_print.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/entropy_encode.o: guetzli/entropy_encode.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/fdct.o: guetzli/fdct.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/gamma_correct.o: guetzli/gamma_correct.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/guetzli.o: guetzli/guetzli.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/idct.o: guetzli/idct.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/jpeg_data.o: guetzli/jpeg_data.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/jpeg_data_decoder.o: guetzli/jpeg_data_decoder.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/jpeg_data_encoder.o: guetzli/jpeg_data_encoder.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/jpeg_data_reader.o: guetzli/jpeg_data_reader.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/jpeg_data_writer.o: guetzli/jpeg_data_writer.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/jpeg_huffman_decode.o: guetzli/jpeg_huffman_decode.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/output_image.o: guetzli/output_image.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/preprocess_downsample.o: guetzli/preprocess_downsample.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/processor.o: guetzli/processor.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/quality.o: guetzli/quality.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/quantize.o: guetzli/quantize.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/score.o: guetzli/score.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/butteraugli.o: third_party/butteraugli/butteraugli/butteraugli.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n\n-include $(OBJECTS:%.o=%.d)\nifneq (,$(PCH))\n  -include $(OBJDIR)/$(notdir $(PCH)).d\nendif"
  },
  {
    "path": "guetzli.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio 14\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"guetzli_static\", \"guetzli_static.vcxproj\", \"{30B3C385-1C81-B78B-0515-28B2F18193F0}\"\r\nEndProject\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"guetzli\", \"guetzli.vcxproj\", \"{C9FCBE14-35DC-3DB0-3EF4-C886AA52A411}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|Win32 = Debug|Win32\r\n\t\tDebug|x64 = Debug|x64\r\n\t\tRelease|Win32 = Release|Win32\r\n\t\tRelease|x64 = Release|x64\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{30B3C385-1C81-B78B-0515-28B2F18193F0}.Debug|Win32.ActiveCfg = Debug|Win32\r\n\t\t{30B3C385-1C81-B78B-0515-28B2F18193F0}.Debug|Win32.Build.0 = Debug|Win32\r\n\t\t{30B3C385-1C81-B78B-0515-28B2F18193F0}.Debug|x64.ActiveCfg = Debug|x64\r\n\t\t{30B3C385-1C81-B78B-0515-28B2F18193F0}.Debug|x64.Build.0 = Debug|x64\r\n\t\t{30B3C385-1C81-B78B-0515-28B2F18193F0}.Release|Win32.ActiveCfg = Release|Win32\r\n\t\t{30B3C385-1C81-B78B-0515-28B2F18193F0}.Release|Win32.Build.0 = Release|Win32\r\n\t\t{30B3C385-1C81-B78B-0515-28B2F18193F0}.Release|x64.ActiveCfg = Release|x64\r\n\t\t{30B3C385-1C81-B78B-0515-28B2F18193F0}.Release|x64.Build.0 = Release|x64\r\n\t\t{C9FCBE14-35DC-3DB0-3EF4-C886AA52A411}.Debug|Win32.ActiveCfg = Debug|Win32\r\n\t\t{C9FCBE14-35DC-3DB0-3EF4-C886AA52A411}.Debug|Win32.Build.0 = Debug|Win32\r\n\t\t{C9FCBE14-35DC-3DB0-3EF4-C886AA52A411}.Debug|x64.ActiveCfg = Debug|x64\r\n\t\t{C9FCBE14-35DC-3DB0-3EF4-C886AA52A411}.Debug|x64.Build.0 = Debug|x64\r\n\t\t{C9FCBE14-35DC-3DB0-3EF4-C886AA52A411}.Release|Win32.ActiveCfg = Release|Win32\r\n\t\t{C9FCBE14-35DC-3DB0-3EF4-C886AA52A411}.Release|Win32.Build.0 = Release|Win32\r\n\t\t{C9FCBE14-35DC-3DB0-3EF4-C886AA52A411}.Release|x64.ActiveCfg = Release|x64\r\n\t\t{C9FCBE14-35DC-3DB0-3EF4-C886AA52A411}.Release|x64.Build.0 = Release|x64\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "guetzli.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"14.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{C9FCBE14-35DC-3DB0-3EF4-C886AA52A411}</ProjectGuid>\r\n    <IgnoreWarnCompileDuplicatedFilename>true</IgnoreWarnCompileDuplicatedFilename>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>guetzli</RootNamespace>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n    <PlatformToolset>v140</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n    <PlatformToolset>v140</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n    <PlatformToolset>v140</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>Application</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n    <PlatformToolset>v140</PlatformToolset>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n    <OutDir>bin\\x86_64\\Release\\</OutDir>\r\n    <IntDir>obj\\x86_64\\Release\\guetzli\\</IntDir>\r\n    <TargetName>guetzli</TargetName>\r\n    <TargetExt>.exe</TargetExt>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <LinkIncremental>false</LinkIncremental>\r\n    <OutDir>bin\\x86\\Release\\</OutDir>\r\n    <IntDir>obj\\x86\\Release\\guetzli\\</IntDir>\r\n    <TargetName>guetzli</TargetName>\r\n    <TargetExt>.exe</TargetExt>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n    <OutDir>bin\\x86_64\\Debug\\</OutDir>\r\n    <IntDir>obj\\x86_64\\Debug\\guetzli\\</IntDir>\r\n    <TargetName>guetzli</TargetName>\r\n    <TargetExt>.exe</TargetExt>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <LinkIncremental>true</LinkIncremental>\r\n    <OutDir>bin\\x86\\Debug\\</OutDir>\r\n    <IntDir>obj\\x86\\Debug\\guetzli\\</IntDir>\r\n    <TargetName>guetzli</TargetName>\r\n    <TargetExt>.exe</TargetExt>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <AdditionalIncludeDirectories>.;third_party\\butteraugli;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <Optimization>Full</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <StringPooling>true</StringPooling>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n      <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n      <EntryPointSymbol>mainCRTStartup</EntryPointSymbol>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <AdditionalIncludeDirectories>.;third_party\\butteraugli;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <Optimization>Full</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <StringPooling>true</StringPooling>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n      <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n      <EntryPointSymbol>mainCRTStartup</EntryPointSymbol>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <AdditionalIncludeDirectories>.;third_party\\butteraugli;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>\r\n      <Optimization>Disabled</Optimization>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n      <EntryPointSymbol>mainCRTStartup</EntryPointSymbol>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <AdditionalIncludeDirectories>.;third_party\\butteraugli;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>\r\n      <Optimization>Disabled</Optimization>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Console</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n      <EntryPointSymbol>mainCRTStartup</EntryPointSymbol>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"guetzli\\butteraugli_comparator.h\" />\r\n    <ClInclude Include=\"guetzli\\color_transform.h\" />\r\n    <ClInclude Include=\"guetzli\\comparator.h\" />\r\n    <ClInclude Include=\"guetzli\\dct_double.h\" />\r\n    <ClInclude Include=\"guetzli\\debug_print.h\" />\r\n    <ClInclude Include=\"guetzli\\entropy_encode.h\" />\r\n    <ClInclude Include=\"guetzli\\fast_log.h\" />\r\n    <ClInclude Include=\"guetzli\\fdct.h\" />\r\n    <ClInclude Include=\"guetzli\\gamma_correct.h\" />\r\n    <ClInclude Include=\"guetzli\\idct.h\" />\r\n    <ClInclude Include=\"guetzli\\jpeg_bit_writer.h\" />\r\n    <ClInclude Include=\"guetzli\\jpeg_data.h\" />\r\n    <ClInclude Include=\"guetzli\\jpeg_data_decoder.h\" />\r\n    <ClInclude Include=\"guetzli\\jpeg_data_encoder.h\" />\r\n    <ClInclude Include=\"guetzli\\jpeg_data_reader.h\" />\r\n    <ClInclude Include=\"guetzli\\jpeg_data_writer.h\" />\r\n    <ClInclude Include=\"guetzli\\jpeg_error.h\" />\r\n    <ClInclude Include=\"guetzli\\jpeg_huffman_decode.h\" />\r\n    <ClInclude Include=\"guetzli\\output_image.h\" />\r\n    <ClInclude Include=\"guetzli\\preprocess_downsample.h\" />\r\n    <ClInclude Include=\"guetzli\\processor.h\" />\r\n    <ClInclude Include=\"guetzli\\quality.h\" />\r\n    <ClInclude Include=\"guetzli\\quantize.h\" />\r\n    <ClInclude Include=\"guetzli\\score.h\" />\r\n    <ClInclude Include=\"guetzli\\stats.h\" />\r\n    <ClInclude Include=\"third_party\\butteraugli\\butteraugli\\butteraugli.h\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"guetzli\\butteraugli_comparator.cc\" />\r\n    <ClCompile Include=\"guetzli\\dct_double.cc\" />\r\n    <ClCompile Include=\"guetzli\\debug_print.cc\" />\r\n    <ClCompile Include=\"guetzli\\entropy_encode.cc\" />\r\n    <ClCompile Include=\"guetzli\\fdct.cc\" />\r\n    <ClCompile Include=\"guetzli\\gamma_correct.cc\" />\r\n    <ClCompile Include=\"guetzli\\guetzli.cc\" />\r\n    <ClCompile Include=\"guetzli\\idct.cc\" />\r\n    <ClCompile Include=\"guetzli\\jpeg_data.cc\" />\r\n    <ClCompile Include=\"guetzli\\jpeg_data_decoder.cc\" />\r\n    <ClCompile Include=\"guetzli\\jpeg_data_encoder.cc\" />\r\n    <ClCompile Include=\"guetzli\\jpeg_data_reader.cc\" />\r\n    <ClCompile Include=\"guetzli\\jpeg_data_writer.cc\" />\r\n    <ClCompile Include=\"guetzli\\jpeg_huffman_decode.cc\" />\r\n    <ClCompile Include=\"guetzli\\output_image.cc\" />\r\n    <ClCompile Include=\"guetzli\\preprocess_downsample.cc\" />\r\n    <ClCompile Include=\"guetzli\\processor.cc\" />\r\n    <ClCompile Include=\"guetzli\\quality.cc\" />\r\n    <ClCompile Include=\"guetzli\\quantize.cc\" />\r\n    <ClCompile Include=\"guetzli\\score.cc\" />\r\n    <ClCompile Include=\"third_party\\butteraugli\\butteraugli\\butteraugli.cc\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>"
  },
  {
    "path": "guetzli.vcxproj.filters",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup>\r\n    <Filter Include=\"guetzli\">\r\n      <UniqueIdentifier>{C9FCBE14-35DC-3DB0-3EF4-C886AA52A411}</UniqueIdentifier>\r\n    </Filter>\r\n    <Filter Include=\"third_party\">\r\n      <UniqueIdentifier>{0FB18DF1-7B66-06E7-045B-00BE700FFDEA}</UniqueIdentifier>\r\n    </Filter>\r\n    <Filter Include=\"third_party\\butteraugli\">\r\n      <UniqueIdentifier>{468B2B32-B2C2-73C9-BBCC-D7EC27839AC2}</UniqueIdentifier>\r\n    </Filter>\r\n    <Filter Include=\"third_party\\butteraugli\\butteraugli\">\r\n      <UniqueIdentifier>{FD6FCB41-6929-36EC-F288-50C65E41EC5B}</UniqueIdentifier>\r\n    </Filter>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"guetzli\\butteraugli_comparator.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\color_transform.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\comparator.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\dct_double.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\debug_print.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\entropy_encode.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\fast_log.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\fdct.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\gamma_correct.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\idct.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\jpeg_bit_writer.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\jpeg_data.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\jpeg_data_decoder.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\jpeg_data_encoder.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\jpeg_data_reader.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\jpeg_data_writer.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\jpeg_error.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\jpeg_huffman_decode.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\output_image.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\preprocess_downsample.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\processor.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\quality.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\quantize.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\score.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\stats.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"third_party\\butteraugli\\butteraugli\\butteraugli.h\">\r\n      <Filter>third_party\\butteraugli\\butteraugli</Filter>\r\n    </ClInclude>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"guetzli\\butteraugli_comparator.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\dct_double.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\debug_print.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\entropy_encode.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\fdct.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\gamma_correct.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\guetzli.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\idct.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\jpeg_data.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\jpeg_data_decoder.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\jpeg_data_encoder.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\jpeg_data_reader.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\jpeg_data_writer.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\jpeg_huffman_decode.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\output_image.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\preprocess_downsample.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\processor.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\quality.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\quantize.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\score.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"third_party\\butteraugli\\butteraugli\\butteraugli.cc\">\r\n      <Filter>third_party\\butteraugli\\butteraugli</Filter>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "guetzli_static.make",
    "content": "# GNU Make project makefile autogenerated by Premake\n\nifndef config\n  config=release\nendif\n\nifndef verbose\n  SILENT = @\nendif\n\n.PHONY: clean prebuild prelink\n\nifeq ($(config),release)\n  RESCOMP = windres\n  TARGETDIR = bin/Release\n  TARGET = $(TARGETDIR)/libguetzli_static.a\n  OBJDIR = obj/Release/guetzli_static\n  DEFINES +=\n  INCLUDES += -I. -Ithird_party/butteraugli\n  FORCE_INCLUDE +=\n  ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)\n  ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -O3 -g `pkg-config --static --cflags libpng || libpng-config --static --cflags`\n  ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -O3 -g -std=c++11 `pkg-config --static --cflags libpng || libpng-config --static --cflags`\n  ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)\n  LIBS +=\n  LDDEPS +=\n  ALL_LDFLAGS += $(LDFLAGS) `pkg-config --static --libs libpng || libpng-config --static --ldflags`\n  LINKCMD = $(AR) -rcs \"$@\" $(OBJECTS)\n  define PREBUILDCMDS\n  endef\n  define PRELINKCMDS\n  endef\n  define POSTBUILDCMDS\n  endef\nall: $(TARGETDIR) $(OBJDIR) prebuild prelink $(TARGET)\n\t@:\n\nendif\n\nifeq ($(config),debug)\n  RESCOMP = windres\n  TARGETDIR = bin/Debug\n  TARGET = $(TARGETDIR)/libguetzli_static.a\n  OBJDIR = obj/Debug/guetzli_static\n  DEFINES +=\n  INCLUDES += -I. -Ithird_party/butteraugli\n  FORCE_INCLUDE +=\n  ALL_CPPFLAGS += $(CPPFLAGS) -MMD -MP $(DEFINES) $(INCLUDES)\n  ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -g `pkg-config --static --cflags libpng || libpng-config --static --cflags`\n  ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS) -g -std=c++11 `pkg-config --static --cflags libpng || libpng-config --static --cflags`\n  ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)\n  LIBS +=\n  LDDEPS +=\n  ALL_LDFLAGS += $(LDFLAGS) `pkg-config --static --libs libpng || libpng-config --static --ldflags`\n  LINKCMD = $(AR) -rcs \"$@\" $(OBJECTS)\n  define PREBUILDCMDS\n  endef\n  define PRELINKCMDS\n  endef\n  define POSTBUILDCMDS\n  endef\nall: $(TARGETDIR) $(OBJDIR) prebuild prelink $(TARGET)\n\t@:\n\nendif\n\nOBJECTS := \\\n\t$(OBJDIR)/butteraugli_comparator.o \\\n\t$(OBJDIR)/dct_double.o \\\n\t$(OBJDIR)/debug_print.o \\\n\t$(OBJDIR)/entropy_encode.o \\\n\t$(OBJDIR)/fdct.o \\\n\t$(OBJDIR)/gamma_correct.o \\\n\t$(OBJDIR)/idct.o \\\n\t$(OBJDIR)/jpeg_data.o \\\n\t$(OBJDIR)/jpeg_data_decoder.o \\\n\t$(OBJDIR)/jpeg_data_encoder.o \\\n\t$(OBJDIR)/jpeg_data_reader.o \\\n\t$(OBJDIR)/jpeg_data_writer.o \\\n\t$(OBJDIR)/jpeg_huffman_decode.o \\\n\t$(OBJDIR)/output_image.o \\\n\t$(OBJDIR)/preprocess_downsample.o \\\n\t$(OBJDIR)/processor.o \\\n\t$(OBJDIR)/quality.o \\\n\t$(OBJDIR)/quantize.o \\\n\t$(OBJDIR)/score.o \\\n\t$(OBJDIR)/butteraugli.o \\\n\nRESOURCES := \\\n\nCUSTOMFILES := \\\n\nSHELLTYPE := msdos\nifeq (,$(ComSpec)$(COMSPEC))\n  SHELLTYPE := posix\nendif\nifeq (/bin,$(findstring /bin,$(SHELL)))\n  SHELLTYPE := posix\nendif\n\n$(TARGET): $(GCH) ${CUSTOMFILES} $(OBJECTS) $(LDDEPS) $(RESOURCES)\n\t@echo Linking guetzli_static\n\t$(SILENT) $(LINKCMD)\n\t$(POSTBUILDCMDS)\n\n$(TARGETDIR):\n\t@echo Creating $(TARGETDIR)\nifeq (posix,$(SHELLTYPE))\n\t$(SILENT) mkdir -p $(TARGETDIR)\nelse\n\t$(SILENT) mkdir $(subst /,\\\\,$(TARGETDIR))\nendif\n\n$(OBJDIR):\n\t@echo Creating $(OBJDIR)\nifeq (posix,$(SHELLTYPE))\n\t$(SILENT) mkdir -p $(OBJDIR)\nelse\n\t$(SILENT) mkdir $(subst /,\\\\,$(OBJDIR))\nendif\n\nclean:\n\t@echo Cleaning guetzli_static\nifeq (posix,$(SHELLTYPE))\n\t$(SILENT) rm -f  $(TARGET)\n\t$(SILENT) rm -rf $(OBJDIR)\nelse\n\t$(SILENT) if exist $(subst /,\\\\,$(TARGET)) del $(subst /,\\\\,$(TARGET))\n\t$(SILENT) if exist $(subst /,\\\\,$(OBJDIR)) rmdir /s /q $(subst /,\\\\,$(OBJDIR))\nendif\n\nprebuild:\n\t$(PREBUILDCMDS)\n\nprelink:\n\t$(PRELINKCMDS)\n\nifneq (,$(PCH))\n$(OBJECTS): $(GCH) $(PCH)\n$(GCH): $(PCH)\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) -x c++-header $(ALL_CXXFLAGS) -o \"$@\" -MF \"$(@:%.gch=%.d)\" -c \"$<\"\nendif\n\n$(OBJDIR)/butteraugli_comparator.o: guetzli/butteraugli_comparator.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/dct_double.o: guetzli/dct_double.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/debug_print.o: guetzli/debug_print.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/entropy_encode.o: guetzli/entropy_encode.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/fdct.o: guetzli/fdct.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/gamma_correct.o: guetzli/gamma_correct.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/idct.o: guetzli/idct.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/jpeg_data.o: guetzli/jpeg_data.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/jpeg_data_decoder.o: guetzli/jpeg_data_decoder.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/jpeg_data_encoder.o: guetzli/jpeg_data_encoder.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/jpeg_data_reader.o: guetzli/jpeg_data_reader.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/jpeg_data_writer.o: guetzli/jpeg_data_writer.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/jpeg_huffman_decode.o: guetzli/jpeg_huffman_decode.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/output_image.o: guetzli/output_image.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/preprocess_downsample.o: guetzli/preprocess_downsample.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/processor.o: guetzli/processor.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/quality.o: guetzli/quality.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/quantize.o: guetzli/quantize.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/score.o: guetzli/score.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n$(OBJDIR)/butteraugli.o: third_party/butteraugli/butteraugli/butteraugli.cc\n\t@echo $(notdir $<)\n\t$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o \"$@\" -MF \"$(@:%.o=%.d)\" -c \"$<\"\n\n-include $(OBJECTS:%.o=%.d)\nifneq (,$(PCH))\n  -include $(OBJDIR)/$(notdir $(PCH)).d\nendif"
  },
  {
    "path": "guetzli_static.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"14.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <ProjectGuid>{30B3C385-1C81-B78B-0515-28B2F18193F0}</ProjectGuid>\r\n    <IgnoreWarnCompileDuplicatedFilename>true</IgnoreWarnCompileDuplicatedFilename>\r\n    <Keyword>Win32Proj</Keyword>\r\n    <RootNamespace>guetzli_static</RootNamespace>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n    <PlatformToolset>v140</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>false</UseDebugLibraries>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n    <PlatformToolset>v140</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n    <PlatformToolset>v140</PlatformToolset>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseDebugLibraries>true</UseDebugLibraries>\r\n    <CharacterSet>Unicode</CharacterSet>\r\n    <PlatformToolset>v140</PlatformToolset>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <OutDir>bin\\x86_64\\Release\\</OutDir>\r\n    <IntDir>obj\\x86_64\\Release\\guetzli_static\\</IntDir>\r\n    <TargetName>guetzli_static</TargetName>\r\n    <TargetExt>.lib</TargetExt>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <OutDir>bin\\x86\\Release\\</OutDir>\r\n    <IntDir>obj\\x86\\Release\\guetzli_static\\</IntDir>\r\n    <TargetName>guetzli_static</TargetName>\r\n    <TargetExt>.lib</TargetExt>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <OutDir>bin\\x86_64\\Debug\\</OutDir>\r\n    <IntDir>obj\\x86_64\\Debug\\guetzli_static\\</IntDir>\r\n    <TargetName>guetzli_static</TargetName>\r\n    <TargetExt>.lib</TargetExt>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <OutDir>bin\\x86\\Debug\\</OutDir>\r\n    <IntDir>obj\\x86\\Debug\\guetzli_static\\</IntDir>\r\n    <TargetName>guetzli_static</TargetName>\r\n    <TargetExt>.lib</TargetExt>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <AdditionalIncludeDirectories>.;third_party\\butteraugli;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <Optimization>Full</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <StringPooling>true</StringPooling>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <AdditionalIncludeDirectories>.;third_party\\butteraugli;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <Optimization>Full</Optimization>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <IntrinsicFunctions>true</IntrinsicFunctions>\r\n      <MinimalRebuild>false</MinimalRebuild>\r\n      <StringPooling>true</StringPooling>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\r\n      <OptimizeReferences>true</OptimizeReferences>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <AdditionalIncludeDirectories>.;third_party\\butteraugli;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>\r\n      <Optimization>Disabled</Optimization>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <PrecompiledHeader>NotUsing</PrecompiledHeader>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <AdditionalIncludeDirectories>.;third_party\\butteraugli;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>\r\n      <Optimization>Disabled</Optimization>\r\n    </ClCompile>\r\n    <Link>\r\n      <SubSystem>Windows</SubSystem>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"guetzli\\butteraugli_comparator.h\" />\r\n    <ClInclude Include=\"guetzli\\color_transform.h\" />\r\n    <ClInclude Include=\"guetzli\\comparator.h\" />\r\n    <ClInclude Include=\"guetzli\\dct_double.h\" />\r\n    <ClInclude Include=\"guetzli\\debug_print.h\" />\r\n    <ClInclude Include=\"guetzli\\entropy_encode.h\" />\r\n    <ClInclude Include=\"guetzli\\fast_log.h\" />\r\n    <ClInclude Include=\"guetzli\\fdct.h\" />\r\n    <ClInclude Include=\"guetzli\\gamma_correct.h\" />\r\n    <ClInclude Include=\"guetzli\\idct.h\" />\r\n    <ClInclude Include=\"guetzli\\jpeg_bit_writer.h\" />\r\n    <ClInclude Include=\"guetzli\\jpeg_data.h\" />\r\n    <ClInclude Include=\"guetzli\\jpeg_data_decoder.h\" />\r\n    <ClInclude Include=\"guetzli\\jpeg_data_encoder.h\" />\r\n    <ClInclude Include=\"guetzli\\jpeg_data_reader.h\" />\r\n    <ClInclude Include=\"guetzli\\jpeg_data_writer.h\" />\r\n    <ClInclude Include=\"guetzli\\jpeg_error.h\" />\r\n    <ClInclude Include=\"guetzli\\jpeg_huffman_decode.h\" />\r\n    <ClInclude Include=\"guetzli\\output_image.h\" />\r\n    <ClInclude Include=\"guetzli\\preprocess_downsample.h\" />\r\n    <ClInclude Include=\"guetzli\\processor.h\" />\r\n    <ClInclude Include=\"guetzli\\quality.h\" />\r\n    <ClInclude Include=\"guetzli\\quantize.h\" />\r\n    <ClInclude Include=\"guetzli\\score.h\" />\r\n    <ClInclude Include=\"guetzli\\stats.h\" />\r\n    <ClInclude Include=\"third_party\\butteraugli\\butteraugli\\butteraugli.h\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"guetzli\\butteraugli_comparator.cc\" />\r\n    <ClCompile Include=\"guetzli\\dct_double.cc\" />\r\n    <ClCompile Include=\"guetzli\\debug_print.cc\" />\r\n    <ClCompile Include=\"guetzli\\entropy_encode.cc\" />\r\n    <ClCompile Include=\"guetzli\\fdct.cc\" />\r\n    <ClCompile Include=\"guetzli\\gamma_correct.cc\" />\r\n    <ClCompile Include=\"guetzli\\idct.cc\" />\r\n    <ClCompile Include=\"guetzli\\jpeg_data.cc\" />\r\n    <ClCompile Include=\"guetzli\\jpeg_data_decoder.cc\" />\r\n    <ClCompile Include=\"guetzli\\jpeg_data_encoder.cc\" />\r\n    <ClCompile Include=\"guetzli\\jpeg_data_reader.cc\" />\r\n    <ClCompile Include=\"guetzli\\jpeg_data_writer.cc\" />\r\n    <ClCompile Include=\"guetzli\\jpeg_huffman_decode.cc\" />\r\n    <ClCompile Include=\"guetzli\\output_image.cc\" />\r\n    <ClCompile Include=\"guetzli\\preprocess_downsample.cc\" />\r\n    <ClCompile Include=\"guetzli\\processor.cc\" />\r\n    <ClCompile Include=\"guetzli\\quality.cc\" />\r\n    <ClCompile Include=\"guetzli\\quantize.cc\" />\r\n    <ClCompile Include=\"guetzli\\score.cc\" />\r\n    <ClCompile Include=\"third_party\\butteraugli\\butteraugli\\butteraugli.cc\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>"
  },
  {
    "path": "guetzli_static.vcxproj.filters",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup>\r\n    <Filter Include=\"guetzli\">\r\n      <UniqueIdentifier>{C9FCBE14-35DC-3DB0-3EF4-C886AA52A411}</UniqueIdentifier>\r\n    </Filter>\r\n    <Filter Include=\"third_party\">\r\n      <UniqueIdentifier>{0FB18DF1-7B66-06E7-045B-00BE700FFDEA}</UniqueIdentifier>\r\n    </Filter>\r\n    <Filter Include=\"third_party\\butteraugli\">\r\n      <UniqueIdentifier>{468B2B32-B2C2-73C9-BBCC-D7EC27839AC2}</UniqueIdentifier>\r\n    </Filter>\r\n    <Filter Include=\"third_party\\butteraugli\\butteraugli\">\r\n      <UniqueIdentifier>{FD6FCB41-6929-36EC-F288-50C65E41EC5B}</UniqueIdentifier>\r\n    </Filter>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"guetzli\\butteraugli_comparator.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\color_transform.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\comparator.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\dct_double.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\debug_print.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\entropy_encode.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\fast_log.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\fdct.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\gamma_correct.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\idct.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\jpeg_bit_writer.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\jpeg_data.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\jpeg_data_decoder.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\jpeg_data_encoder.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\jpeg_data_reader.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\jpeg_data_writer.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\jpeg_error.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\jpeg_huffman_decode.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\output_image.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\preprocess_downsample.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\processor.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\quality.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\quantize.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\score.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"guetzli\\stats.h\">\r\n      <Filter>guetzli</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"third_party\\butteraugli\\butteraugli\\butteraugli.h\">\r\n      <Filter>third_party\\butteraugli\\butteraugli</Filter>\r\n    </ClInclude>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"guetzli\\butteraugli_comparator.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\dct_double.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\debug_print.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\entropy_encode.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\fdct.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\gamma_correct.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\idct.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\jpeg_data.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\jpeg_data_decoder.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\jpeg_data_encoder.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\jpeg_data_reader.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\jpeg_data_writer.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\jpeg_huffman_decode.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\output_image.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\preprocess_downsample.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\processor.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\quality.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\quantize.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"guetzli\\score.cc\">\r\n      <Filter>guetzli</Filter>\r\n    </ClCompile>\r\n    <ClCompile Include=\"third_party\\butteraugli\\butteraugli\\butteraugli.cc\">\r\n      <Filter>third_party\\butteraugli\\butteraugli</Filter>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "premake5.lua",
    "content": "workspace \"guetzli\"\n  configurations { \"Release\", \"Debug\" }\n  language \"C++\"\n  flags { \"C++11\" }\n  includedirs { \".\", \"third_party/butteraugli\" }\n\n  filter \"action:vs*\"\n    platforms { \"x86_64\", \"x86\" }\n\n  filter \"platforms:x86\"\n    architecture \"x86\"\n  filter \"platforms:x86_64\"\n    architecture \"x86_64\"\n\n  -- workaround for #41\n  filter \"action:gmake\"\n    symbols \"On\"\n\n  filter \"configurations:Debug\"\n    symbols \"On\"\n  filter \"configurations:Release\"\n    optimize \"Full\"\n  filter {}\n\n  project \"guetzli_static\"\n    kind \"StaticLib\"\n    files\n      {\n        \"guetzli/*.cc\",\n        \"guetzli/*.h\",\n        \"third_party/butteraugli/butteraugli/butteraugli.cc\",\n        \"third_party/butteraugli/butteraugli/butteraugli.h\"\n      }\n    removefiles \"guetzli/guetzli.cc\"\n    filter \"action:gmake\"\n      linkoptions { \"`pkg-config --static --libs libpng || libpng-config --static --ldflags`\" }\n      buildoptions { \"`pkg-config --static --cflags libpng || libpng-config --static --cflags`\" }\n\n  project \"guetzli\"\n    kind \"ConsoleApp\"\n    filter \"action:gmake\"\n      linkoptions { \"`pkg-config --libs libpng || libpng-config --ldflags`\" }\n      buildoptions { \"`pkg-config --cflags libpng || libpng-config --cflags`\" }\n    filter \"action:vs*\"\n      links { \"shlwapi\" }\n    filter {}\n    files\n      {\n        \"guetzli/*.cc\",\n        \"guetzli/*.h\",\n        \"third_party/butteraugli/butteraugli/butteraugli.cc\",\n        \"third_party/butteraugli/butteraugli/butteraugli.h\"\n      }\n"
  },
  {
    "path": "snapcraft.yaml",
    "content": "name: guetzli\nversion: master\nsummary: JPEG encoder for excellent compression density at high visual quality\ndescription: |\n  Guetzli-generated images are typically 20-30% smaller than images of\n  equivalent quality generated by libjpeg. Guetzli generates only sequential\n  (nonprogressive) JPEGs due to faster decompression speeds they offer.\n\ngrade: devel\nconfinement: strict\n\napps:\n  guetzli:\n    command: bin/Release/guetzli\n    plugs: [home]\n\nparts:\n  guetzli:\n    source: .\n    plugin: make\n    build-packages: [libpng12-dev, libgflags-dev]\n    artifacts: [bin/Release/guetzli]\n"
  },
  {
    "path": "tests/golden_checksums.txt",
    "content": "40a8e87fc2feecfe5db466f44c248bb970591132f357140fd3cc93705cd233fb  bees-420.jpg.guetzli.jpg\n2da4c37bfc1ed94c1285bf43e07b61c647d160179b8c685707bdf6dcc7d28b33  bees.jpg.guetzli.jpg\n39cfc110d3d389ddf3b9c47adece290ef077b78467f1ed39679b691c6fb7c96d  bees.png.guetzli.jpg\n2da4c37bfc1ed94c1285bf43e07b61c647d160179b8c685707bdf6dcc7d28b33  bees-progressive.jpg.guetzli.jpg\n80fb7d673090ed5269b680a90d958e876190516b76f12caca7d4bf227ef9da3d  bench2-420.jpg.guetzli.jpg\n8f76f86500851858c047d03c2fc49841d60a84163656bf45662d653ee58e66b8  bench2.jpg.guetzli.jpg\n9d7734214baaf01bcd44ee080ea3b7fef8829f2fa51b53b79a8b6474c0c5ae80  bench2.png.guetzli.jpg\n8f76f86500851858c047d03c2fc49841d60a84163656bf45662d653ee58e66b8  bench2-progressive.jpg.guetzli.jpg\ne2ca9887fbe6c98fc84a2688c3278387ba17a19b4cc481c267044ddc8d16ed55  bench-420.jpg.guetzli.jpg\n7cec578c81ab24525dd4fffd793886114d87d80b48a8666795b3b6c400a18bd0  bench.jpg.guetzli.jpg\n4aa0a50b196dba81534e31f451517d952cfbdd40b32c9a9c3c26dd1f954216ce  bench.png.guetzli.jpg\n7cec578c81ab24525dd4fffd793886114d87d80b48a8666795b3b6c400a18bd0  bench-progressive.jpg.guetzli.jpg\ne2dfa6c9642ef73947f64671d3d288c82595293cfeb6aabe490bda6e63d6e824  bicycles-420.jpg.guetzli.jpg\n0dfd25366c2cfe0b93b0ad8da38be623dd067f590968f6dbde66e8fe6b04ceff  bicycles.jpg.guetzli.jpg\na9439e530c365a62c965e2d858c8de8fbd3f44e6e9c6ade1c2248ad373fb8755  bicycles.png.guetzli.jpg\n0dfd25366c2cfe0b93b0ad8da38be623dd067f590968f6dbde66e8fe6b04ceff  bicycles-progressive.jpg.guetzli.jpg\n8ebf427aa736b66d315cead233b6eb6bc0dc49e83529b0c53315b272d35148fa  blue-rose-420.jpg.guetzli.jpg\n9ccdf1be8f0d121d4b6888e47d187e8b6378b63d5592a2b9f001a4d017506bde  blue-rose.jpg.guetzli.jpg\n6c75b537c2d603aa51c9fbc8f7f6b1bff413de269b13eec5982d24cfcf1e0d08  blue-rose.png.guetzli.jpg\n9ccdf1be8f0d121d4b6888e47d187e8b6378b63d5592a2b9f001a4d017506bde  blue-rose-progressive.jpg.guetzli.jpg\n2763977af200403d57c7b9d9d33cfb76947076413c9531b16171c0a5fbc83339  brake-light-420.jpg.guetzli.jpg\na84703948373a12cc1db1edd32616c983db08ff07f550a3be7b111bf9e4ba06f  brake-light.jpg.guetzli.jpg\neda4f537c54ca55eddc03097b6aa57db61c57f80e276306ff4c73b72123e5402  brake-light.png.guetzli.jpg\na84703948373a12cc1db1edd32616c983db08ff07f550a3be7b111bf9e4ba06f  brake-light-progressive.jpg.guetzli.jpg\nc25be9699cad0796f1c663d8a7d92c2e99dfb95bed4efd0d58c15d52bb4fe049  cloth-420.jpg.guetzli.jpg\n6bf2ef7d27a5a8614db0f698b1f977cd0f486fdc2d4260b90f0d64d653383c35  cloth.jpg.guetzli.jpg\n446850f4decd68d77c8a5b09d925fdc9075f44fdfe6dfb2e60a1064677678a3f  cloth.png.guetzli.jpg\n6bf2ef7d27a5a8614db0f698b1f977cd0f486fdc2d4260b90f0d64d653383c35  cloth-progressive.jpg.guetzli.jpg\ne6407f3d38f70dde51584ee174ae29f53cc2d2d7e63812a3405d20079d67a45c  geranium2-420.jpg.guetzli.jpg\n14fb9aab1ebd6d7b8779566665fe2f94b07158b277f29296ef2f9fee71c3c4a4  geranium2.jpg.guetzli.jpg\nb7ded98029eca0ecf75bb01d5ea54a833fa13377136b4e7e404ae35503f82eb8  geranium2.png.guetzli.jpg\n14fb9aab1ebd6d7b8779566665fe2f94b07158b277f29296ef2f9fee71c3c4a4  geranium2-progressive.jpg.guetzli.jpg\n9eea5d54068ccaacfb1839c67c401685646c73edfdc38bac8e1e3a084e268f0d  geranium-420.jpg.guetzli.jpg\n4f249d42280d6f982fa093343236b318be887dc0f2241e125fcf6d4c913305e3  geranium.jpg.guetzli.jpg\n31040ac1623c371187c94946ae91c548c553403f60cc43899d9e13bcf39b6a7c  geranium.png.guetzli.jpg\n4f249d42280d6f982fa093343236b318be887dc0f2241e125fcf6d4c913305e3  geranium-progressive.jpg.guetzli.jpg\n86fb6f73a32bac4c7f3dfd009d5f745dd3694bc773bd853278e14624a91f7434  green-420.jpg.guetzli.jpg\ne1fbdb05fe74f2d78cf6547621d99afea6e72069d8de68da274d66530b5dcdd7  green.jpg.guetzli.jpg\n3df6e963406121db078b99653d7c4e49ce2affe99b31212b026239d082748291  green.png.guetzli.jpg\ne1fbdb05fe74f2d78cf6547621d99afea6e72069d8de68da274d66530b5dcdd7  green-progressive.jpg.guetzli.jpg\nc2fcd25260b5c52871def4a7ef0136be7e7e7f63f836a974c51a4681a651d7c7  green-rose-420.jpg.guetzli.jpg\n90998e98318bb62538fe64f3b60d3100230474bf57dda72cd737eeee8ea482ae  green-rose.jpg.guetzli.jpg\n513e03accb79e60e9c8a2e9832bdbf9f1af8b23c6905cdb476e0626e1a7009d2  green-rose.png.guetzli.jpg\n90998e98318bb62538fe64f3b60d3100230474bf57dda72cd737eeee8ea482ae  green-rose-progressive.jpg.guetzli.jpg\nf1279ca9177e0aea7451bafa4abcd8ecfdf8a939ae97c974fbc802b668d8a56b  hand-420.jpg.guetzli.jpg\n8d2d8f4a95deea2dca8539a0c12ba8186ea93e7d9bcff9cb2c0bb9eab5504d1f  hand.jpg.guetzli.jpg\n4d156e4dbec82cb2f8fa324ea9f9142327d63853379cf3332775fdd40bbafc2f  hand.png.guetzli.jpg\n8d2d8f4a95deea2dca8539a0c12ba8186ea93e7d9bcff9cb2c0bb9eab5504d1f  hand-progressive.jpg.guetzli.jpg\nc3a3f86da0eeacc015504139181c19f874e9631336bb5f90fbf8a367058ec95f  lichen-420.jpg.guetzli.jpg\n44db143ce962b2eb45fcc1a79468d96ca7c677cba864efd9a984e3f86de5a0a5  lichen.jpg.guetzli.jpg\n9df98b8aa95a160a5937b602de715a9c54dde5ea9cd25d188643642656bb22b7  lichen.png.guetzli.jpg\n44db143ce962b2eb45fcc1a79468d96ca7c677cba864efd9a984e3f86de5a0a5  lichen-progressive.jpg.guetzli.jpg\nc8b301b6ecfba6c9e9119b5a59e6174b6e7e9341b96943efd32f5d53c8d5e858  minerology-420.jpg.guetzli.jpg\ncff08d7d8896f5a4a95f04e34a1af6b6e165672532d828e95ade620beca9026f  minerology.jpg.guetzli.jpg\n4d789c23515fabc586d7e16c41e56df3013d6512f4bde17a6e8872b37911fcff  minerology.png.guetzli.jpg\ncff08d7d8896f5a4a95f04e34a1af6b6e165672532d828e95ade620beca9026f  minerology-progressive.jpg.guetzli.jpg\ne4ff3c1299f42d6ee537c1122954a5131f48a0d4fe0ef03c220c4c93543964da  out-of-focus-420.jpg.guetzli.jpg\n57590d333c2970e920fd5239bef84542823a465049aab17a111092863f2126ca  out-of-focus.jpg.guetzli.jpg\n087e342be60df5851977c9a178bf52460108d169eaab8272c2c83789da44d818  out-of-focus.png.guetzli.jpg\n57590d333c2970e920fd5239bef84542823a465049aab17a111092863f2126ca  out-of-focus-progressive.jpg.guetzli.jpg\ndcff545a0bf03441a679134a4fa3922325bd0196447cdf2de63eea48eac7fab3  pimpinelli-420.jpg.guetzli.jpg\nfccbde44ee9cc867bda3f2d93ae8b47a893ba683a0f0e2aff7c9213c126b4830  pimpinelli.jpg.guetzli.jpg\na51ffedd0a9c814a5d763418e523b18c25abf167088f245018196a19c7830583  pimpinelli.png.guetzli.jpg\nfccbde44ee9cc867bda3f2d93ae8b47a893ba683a0f0e2aff7c9213c126b4830  pimpinelli-progressive.jpg.guetzli.jpg\na23ecdeb4a556d25a596fe5d49dde55eb7d4531d999629a610e68953811c678f  pink-flower-420.jpg.guetzli.jpg\n87ac31333c5a61e00947a2f7f94078d9b753b6a0291d218ca5547861a7c28b57  pink-flower.jpg.guetzli.jpg\n773fb2075a222ca7e07bec1c87f7453fc4d4b578e44ee816bc2f90d971706a70  pink-flower.png.guetzli.jpg\n87ac31333c5a61e00947a2f7f94078d9b753b6a0291d218ca5547861a7c28b57  pink-flower-progressive.jpg.guetzli.jpg\naceb338115241d9984510fd2e8a2bf46b3c5fc431e827a2d1efe496dff038675  port-420.jpg.guetzli.jpg\n02aac145b6df57db2913952d3d46c8d456e2f000cff1ff4bfc574b27175335e0  port.jpg.guetzli.jpg\n157a25812bcf7bce343fe6c6a88932ee49117b46b5d3ba0aa3421edc8f2f1a09  port.png.guetzli.jpg\n02aac145b6df57db2913952d3d46c8d456e2f000cff1ff4bfc574b27175335e0  port-progressive.jpg.guetzli.jpg\nf41f613ecfae42d050115b785c1591724fcb7937c361b9c5d2a3248b7580953f  rainbow-420.jpg.guetzli.jpg\n74d94a13c52b0d582c50d6bc70cecb6762c08740db6c234dff9b0e1c04fccbb5  rainbow.jpg.guetzli.jpg\n657efb5cfa742fbdfd6304703b131a63c2ddf8b686600840a800e7d94b4da0eb  rainbow.png.guetzli.jpg\n74d94a13c52b0d582c50d6bc70cecb6762c08740db6c234dff9b0e1c04fccbb5  rainbow-progressive.jpg.guetzli.jpg\n4667bf8db85507bf260bdbad439b87250e0613092131e0a662f22164d7abb91e  red-flowers-420.jpg.guetzli.jpg\nd48d6208e857fc92e9fb584d19e5738d767b520d3fd57937f16657a4cfe57743  red-flowers.jpg.guetzli.jpg\n6a2fb22da73b20e80d31fd2920d16c847cc0f68eb44af3f924766339f8869a99  red-flowers.png.guetzli.jpg\nd48d6208e857fc92e9fb584d19e5738d767b520d3fd57937f16657a4cfe57743  red-flowers-progressive.jpg.guetzli.jpg\n37827b7701fcb66ee2d808f5c7a5be5f6fbe732241f178f152b12103e3581d35  red-room-420.jpg.guetzli.jpg\ned14d00d18dc1b810ad595db079ce8897d00afc39e9cd09dcc122eb8c9ab59f6  red-room.jpg.guetzli.jpg\nb1ca1634719fda14c88dea08f99fab9cd5450cc41dd32385387fd774e33896ce  red-room.png.guetzli.jpg\ned14d00d18dc1b810ad595db079ce8897d00afc39e9cd09dcc122eb8c9ab59f6  red-room-progressive.jpg.guetzli.jpg\nc5499fdc97b3ae02d77ea12140d6da8ad645406e66adc88250a3c980bb70fe7d  red-rose-420.jpg.guetzli.jpg\nf9a97e475af9127ea6d6d4d41fec52330ca075aae707185d90910fe198695e8d  red-rose.jpg.guetzli.jpg\n22f21955e7078745d03c1eb1985b8c5ffbd0b615870071a821102c44bd94af97  red-rose.png.guetzli.jpg\nf9a97e475af9127ea6d6d4d41fec52330ca075aae707185d90910fe198695e8d  red-rose-progressive.jpg.guetzli.jpg\n4df6d9b244c2d02cacff35ada998da3be13d1c9f5e42d4a2ab9b4725fc78dfa5  rgb-420.jpg.guetzli.jpg\n19256b30557be9dc6a7effe6418f2c1ba6e624940ef1f41c0ca71e356963014c  rgb.jpg.guetzli.jpg\nc1f8e4161a8b6baddea1d279f4d490670560d9c5d1161b66ee101c4250d8dd48  rgb.png.guetzli.jpg\n19256b30557be9dc6a7effe6418f2c1ba6e624940ef1f41c0ca71e356963014c  rgb-progressive.jpg.guetzli.jpg\na2eccf2d9e63c830f6666b7082589e5e58f551af460277110b0a21f07bc8e6d6  station-420.jpg.guetzli.jpg\n0d5a61eaeda6616f026bea9789620b91c47d4c4b099099d28c76708c9e9caf8f  station.jpg.guetzli.jpg\n3cf674f2687bee61e43ef0e57bcec15d93a945c14e4413d22c1a0ee13d64f4f2  station.png.guetzli.jpg\n0d5a61eaeda6616f026bea9789620b91c47d4c4b099099d28c76708c9e9caf8f  station-progressive.jpg.guetzli.jpg\n632c1b8aa116847dbb3471fce2e7201eb2bd028f22ebc920ae97344bfaf8c263  stp2-420.jpg.guetzli.jpg\n6e741f8d6f27c4aec93d3bd01216b28aac31f7f8f501e0cbb49249ba02e3b82a  stp2.jpg.guetzli.jpg\n976fd6eca7e49b097817a1424a41ba71c8d3edbdb3fe9b55db4a4a36404c0b34  stp2.png.guetzli.jpg\n6e741f8d6f27c4aec93d3bd01216b28aac31f7f8f501e0cbb49249ba02e3b82a  stp2-progressive.jpg.guetzli.jpg\na817caaf39ca100eb14ab96295298e9ce744e9262d6e678d31e86d2990ac5c2d  stp-420.jpg.guetzli.jpg\nb7d1652051ed7ee6e07cb4b8918ab92042573746cd5a8fd1342aeaefc3e11c26  stp.jpg.guetzli.jpg\n126d2d08e936828e38d70543c576fc81021aba7010a8368842abfcd777672e9a  stp.png.guetzli.jpg\nb7d1652051ed7ee6e07cb4b8918ab92042573746cd5a8fd1342aeaefc3e11c26  stp-progressive.jpg.guetzli.jpg\ncf30b2be770b1cf597cae579d7d9e027586e0b53eb022d9f875bc56152c44cd4  vflower-420.jpg.guetzli.jpg\nc65d52efb093cb3764dce7efdf6320728faf5fbb48545ec7de107b505abd00cb  vflower.jpg.guetzli.jpg\n29f303b8354443478b6f209b6e250ab40740b58cbcee34bc5669b0eb8da4ee2b  vflower.png.guetzli.jpg\nc65d52efb093cb3764dce7efdf6320728faf5fbb48545ec7de107b505abd00cb  vflower-progressive.jpg.guetzli.jpg\n6c53f97edc667a5d871045fa97bd372c2a84a94a126f5d5d25d6ffadc97574c7  white-yellow-420.jpg.guetzli.jpg\n7b83bd4ab679f721bc067ce86e2375c355265bb1f01668efc47423f0c707cd0b  white-yellow.jpg.guetzli.jpg\n62759933c7b505e531fda54bf863ad494b09a9c91262c43a715777658d29fee7  white-yellow.png.guetzli.jpg\n7b83bd4ab679f721bc067ce86e2375c355265bb1f01668efc47423f0c707cd0b  white-yellow-progressive.jpg.guetzli.jpg\nf0cea391185003b58594a9d4e43226be4e6e68ed882ebee8e0787fc129613d74  wollerau-420.jpg.guetzli.jpg\nafe66a4d5b26180cc2471aac20253f7f7a0064fb458bdbc8a4376819779d126f  wollerau.jpg.guetzli.jpg\ned223e0974fa0030ad83c20b4616105355efd5d3360fbb496956ad3d4f9e59e8  wollerau.png.guetzli.jpg\nafe66a4d5b26180cc2471aac20253f7f7a0064fb458bdbc8a4376819779d126f  wollerau-progressive.jpg.guetzli.jpg\na9f72e6805afa346e8dff15d9f113d5cf637a4a11c670b4454c9a1dedd1824f8  yellow2-420.jpg.guetzli.jpg\n62295c47a2abbebd89d56ad9cbdb85fa4f3e80781bee69977058b7869de46b5c  yellow2.jpg.guetzli.jpg\n5be448a421e9fef23e054a683bef8e3fe19cd968a72d250944050b176600e540  yellow2.png.guetzli.jpg\n62295c47a2abbebd89d56ad9cbdb85fa4f3e80781bee69977058b7869de46b5c  yellow2-progressive.jpg.guetzli.jpg\nd268fb8e60412f56689f473da391330c594d661b80af9bd46dbfbfed7a40d5f3  yellow-420.jpg.guetzli.jpg\n31a33c73f06c4b5b3abdfafff7a50e1b107b013802858a3dc405a59d333b6b8b  yellow.jpg.guetzli.jpg\nee5b2844b2b23ad50434539ae31ec4b3ea027595fd959d192c03553bf167ae24  yellow.png.guetzli.jpg\n31a33c73f06c4b5b3abdfafff7a50e1b107b013802858a3dc405a59d333b6b8b  yellow-progressive.jpg.guetzli.jpg\n"
  },
  {
    "path": "tests/golden_test.sh",
    "content": "#!/bin/bash\n\nGUETZLI=${1:-bin/Release/guetzli}\nINPUT_DIR_PNG=$HOME/.cache/guetzli-test-corpus\nINPUT_DIR_JPG=$(mktemp -d)\nOUTPUT_DIR=$(mktemp -d)\n\nif [[ -d $INPUT_DIR_PNG ]]; then\n  (cd $INPUT_DIR_PNG ; sha256sum -c ; exit $? ) < $(dirname $0)/png_checksums.txt || rm -r ${INPUT_DIR_PNG}\nfi\n\nif [[ ! -d $INPUT_DIR_PNG ]]; then\n  mkdir -p $INPUT_DIR_PNG || exit 2\n  curl https://storage.googleapis.com/test-image-corpus/test-corpus.tgz | tar -C $INPUT_DIR_PNG -zxf - || exit 2\nfi\n\nfor i in $INPUT_DIR_PNG/*.png; do\n  pngtopnm < $i | cjpeg -sample 1x1 -quality 100 > $INPUT_DIR_JPG/$(basename $i .png).jpg || exit 2\n  pngtopnm < $i | cjpeg -sample 1x1 -progressive -quality 100 > $INPUT_DIR_JPG/$(basename $i .png)-progressive.jpg || exit 2\n  pngtopnm < $i | cjpeg -sample 2x2,1x1,1x1 -quality 100 > $INPUT_DIR_JPG/$(basename $i .png)-420.jpg || exit 2\ndone\n\nfor i in $INPUT_DIR_PNG/*.png $INPUT_DIR_JPG/*.jpg; do\n  echo $i $OUTPUT_DIR/$(basename $i).guetzli.jpg\ndone | xargs -L 1 -P $(getconf _NPROCESSORS_ONLN) -t $GUETZLI\n\nif [[ -n \"$UPDATE_GOLDEN\" ]]; then\n  (cd $OUTPUT_DIR ; sha256sum *) > $(dirname $0)/golden_checksums.txt\nelse\n  (cd $OUTPUT_DIR ; sha256sum -c) < $(dirname $0)/golden_checksums.txt\nfi\n\n"
  },
  {
    "path": "tests/png_checksums.txt",
    "content": "3531f6d5d30faf9d781f444398f32495a94ad276f2654c579a489bb2014f156b  bees.png\n0210aa8ed8d044e10b6e816ef0f6603de1d5834a1d0d2027898fafdc7bcac396  bench2.png\ne1fe02c671620555d25ee4e3ed8d16a216a40d52edf1b73c0d5088cee1b19da5  bench.png\n616a52b544630ed7a6b8db33ca12c1bd6aad4ca07faf8f658cf4d1981af5b0bd  bicycles.png\n43252838b12e987502dfc98446e474a9b1008a6a49987ea6f6692eee68d3f541  blue-rose.png\n8f6a52fb6c3ffb1b2c32e47075c7e2c22975570f0da0694c4272e1cec4c05438  brake-light.png\n7572fc7bb317838b3ad0a7e39154e92c04f56c172ea347e85752146da2978578  cloth.png\neb7797c8929576ccd8acc27907a3f354ae19246a58d6f02279918be541359692  geranium2.png\nd0f99e29280546f75b2065590ef3d9bed7ba86c3f1e2fb403971c83ebc996594  geranium.png\nc1f96c117f607205773ce5339ee2a1076b8f99de4bf674ee0aae25984f39c243  green.png\n7564f4e4693ac423581e1d496fe339dd13cbfc6b1393c0d9e8c5bd4a1f53a8e8  green-rose.png\nb5236607cdce8f88e42132f6760c1b61a8dc83d975cd44a0be40684eaf035294  hand.png\na784868d51b5a1d170fa1ae733cced48e1fc30f0bf42c81e6a9fd664e462bb83  lichen.png\nab0eff8e6b57694455bffa91e74c91a0507e61294032521caacc28128ad3cf90  minerology.png\nd30b95585fe03802b9d9a70d62f8ce3b7b84f1072de67efabf59b8a83b4d9eae  out-of-focus.png\na6e05977e62e39d5faa7154fe00d2aaa3d18f28b9f933ccd8906c2f8a1f11633  pimpinelli.png\n334d783039d60b805f46a2e9e4667749bc64ccdd97bb356e5faa045311861efb  pink-flower.png\n390ecd976e1e73f46addd6f245efe9fd886fed7437cbf5049db02e2f6f2d6afd  port.png\n858018895b71dd3cc612c3939832193c2d656b2ae1a8aec7a38c2f41f61d7005  rainbow.png\nd81b49b0a4db09aab24f573c141899955391440ccfdfa57c060eafa76c3416a4  red-flowers.png\n0ebf4139eece6a5fdaf5397ed64c7fb8b9115a607ae7f82b2e4bf56ae0405f99  red-room.png\n82a845a65b2c7217f7baea1d6b63f466d70dec5783ebb4b6b41f060348d5bfb3  red-rose.png\n37b9c33b7ee63788ad5f09eb8f53127ebb2fc9d9113b856e38c440fe8a78df05  rgb.png\n7dae648bb5f9bf22bb3c1f51f94ef1a04ec20b981c9d5b01f42f364d5ab63c11  station.png\n8241aa8f95a8b051936cd519e0205a67a9d1f48e229c6c5745575adf68f4a3ab  stp2.png\n03c0139181f9212e188348e2315caf73a54e213f03e872f04759ef2936f851dc  stp.png\n4938aa4a4527df8deecd10c4007d4a63ff8d4f81f63b9fd87c471a0b5437a50c  vflower.png\nada6424a72548e7b80ac58590b58d1b7a7e185ef87ebdf76841b2a573b9cf23a  white-yellow.png\n77a5d289b2fa19b0f72de03cad2ce311c8b3cb088a32928811b2287fbeb18ce9  wollerau.png\nf1dbf76f4bb7b7e79c98fd1e8572f5b9a01d1a6c0ea8a1e6361dc2b8188f094e  yellow2.png\n37ccbd18a347ba4d326fb8d9ae56fbadaaefca9bb5a2298b9d09f30c18616ae6  yellow.png\n"
  },
  {
    "path": "tests/smoke_test.sh",
    "content": "#!/bin/bash\n\nGUETZLI=${1:-bin/Release/guetzli}\nBEES_PNG=$(dirname $0)/bees.png\nBEES_JPG=$(mktemp ${TMPDIR:-/tmp}/beesXXXX.jpg)\nBUTTERAUGLI=$2\n\npngtopnm < $BEES_PNG | cjpeg -sample 1x1 -quality 100 > $BEES_JPG || exit 2\n\nfunction run_test() {\n  # png/jpeg stdin/file stdout/file flags...\n  local in=\n  local out=$(mktemp ${TMPDIR:-/tmp}/beesXXX.guetzli.jpg)\n  echo \"Testing $@, output in $out\"\n  case \"$1\" in\n    png) in=$BEES_PNG ;;\n    jpeg) in=$BEES_JPG ;;\n    *) exit 2 ;;\n  esac\n  shift\n  local inouthandling=\"$1:$2\"\n  shift; shift\n  case \"$inouthandling\" in\n    file:file) $GUETZLI $@ $in $out ;;\n    stdin:file) $GUETZLI $@ - $out < $in ;;\n    file:stdout) $GUETZLI $@ $in - > $out ;;\n    stdin:stdout) $GUETZLI $@ - - < $in > $out ;;\n    *) exit 2 ;;\n  esac\n  test -f \"$out\" || { echo \"$out doesn't exist\"; exit 1; }\n  djpeg < $out > /dev/null || { echo \"$out is not a valid JPEG\"; exit 1; }\n  if [ -n \"$BUTTERAUGLI\" ]; then\n    $BUTTERAUGLI $in $out\n  fi\n  rm $out\n  echo \"OK\"\n}\n\nrun_test png file file\nrun_test png stdin file\nrun_test jpeg file file\nrun_test jpeg stdin file\n\nrun_test png stdin stdout\nrun_test png file stdout\n\nrun_test png file stdout --verbose\nrun_test png file stdout --nomemlimit\nrun_test png file stdout --memlimit 100\nrun_test png file stdout --quality 85\n\necho $GUETZLI /dev/null /dev/null\n$GUETZLI /dev/null /dev/null\nif [[ $? -ne 1 ]]; then\n  echo \"Expected a clean failure\"\n  exit 1\nfi\n\n"
  },
  {
    "path": "third_party/butteraugli/BUILD",
    "content": "cc_library(\n    name = \"butteraugli_lib\",\n    srcs = [\n        \"butteraugli/butteraugli.cc\",\n        \"butteraugli/butteraugli.h\",\n    ],\n    hdrs = [\n        \"butteraugli/butteraugli.h\",\n    ],\n    copts = [\"-Wno-sign-compare\"],\n    visibility = [\"//visibility:public\"],\n)\n\ncc_binary(\n    name = \"butteraugli\",\n    srcs = [\"butteraugli/butteraugli_main.cc\"],\n    copts = [\"-Wno-sign-compare\"],\n    visibility = [\"//visibility:public\"],\n    deps = [\n        \":butteraugli_lib\",\n        \"@jpeg_archive//:jpeg\",\n        \"@png_archive//:png\",\n    ],\n)\n"
  },
  {
    "path": "third_party/butteraugli/LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "third_party/butteraugli/README.md",
    "content": "# butteraugli\n\n> A tool for measuring perceived differences between images\n\n## Introduction\n\nButteraugli is a project that estimates the psychovisual similarity of two\nimages. It gives a score for the images that is reliable in the domain of barely\nnoticeable differences. Butteraugli not only gives a scalar score, but also\ncomputes a spatial map of the level of differences.\n\nOne of the main motivations for this project is the statistical differences in\nlocation and density of different color receptors, particularly the low density\nof blue cones in the fovea. Another motivation comes from more accurate modeling\nof ganglion cells, particularly the frequency space inhibition.\n\n## Use\n\nButteraugli can work as a quality metric for lossy image and video compression.\nOn our small test corpus butteraugli performs better than our implementations of\nthe reference methods, psnrhsv-m, ssim, and our yuv-color-space variant of ssim.\nOne possible use is to define the quality level setting used in a jpeg\ncompressor, or to compare two or more compression methods at the same level of\npsychovisual differences.\n\nButteraugli is intended to be a research tool more than a practical tool for\nchoosing compression formats. We don't know how well butteraugli performs with\nmajor deformations -- we have mostly tuned it within a small range of quality,\nroughly corresponding to jpeg qualities 90 to 95.\n\n## Interface\n\nOnly a C++ interface is provided. The interface takes two images and outputs a\nmap together with a scalar value defining the difference. The scalar value can\nbe compared to two reference values that divide the value space into three\nexperience classes: 'great', 'acceptable' and 'not acceptable'.\n\n## Build instructions\n\nInstall [Bazel](http://bazel.build) by following the\n[instructions](https://www.bazel.build/docs/install.html). Run `bazel build -c opt\n//:butteraugli` in the directory that contains this README file to build the\n[command-line utility](#cmdline-tool). If you want to use Butteraugli as a\nlibrary, depend on the `//:butteraugli_lib` target.\n\nAlternatively, you can use the Makefile provided in the `butteraugli` directory,\nafter ensuring that [libpng](http://www.libpng.org/) and\n[libjpeg](http://ijg.org/) are installed. On some systems you might need to also\ninstall corresponding `-dev` packages.\n\nThe code is portable and also compiles on Windows after defining\n`_CRT_SECURE_NO_WARNINGS` in the project settings.\n\n## Command-line utility {#cmdline-tool}\n\nButteraugli, apart from the library, comes bundled with a comparison tool. The\ncomparison tool supports PNG and JPG images as inputs. To compare images, run:\n\n```\nbutteraugli image1.{png|jpg} image2.{png|jpg}\n```\n\nThe tool can also produce a heatmap of differences between images. The heatmap\nwill be output as a PNM image. To produce one, run:\n\n```\nbutteraugli image1.{png|jpg} image2.{png|jpg} heatmap.pnm\n```\n"
  },
  {
    "path": "third_party/butteraugli/WORKSPACE",
    "content": "workspace(name = \"butteraugli\")\n\nnew_http_archive(\n  name = \"png_archive\",\n  url = \"http://github.com/glennrp/libpng/archive/v1.2.57.zip\",\n  sha256 = \"a941dc09ca00148fe7aaf4ecdd6a67579c293678ed1e1cf633b5ffc02f4f8cf7\",\n  strip_prefix = \"libpng-1.2.57\",\n  build_file = \"png.BUILD\",\n)\n\nnew_http_archive(\n  name = \"zlib_archive\",\n  url = \"http://zlib.net/fossils/zlib-1.2.10.tar.gz\",\n  sha256 = \"8d7e9f698ce48787b6e1c67e6bff79e487303e66077e25cb9784ac8835978017\",\n  strip_prefix = \"zlib-1.2.10\",\n  build_file = \"zlib.BUILD\",\n)\n\nnew_http_archive(\n  name = \"jpeg_archive\",\n  url = \"http://www.ijg.org/files/jpegsrc.v9b.tar.gz\",\n  sha256 = \"240fd398da741669bf3c90366f58452ea59041cacc741a489b99f2f6a0bad052\",\n  strip_prefix = \"jpeg-9b\",\n  build_file = \"jpeg.BUILD\",\n)\n"
  },
  {
    "path": "third_party/butteraugli/butteraugli/Makefile",
    "content": "LDLIBS += -lpng -ljpeg\nCXXFLAGS += -std=c++11 -I..\nLINK.o = $(LINK.cc)\n\nall: butteraugli.o butteraugli_main.o butteraugli\n\nbutteraugli: butteraugli.o butteraugli_main.o\n"
  },
  {
    "path": "third_party/butteraugli/butteraugli/butteraugli.cc",
    "content": "// Copyright 2016 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n// Author: Jyrki Alakuijala (jyrki.alakuijala@gmail.com)\n//\n// The physical architecture of butteraugli is based on the following naming\n// convention:\n//   * Opsin - dynamics of the photosensitive chemicals in the retina\n//             with their immediate electrical processing\n//   * Xyb - hybrid opponent/trichromatic color space\n//     x is roughly red-subtract-green.\n//     y is yellow.\n//     b is blue.\n//     Xyb values are computed from Opsin mixing, not directly from rgb.\n//   * Mask - for visual masking\n//   * Hf - color modeling for spatially high-frequency features\n//   * Lf - color modeling for spatially low-frequency features\n//   * Diffmap - to cluster and build an image of error between the images\n//   * Blur - to hold the smoothing code\n\n#include \"butteraugli/butteraugli.h\"\n\n#include <assert.h>\n#include <math.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <string.h>\n\n#include <algorithm>\n#include <array>\n\n\n// Restricted pointers speed up Convolution(); MSVC uses a different keyword.\n#ifdef _MSC_VER\n#define __restrict__ __restrict\n#endif\n\n#ifndef PROFILER_ENABLED\n#define PROFILER_ENABLED 0\n#endif\n#if PROFILER_ENABLED\n#else\n#define PROFILER_FUNC\n#define PROFILER_ZONE(name)\n#endif\n\nnamespace butteraugli {\n\nvoid *CacheAligned::Allocate(const size_t bytes) {\n  char *const allocated = static_cast<char *>(malloc(bytes + kCacheLineSize));\n  if (allocated == nullptr) {\n    return nullptr;\n  }\n  const uintptr_t misalignment =\n      reinterpret_cast<uintptr_t>(allocated) & (kCacheLineSize - 1);\n  // malloc is at least kPointerSize aligned, so we can store the \"allocated\"\n  // pointer immediately before the aligned memory.\n  assert(misalignment % kPointerSize == 0);\n  char *const aligned = allocated + kCacheLineSize - misalignment;\n  memcpy(aligned - kPointerSize, &allocated, kPointerSize);\n  return BUTTERAUGLI_ASSUME_ALIGNED(aligned, 64);\n}\n\nvoid CacheAligned::Free(void *aligned_pointer) {\n  if (aligned_pointer == nullptr) {\n    return;\n  }\n  char *const aligned = static_cast<char *>(aligned_pointer);\n  assert(reinterpret_cast<uintptr_t>(aligned) % kCacheLineSize == 0);\n  char *allocated;\n  memcpy(&allocated, aligned - kPointerSize, kPointerSize);\n  assert(allocated <= aligned - kPointerSize);\n  assert(allocated >= aligned - kCacheLineSize);\n  free(allocated);\n}\n\nstatic inline bool IsNan(const float x) {\n  uint32_t bits;\n  memcpy(&bits, &x, sizeof(bits));\n  const uint32_t bitmask_exp = 0x7F800000;\n  return (bits & bitmask_exp) == bitmask_exp && (bits & 0x7FFFFF);\n}\n\nstatic inline bool IsNan(const double x) {\n  uint64_t bits;\n  memcpy(&bits, &x, sizeof(bits));\n  return (0x7ff0000000000001ULL <= bits && bits <= 0x7fffffffffffffffULL) ||\n         (0xfff0000000000001ULL <= bits && bits <= 0xffffffffffffffffULL);\n}\n\nstatic inline void CheckImage(const ImageF &image, const char *name) {\n  for (size_t y = 0; y < image.ysize(); ++y) {\n    const float * const BUTTERAUGLI_RESTRICT row = image.Row(y);\n    for (size_t x = 0; x < image.xsize(); ++x) {\n      if (IsNan(row[x])) {\n        printf(\"Image %s @ %lu,%lu (of %lu,%lu)\\n\", name, x, y, image.xsize(),\n               image.ysize());\n        exit(1);\n      }\n    }\n  }\n}\n\n#if BUTTERAUGLI_ENABLE_CHECKS\n\n#define CHECK_NAN(x, str)                \\\n  do {                                   \\\n    if (IsNan(x)) {                      \\\n      printf(\"%d: %s\\n\", __LINE__, str); \\\n      abort();                           \\\n    }                                    \\\n  } while (0)\n\n#define CHECK_IMAGE(image, name) CheckImage(image, name)\n\n#else\n\n#define CHECK_NAN(x, str)\n#define CHECK_IMAGE(image, name)\n\n#endif\n\n\n// Purpose of kInternalGoodQualityThreshold:\n// Normalize 'ok' image degradation to 1.0 across different versions of\n// butteraugli.\nstatic const double kInternalGoodQualityThreshold = 20.35;\nstatic const double kGlobalScale = 1.0 / kInternalGoodQualityThreshold;\n\ninline float DotProduct(const float u[3], const float v[3]) {\n  return u[0] * v[0] + u[1] * v[1] + u[2] * v[2];\n}\n\nstd::vector<float> ComputeKernel(float sigma) {\n  const float m = 2.25;  // Accuracy increases when m is increased.\n  const float scaler = -1.0 / (2 * sigma * sigma);\n  const int diff = std::max<int>(1, m * fabs(sigma));\n  std::vector<float> kernel(2 * diff + 1);\n  for (int i = -diff; i <= diff; ++i) {\n    kernel[i + diff] = exp(scaler * i * i);\n  }\n  return kernel;\n}\n\nvoid ConvolveBorderColumn(\n    const ImageF& in,\n    const std::vector<float>& kernel,\n    const float weight_no_border,\n    const float border_ratio,\n    const size_t x,\n    float* const BUTTERAUGLI_RESTRICT row_out) {\n  const int offset = kernel.size() / 2;\n  int minx = x < offset ? 0 : x - offset;\n  int maxx = std::min<int>(in.xsize() - 1, x + offset);\n  float weight = 0.0f;\n  for (int j = minx; j <= maxx; ++j) {\n    weight += kernel[j - x + offset];\n  }\n  // Interpolate linearly between the no-border scaling and border scaling.\n  weight = (1.0f - border_ratio) * weight + border_ratio * weight_no_border;\n  float scale = 1.0f / weight;\n  for (size_t y = 0; y < in.ysize(); ++y) {\n    const float* const BUTTERAUGLI_RESTRICT row_in = in.Row(y);\n    float sum = 0.0f;\n    for (int j = minx; j <= maxx; ++j) {\n      sum += row_in[j] * kernel[j - x + offset];\n    }\n    row_out[y] = sum * scale;\n  }\n}\n\n// Computes a horizontal convolution and transposes the result.\nImageF Convolution(const ImageF& in,\n                   const std::vector<float>& kernel,\n                   const float border_ratio) {\n  ImageF out(in.ysize(), in.xsize());\n  const int len = kernel.size();\n  const int offset = kernel.size() / 2;\n  float weight_no_border = 0.0f;\n  for (int j = 0; j < len; ++j) {\n    weight_no_border += kernel[j];\n  }\n  float scale_no_border = 1.0f / weight_no_border;\n  const int border1 = in.xsize() <= offset ? in.xsize() : offset;\n  const int border2 = in.xsize() - offset;\n  std::vector<float> scaled_kernel = kernel;\n  for (int i = 0; i < scaled_kernel.size(); ++i) {\n    scaled_kernel[i] *= scale_no_border;\n  }\n  // left border\n  for (int x = 0; x < border1; ++x) {\n    ConvolveBorderColumn(in, kernel, weight_no_border, border_ratio, x,\n                         out.Row(x));\n  }\n  // middle\n  for (size_t y = 0; y < in.ysize(); ++y) {\n    const float* const BUTTERAUGLI_RESTRICT row_in = in.Row(y);\n    for (int x = border1; x < border2; ++x) {\n      const int d = x - offset;\n      float* const BUTTERAUGLI_RESTRICT row_out = out.Row(x);\n      float sum = 0.0f;\n      for (int j = 0; j < len; ++j) {\n        sum += row_in[d + j] * scaled_kernel[j];\n      }\n      row_out[y] = sum;\n    }\n  }\n  // right border\n  for (int x = border2; x < in.xsize(); ++x) {\n    ConvolveBorderColumn(in, kernel, weight_no_border, border_ratio, x,\n                         out.Row(x));\n  }\n  return out;\n}\n\n// A blur somewhat similar to a 2D Gaussian blur.\n// See: https://en.wikipedia.org/wiki/Gaussian_blur\nImageF Blur(const ImageF& in, float sigma, float border_ratio) {\n  std::vector<float> kernel = ComputeKernel(sigma);\n  return Convolution(Convolution(in, kernel, border_ratio),\n                     kernel, border_ratio);\n}\n\n// Clamping linear interpolator.\ninline double InterpolateClampNegative(const double *array,\n                                       int size, double ix) {\n  if (ix < 0) {\n    ix = 0;\n  }\n  int baseix = static_cast<int>(ix);\n  double res;\n  if (baseix >= size - 1) {\n    res = array[size - 1];\n  } else {\n    double mix = ix - baseix;\n    int nextix = baseix + 1;\n    res = array[baseix] + mix * (array[nextix] - array[baseix]);\n  }\n  return res;\n}\n\ndouble GammaMinArg() {\n  double out0, out1, out2;\n  OpsinAbsorbance(0.0, 0.0, 0.0, &out0, &out1, &out2);\n  return std::min(out0, std::min(out1, out2));\n}\n\ndouble GammaMaxArg() {\n  double out0, out1, out2;\n  OpsinAbsorbance(255.0, 255.0, 255.0, &out0, &out1, &out2);\n  return std::max(out0, std::max(out1, out2));\n}\n\ndouble SimpleGamma(double v) {\n  static const double kGamma = 0.372322653176;\n  static const double limit = 37.8000499603;\n  double bright = v - limit;\n  if (bright >= 0) {\n    static const double mul = 0.0950819040934;\n    v -= bright * mul;\n  }\n  {\n    static const double limit2 = 74.6154406429;\n    double bright2 = v - limit2;\n    if (bright2 >= 0) {\n      static const double mul = 0.01;\n      v -= bright2 * mul;\n    }\n  }\n  {\n    static const double limit2 = 82.8505938033;\n    double bright2 = v - limit2;\n    if (bright2 >= 0) {\n      static const double mul = 0.0316722592629;\n      v -= bright2 * mul;\n    }\n  }\n  {\n    static const double limit2 = 92.8505938033;\n    double bright2 = v - limit2;\n    if (bright2 >= 0) {\n      static const double mul = 0.221249885752;\n      v -= bright2 * mul;\n    }\n  }\n  {\n    static const double limit2 = 102.8505938033;\n    double bright2 = v - limit2;\n    if (bright2 >= 0) {\n      static const double mul = 0.0402547853939;\n      v -= bright2 * mul;\n    }\n  }\n  {\n    static const double limit2 = 112.8505938033;\n    double bright2 = v - limit2;\n    if (bright2 >= 0) {\n      static const double mul = 0.021471798711500003;\n      v -= bright2 * mul;\n    }\n  }\n  static const double offset = 0.106544447664;\n  static const double scale = 10.7950943969;\n  double retval = scale * (offset + pow(v, kGamma));\n  return retval;\n}\n\nstatic inline double Gamma(double v) {\n  //return SimpleGamma(v);\n  return GammaPolynomial(v);\n}\n\nstd::vector<ImageF> OpsinDynamicsImage(const std::vector<ImageF>& rgb) {\n  PROFILER_FUNC;\n  std::vector<ImageF> xyb(3);\n  std::vector<ImageF> blurred(3);\n  const double kSigma = 1.2;\n  for (int i = 0; i < 3; ++i) {\n    xyb[i] = ImageF(rgb[i].xsize(), rgb[i].ysize());\n    blurred[i] = Blur(rgb[i], kSigma, 0.0f);\n  }\n  for (size_t y = 0; y < rgb[0].ysize(); ++y) {\n    const float* const BUTTERAUGLI_RESTRICT row_r = rgb[0].Row(y);\n    const float* const BUTTERAUGLI_RESTRICT row_g = rgb[1].Row(y);\n    const float* const BUTTERAUGLI_RESTRICT row_b = rgb[2].Row(y);\n    const float* const BUTTERAUGLI_RESTRICT row_blurred_r = blurred[0].Row(y);\n    const float* const BUTTERAUGLI_RESTRICT row_blurred_g = blurred[1].Row(y);\n    const float* const BUTTERAUGLI_RESTRICT row_blurred_b = blurred[2].Row(y);\n    float* const BUTTERAUGLI_RESTRICT row_out_x = xyb[0].Row(y);\n    float* const BUTTERAUGLI_RESTRICT row_out_y = xyb[1].Row(y);\n    float* const BUTTERAUGLI_RESTRICT row_out_b = xyb[2].Row(y);\n    for (size_t x = 0; x < rgb[0].xsize(); ++x) {\n      float sensitivity[3];\n      {\n        // Calculate sensitivity based on the smoothed image gamma derivative.\n        float pre_mixed0, pre_mixed1, pre_mixed2;\n        OpsinAbsorbance(row_blurred_r[x], row_blurred_g[x], row_blurred_b[x],\n                        &pre_mixed0, &pre_mixed1, &pre_mixed2);\n        // TODO: use new polynomial to compute Gamma(x)/x derivative.\n        sensitivity[0] = Gamma(pre_mixed0) / pre_mixed0;\n        sensitivity[1] = Gamma(pre_mixed1) / pre_mixed1;\n        sensitivity[2] = Gamma(pre_mixed2) / pre_mixed2;\n      }\n      float cur_mixed0, cur_mixed1, cur_mixed2;\n      OpsinAbsorbance(row_r[x], row_g[x], row_b[x],\n                      &cur_mixed0, &cur_mixed1, &cur_mixed2);\n      cur_mixed0 *= sensitivity[0];\n      cur_mixed1 *= sensitivity[1];\n      cur_mixed2 *= sensitivity[2];\n      RgbToXyb(cur_mixed0, cur_mixed1, cur_mixed2,\n               &row_out_x[x], &row_out_y[x], &row_out_b[x]);\n    }\n  }\n  return xyb;\n}\n\n// Make area around zero less important (remove it).\nstatic BUTTERAUGLI_INLINE float RemoveRangeAroundZero(float w, float x) {\n  return x > w ? x - w : x < -w ? x + w : 0.0f;\n}\n\n// Make area around zero more important (2x it until the limit).\nstatic BUTTERAUGLI_INLINE float AmplifyRangeAroundZero(float w, float x) {\n  return x > w ? x + w : x < -w ? x - w : 2.0f * x;\n}\n\n// XybLowFreqToVals converts from low-frequency XYB space to the 'vals' space.\n// Vals space can be converted to L2-norm space (Euclidean and normalized)\n// through visual masking.\ntemplate <class V>\nBUTTERAUGLI_INLINE void XybLowFreqToVals(const V &x, const V &y, const V &b_arg,\n                                         V *BUTTERAUGLI_RESTRICT valx,\n                                         V *BUTTERAUGLI_RESTRICT valy,\n                                         V *BUTTERAUGLI_RESTRICT valb) {\n  static const double xmuli = 5.57547552483;\n  static const double ymuli = 1.20828034498;\n  static const double bmuli = 6.08319517575;\n  static const double y_to_b_muli = -0.628811683685;\n\n  const V xmul(xmuli);\n  const V ymul(ymuli);\n  const V bmul(bmuli);\n  const V y_to_b_mul(y_to_b_muli);\n  const V b = b_arg + y_to_b_mul * y;\n  *valb = b * bmul;\n  *valx = x * xmul;\n  *valy = y * ymul;\n}\n\nstatic ImageF SuppressInBrightAreas(size_t xsize, size_t ysize,\n                                    double mul, double mul2, double reg,\n                                    const ImageF& hf,\n                                    const ImageF& brightness) {\n  ImageF inew(xsize, ysize);\n  for (size_t y = 0; y < ysize; ++y) {\n    const float* const rowhf = hf.Row(y);\n    const float* const rowbr = brightness.Row(y);\n    float* const rownew = inew.Row(y);\n    for (size_t x = 0; x < xsize; ++x) {\n      float v = rowhf[x];\n      float scaler = mul * reg / (reg + rowbr[x]);\n      rownew[x] = scaler * v;\n    }\n  }\n  return inew;\n}\n\n\nstatic float SuppressHfInBrightAreas(float hf, float brightness,\n                                     float mul, float reg) {\n  float scaler = mul * reg / (reg + brightness);\n  return scaler * hf;\n}\n\nstatic float SuppressUhfInBrightAreas(float hf, float brightness,\n                                      float mul, float reg) {\n  float scaler = mul * reg / (reg + brightness);\n  return scaler * hf;\n}\n\nstatic float MaximumClamp(float v, float maxval) {\n  static const double kMul = 0.688059627878;\n  if (v >= maxval) {\n    v -= maxval;\n    v *= kMul;\n    v += maxval;\n  } else if (v < -maxval) {\n    v += maxval;\n    v *= kMul;\n    v -= maxval;\n  }\n  return v;\n}\n\nstatic ImageF MaximumClamping(size_t xsize, size_t ysize, const ImageF& ix,\n                              double yw) {\n  static const double kMul = 0.688059627878;\n  ImageF inew(xsize, ysize);\n  for (size_t y = 0; y < ysize; ++y) {\n    const float* const rowx = ix.Row(y);\n    float* const rownew = inew.Row(y);\n    for (size_t x = 0; x < xsize; ++x) {\n      double v = rowx[x];\n      if (v >= yw) {\n        v -= yw;\n        v *= kMul;\n        v += yw;\n      } else if (v < -yw) {\n        v += yw;\n        v *= kMul;\n        v -= yw;\n      }\n      rownew[x] = v;\n    }\n  }\n  return inew;\n}\n\nstatic ImageF SuppressXByY(size_t xsize, size_t ysize,\n                           const ImageF& ix, const ImageF& iy,\n                           const double yw) {\n  static const double s = 0.745954517135;\n  ImageF inew(xsize, ysize);\n  for (size_t y = 0; y < ysize; ++y) {\n    const float* const rowx = ix.Row(y);\n    const float* const rowy = iy.Row(y);\n    float* const rownew = inew.Row(y);\n    for (size_t x = 0; x < xsize; ++x) {\n      const double xval = rowx[x];\n      const double yval = rowy[x];\n      const double scaler = s + (yw * (1.0 - s)) / (yw + yval * yval);\n      rownew[x] = scaler * xval;\n    }\n  }\n  return inew;\n}\n\nstatic void SeparateFrequencies(\n    size_t xsize, size_t ysize,\n    const std::vector<ImageF>& xyb,\n    PsychoImage &ps) {\n  PROFILER_FUNC;\n  ps.lf.resize(3);   // XYB\n  ps.mf.resize(3);   // XYB\n  ps.hf.resize(2);   // XY\n  ps.uhf.resize(2);  // XY\n  // Extract lf ...\n  static const double kSigmaLf = 7.46953768697;\n  static const double kSigmaHf = 3.734768843485;\n  static const double kSigmaUhf = 1.8673844217425;\n  // At borders we move some more of the energy to the high frequency\n  // parts, because there can be unfortunate continuations in tiling\n  // background color etc. So we want to represent the borders with\n  // some more accuracy.\n  static double border_lf = -0.00457628248637;\n  static double border_mf = -0.271277366628;\n  static double border_hf = 0.147068973249;\n  for (int i = 0; i < 3; ++i) {\n    ps.lf[i] = Blur(xyb[i], kSigmaLf, border_lf);\n    // ... and keep everything else in mf.\n    ps.mf[i] = ImageF(xsize, ysize);\n    for (size_t y = 0; y < ysize; ++y) {\n      for (size_t x = 0; x < xsize; ++x) {\n        ps.mf[i].Row(y)[x] = xyb[i].Row(y)[x] - ps.lf[i].Row(y)[x];\n      }\n    }\n    if (i == 2) {\n      ps.mf[i] = Blur(ps.mf[i], kSigmaHf, border_mf);\n      break;\n    }\n    // Divide mf into mf and hf.\n    ps.hf[i] = ImageF(xsize, ysize);\n    for (size_t y = 0; y < ysize; ++y) {\n      float* BUTTERAUGLI_RESTRICT const row_mf = ps.mf[i].Row(y);\n      float* BUTTERAUGLI_RESTRICT const row_hf = ps.hf[i].Row(y);\n      for (size_t x = 0; x < xsize; ++x) {\n        row_hf[x] = row_mf[x];\n      }\n    }\n    ps.mf[i] = Blur(ps.mf[i], kSigmaHf, border_mf);\n    static const double w0 = 0.120079806822;\n    static const double w1 = 0.03430529365;\n    if (i == 0) {\n      for (size_t y = 0; y < ysize; ++y) {\n        float* BUTTERAUGLI_RESTRICT const row_mf = ps.mf[0].Row(y);\n        float* BUTTERAUGLI_RESTRICT const row_hf = ps.hf[0].Row(y);\n        for (size_t x = 0; x < xsize; ++x) {\n          row_hf[x] -= row_mf[x];\n          row_mf[x] = RemoveRangeAroundZero(w0, row_mf[x]);\n        }\n      }\n    } else {\n      for (size_t y = 0; y < ysize; ++y) {\n        float* BUTTERAUGLI_RESTRICT const row_mf = ps.mf[1].Row(y);\n        float* BUTTERAUGLI_RESTRICT const row_hf = ps.hf[1].Row(y);\n        for (size_t x = 0; x < xsize; ++x) {\n          row_hf[x] -= row_mf[x];\n          row_mf[x] = AmplifyRangeAroundZero(w1, row_mf[x]);\n        }\n      }\n    }\n  }\n  // Suppress red-green by intensity change in the high freq channels.\n  static const double suppress = 2.96534974403;\n  ps.hf[0] = SuppressXByY(xsize, ysize, ps.hf[0], ps.hf[1], suppress);\n\n  for (int i = 0; i < 2; ++i) {\n    // Divide hf into hf and uhf.\n    ps.uhf[i] = ImageF(xsize, ysize);\n    for (size_t y = 0; y < ysize; ++y) {\n      float* BUTTERAUGLI_RESTRICT const row_uhf = ps.uhf[i].Row(y);\n      float* BUTTERAUGLI_RESTRICT const row_hf = ps.hf[i].Row(y);\n      for (size_t x = 0; x < xsize; ++x) {\n        row_uhf[x] = row_hf[x];\n      }\n    }\n    ps.hf[i] = Blur(ps.hf[i], kSigmaUhf, border_hf);\n    static const double kRemoveHfRange = 0.0287615200377;\n    static const double kMaxclampHf = 78.8223237675;\n    static const double kMaxclampUhf = 5.8907152736;\n    static const float kMulSuppressHf = 1.10684769012;\n    static const float kMulRegHf = 0.478741530298;\n    static const float kRegHf = 2000 * kMulRegHf;\n    static const float kMulSuppressUhf = 1.76905001176;\n    static const float kMulRegUhf = 0.310148420674;\n    static const float kRegUhf = 2000 * kMulRegUhf;\n\n    if (i == 0) {\n      for (size_t y = 0; y < ysize; ++y) {\n        float* BUTTERAUGLI_RESTRICT const row_uhf = ps.uhf[0].Row(y);\n        float* BUTTERAUGLI_RESTRICT const row_hf = ps.hf[0].Row(y);\n        for (size_t x = 0; x < xsize; ++x) {\n          row_uhf[x] -= row_hf[x];\n          row_hf[x] = RemoveRangeAroundZero(kRemoveHfRange, row_hf[x]);\n        }\n      }\n    } else {\n      for (size_t y = 0; y < ysize; ++y) {\n        float* BUTTERAUGLI_RESTRICT const row_uhf = ps.uhf[1].Row(y);\n        float* BUTTERAUGLI_RESTRICT const row_hf = ps.hf[1].Row(y);\n        float* BUTTERAUGLI_RESTRICT const row_lf = ps.lf[1].Row(y);\n        for (size_t x = 0; x < xsize; ++x) {\n          row_uhf[x] -= row_hf[x];\n          row_hf[x] = MaximumClamp(row_hf[x], kMaxclampHf);\n          row_uhf[x] = MaximumClamp(row_uhf[x], kMaxclampUhf);\n          row_uhf[x] = SuppressUhfInBrightAreas(row_uhf[x], row_lf[x],\n                                                kMulSuppressUhf, kRegUhf);\n          row_hf[x] = SuppressHfInBrightAreas(row_hf[x], row_lf[x],\n                                              kMulSuppressHf, kRegHf);\n\n        }\n      }\n    }\n  }\n  // Modify range around zero code only concerns the high frequency\n  // planes and only the X and Y channels.\n  // Convert low freq xyb to vals space so that we can do a simple squared sum\n  // diff on the low frequencies later.\n  for (size_t y = 0; y < ysize; ++y) {\n    float* BUTTERAUGLI_RESTRICT const row_x = ps.lf[0].Row(y);\n    float* BUTTERAUGLI_RESTRICT const row_y = ps.lf[1].Row(y);\n    float* BUTTERAUGLI_RESTRICT const row_b = ps.lf[2].Row(y);\n    for (size_t x = 0; x < xsize; ++x) {\n      float valx, valy, valb;\n      XybLowFreqToVals(row_x[x], row_y[x], row_b[x], &valx, &valy, &valb);\n      row_x[x] = valx;\n      row_y[x] = valy;\n      row_b[x] = valb;\n    }\n  }\n}\n\nstatic void SameNoiseLevels(const ImageF& i0, const ImageF& i1,\n                            const double kSigma,\n                            const double w,\n                            const double maxclamp,\n                            ImageF* BUTTERAUGLI_RESTRICT diffmap) {\n  ImageF blurred(i0.xsize(), i0.ysize());\n  for (size_t y = 0; y < i0.ysize(); ++y) {\n    const float* BUTTERAUGLI_RESTRICT const row0 = i0.Row(y);\n    const float* BUTTERAUGLI_RESTRICT const row1 = i1.Row(y);\n    float* BUTTERAUGLI_RESTRICT const to = blurred.Row(y);\n    for (size_t x = 0; x < i0.xsize(); ++x) {\n      double v0 = fabs(row0[x]);\n      double v1 = fabs(row1[x]);\n      if (v0 > maxclamp) v0 = maxclamp;\n      if (v1 > maxclamp) v1 = maxclamp;\n      to[x] = v0 - v1;\n    }\n\n  }\n  blurred = Blur(blurred, kSigma, 0.0);\n  for (size_t y = 0; y < i0.ysize(); ++y) {\n    const float* BUTTERAUGLI_RESTRICT const row = blurred.Row(y);\n    float* BUTTERAUGLI_RESTRICT const row_diff = diffmap->Row(y);\n    for (size_t x = 0; x < i0.xsize(); ++x) {\n      double diff = row[x];\n      row_diff[x] += w * diff * diff;\n    }\n  }\n}\n\nstatic void L2Diff(const ImageF& i0, const ImageF& i1, const double w,\n                   ImageF* BUTTERAUGLI_RESTRICT diffmap) {\n  if (w == 0) {\n    return;\n  }\n  for (size_t y = 0; y < i0.ysize(); ++y) {\n    const float* BUTTERAUGLI_RESTRICT const row0 = i0.Row(y);\n    const float* BUTTERAUGLI_RESTRICT const row1 = i1.Row(y);\n    float* BUTTERAUGLI_RESTRICT const row_diff = diffmap->Row(y);\n    for (size_t x = 0; x < i0.xsize(); ++x) {\n      double diff = row0[x] - row1[x];\n      row_diff[x] += w * diff * diff;\n    }\n  }\n}\n\n// i0 is the original image.\n// i1 is the deformed copy.\nstatic void L2DiffAsymmetric(const ImageF& i0, const ImageF& i1,\n                             double w_0gt1,\n                             double w_0lt1,\n                             ImageF* BUTTERAUGLI_RESTRICT diffmap) {\n  if (w_0gt1 == 0 && w_0lt1 == 0) {\n    return;\n  }\n  w_0gt1 *= 0.8;\n  w_0lt1 *= 0.8;\n  for (size_t y = 0; y < i0.ysize(); ++y) {\n    const float* BUTTERAUGLI_RESTRICT const row0 = i0.Row(y);\n    const float* BUTTERAUGLI_RESTRICT const row1 = i1.Row(y);\n    float* BUTTERAUGLI_RESTRICT const row_diff = diffmap->Row(y);\n    for (size_t x = 0; x < i0.xsize(); ++x) {\n      // Primary symmetric quadratic objective.\n      double diff = row0[x] - row1[x];\n      row_diff[x] += w_0gt1 * diff * diff;\n\n      // Secondary half-open quadratic objectives.\n      const double fabs0 = fabs(row0[x]);\n      const double too_small = 0.4 * fabs0;\n      const double too_big = 1.0 * fabs0;\n\n      if (row0[x] < 0) {\n        if (row1[x] > -too_small) {\n          double v = row1[x] + too_small;\n          row_diff[x] += w_0lt1 * v * v;\n        } else if (row1[x] < -too_big) {\n          double v = -row1[x] - too_big;\n          row_diff[x] += w_0lt1 * v * v;\n        }\n      } else {\n        if (row1[x] < too_small) {\n          double v = too_small - row1[x];\n          row_diff[x] += w_0lt1 * v * v;\n        } else if (row1[x] > too_big) {\n          double v = row1[x] - too_big;\n          row_diff[x] += w_0lt1 * v * v;\n        }\n      }\n    }\n  }\n}\n\n// Making a cluster of local errors to be more impactful than\n// just a single error.\nImageF CalculateDiffmap(const ImageF& diffmap_in) {\n  PROFILER_FUNC;\n  // Take square root.\n  ImageF diffmap(diffmap_in.xsize(), diffmap_in.ysize());\n  static const float kInitialSlope = 100.0f;\n  for (size_t y = 0; y < diffmap.ysize(); ++y) {\n    const float* const BUTTERAUGLI_RESTRICT row_in = diffmap_in.Row(y);\n    float* const BUTTERAUGLI_RESTRICT row_out = diffmap.Row(y);\n    for (size_t x = 0; x < diffmap.xsize(); ++x) {\n      const float orig_val = row_in[x];\n      // TODO(b/29974893): Until that is fixed do not call sqrt on very small\n      // numbers.\n      row_out[x] = (orig_val < (1.0f / (kInitialSlope * kInitialSlope))\n                    ? kInitialSlope * orig_val\n                    : std::sqrt(orig_val));\n    }\n  }\n  {\n    static const double kSigma = 1.72547472444;\n    static const double mul1 = 0.458794906198;\n    static const float scale = 1.0f / (1.0f + mul1);\n    static const double border_ratio = 1.0; // 2.01209066992;\n    ImageF blurred = Blur(diffmap, kSigma, border_ratio);\n    for (int y = 0; y < diffmap.ysize(); ++y) {\n      const float* const BUTTERAUGLI_RESTRICT row_blurred = blurred.Row(y);\n      float* const BUTTERAUGLI_RESTRICT row = diffmap.Row(y);\n      for (int x = 0; x < diffmap.xsize(); ++x) {\n        row[x] += mul1 * row_blurred[x];\n        row[x] *= scale;\n      }\n    }\n  }\n  return diffmap;\n}\n\nvoid MaskPsychoImage(const PsychoImage& pi0, const PsychoImage& pi1,\n                     const size_t xsize, const size_t ysize,\n                     std::vector<ImageF>* BUTTERAUGLI_RESTRICT mask,\n                     std::vector<ImageF>* BUTTERAUGLI_RESTRICT mask_dc) {\n  std::vector<ImageF> mask_xyb0 = CreatePlanes<float>(xsize, ysize, 3);\n  std::vector<ImageF> mask_xyb1 = CreatePlanes<float>(xsize, ysize, 3);\n  static const double muls[4] = {\n    0,\n    1.64178305129,\n    0.831081703362,\n    3.23680933546,\n  };\n  for (int i = 0; i < 2; ++i) {\n    double a = muls[2 * i];\n    double b = muls[2 * i + 1];\n    for (size_t y = 0; y < ysize; ++y) {\n      const float* const BUTTERAUGLI_RESTRICT row_hf0 = pi0.hf[i].Row(y);\n      const float* const BUTTERAUGLI_RESTRICT row_hf1 = pi1.hf[i].Row(y);\n      const float* const BUTTERAUGLI_RESTRICT row_uhf0 = pi0.uhf[i].Row(y);\n      const float* const BUTTERAUGLI_RESTRICT row_uhf1 = pi1.uhf[i].Row(y);\n      float* const BUTTERAUGLI_RESTRICT row0 = mask_xyb0[i].Row(y);\n      float* const BUTTERAUGLI_RESTRICT row1 = mask_xyb1[i].Row(y);\n      for (size_t x = 0; x < xsize; ++x) {\n        row0[x] = a * row_uhf0[x] + b * row_hf0[x];\n        row1[x] = a * row_uhf1[x] + b * row_hf1[x];\n      }\n    }\n  }\n  Mask(mask_xyb0, mask_xyb1, mask, mask_dc);\n}\n\nButteraugliComparator::ButteraugliComparator(const std::vector<ImageF>& rgb0)\n    : xsize_(rgb0[0].xsize()),\n      ysize_(rgb0[0].ysize()),\n      num_pixels_(xsize_ * ysize_) {\n  if (xsize_ < 8 || ysize_ < 8) return;\n  std::vector<ImageF> xyb0 = OpsinDynamicsImage(rgb0);\n  SeparateFrequencies(xsize_, ysize_, xyb0, pi0_);\n}\n\nvoid ButteraugliComparator::Mask(\n    std::vector<ImageF>* BUTTERAUGLI_RESTRICT mask,\n    std::vector<ImageF>* BUTTERAUGLI_RESTRICT mask_dc) const {\n  MaskPsychoImage(pi0_, pi0_, xsize_, ysize_, mask, mask_dc);\n}\n\nvoid ButteraugliComparator::Diffmap(const std::vector<ImageF>& rgb1,\n                                    ImageF &result) const {\n  PROFILER_FUNC;\n  if (xsize_ < 8 || ysize_ < 8) return;\n  DiffmapOpsinDynamicsImage(OpsinDynamicsImage(rgb1), result);\n}\n\nvoid ButteraugliComparator::DiffmapOpsinDynamicsImage(\n    const std::vector<ImageF>& xyb1,\n    ImageF &result) const {\n  PROFILER_FUNC;\n  if (xsize_ < 8 || ysize_ < 8) return;\n  PsychoImage pi1;\n  SeparateFrequencies(xsize_, ysize_, xyb1, pi1);\n  result = ImageF(xsize_, ysize_);\n  DiffmapPsychoImage(pi1, result);\n}\n\nvoid ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1,\n                                               ImageF& result) const {\n  PROFILER_FUNC;\n  const float hf_asymmetry_ = 0.8f;\n  if (xsize_ < 8 || ysize_ < 8) {\n    return;\n  }\n  std::vector<ImageF> block_diff_dc(3);\n  std::vector<ImageF> block_diff_ac(3);\n  for (int c = 0; c < 3; ++c) {\n    block_diff_dc[c] = ImageF(xsize_, ysize_, 0.0);\n    block_diff_ac[c] = ImageF(xsize_, ysize_, 0.0);\n  }\n\n  static const double wUhfMalta = 5.1409625726;\n  static const double norm1Uhf = 58.5001247061;\n  MaltaDiffMap(pi0_.uhf[1], pi1.uhf[1],\n               wUhfMalta * hf_asymmetry_,\n               wUhfMalta / hf_asymmetry_,\n               norm1Uhf,\n               &block_diff_ac[1]);\n\n  static const double wUhfMaltaX = 4.91743441556;\n  static const double norm1UhfX = 687196.39002;\n  MaltaDiffMap(pi0_.uhf[0], pi1.uhf[0],\n               wUhfMaltaX * hf_asymmetry_,\n               wUhfMaltaX / hf_asymmetry_,\n               norm1UhfX,\n               &block_diff_ac[0]);\n\n  static const double wHfMalta = 153.671655716;\n  static const double norm1Hf = 83150785.9592;\n  MaltaDiffMapLF(pi0_.hf[1], pi1.hf[1],\n                 wHfMalta * sqrt(hf_asymmetry_),\n                 wHfMalta / sqrt(hf_asymmetry_),\n                 norm1Hf,\n                 &block_diff_ac[1]);\n\n  static const double wHfMaltaX = 668.358918152;\n  static const double norm1HfX = 0.882954368025;\n  MaltaDiffMapLF(pi0_.hf[0], pi1.hf[0],\n                 wHfMaltaX * sqrt(hf_asymmetry_),\n                 wHfMaltaX / sqrt(hf_asymmetry_),\n                 norm1HfX,\n                 &block_diff_ac[0]);\n\n  static const double wMfMalta = 6841.81248144;\n  static const double norm1Mf = 0.0135134962487;\n  MaltaDiffMapLF(pi0_.mf[1], pi1.mf[1], wMfMalta, wMfMalta, norm1Mf,\n                 &block_diff_ac[1]);\n\n  static const double wMfMaltaX = 813.901703816;\n  static const double norm1MfX = 16792.9322251;\n  MaltaDiffMapLF(pi0_.mf[0], pi1.mf[0], wMfMaltaX, wMfMaltaX, norm1MfX,\n                 &block_diff_ac[0]);\n\n  static const double wmul[9] = {\n    0,\n    32.4449876135,\n    0,\n    0,\n    0,\n    0,\n    1.01370836411,\n    0,\n    1.74566011615,\n  };\n\n  static const double maxclamp = 85.7047444518;\n  static const double kSigmaHfX = 10.6666499623;\n  static const double w = 884.809801415;\n  SameNoiseLevels(pi0_.hf[1], pi1.hf[1], kSigmaHfX, w, maxclamp,\n                  &block_diff_ac[1]);\n\n  for (int c = 0; c < 3; ++c) {\n    if (c < 2) {\n      L2DiffAsymmetric(pi0_.hf[c], pi1.hf[c],\n                       wmul[c] * hf_asymmetry_,\n                       wmul[c] / hf_asymmetry_,\n                       &block_diff_ac[c]);\n    }\n    L2Diff(pi0_.mf[c], pi1.mf[c], wmul[3 + c], &block_diff_ac[c]);\n    L2Diff(pi0_.lf[c], pi1.lf[c], wmul[6 + c], &block_diff_dc[c]);\n  }\n\n  std::vector<ImageF> mask_xyb;\n  std::vector<ImageF> mask_xyb_dc;\n  MaskPsychoImage(pi0_, pi1, xsize_, ysize_, &mask_xyb, &mask_xyb_dc);\n\n  result = CalculateDiffmap(\n      CombineChannels(mask_xyb, mask_xyb_dc, block_diff_dc, block_diff_ac));\n}\n\n// Allows PaddedMaltaUnit to call either function via overloading.\nstruct MaltaTagLF {};\nstruct MaltaTag {};\n\nstatic float MaltaUnit(MaltaTagLF, const float* BUTTERAUGLI_RESTRICT d,\n                       const int xs) {\n  const int xs3 = 3 * xs;\n  float retval = 0;\n  {\n    // x grows, y constant\n    float sum =\n        d[-4] +\n        d[-2] +\n        d[0] +\n        d[2] +\n        d[4];\n    retval += sum * sum;\n  }\n  {\n    // y grows, x constant\n    float sum =\n        d[-xs3 - xs] +\n        d[-xs - xs] +\n        d[0] +\n        d[xs + xs] +\n        d[xs3 + xs];\n    retval += sum * sum;\n  }\n  {\n    // both grow\n    float sum =\n        d[-xs3 - 3] +\n        d[-xs - xs - 2] +\n        d[0] +\n        d[xs + xs + 2] +\n        d[xs3 + 3];\n    retval += sum * sum;\n  }\n  {\n    // y grows, x shrinks\n    float sum =\n        d[-xs3 + 3] +\n        d[-xs - xs + 2] +\n        d[0] +\n        d[xs + xs - 2] +\n        d[xs3 - 3];\n    retval += sum * sum;\n  }\n  {\n    // y grows -4 to 4, x shrinks 1 -> -1\n    float sum =\n        d[-xs3 - xs + 1] +\n        d[-xs - xs + 1] +\n        d[0] +\n        d[xs + xs - 1] +\n        d[xs3 + xs - 1];\n    retval += sum * sum;\n  }\n  {\n    //  y grows -4 to 4, x grows -1 -> 1\n    float sum =\n        d[-xs3 - xs - 1] +\n        d[-xs - xs - 1] +\n        d[0] +\n        d[xs + xs + 1] +\n        d[xs3 + xs + 1];\n    retval += sum * sum;\n  }\n  {\n    // x grows -4 to 4, y grows -1 to 1\n    float sum =\n        d[-4 - xs] +\n        d[-2 - xs] +\n        d[0] +\n        d[2 + xs] +\n        d[4 + xs];\n    retval += sum * sum;\n  }\n  {\n    // x grows -4 to 4, y shrinks 1 to -1\n    float sum =\n        d[-4 + xs] +\n        d[-2 + xs] +\n        d[0] +\n        d[2 - xs] +\n        d[4 - xs];\n    retval += sum * sum;\n  }\n  {\n    /* 0_________\n       1__*______\n       2___*_____\n       3_________\n       4____0____\n       5_________\n       6_____*___\n       7______*__\n       8_________ */\n    float sum =\n        d[-xs3 - 2] +\n        d[-xs - xs - 1] +\n        d[0] +\n        d[xs + xs + 1] +\n        d[xs3 + 2];\n    retval += sum * sum;\n  }\n  {\n    /* 0_________\n       1______*__\n       2_____*___\n       3_________\n       4____0____\n       5_________\n       6___*_____\n       7__*______\n       8_________ */\n    float sum =\n        d[-xs3 + 2] +\n        d[-xs - xs + 1] +\n        d[0] +\n        d[xs + xs - 1] +\n        d[xs3 - 2];\n    retval += sum * sum;\n  }\n  {\n    /* 0_________\n       1_________\n       2_*_______\n       3__*______\n       4____0____\n       5______*__\n       6_______*_\n       7_________\n       8_________ */\n    float sum =\n        d[-xs - xs - 3] +\n        d[-xs - 2] +\n        d[0] +\n        d[xs + 2] +\n        d[xs + xs + 3];\n    retval += sum * sum;\n  }\n  {\n    /* 0_________\n       1_________\n       2_______*_\n       3______*__\n       4____0____\n       5__*______\n       6_*_______\n       7_________\n       8_________ */\n    float sum =\n        d[-xs - xs + 3] +\n        d[-xs + 2] +\n        d[0] +\n        d[xs - 2] +\n        d[xs + xs - 3];\n    retval += sum * sum;\n  }\n  {\n    /* 0_________\n       1_________\n       2________*\n       3______*__\n       4____0____\n       5__*______\n       6*________\n       7_________\n       8_________ */\n\n    float sum =\n        d[xs + xs - 4] +\n        d[xs - 2] +\n        d[0] +\n        d[-xs + 2] +\n        d[-xs - xs + 4];\n    retval += sum * sum;\n  }\n  {\n    /* 0_________\n       1_________\n       2*________\n       3__*______\n       4____0____\n       5______*__\n       6________*\n       7_________\n       8_________ */\n    float sum =\n        d[-xs - xs - 4] +\n        d[-xs - 2] +\n        d[0] +\n        d[xs + 2] +\n        d[xs + xs + 4];\n    retval += sum * sum;\n  }\n  {\n    /* 0__*______\n       1_________\n       2___*_____\n       3_________\n       4____0____\n       5_________\n       6_____*___\n       7_________\n       8______*__ */\n    float sum =\n        d[-xs3 - xs - 2] +\n        d[-xs - xs - 1] +\n        d[0] +\n        d[xs + xs + 1] +\n        d[xs3 + xs + 2];\n    retval += sum * sum;\n  }\n  {\n    /* 0______*__\n       1_________\n       2_____*___\n       3_________\n       4____0____\n       5_________\n       6___*_____\n       7_________\n       8__*______ */\n    float sum =\n        d[-xs3 - xs + 2] +\n        d[-xs - xs + 1] +\n        d[0] +\n        d[xs + xs - 1] +\n        d[xs3 + xs - 2];\n    retval += sum * sum;\n  }\n  return retval;\n}\n\nstatic float MaltaUnit(MaltaTag, const float* BUTTERAUGLI_RESTRICT d,\n                       const int xs) {\n  const int xs3 = 3 * xs;\n  float retval = 0;\n  {\n    // x grows, y constant\n    float sum =\n        d[-4] +\n        d[-3] +\n        d[-2] +\n        d[-1] +\n        d[0] +\n        d[1] +\n        d[2] +\n        d[3] +\n        d[4];\n    retval += sum * sum;\n  }\n  {\n    // y grows, x constant\n    float sum =\n        d[-xs3 - xs] +\n        d[-xs3] +\n        d[-xs - xs] +\n        d[-xs] +\n        d[0] +\n        d[xs] +\n        d[xs + xs] +\n        d[xs3] +\n        d[xs3 + xs];\n    retval += sum * sum;\n  }\n  {\n    // both grow\n    float sum =\n        d[-xs3 - 3] +\n        d[-xs - xs - 2] +\n        d[-xs - 1] +\n        d[0] +\n        d[xs + 1] +\n        d[xs + xs + 2] +\n        d[xs3 + 3];\n    retval += sum * sum;\n  }\n  {\n    // y grows, x shrinks\n    float sum =\n        d[-xs3 + 3] +\n        d[-xs - xs + 2] +\n        d[-xs + 1] +\n        d[0] +\n        d[xs - 1] +\n        d[xs + xs - 2] +\n        d[xs3 - 3];\n    retval += sum * sum;\n  }\n  {\n    // y grows -4 to 4, x shrinks 1 -> -1\n    float sum =\n        d[-xs3 - xs + 1] +\n        d[-xs3 + 1] +\n        d[-xs - xs + 1] +\n        d[-xs] +\n        d[0] +\n        d[xs] +\n        d[xs + xs - 1] +\n        d[xs3 - 1] +\n        d[xs3 + xs - 1];\n    retval += sum * sum;\n  }\n  {\n    //  y grows -4 to 4, x grows -1 -> 1\n    float sum =\n        d[-xs3 - xs - 1] +\n        d[-xs3 - 1] +\n        d[-xs - xs - 1] +\n        d[-xs] +\n        d[0] +\n        d[xs] +\n        d[xs + xs + 1] +\n        d[xs3 + 1] +\n        d[xs3 + xs + 1];\n    retval += sum * sum;\n  }\n  {\n    // x grows -4 to 4, y grows -1 to 1\n    float sum =\n        d[-4 - xs] +\n        d[-3 - xs] +\n        d[-2 - xs] +\n        d[-1] +\n        d[0] +\n        d[1] +\n        d[2 + xs] +\n        d[3 + xs] +\n        d[4 + xs];\n    retval += sum * sum;\n  }\n  {\n    // x grows -4 to 4, y shrinks 1 to -1\n    float sum =\n        d[-4 + xs] +\n        d[-3 + xs] +\n        d[-2 + xs] +\n        d[-1] +\n        d[0] +\n        d[1] +\n        d[2 - xs] +\n        d[3 - xs] +\n        d[4 - xs];\n    retval += sum * sum;\n  }\n  {\n    /* 0_________\n       1__*______\n       2___*_____\n       3___*_____\n       4____0____\n       5_____*___\n       6_____*___\n       7______*__\n       8_________ */\n    float sum =\n        d[-xs3 - 2] +\n        d[-xs - xs - 1] +\n        d[-xs - 1] +\n        d[0] +\n        d[xs + 1] +\n        d[xs + xs + 1] +\n        d[xs3 + 2];\n    retval += sum * sum;\n  }\n  {\n    /* 0_________\n       1______*__\n       2_____*___\n       3_____*___\n       4____0____\n       5___*_____\n       6___*_____\n       7__*______\n       8_________ */\n    float sum =\n        d[-xs3 + 2] +\n        d[-xs - xs + 1] +\n        d[-xs + 1] +\n        d[0] +\n        d[xs - 1] +\n        d[xs + xs - 1] +\n        d[xs3 - 2];\n    retval += sum * sum;\n  }\n  {\n    /* 0_________\n       1_________\n       2_*_______\n       3__**_____\n       4____0____\n       5_____**__\n       6_______*_\n       7_________\n       8_________ */\n    float sum =\n        d[-xs - xs - 3] +\n        d[-xs - 2] +\n        d[-xs - 1] +\n        d[0] +\n        d[xs + 1] +\n        d[xs + 2] +\n        d[xs + xs + 3];\n    retval += sum * sum;\n  }\n  {\n    /* 0_________\n       1_________\n       2_______*_\n       3_____**__\n       4____0____\n       5__**_____\n       6_*_______\n       7_________\n       8_________ */\n    float sum =\n        d[-xs - xs + 3] +\n        d[-xs + 2] +\n        d[-xs + 1] +\n        d[0] +\n        d[xs - 1] +\n        d[xs - 2] +\n        d[xs + xs - 3];\n    retval += sum * sum;\n  }\n  {\n    /* 0_________\n       1_________\n       2_________\n       3______**_\n       4____0*___\n       5__**_____\n       6**_______\n       7_________\n       8_________ */\n\n    float sum =\n        d[xs + xs - 4] +\n        d[xs + xs - 3] +\n        d[xs - 2] +\n        d[xs - 1] +\n        d[0] +\n        d[1] +\n        d[-xs + 2] +\n        d[-xs + 3];\n    retval += sum * sum;\n  }\n  {\n    /* 0_________\n       1_________\n       2**_______\n       3__**_____\n       4____0*___\n       5______**_\n       6_________\n       7_________\n       8_________ */\n    float sum =\n        d[-xs - xs - 4] +\n        d[-xs - xs - 3] +\n        d[-xs - 2] +\n        d[-xs - 1] +\n        d[0] +\n        d[1] +\n        d[xs + 2] +\n        d[xs + 3];\n    retval += sum * sum;\n  }\n  {\n    /* 0__*______\n       1__*______\n       2___*_____\n       3___*_____\n       4____0____\n       5____*____\n       6_____*___\n       7_____*___\n       8_________ */\n    float sum =\n        d[-xs3 - xs - 2] +\n        d[-xs3 - 2] +\n        d[-xs - xs - 1] +\n        d[-xs - 1] +\n        d[0] +\n        d[xs] +\n        d[xs + xs + 1] +\n        d[xs3 + 1];\n    retval += sum * sum;\n  }\n  {\n    /* 0______*__\n       1______*__\n       2_____*___\n       3_____*___\n       4____0____\n       5____*____\n       6___*_____\n       7___*_____\n       8_________ */\n    float sum =\n        d[-xs3 - xs + 2] +\n        d[-xs3 + 2] +\n        d[-xs - xs + 1] +\n        d[-xs + 1] +\n        d[0] +\n        d[xs] +\n        d[xs + xs - 1] +\n        d[xs3 - 1];\n    retval += sum * sum;\n  }\n  return retval;\n}\n\n// Returns MaltaUnit. \"fastMode\" avoids bounds-checks when x0 and y0 are known\n// to be far enough from the image borders.\ntemplate <bool fastMode, class Tag>\nstatic BUTTERAUGLI_INLINE float PaddedMaltaUnit(\n    float* const BUTTERAUGLI_RESTRICT diffs, const size_t x0, const size_t y0,\n    const size_t xsize_, const size_t ysize_) {\n  int ix0 = y0 * xsize_ + x0;\n  const float* BUTTERAUGLI_RESTRICT d = &diffs[ix0];\n  if (fastMode ||\n      (x0 >= 4 && y0 >= 4 && x0 < (xsize_ - 4) && y0 < (ysize_ - 4))) {\n    return MaltaUnit(Tag(), d, xsize_);\n  }\n\n  float borderimage[9 * 9];\n  for (int dy = 0; dy < 9; ++dy) {\n    int y = y0 + dy - 4;\n    if (y < 0 || y >= ysize_) {\n      for (int dx = 0; dx < 9; ++dx) {\n        borderimage[dy * 9 + dx] = 0.0f;\n      }\n    } else {\n      for (int dx = 0; dx < 9; ++dx) {\n        int x = x0 + dx - 4;\n        if (x < 0 || x >= xsize_) {\n          borderimage[dy * 9 + dx] = 0.0f;\n        } else {\n          borderimage[dy * 9 + dx] = diffs[y * xsize_ + x];\n        }\n      }\n    }\n  }\n  return MaltaUnit(Tag(), &borderimage[4 * 9 + 4], 9);\n}\n\ntemplate <class Tag>\nstatic void MaltaDiffMapImpl(const ImageF& lum0, const ImageF& lum1,\n                             const size_t xsize_, const size_t ysize_,\n                             const double w_0gt1,\n                             const double w_0lt1,\n                             double norm1,\n                             const double len, const double mulli,\n                             ImageF* block_diff_ac) {\n  const float kWeight0 = 0.5;\n  const float kWeight1 = 0.33;\n\n  const double w_pre0gt1 = mulli * sqrt(kWeight0 * w_0gt1) / (len * 2 + 1);\n  const double w_pre0lt1 = mulli * sqrt(kWeight1 * w_0lt1) / (len * 2 + 1);\n  const float norm2_0gt1 = w_pre0gt1 * norm1;\n  const float norm2_0lt1 = w_pre0lt1 * norm1;\n\n  std::vector<float> diffs(ysize_ * xsize_);\n  for (size_t y = 0, ix = 0; y < ysize_; ++y) {\n    const float* BUTTERAUGLI_RESTRICT const row0 = lum0.Row(y);\n    const float* BUTTERAUGLI_RESTRICT const row1 = lum1.Row(y);\n    for (size_t x = 0; x < xsize_; ++x, ++ix) {\n      const float absval = 0.5 * std::abs(row0[x]) + 0.5 * std::abs(row1[x]);\n      const float diff = row0[x] - row1[x];\n      const float scaler = norm2_0gt1 / (static_cast<float>(norm1) + absval);\n\n      // Primary symmetric quadratic objective.\n      diffs[ix] = scaler * diff;\n\n      const float scaler2 = norm2_0lt1 / (static_cast<float>(norm1) + absval);\n      const double fabs0 = fabs(row0[x]);\n\n      // Secondary half-open quadratic objectives.\n      const double too_small = 0.55 * fabs0;\n      const double too_big = 1.05 * fabs0;\n\n      if (row0[x] < 0) {\n        if (row1[x] > -too_small) {\n          double impact = scaler2 * (row1[x] + too_small);\n          if (diff < 0) {\n            diffs[ix] -= impact;\n          } else {\n            diffs[ix] += impact;\n          }\n        } else if (row1[x] < -too_big) {\n          double impact = scaler2 * (-row1[x] - too_big);\n          if (diff < 0) {\n            diffs[ix] -= impact;\n          } else {\n            diffs[ix] += impact;\n          }\n        }\n      } else {\n        if (row1[x] < too_small) {\n          double impact = scaler2 * (too_small - row1[x]);\n          if (diff < 0) {\n            diffs[ix] -= impact;\n          } else {\n            diffs[ix] += impact;\n          }\n        } else if (row1[x] > too_big) {\n          double impact = scaler2 * (row1[x] - too_big);\n          if (diff < 0) {\n            diffs[ix] -= impact;\n          } else {\n            diffs[ix] += impact;\n          }\n        }\n      }\n    }\n  }\n\n  size_t y0 = 0;\n  // Top\n  for (; y0 < 4; ++y0) {\n    float* const BUTTERAUGLI_RESTRICT row_diff = block_diff_ac->Row(y0);\n    for (size_t x0 = 0; x0 < xsize_; ++x0) {\n      row_diff[x0] +=\n          PaddedMaltaUnit<false, Tag>(&diffs[0], x0, y0, xsize_, ysize_);\n    }\n  }\n\n  // Middle\n  for (; y0 < ysize_ - 4; ++y0) {\n    float* const BUTTERAUGLI_RESTRICT row_diff = block_diff_ac->Row(y0);\n    size_t x0 = 0;\n    for (; x0 < 4; ++x0) {\n      row_diff[x0] +=\n          PaddedMaltaUnit<false, Tag>(&diffs[0], x0, y0, xsize_, ysize_);\n    }\n    for (; x0 < xsize_ - 4; ++x0) {\n      row_diff[x0] +=\n          PaddedMaltaUnit<true, Tag>(&diffs[0], x0, y0, xsize_, ysize_);\n    }\n\n    for (; x0 < xsize_; ++x0) {\n      row_diff[x0] +=\n          PaddedMaltaUnit<false, Tag>(&diffs[0], x0, y0, xsize_, ysize_);\n    }\n  }\n\n  // Bottom\n  for (; y0 < ysize_; ++y0) {\n    float* const BUTTERAUGLI_RESTRICT row_diff = block_diff_ac->Row(y0);\n    for (size_t x0 = 0; x0 < xsize_; ++x0) {\n      row_diff[x0] +=\n          PaddedMaltaUnit<false, Tag>(&diffs[0], x0, y0, xsize_, ysize_);\n    }\n  }\n}\n\nvoid ButteraugliComparator::MaltaDiffMap(\n    const ImageF& lum0, const ImageF& lum1,\n    const double w_0gt1,\n    const double w_0lt1,\n    const double norm1, ImageF* BUTTERAUGLI_RESTRICT block_diff_ac) const {\n  PROFILER_FUNC;\n  const double len = 3.75;\n  static const double mulli = 0.354191303559;\n  MaltaDiffMapImpl<MaltaTag>(lum0, lum1, xsize_, ysize_, w_0gt1, w_0lt1,\n                             norm1, len,\n                             mulli, block_diff_ac);\n}\n\nvoid ButteraugliComparator::MaltaDiffMapLF(\n    const ImageF& lum0, const ImageF& lum1,\n    const double w_0gt1,\n    const double w_0lt1,\n    const double norm1, ImageF* BUTTERAUGLI_RESTRICT block_diff_ac) const {\n  PROFILER_FUNC;\n  const double len = 3.75;\n  static const double mulli = 0.405371989604;\n  MaltaDiffMapImpl<MaltaTagLF>(lum0, lum1, xsize_, ysize_,\n                               w_0gt1, w_0lt1,\n                               norm1, len,\n                               mulli, block_diff_ac);\n}\n\nImageF ButteraugliComparator::CombineChannels(\n    const std::vector<ImageF>& mask_xyb,\n    const std::vector<ImageF>& mask_xyb_dc,\n    const std::vector<ImageF>& block_diff_dc,\n    const std::vector<ImageF>& block_diff_ac) const {\n  PROFILER_FUNC;\n  ImageF result(xsize_, ysize_);\n  for (size_t y = 0; y < ysize_; ++y) {\n    float* const BUTTERAUGLI_RESTRICT row_out = result.Row(y);\n    for (size_t x = 0; x < xsize_; ++x) {\n      float mask[3];\n      float dc_mask[3];\n      float diff_dc[3];\n      float diff_ac[3];\n      for (int i = 0; i < 3; ++i) {\n        mask[i] = mask_xyb[i].Row(y)[x];\n        dc_mask[i] = mask_xyb_dc[i].Row(y)[x];\n        diff_dc[i] = block_diff_dc[i].Row(y)[x];\n        diff_ac[i] = block_diff_ac[i].Row(y)[x];\n      }\n      row_out[x] = (DotProduct(diff_dc, dc_mask) + DotProduct(diff_ac, mask));\n    }\n  }\n  return result;\n}\n\ndouble ButteraugliScoreFromDiffmap(const ImageF& diffmap) {\n  PROFILER_FUNC;\n  float retval = 0.0f;\n  for (size_t y = 0; y < diffmap.ysize(); ++y) {\n    const float * const BUTTERAUGLI_RESTRICT row = diffmap.Row(y);\n    for (size_t x = 0; x < diffmap.xsize(); ++x) {\n      retval = std::max(retval, row[x]);\n    }\n  }\n  return retval;\n}\n\n#include <stdio.h>\n\n// ===== Functions used by Mask only =====\nstatic std::array<double, 512> MakeMask(\n    double extmul, double extoff,\n    double mul, double offset,\n    double scaler) {\n  std::array<double, 512> lut;\n  for (int i = 0; i < lut.size(); ++i) {\n    const double c = mul / ((0.01 * scaler * i) + offset);\n    lut[i] = kGlobalScale * (1.0 + extmul * (c + extoff));\n    if (lut[i] < 1e-5) {\n      lut[i] = 1e-5;\n    }\n    assert(lut[i] >= 0.0);\n    lut[i] *= lut[i];\n  }\n  return lut;\n}\n\ndouble MaskX(double delta) {\n  static const double extmul = 2.59885507073;\n  static const double extoff = 3.08805636789;\n  static const double offset = 0.315424196682;\n  static const double scaler = 16.2770141832;\n  static const double mul = 5.62939030582;\n  static const std::array<double, 512> lut =\n                MakeMask(extmul, extoff, mul, offset, scaler);\n  return InterpolateClampNegative(lut.data(), lut.size(), delta);\n}\n\ndouble MaskY(double delta) {\n  static const double extmul = 0.9613705131;\n  static const double extoff = -0.581933100068;\n  static const double offset = 1.00846207765;\n  static const double scaler = 2.2342321176;\n  static const double mul = 6.64307621174;\n  static const std::array<double, 512> lut =\n      MakeMask(extmul, extoff, mul, offset, scaler);\n  return InterpolateClampNegative(lut.data(), lut.size(), delta);\n}\n\ndouble MaskDcX(double delta) {\n  static const double extmul = 10.0470705878;\n  static const double extoff = 3.18472654033;\n  static const double offset = 0.0551512255218;\n  static const double scaler = 70.0;\n  static const double mul = 0.373092999662;\n  static const std::array<double, 512> lut =\n      MakeMask(extmul, extoff, mul, offset, scaler);\n  return InterpolateClampNegative(lut.data(), lut.size(), delta);\n}\n\ndouble MaskDcY(double delta) {\n  static const double extmul = 0.0115640939227;\n  static const double extoff = 45.9483175519;\n  static const double offset = 0.0142290066313;\n  static const double scaler = 5.0;\n  static const double mul = 2.52611324247;\n  static const std::array<double, 512> lut =\n      MakeMask(extmul, extoff, mul, offset, scaler);\n  return InterpolateClampNegative(lut.data(), lut.size(), delta);\n}\n\nImageF DiffPrecompute(const ImageF& xyb0, const ImageF& xyb1) {\n  PROFILER_FUNC;\n  const size_t xsize = xyb0.xsize();\n  const size_t ysize = xyb0.ysize();\n  ImageF result(xsize, ysize);\n  size_t x2, y2;\n  for (size_t y = 0; y < ysize; ++y) {\n    if (y + 1 < ysize) {\n      y2 = y + 1;\n    } else if (y > 0) {\n      y2 = y - 1;\n    } else {\n      y2 = y;\n    }\n    const float* const BUTTERAUGLI_RESTRICT row0_in = xyb0.Row(y);\n    const float* const BUTTERAUGLI_RESTRICT row1_in = xyb1.Row(y);\n    const float* const BUTTERAUGLI_RESTRICT row0_in2 = xyb0.Row(y2);\n    const float* const BUTTERAUGLI_RESTRICT row1_in2 = xyb1.Row(y2);\n    float* const BUTTERAUGLI_RESTRICT row_out = result.Row(y);\n    for (size_t x = 0; x < xsize; ++x) {\n      if (x + 1 < xsize) {\n        x2 = x + 1;\n      } else if (x > 0) {\n        x2 = x - 1;\n      } else {\n        x2 = x;\n      }\n      double sup0 = (fabs(row0_in[x] - row0_in[x2]) +\n                     fabs(row0_in[x] - row0_in2[x]));\n      double sup1 = (fabs(row1_in[x] - row1_in[x2]) +\n                     fabs(row1_in[x] - row1_in2[x]));\n      static const double mul0 = 0.918416534734;\n      row_out[x] = mul0 * std::min(sup0, sup1);\n      static const double cutoff = 55.0184555849;\n      if (row_out[x] >= cutoff) {\n        row_out[x] = cutoff;\n      }\n    }\n  }\n  return result;\n}\n\nvoid Mask(const std::vector<ImageF>& xyb0,\n          const std::vector<ImageF>& xyb1,\n          std::vector<ImageF>* BUTTERAUGLI_RESTRICT mask,\n          std::vector<ImageF>* BUTTERAUGLI_RESTRICT mask_dc) {\n  PROFILER_FUNC;\n  const size_t xsize = xyb0[0].xsize();\n  const size_t ysize = xyb0[0].ysize();\n  mask->resize(3);\n  *mask_dc = CreatePlanes<float>(xsize, ysize, 3);\n  double muls[2] = {\n    0.207017089891,\n    0.267138152891,\n  };\n  double normalizer = {\n    1.0 / (muls[0] + muls[1]),\n  };\n  static const double r0 = 2.3770330432;\n  static const double r1 = 9.04353323561;\n  static const double r2 = 9.24456601467;\n  static const double border_ratio = -0.0724948220913;\n\n  {\n    // X component\n    ImageF diff = DiffPrecompute(xyb0[0], xyb1[0]);\n    ImageF blurred = Blur(diff, r2, border_ratio);\n    (*mask)[0] = ImageF(xsize, ysize);\n    for (size_t y = 0; y < ysize; ++y) {\n      for (size_t x = 0; x < xsize; ++x) {\n        (*mask)[0].Row(y)[x] = blurred.Row(y)[x];\n      }\n    }\n  }\n  {\n    // Y component\n    (*mask)[1] = ImageF(xsize, ysize);\n    ImageF diff = DiffPrecompute(xyb0[1], xyb1[1]);\n    ImageF blurred1 = Blur(diff, r0, border_ratio);\n    ImageF blurred2 = Blur(diff, r1, border_ratio);\n    for (size_t y = 0; y < ysize; ++y) {\n      for (size_t x = 0; x < xsize; ++x) {\n        const double val = normalizer * (\n            muls[0] * blurred1.Row(y)[x] +\n            muls[1] * blurred2.Row(y)[x]);\n        (*mask)[1].Row(y)[x] = val;\n      }\n    }\n  }\n  // B component\n  (*mask)[2] = ImageF(xsize, ysize);\n  static const double mul[2] = {\n    16.6963293877,\n    2.1364621982,\n  };\n  static const double w00 = 36.4671237619;\n  static const double w11 = 2.1887170895;\n  static const double w_ytob_hf = std::max<double>(\n      0.086624184478,\n      0.0);\n  static const double w_ytob_lf = 21.6804277046;\n  static const double p1_to_p0 = 0.0513061271723;\n\n  for (size_t y = 0; y < ysize; ++y) {\n    for (size_t x = 0; x < xsize; ++x) {\n      const double s0 = (*mask)[0].Row(y)[x];\n      const double s1 = (*mask)[1].Row(y)[x];\n      const double p1 = mul[1] * w11 * s1;\n      const double p0 = mul[0] * w00 * s0 + p1_to_p0 * p1;\n\n      (*mask)[0].Row(y)[x] = MaskX(p0);\n      (*mask)[1].Row(y)[x] = MaskY(p1);\n      (*mask)[2].Row(y)[x] = w_ytob_hf * MaskY(p1);\n      (*mask_dc)[0].Row(y)[x] = MaskDcX(p0);\n      (*mask_dc)[1].Row(y)[x] = MaskDcY(p1);\n      (*mask_dc)[2].Row(y)[x] = w_ytob_lf * MaskDcY(p1);\n    }\n  }\n}\n\nvoid ButteraugliDiffmap(const std::vector<ImageF> &rgb0_image,\n                        const std::vector<ImageF> &rgb1_image,\n                        ImageF &result_image) {\n  const size_t xsize = rgb0_image[0].xsize();\n  const size_t ysize = rgb0_image[0].ysize();\n  static const int kMax = 8;\n  if (xsize < kMax || ysize < kMax) {\n    // Butteraugli values for small (where xsize or ysize is smaller\n    // than 8 pixels) images are non-sensical, but most likely it is\n    // less disruptive to try to compute something than just give up.\n    // Temporarily extend the borders of the image to fit 8 x 8 size.\n    int xborder = xsize < kMax ? (kMax - xsize) / 2 : 0;\n    int yborder = ysize < kMax ? (kMax - ysize) / 2 : 0;\n    size_t xscaled = std::max<size_t>(kMax, xsize);\n    size_t yscaled = std::max<size_t>(kMax, ysize);\n    std::vector<ImageF> scaled0 = CreatePlanes<float>(xscaled, yscaled, 3);\n    std::vector<ImageF> scaled1 = CreatePlanes<float>(xscaled, yscaled, 3);\n    for (int i = 0; i < 3; ++i) {\n      for (int y = 0; y < yscaled; ++y) {\n        for (int x = 0; x < xscaled; ++x) {\n          size_t x2 = std::min<size_t>(xsize - 1, std::max(0, x - xborder));\n          size_t y2 = std::min<size_t>(ysize - 1, std::max(0, y - yborder));\n          scaled0[i].Row(y)[x] = rgb0_image[i].Row(y2)[x2];\n          scaled1[i].Row(y)[x] = rgb1_image[i].Row(y2)[x2];\n        }\n      }\n    }\n    ImageF diffmap_scaled;\n    ButteraugliDiffmap(scaled0, scaled1, diffmap_scaled);\n    result_image = ImageF(xsize, ysize);\n    for (int y = 0; y < ysize; ++y) {\n      for (int x = 0; x < xsize; ++x) {\n        result_image.Row(y)[x] = diffmap_scaled.Row(y + yborder)[x + xborder];\n      }\n    }\n    return;\n  }\n  ButteraugliComparator butteraugli(rgb0_image);\n  butteraugli.Diffmap(rgb1_image, result_image);\n}\n\nbool ButteraugliInterface(const std::vector<ImageF> &rgb0,\n                          const std::vector<ImageF> &rgb1,\n                          ImageF &diffmap,\n                          double &diffvalue) {\n  const size_t xsize = rgb0[0].xsize();\n  const size_t ysize = rgb0[0].ysize();\n  if (xsize < 1 || ysize < 1) {\n    return false;  // No image.\n  }\n  for (int i = 1; i < 3; i++) {\n    if (rgb0[i].xsize() != xsize || rgb0[i].ysize() != ysize ||\n        rgb1[i].xsize() != xsize || rgb1[i].ysize() != ysize) {\n      return false;  // Image planes must have same dimensions.\n    }\n  }\n  ButteraugliDiffmap(rgb0, rgb1, diffmap);\n  diffvalue = ButteraugliScoreFromDiffmap(diffmap);\n  return true;\n}\n\nbool ButteraugliAdaptiveQuantization(size_t xsize, size_t ysize,\n    const std::vector<std::vector<float> > &rgb, std::vector<float> &quant) {\n  if (xsize < 16 || ysize < 16) {\n    return false;  // Butteraugli is undefined for small images.\n  }\n  size_t size = xsize * ysize;\n\n  std::vector<ImageF> rgb_planes = PlanesFromPacked(xsize, ysize, rgb);\n  std::vector<ImageF> scale_xyb;\n  std::vector<ImageF> scale_xyb_dc;\n  Mask(rgb_planes, rgb_planes, &scale_xyb, &scale_xyb_dc);\n  quant.reserve(size);\n\n  // Mask gives us values in 3 color channels, but for now we take only\n  // the intensity channel.\n  for (size_t y = 0; y < ysize; ++y) {\n    for (size_t x = 0; x < xsize; ++x) {\n      quant.push_back(scale_xyb[1].Row(y)[x]);\n    }\n  }\n  return true;\n}\n\ndouble ButteraugliFuzzyClass(double score) {\n  static const double fuzzy_width_up = 6.07887388532;\n  static const double fuzzy_width_down = 5.50793514384;\n  static const double m0 = 2.0;\n  static const double scaler = 0.840253347958;\n  double val;\n  if (score < 1.0) {\n    // val in [scaler .. 2.0]\n    val = m0 / (1.0 + exp((score - 1.0) * fuzzy_width_down));\n    val -= 1.0;  // from [1 .. 2] to [0 .. 1]\n    val *= 2.0 - scaler;  // from [0 .. 1] to [0 .. 2.0 - scaler]\n    val += scaler;  // from [0 .. 2.0 - scaler] to [scaler .. 2.0]\n  } else {\n    // val in [0 .. scaler]\n    val = m0 / (1.0 + exp((score - 1.0) * fuzzy_width_up));\n    val *= scaler;\n  }\n  return val;\n}\n\ndouble ButteraugliFuzzyInverse(double seek) {\n  double pos = 0;\n  for (double range = 1.0; range >= 1e-10; range *= 0.5) {\n    double cur = ButteraugliFuzzyClass(pos);\n    if (cur < seek) {\n      pos -= range;\n    } else {\n      pos += range;\n    }\n  }\n  return pos;\n}\n\nnamespace {\n\nvoid ScoreToRgb(double score, double good_threshold, double bad_threshold,\n                uint8_t rgb[3]) {\n  double heatmap[12][3] = {\n      {0, 0, 0},\n      {0, 0, 1},\n      {0, 1, 1},\n      {0, 1, 0},  // Good level\n      {1, 1, 0},\n      {1, 0, 0},  // Bad level\n      {1, 0, 1},\n      {0.5, 0.5, 1.0},\n      {1.0, 0.5, 0.5},  // Pastel colors for the very bad quality range.\n      {1.0, 1.0, 0.5},\n      {\n          1, 1, 1,\n      },\n      {\n          1, 1, 1,\n      },\n  };\n  if (score < good_threshold) {\n    score = (score / good_threshold) * 0.3;\n  } else if (score < bad_threshold) {\n    score = 0.3 +\n            (score - good_threshold) / (bad_threshold - good_threshold) * 0.15;\n  } else {\n    score = 0.45 + (score - bad_threshold) / (bad_threshold * 12) * 0.5;\n  }\n  static const int kTableSize = sizeof(heatmap) / sizeof(heatmap[0]);\n  score = std::min<double>(std::max<double>(score * (kTableSize - 1), 0.0),\n                           kTableSize - 2);\n  int ix = static_cast<int>(score);\n  double mix = score - ix;\n  for (int i = 0; i < 3; ++i) {\n    double v = mix * heatmap[ix + 1][i] + (1 - mix) * heatmap[ix][i];\n    rgb[i] = static_cast<uint8_t>(255 * pow(v, 0.5) + 0.5);\n  }\n}\n\n}  // namespace\n\nvoid CreateHeatMapImage(const std::vector<float>& distmap,\n                        double good_threshold, double bad_threshold,\n                        size_t xsize, size_t ysize,\n                        std::vector<uint8_t>* heatmap) {\n  heatmap->resize(3 * xsize * ysize);\n  for (size_t y = 0; y < ysize; ++y) {\n    for (size_t x = 0; x < xsize; ++x) {\n      int px = xsize * y + x;\n      double d = distmap[px];\n      uint8_t* rgb = &(*heatmap)[3 * px];\n      ScoreToRgb(d, good_threshold, bad_threshold, rgb);\n    }\n  }\n}\n\n}  // namespace butteraugli\n"
  },
  {
    "path": "third_party/butteraugli/butteraugli/butteraugli.h",
    "content": "// Copyright 2016 Google Inc. All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n// Disclaimer: This is not an official Google product.\n//\n// Author: Jyrki Alakuijala (jyrki.alakuijala@gmail.com)\n\n#ifndef BUTTERAUGLI_BUTTERAUGLI_H_\n#define BUTTERAUGLI_BUTTERAUGLI_H_\n\n#include <cassert>\n#include <cmath>\n#include <cstdint>\n#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n#include <memory>\n#include <vector>\n\n#define BUTTERAUGLI_ENABLE_CHECKS 0\n\n// This is the main interface to butteraugli image similarity\n// analysis function.\n\nnamespace butteraugli {\n\ntemplate<typename T>\nclass Image;\n\nusing Image8 = Image<uint8_t>;\nusing ImageF = Image<float>;\n\n// ButteraugliInterface defines the public interface for butteraugli.\n//\n// It calculates the difference between rgb0 and rgb1.\n//\n// rgb0 and rgb1 contain the images. rgb0[c][px] and rgb1[c][px] contains\n// the red image for c == 0, green for c == 1, blue for c == 2. Location index\n// px is calculated as y * xsize + x.\n//\n// Value of pixels of images rgb0 and rgb1 need to be represented as raw\n// intensity. Most image formats store gamma corrected intensity in pixel\n// values. This gamma correction has to be removed, by applying the following\n// function:\n// butteraugli_val = 255.0 * pow(png_val / 255.0, gamma);\n// A typical value of gamma is 2.2. It is usually stored in the image header.\n// Take care not to confuse that value with its inverse. The gamma value should\n// be always greater than one.\n// Butteraugli does not work as intended if the caller does not perform\n// gamma correction.\n//\n// diffmap will contain an image of the size xsize * ysize, containing\n// localized differences for values px (indexed with the px the same as rgb0\n// and rgb1). diffvalue will give a global score of similarity.\n//\n// A diffvalue smaller than kButteraugliGood indicates that images can be\n// observed as the same image.\n// diffvalue larger than kButteraugliBad indicates that a difference between\n// the images can be observed.\n// A diffvalue between kButteraugliGood and kButteraugliBad indicates that\n// a subtle difference can be observed between the images.\n//\n// Returns true on success.\n\nbool ButteraugliInterface(const std::vector<ImageF> &rgb0,\n                          const std::vector<ImageF> &rgb1,\n                          ImageF &diffmap,\n                          double &diffvalue);\n\nconst double kButteraugliQuantLow = 0.26;\nconst double kButteraugliQuantHigh = 1.454;\n\n// Converts the butteraugli score into fuzzy class values that are continuous\n// at the class boundary. The class boundary location is based on human\n// raters, but the slope is arbitrary. Particularly, it does not reflect\n// the expectation value of probabilities of the human raters. It is just\n// expected that a smoother class boundary will allow for higher-level\n// optimization algorithms to work faster.\n//\n// Returns 2.0 for a perfect match, and 1.0 for 'ok', 0.0 for bad. Because the\n// scoring is fuzzy, a butteraugli score of 0.96 would return a class of\n// around 1.9.\ndouble ButteraugliFuzzyClass(double score);\n\n// Input values should be in range 0 (bad) to 2 (good). Use\n// kButteraugliNormalization as normalization.\ndouble ButteraugliFuzzyInverse(double seek);\n\n// Returns a map which can be used for adaptive quantization. Values can\n// typically range from kButteraugliQuantLow to kButteraugliQuantHigh. Low\n// values require coarse quantization (e.g. near random noise), high values\n// require fine quantization (e.g. in smooth bright areas).\nbool ButteraugliAdaptiveQuantization(size_t xsize, size_t ysize,\n    const std::vector<std::vector<float> > &rgb, std::vector<float> &quant);\n\n// Implementation details, don't use anything below or your code will\n// break in the future.\n\n#ifdef _MSC_VER\n#define BUTTERAUGLI_RESTRICT __restrict\n#else\n#define BUTTERAUGLI_RESTRICT __restrict__\n#endif\n\n#ifdef _MSC_VER\n#define BUTTERAUGLI_INLINE __forceinline\n#else\n#define BUTTERAUGLI_INLINE inline\n#endif\n\n#ifdef __clang__\n// Early versions of Clang did not support __builtin_assume_aligned.\n#define BUTTERAUGLI_HAS_ASSUME_ALIGNED __has_builtin(__builtin_assume_aligned)\n#elif defined(__GNUC__)\n#define BUTTERAUGLI_HAS_ASSUME_ALIGNED 1\n#else\n#define BUTTERAUGLI_HAS_ASSUME_ALIGNED 0\n#endif\n\n// Returns a void* pointer which the compiler then assumes is N-byte aligned.\n// Example: float* PIK_RESTRICT aligned = (float*)PIK_ASSUME_ALIGNED(in, 32);\n//\n// The assignment semantics are required by GCC/Clang. ICC provides an in-place\n// __assume_aligned, whereas MSVC's __assume appears unsuitable.\n#if BUTTERAUGLI_HAS_ASSUME_ALIGNED\n#define BUTTERAUGLI_ASSUME_ALIGNED(ptr, align) __builtin_assume_aligned((ptr), (align))\n#else\n#define BUTTERAUGLI_ASSUME_ALIGNED(ptr, align) (ptr)\n#endif  // BUTTERAUGLI_HAS_ASSUME_ALIGNED\n\n// Functions that depend on the cache line size.\nclass CacheAligned {\n public:\n  static constexpr size_t kPointerSize = sizeof(void *);\n  static constexpr size_t kCacheLineSize = 64;\n\n  // The aligned-return annotation is only allowed on function declarations.\n  static void *Allocate(const size_t bytes);\n  static void Free(void *aligned_pointer);\n};\n\ntemplate <typename T>\nusing CacheAlignedUniquePtrT = std::unique_ptr<T[], void (*)(void *)>;\n\nusing CacheAlignedUniquePtr = CacheAlignedUniquePtrT<uint8_t>;\n\ntemplate <typename T = uint8_t>\nstatic inline CacheAlignedUniquePtrT<T> Allocate(const size_t entries) {\n  return CacheAlignedUniquePtrT<T>(\n      static_cast<T * const BUTTERAUGLI_RESTRICT>(\n          CacheAligned::Allocate(entries * sizeof(T))),\n      CacheAligned::Free);\n}\n\n// Returns the smallest integer not less than \"amount\" that is divisible by\n// \"multiple\", which must be a power of two.\ntemplate <size_t multiple>\nstatic inline size_t Align(const size_t amount) {\n  static_assert(multiple != 0 && ((multiple & (multiple - 1)) == 0),\n                \"Align<> argument must be a power of two\");\n  return (amount + multiple - 1) & ~(multiple - 1);\n}\n\n// Single channel, contiguous (cache-aligned) rows separated by padding.\n// T must be POD.\n//\n// Rationale: vectorization benefits from aligned operands - unaligned loads and\n// especially stores are expensive when the address crosses cache line\n// boundaries. Introducing padding after each row ensures the start of a row is\n// aligned, and that row loops can process entire vectors (writes to the padding\n// are allowed and ignored).\n//\n// We prefer a planar representation, where channels are stored as separate\n// 2D arrays, because that simplifies vectorization (repeating the same\n// operation on multiple adjacent components) without the complexity of a\n// hybrid layout (8 R, 8 G, 8 B, ...). In particular, clients can easily iterate\n// over all components in a row and Image requires no knowledge of the pixel\n// format beyond the component type \"T\". The downside is that we duplicate the\n// xsize/ysize members for each channel.\n//\n// This image layout could also be achieved with a vector and a row accessor\n// function, but a class wrapper with support for \"deleter\" allows wrapping\n// existing memory allocated by clients without copying the pixels. It also\n// provides convenient accessors for xsize/ysize, which shortens function\n// argument lists. Supports move-construction so it can be stored in containers.\ntemplate <typename ComponentType>\nclass Image {\n  // Returns cache-aligned row stride, being careful to avoid 2K aliasing.\n  static size_t BytesPerRow(const size_t xsize) {\n    // Allow reading one extra AVX-2 vector on the right margin.\n    const size_t row_size = xsize * sizeof(T) + 32;\n    const size_t align = CacheAligned::kCacheLineSize;\n    size_t bytes_per_row = (row_size + align - 1) & ~(align - 1);\n    // During the lengthy window before writes are committed to memory, CPUs\n    // guard against read after write hazards by checking the address, but\n    // only the lower 11 bits. We avoid a false dependency between writes to\n    // consecutive rows by ensuring their sizes are not multiples of 2 KiB.\n    if (bytes_per_row % 2048 == 0) {\n      bytes_per_row += align;\n    }\n    return bytes_per_row;\n  }\n\n public:\n  using T = ComponentType;\n\n  Image() : xsize_(0), ysize_(0), bytes_per_row_(0),\n            bytes_(static_cast<uint8_t*>(nullptr), Ignore) {}\n\n  Image(const size_t xsize, const size_t ysize)\n      : xsize_(xsize),\n        ysize_(ysize),\n        bytes_per_row_(BytesPerRow(xsize)),\n        bytes_(Allocate(bytes_per_row_ * ysize)) {}\n\n  Image(const size_t xsize, const size_t ysize, T val)\n      : xsize_(xsize),\n        ysize_(ysize),\n        bytes_per_row_(BytesPerRow(xsize)),\n        bytes_(Allocate(bytes_per_row_ * ysize)) {\n    for (size_t y = 0; y < ysize_; ++y) {\n      T* const BUTTERAUGLI_RESTRICT row = Row(y);\n      for (int x = 0; x < xsize_; ++x) {\n        row[x] = val;\n      }\n    }\n  }\n\n  Image(const size_t xsize, const size_t ysize,\n        uint8_t * const BUTTERAUGLI_RESTRICT bytes,\n        const size_t bytes_per_row)\n      : xsize_(xsize),\n        ysize_(ysize),\n        bytes_per_row_(bytes_per_row),\n        bytes_(bytes, Ignore) {}\n\n  // Move constructor (required for returning Image from function)\n  Image(Image &&other)\n      : xsize_(other.xsize_),\n        ysize_(other.ysize_),\n        bytes_per_row_(other.bytes_per_row_),\n        bytes_(std::move(other.bytes_)) {}\n\n  // Move assignment (required for std::vector)\n  Image &operator=(Image &&other) {\n    xsize_ = other.xsize_;\n    ysize_ = other.ysize_;\n    bytes_per_row_ = other.bytes_per_row_;\n    bytes_ = std::move(other.bytes_);\n    return *this;\n  }\n\n  void Swap(Image &other) {\n    std::swap(xsize_, other.xsize_);\n    std::swap(ysize_, other.ysize_);\n    std::swap(bytes_per_row_, other.bytes_per_row_);\n    std::swap(bytes_, other.bytes_);\n  }\n\n  // How many pixels.\n  size_t xsize() const { return xsize_; }\n  size_t ysize() const { return ysize_; }\n\n  T *const BUTTERAUGLI_RESTRICT Row(const size_t y) {\n#ifdef BUTTERAUGLI_ENABLE_CHECKS\n    if (y >= ysize_) {\n      printf(\"Row %zu out of bounds (ysize=%zu)\\n\", y, ysize_);\n      abort();\n    }\n#endif\n    void *row = bytes_.get() + y * bytes_per_row_;\n    return reinterpret_cast<T *>(BUTTERAUGLI_ASSUME_ALIGNED(row, 64));\n  }\n\n  const T *const BUTTERAUGLI_RESTRICT Row(const size_t y) const {\n#ifdef BUTTERAUGLI_ENABLE_CHECKS\n    if (y >= ysize_) {\n      printf(\"Const row %zu out of bounds (ysize=%zu)\\n\", y, ysize_);\n      abort();\n    }\n#endif\n    void *row = bytes_.get() + y * bytes_per_row_;\n    return reinterpret_cast<const T *>(BUTTERAUGLI_ASSUME_ALIGNED(row, 64));\n  }\n\n  // Raw access to byte contents, for interfacing with other libraries.\n  // Unsigned char instead of char to avoid surprises (sign extension).\n  uint8_t * const BUTTERAUGLI_RESTRICT bytes() { return bytes_.get(); }\n  const uint8_t * const BUTTERAUGLI_RESTRICT bytes() const {\n      return bytes_.get();\n  }\n  size_t bytes_per_row() const { return bytes_per_row_; }\n\n  // Returns number of pixels (some of which are padding) per row. Useful for\n  // computing other rows via pointer arithmetic.\n  intptr_t PixelsPerRow() const {\n    static_assert(CacheAligned::kCacheLineSize % sizeof(T) == 0,\n                  \"Padding must be divisible by the pixel size.\");\n    return static_cast<intptr_t>(bytes_per_row_ / sizeof(T));\n  }\n\n private:\n  // Deleter used when bytes are not owned.\n  static void Ignore(void *ptr) {}\n\n  // (Members are non-const to enable assignment during move-assignment.)\n  size_t xsize_;  // original intended pixels, not including any padding.\n  size_t ysize_;\n  size_t bytes_per_row_;  // [bytes] including padding.\n  CacheAlignedUniquePtr bytes_;\n};\n\n// Returns newly allocated planes of the given dimensions.\ntemplate <typename T>\nstatic inline std::vector<Image<T>> CreatePlanes(const size_t xsize,\n                                                 const size_t ysize,\n                                                 const size_t num_planes) {\n  std::vector<Image<T>> planes;\n  planes.reserve(num_planes);\n  for (size_t i = 0; i < num_planes; ++i) {\n    planes.emplace_back(xsize, ysize);\n  }\n  return planes;\n}\n\n// Returns a new image with the same dimensions and pixel values.\ntemplate <typename T>\nstatic inline Image<T> CopyPixels(const Image<T> &other) {\n  Image<T> copy(other.xsize(), other.ysize());\n  const void *BUTTERAUGLI_RESTRICT from = other.bytes();\n  void *BUTTERAUGLI_RESTRICT to = copy.bytes();\n  memcpy(to, from, other.ysize() * other.bytes_per_row());\n  return copy;\n}\n\n// Returns new planes with the same dimensions and pixel values.\ntemplate <typename T>\nstatic inline std::vector<Image<T>> CopyPlanes(\n    const std::vector<Image<T>> &planes) {\n  std::vector<Image<T>> copy;\n  copy.reserve(planes.size());\n  for (const Image<T> &plane : planes) {\n    copy.push_back(CopyPixels(plane));\n  }\n  return copy;\n}\n\n// Compacts a padded image into a preallocated packed vector.\ntemplate <typename T>\nstatic inline void CopyToPacked(const Image<T> &from, std::vector<T> *to) {\n  const size_t xsize = from.xsize();\n  const size_t ysize = from.ysize();\n#if BUTTERAUGLI_ENABLE_CHECKS\n  if (to->size() < xsize * ysize) {\n    printf(\"%zu x %zu exceeds %zu capacity\\n\", xsize, ysize, to->size());\n    abort();\n  }\n#endif\n  for (size_t y = 0; y < ysize; ++y) {\n    const float* const BUTTERAUGLI_RESTRICT row_from = from.Row(y);\n    float* const BUTTERAUGLI_RESTRICT row_to = to->data() + y * xsize;\n    memcpy(row_to, row_from, xsize * sizeof(T));\n  }\n}\n\n// Expands a packed vector into a preallocated padded image.\ntemplate <typename T>\nstatic inline void CopyFromPacked(const std::vector<T> &from, Image<T> *to) {\n  const size_t xsize = to->xsize();\n  const size_t ysize = to->ysize();\n  assert(from.size() == xsize * ysize);\n  for (size_t y = 0; y < ysize; ++y) {\n    const float* const BUTTERAUGLI_RESTRICT row_from =\n        from.data() + y * xsize;\n    float* const BUTTERAUGLI_RESTRICT row_to = to->Row(y);\n    memcpy(row_to, row_from, xsize * sizeof(T));\n  }\n}\n\ntemplate <typename T>\nstatic inline std::vector<Image<T>> PlanesFromPacked(\n    const size_t xsize, const size_t ysize,\n    const std::vector<std::vector<T>> &packed) {\n  std::vector<Image<T>> planes;\n  planes.reserve(packed.size());\n  for (const std::vector<T> &p : packed) {\n    planes.push_back(Image<T>(xsize, ysize));\n    CopyFromPacked(p, &planes.back());\n  }\n  return planes;\n}\n\ntemplate <typename T>\nstatic inline std::vector<std::vector<T>> PackedFromPlanes(\n    const std::vector<Image<T>> &planes) {\n  assert(!planes.empty());\n  const size_t num_pixels = planes[0].xsize() * planes[0].ysize();\n  std::vector<std::vector<T>> packed;\n  packed.reserve(planes.size());\n  for (const Image<T> &image : planes) {\n    packed.push_back(std::vector<T>(num_pixels));\n    CopyToPacked(image, &packed.back());\n  }\n  return packed;\n}\n\nstruct PsychoImage {\n  std::vector<ImageF> uhf;\n  std::vector<ImageF> hf;\n  std::vector<ImageF> mf;\n  std::vector<ImageF> lf;\n};\n\nclass ButteraugliComparator {\n public:\n  ButteraugliComparator(const std::vector<ImageF>& rgb0);\n\n  // Computes the butteraugli map between the original image given in the\n  // constructor and the distorted image give here.\n  void Diffmap(const std::vector<ImageF>& rgb1, ImageF& result) const;\n\n  // Same as above, but OpsinDynamicsImage() was already applied.\n  void DiffmapOpsinDynamicsImage(const std::vector<ImageF>& xyb1,\n                                 ImageF& result) const;\n\n  // Same as above, but the frequency decomposition was already applied.\n  void DiffmapPsychoImage(const PsychoImage& ps1, ImageF &result) const;\n\n  void Mask(std::vector<ImageF>* BUTTERAUGLI_RESTRICT mask,\n            std::vector<ImageF>* BUTTERAUGLI_RESTRICT mask_dc) const;\n\n private:\n  void MaltaDiffMapLF(const ImageF& y0,\n                      const ImageF& y1,\n                      double w_0gt1,\n                      double w_0lt1,\n                      double normalization,\n                      ImageF* BUTTERAUGLI_RESTRICT block_diff_ac) const;\n\n  void MaltaDiffMap(const ImageF& y0,\n                    const ImageF& y1,\n                    double w_0gt1,\n                    double w_0lt1,\n                    double normalization,\n                    ImageF* BUTTERAUGLI_RESTRICT block_diff_ac) const;\n\n  ImageF CombineChannels(const std::vector<ImageF>& scale_xyb,\n                         const std::vector<ImageF>& scale_xyb_dc,\n                         const std::vector<ImageF>& block_diff_dc,\n                         const std::vector<ImageF>& block_diff_ac) const;\n\n  const size_t xsize_;\n  const size_t ysize_;\n  const size_t num_pixels_;\n  PsychoImage pi0_;\n};\n\nvoid ButteraugliDiffmap(const std::vector<ImageF> &rgb0,\n                        const std::vector<ImageF> &rgb1,\n                        ImageF &diffmap);\n\ndouble ButteraugliScoreFromDiffmap(const ImageF& distmap);\n\n// Generate rgb-representation of the distance between two images.\nvoid CreateHeatMapImage(const std::vector<float> &distmap,\n                        double good_threshold, double bad_threshold,\n                        size_t xsize, size_t ysize,\n                        std::vector<uint8_t> *heatmap);\n\n// Compute values of local frequency and dc masking based on the activity\n// in the two images.\nvoid Mask(const std::vector<ImageF>& xyb0,\n          const std::vector<ImageF>& xyb1,\n          std::vector<ImageF>* BUTTERAUGLI_RESTRICT mask,\n          std::vector<ImageF>* BUTTERAUGLI_RESTRICT mask_dc);\n\ntemplate <class V>\nBUTTERAUGLI_INLINE void RgbToXyb(const V &r, const V &g, const V &b,\n                                 V *BUTTERAUGLI_RESTRICT valx,\n                                 V *BUTTERAUGLI_RESTRICT valy,\n                                 V *BUTTERAUGLI_RESTRICT valb) {\n  *valx = r - g;\n  *valy = r + g;\n  *valb = b;\n}\n\ntemplate <class V>\nBUTTERAUGLI_INLINE void OpsinAbsorbance(const V &in0, const V &in1,\n                                        const V &in2,\n                                        V *BUTTERAUGLI_RESTRICT out0,\n                                        V *BUTTERAUGLI_RESTRICT out1,\n                                        V *BUTTERAUGLI_RESTRICT out2) {\n  // https://en.wikipedia.org/wiki/Photopsin absorbance modeling.\n  static const double mixi0 = 0.254462330846;\n  static const double mixi1 = 0.488238255095;\n  static const double mixi2 = 0.0635278003854;\n  static const double mixi3 = 1.01681026909;\n  static const double mixi4 = 0.195214015766;\n  static const double mixi5 = 0.568019861857;\n  static const double mixi6 = 0.0860755536007;\n  static const double mixi7 = 1.1510118369;\n  static const double mixi8 = 0.07374607900105684;\n  static const double mixi9 = 0.06142425304154509;\n  static const double mixi10 = 0.24416850520714256;\n  static const double mixi11 = 1.20481945273;\n\n  const V mix0(mixi0);\n  const V mix1(mixi1);\n  const V mix2(mixi2);\n  const V mix3(mixi3);\n  const V mix4(mixi4);\n  const V mix5(mixi5);\n  const V mix6(mixi6);\n  const V mix7(mixi7);\n  const V mix8(mixi8);\n  const V mix9(mixi9);\n  const V mix10(mixi10);\n  const V mix11(mixi11);\n\n  *out0 = mix0 * in0 + mix1 * in1 + mix2 * in2 + mix3;\n  *out1 = mix4 * in0 + mix5 * in1 + mix6 * in2 + mix7;\n  *out2 = mix8 * in0 + mix9 * in1 + mix10 * in2 + mix11;\n}\n\nstd::vector<ImageF> OpsinDynamicsImage(const std::vector<ImageF>& rgb);\n\nImageF Blur(const ImageF& in, float sigma, float border_ratio);\n\ndouble SimpleGamma(double v);\n\ndouble GammaMinArg();\ndouble GammaMaxArg();\n\n// Polynomial evaluation via Clenshaw's scheme (similar to Horner's).\n// Template enables compile-time unrolling of the recursion, but must reside\n// outside of a class due to the specialization.\ntemplate <int INDEX>\nstatic inline void ClenshawRecursion(const double x, const double *coefficients,\n                                     double *b1, double *b2) {\n  const double x_b1 = x * (*b1);\n  const double t = (x_b1 + x_b1) - (*b2) + coefficients[INDEX];\n  *b2 = *b1;\n  *b1 = t;\n\n  ClenshawRecursion<INDEX - 1>(x, coefficients, b1, b2);\n}\n\n// Base case\ntemplate <>\ninline void ClenshawRecursion<0>(const double x, const double *coefficients,\n                                 double *b1, double *b2) {\n  const double x_b1 = x * (*b1);\n  // The final iteration differs - no 2 * x_b1 here.\n  *b1 = x_b1 - (*b2) + coefficients[0];\n}\n\n// Rational polynomial := dividing two polynomial evaluations. These are easier\n// to find than minimax polynomials.\nstruct RationalPolynomial {\n  template <int N>\n  static double EvaluatePolynomial(const double x,\n                                   const double (&coefficients)[N]) {\n    double b1 = 0.0;\n    double b2 = 0.0;\n    ClenshawRecursion<N - 1>(x, coefficients, &b1, &b2);\n    return b1;\n  }\n\n  // Evaluates the polynomial at x (in [min_value, max_value]).\n  inline double operator()(const double x) const {\n    // First normalize to [0, 1].\n    const double x01 = (x - min_value) / (max_value - min_value);\n    // And then to [-1, 1] domain of Chebyshev polynomials.\n    const double xc = 2.0 * x01 - 1.0;\n\n    const double yp = EvaluatePolynomial(xc, p);\n    const double yq = EvaluatePolynomial(xc, q);\n    if (yq == 0.0) return 0.0;\n    return static_cast<float>(yp / yq);\n  }\n\n  // Domain of the polynomials; they are undefined elsewhere.\n  double min_value;\n  double max_value;\n\n  // Coefficients of T_n (Chebyshev polynomials of the first kind).\n  // Degree 5/5 is a compromise between accuracy (0.1%) and numerical stability.\n  double p[5 + 1];\n  double q[5 + 1];\n};\n\nstatic inline double GammaPolynomial(double value) {\n  static const RationalPolynomial r = {\n    0.971783, 590.188894,\n    {\n      98.7821300963361, 164.273222212631, 92.948112871376,\n      33.8165311212688, 6.91626704983562, 0.556380877028234\n    },\n    {\n      1, 1.64339473427892, 0.89392405219969, 0.298947051776379,\n      0.0507146002577288, 0.00226495093949756\n    }};\n  return r(value);\n}\n\n}  // namespace butteraugli\n\n#endif  // BUTTERAUGLI_BUTTERAUGLI_H_\n"
  },
  {
    "path": "third_party/butteraugli/butteraugli/butteraugli_main.cc",
    "content": "#include <cmath>\n#include <cstdint>\n#include <cstdio>\n#include <vector>\n#include \"butteraugli/butteraugli.h\"\n\nextern \"C\" {\n#include \"png.h\"\n#include \"jpeglib.h\"\n}\n\nnamespace butteraugli {\nnamespace {\n\n// \"rgb\": cleared and filled with same-sized image planes (one per channel);\n// either RGB, or RGBA if the PNG contains an alpha channel.\nbool ReadPNG(FILE* f, std::vector<Image8>* rgb) {\n  png_structp png_ptr =\n      png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);\n  if (!png_ptr) {\n    return false;\n  }\n\n  png_infop info_ptr = png_create_info_struct(png_ptr);\n  if (!info_ptr) {\n    png_destroy_read_struct(&png_ptr, NULL, NULL);\n    return false;\n  }\n\n  if (setjmp(png_jmpbuf(png_ptr)) != 0) {\n    // Ok we are here because of the setjmp.\n    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);\n    return false;\n  }\n\n  rewind(f);\n  png_init_io(png_ptr, f);\n\n  // The png_transforms flags are as follows:\n  // packing == convert 1,2,4 bit images,\n  // strip == 16 -> 8 bits / channel,\n  // shift == use sBIT dynamics, and\n  // expand == palettes -> rgb, grayscale -> 8 bit images, tRNS -> alpha.\n  const unsigned int png_transforms =\n      PNG_TRANSFORM_PACKING | PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_16;\n\n  png_read_png(png_ptr, info_ptr, png_transforms, NULL);\n\n  png_bytep* row_pointers = png_get_rows(png_ptr, info_ptr);\n\n  const int xsize = png_get_image_width(png_ptr, info_ptr);\n  const int ysize = png_get_image_height(png_ptr, info_ptr);\n  const int components = png_get_channels(png_ptr, info_ptr);\n\n  *rgb = CreatePlanes<uint8_t>(xsize, ysize, 3);\n\n  switch (components) {\n    case 1: {\n      // GRAYSCALE\n      for (int y = 0; y < ysize; ++y) {\n        const uint8_t* const BUTTERAUGLI_RESTRICT row = row_pointers[y];\n        uint8_t* const BUTTERAUGLI_RESTRICT row0 = (*rgb)[0].Row(y);\n        uint8_t* const BUTTERAUGLI_RESTRICT row1 = (*rgb)[1].Row(y);\n        uint8_t* const BUTTERAUGLI_RESTRICT row2 = (*rgb)[2].Row(y);\n\n        for (int x = 0; x < xsize; ++x) {\n          const uint8_t gray = row[x];\n          row0[x] = row1[x] = row2[x] = gray;\n        }\n      }\n      break;\n    }\n    case 2: {\n      // GRAYSCALE_ALPHA\n      rgb->push_back(Image8(xsize, ysize));\n      for (int y = 0; y < ysize; ++y) {\n        const uint8_t* const BUTTERAUGLI_RESTRICT row = row_pointers[y];\n        uint8_t* const BUTTERAUGLI_RESTRICT row0 = (*rgb)[0].Row(y);\n        uint8_t* const BUTTERAUGLI_RESTRICT row1 = (*rgb)[1].Row(y);\n        uint8_t* const BUTTERAUGLI_RESTRICT row2 = (*rgb)[2].Row(y);\n        uint8_t* const BUTTERAUGLI_RESTRICT row3 = (*rgb)[3].Row(y);\n\n        for (int x = 0; x < xsize; ++x) {\n          const uint8_t gray = row[2 * x + 0];\n          const uint8_t alpha = row[2 * x + 1];\n          row0[x] = gray;\n          row1[x] = gray;\n          row2[x] = gray;\n          row3[x] = alpha;\n        }\n      }\n      break;\n    }\n    case 3: {\n      // RGB\n      for (int y = 0; y < ysize; ++y) {\n        const uint8_t* const BUTTERAUGLI_RESTRICT row = row_pointers[y];\n        uint8_t* const BUTTERAUGLI_RESTRICT row0 = (*rgb)[0].Row(y);\n        uint8_t* const BUTTERAUGLI_RESTRICT row1 = (*rgb)[1].Row(y);\n        uint8_t* const BUTTERAUGLI_RESTRICT row2 = (*rgb)[2].Row(y);\n\n        for (int x = 0; x < xsize; ++x) {\n          row0[x] = row[3 * x + 0];\n          row1[x] = row[3 * x + 1];\n          row2[x] = row[3 * x + 2];\n        }\n      }\n      break;\n    }\n    case 4: {\n      // RGBA\n      rgb->push_back(Image8(xsize, ysize));\n      for (int y = 0; y < ysize; ++y) {\n        const uint8_t* const BUTTERAUGLI_RESTRICT row = row_pointers[y];\n        uint8_t* const BUTTERAUGLI_RESTRICT row0 = (*rgb)[0].Row(y);\n        uint8_t* const BUTTERAUGLI_RESTRICT row1 = (*rgb)[1].Row(y);\n        uint8_t* const BUTTERAUGLI_RESTRICT row2 = (*rgb)[2].Row(y);\n        uint8_t* const BUTTERAUGLI_RESTRICT row3 = (*rgb)[3].Row(y);\n\n        for (int x = 0; x < xsize; ++x) {\n          row0[x] = row[4 * x + 0];\n          row1[x] = row[4 * x + 1];\n          row2[x] = row[4 * x + 2];\n          row3[x] = row[4 * x + 3];\n        }\n      }\n      break;\n    }\n    default:\n      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);\n      return false;\n  }\n  png_destroy_read_struct(&png_ptr, &info_ptr, NULL);\n  return true;\n}\n\nconst double* NewSrgbToLinearTable() {\n  double* table = new double[256];\n  for (int i = 0; i < 256; ++i) {\n    const double srgb = i / 255.0;\n    table[i] =\n        255.0 * (srgb <= 0.04045 ? srgb / 12.92\n                                 : std::pow((srgb + 0.055) / 1.055, 2.4));\n  }\n  return table;\n}\n\nvoid jpeg_catch_error(j_common_ptr cinfo) {\n  (*cinfo->err->output_message) (cinfo);\n  jmp_buf* jpeg_jmpbuf = (jmp_buf*) cinfo->client_data;\n  jpeg_destroy(cinfo);\n  longjmp(*jpeg_jmpbuf, 1);\n}\n\n// \"rgb\": cleared and filled with same-sized image planes (one per channel);\n// either RGB, or RGBA if the PNG contains an alpha channel.\nbool ReadJPEG(FILE* f, std::vector<Image8>* rgb) {\n  rewind(f);\n\n  struct jpeg_decompress_struct cinfo;\n  struct jpeg_error_mgr jerr;\n  cinfo.err = jpeg_std_error(&jerr);\n  jmp_buf jpeg_jmpbuf;\n  cinfo.client_data = &jpeg_jmpbuf;\n  jerr.error_exit = jpeg_catch_error;\n  if (setjmp(jpeg_jmpbuf)) {\n    return false;\n  }\n\n  jpeg_create_decompress(&cinfo);\n\n  jpeg_stdio_src(&cinfo, f);\n  jpeg_read_header(&cinfo, TRUE);\n  jpeg_start_decompress(&cinfo);\n\n  int row_stride = cinfo.output_width * cinfo.output_components;\n  JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)\n    ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);\n\n  const size_t xsize = cinfo.output_width;\n  const size_t ysize = cinfo.output_height;\n\n  *rgb = CreatePlanes<uint8_t>(xsize, ysize, 3);\n\n  switch (cinfo.out_color_space) {\n    case JCS_GRAYSCALE:\n      while (cinfo.output_scanline < cinfo.output_height) {\n        jpeg_read_scanlines(&cinfo, buffer, 1);\n\n        const uint8_t* const BUTTERAUGLI_RESTRICT row = buffer[0];\n        uint8_t* const BUTTERAUGLI_RESTRICT row0 =\n            (*rgb)[0].Row(cinfo.output_scanline - 1);\n        uint8_t* const BUTTERAUGLI_RESTRICT row1 =\n            (*rgb)[1].Row(cinfo.output_scanline - 1);\n        uint8_t* const BUTTERAUGLI_RESTRICT row2 =\n            (*rgb)[2].Row(cinfo.output_scanline - 1);\n\n        for (int x = 0; x < xsize; x++) {\n          const uint8_t gray = row[x];\n          row0[x] = row1[x] = row2[x] = gray;\n        }\n      }\n      break;\n\n    case JCS_RGB:\n      while (cinfo.output_scanline < cinfo.output_height) {\n        jpeg_read_scanlines(&cinfo, buffer, 1);\n\n        const uint8_t* const BUTTERAUGLI_RESTRICT row = buffer[0];\n        uint8_t* const BUTTERAUGLI_RESTRICT row0 =\n            (*rgb)[0].Row(cinfo.output_scanline - 1);\n        uint8_t* const BUTTERAUGLI_RESTRICT row1 =\n            (*rgb)[1].Row(cinfo.output_scanline - 1);\n        uint8_t* const BUTTERAUGLI_RESTRICT row2 =\n            (*rgb)[2].Row(cinfo.output_scanline - 1);\n\n        for (int x = 0; x < xsize; x++) {\n          row0[x] = row[3 * x + 0];\n          row1[x] = row[3 * x + 1];\n          row2[x] = row[3 * x + 2];\n        }\n      }\n      break;\n\n    default:\n      jpeg_destroy_decompress(&cinfo);\n      return false;\n  }\n\n  jpeg_finish_decompress(&cinfo);\n  jpeg_destroy_decompress(&cinfo);\n  return true;\n}\n\n// Translate R, G, B channels from sRGB to linear space. If an alpha channel\n// is present, overlay the image over a black or white background. Overlaying\n// is done in the sRGB space; while technically incorrect, this is aligned with\n// many other software (web browsers, WebP near lossless).\nvoid FromSrgbToLinear(const std::vector<Image8>& rgb,\n                      std::vector<ImageF>& linear, int background) {\n  const size_t xsize = rgb[0].xsize();\n  const size_t ysize = rgb[0].ysize();\n  static const double* const kSrgbToLinearTable = NewSrgbToLinearTable();\n\n  if (rgb.size() == 3) {  // RGB\n    for (int c = 0; c < 3; c++) {\n      linear.push_back(ImageF(xsize, ysize));\n      for (int y = 0; y < ysize; ++y) {\n        const uint8_t* const BUTTERAUGLI_RESTRICT row_rgb = rgb[c].Row(y);\n        float* const BUTTERAUGLI_RESTRICT row_linear = linear[c].Row(y);\n        for (size_t x = 0; x < xsize; x++) {\n          const int value = row_rgb[x];\n          row_linear[x] = kSrgbToLinearTable[value];\n        }\n      }\n    }\n  } else {  // RGBA\n    for (int c = 0; c < 3; c++) {\n      linear.push_back(ImageF(xsize, ysize));\n      for (int y = 0; y < ysize; ++y) {\n        const uint8_t* const BUTTERAUGLI_RESTRICT row_rgb = rgb[c].Row(y);\n        float* const BUTTERAUGLI_RESTRICT row_linear = linear[c].Row(y);\n        const uint8_t* const BUTTERAUGLI_RESTRICT row_alpha = rgb[3].Row(y);\n        for (size_t x = 0; x < xsize; x++) {\n          int value;\n          if (row_alpha[x] == 255) {\n            value = row_rgb[x];\n          } else if (row_alpha[x] == 0) {\n            value = background;\n          } else {\n            const int fg_weight = row_alpha[x];\n            const int bg_weight = 255 - fg_weight;\n            value =\n                (row_rgb[x] * fg_weight + background * bg_weight + 127) / 255;\n          }\n          row_linear[x] = kSrgbToLinearTable[value];\n        }\n      }\n    }\n  }\n}\n\nstd::vector<Image8> ReadImageOrDie(const char* filename) {\n  std::vector<Image8> rgb;\n  FILE* f = fopen(filename, \"rb\");\n  if (!f) {\n    fprintf(stderr, \"Cannot open %s\\n\", filename);\n    exit(1);\n  }\n  unsigned char magic[2];\n  if (fread(magic, 1, 2, f) != 2) {\n    fprintf(stderr, \"Cannot read from %s\\n\", filename);\n    exit(1);\n  }\n  if (magic[0] == 0xFF && magic[1] == 0xD8) {\n    if (!ReadJPEG(f, &rgb)) {\n      fprintf(stderr, \"File %s is a malformed JPEG.\\n\", filename);\n      exit(1);\n    }\n  } else {\n    if (!ReadPNG(f, &rgb)) {\n      fprintf(stderr, \"File %s is neither a valid JPEG nor a valid PNG.\\n\",\n              filename);\n      exit(1);\n    }\n  }\n  fclose(f);\n  return rgb;\n}\n\nstatic void ScoreToRgb(double score, double good_threshold,\n                        double bad_threshold, uint8_t rgb[3]) {\n  double heatmap[12][3] = {\n    { 0, 0, 0 },\n    { 0, 0, 1 },\n    { 0, 1, 1 },\n    { 0, 1, 0 }, // Good level\n    { 1, 1, 0 },\n    { 1, 0, 0 }, // Bad level\n    { 1, 0, 1 },\n    { 0.5, 0.5, 1.0 },\n    { 1.0, 0.5, 0.5 },  // Pastel colors for the very bad quality range.\n    { 1.0, 1.0, 0.5 },\n    { 1, 1, 1, },\n    { 1, 1, 1, },\n  };\n  if (score < good_threshold) {\n    score = (score / good_threshold) * 0.3;\n  } else if (score < bad_threshold) {\n    score = 0.3 + (score - good_threshold) /\n        (bad_threshold - good_threshold) * 0.15;\n  } else {\n    score = 0.45 + (score - bad_threshold) /\n        (bad_threshold * 12) * 0.5;\n  }\n  static const int kTableSize = sizeof(heatmap) / sizeof(heatmap[0]);\n  score = std::min<double>(std::max<double>(\n      score * (kTableSize - 1), 0.0), kTableSize - 2);\n  int ix = static_cast<int>(score);\n  double mix = score - ix;\n  for (int i = 0; i < 3; ++i) {\n    double v = mix * heatmap[ix + 1][i] + (1 - mix) * heatmap[ix][i];\n    rgb[i] = static_cast<uint8_t>(255 * pow(v, 0.5) + 0.5);\n  }\n}\n\nvoid CreateHeatMapImage(const ImageF& distmap, double good_threshold,\n                        double bad_threshold, size_t xsize, size_t ysize,\n                        std::vector<uint8_t>* heatmap) {\n  heatmap->resize(3 * xsize * ysize);\n  for (size_t y = 0; y < ysize; ++y) {\n    for (size_t x = 0; x < xsize; ++x) {\n      int px = xsize * y + x;\n      double d = distmap.Row(y)[x];\n      uint8_t* rgb = &(*heatmap)[3 * px];\n      ScoreToRgb(d, good_threshold, bad_threshold, rgb);\n    }\n  }\n}\n\n// main() function, within butteraugli namespace for convenience.\nint Run(int argc, char* argv[]) {\n  if (argc != 3 && argc != 4) {\n    fprintf(stderr,\n            \"Usage: %s {image1.(png|jpg|jpeg)} {image2.(png|jpg|jpeg)} \"\n            \"[heatmap.ppm]\\n\",\n            argv[0]);\n    return 1;\n  }\n\n  std::vector<Image8> rgb1 = ReadImageOrDie(argv[1]);\n  std::vector<Image8> rgb2 = ReadImageOrDie(argv[2]);\n\n  if (rgb1.size() != rgb2.size()) {\n    fprintf(stderr, \"Different number of channels: %lu vs %lu\\n\", rgb1.size(),\n            rgb2.size());\n    exit(1);\n  }\n\n  for (size_t c = 0; c < rgb1.size(); ++c) {\n    if (rgb1[c].xsize() != rgb2[c].xsize() ||\n        rgb1[c].ysize() != rgb2[c].ysize()) {\n      fprintf(\n          stderr, \"The images are not equal in size: (%lu,%lu) vs (%lu,%lu)\\n\",\n          rgb1[c].xsize(), rgb2[c].xsize(), rgb1[c].ysize(), rgb2[c].ysize());\n      return 1;\n    }\n  }\n\n  // TODO: Figure out if it is a good idea to fetch the gamma from the image\n  // instead of applying sRGB conversion.\n  std::vector<ImageF> linear1, linear2;\n  // Overlay the image over a black background.\n  FromSrgbToLinear(rgb1, linear1, 0);\n  FromSrgbToLinear(rgb2, linear2, 0);\n  ImageF diff_map, diff_map_on_white;\n  double diff_value;\n  if (!butteraugli::ButteraugliInterface(linear1, linear2, diff_map,\n                                         diff_value)) {\n    fprintf(stderr, \"Butteraugli comparison failed\\n\");\n    return 1;\n  }\n  ImageF* diff_map_ptr = &diff_map;\n  if (rgb1.size() == 4 || rgb2.size() == 4) {\n    // If the alpha channel is present, overlay the image over a white\n    // background as well.\n    FromSrgbToLinear(rgb1, linear1, 255);\n    FromSrgbToLinear(rgb2, linear2, 255);\n    double diff_value_on_white;\n    if (!butteraugli::ButteraugliInterface(linear1, linear2, diff_map_on_white,\n                                           diff_value_on_white)) {\n      fprintf(stderr, \"Butteraugli comparison failed\\n\");\n      return 1;\n    }\n    if (diff_value_on_white > diff_value) {\n      diff_value = diff_value_on_white;\n      diff_map_ptr = &diff_map_on_white;\n    }\n  }\n  printf(\"%lf\\n\", diff_value);\n\n  if (argc == 4) {\n    const double good_quality = ::butteraugli::ButteraugliFuzzyInverse(1.5);\n    const double bad_quality = ::butteraugli::ButteraugliFuzzyInverse(0.5);\n    std::vector<uint8_t> rgb;\n    CreateHeatMapImage(*diff_map_ptr, good_quality, bad_quality,\n                       rgb1[0].xsize(), rgb2[0].ysize(), &rgb);\n    FILE* const fmap = fopen(argv[3], \"wb\");\n    if (fmap == NULL) {\n      fprintf(stderr, \"Cannot open %s\\n\", argv[3]);\n      perror(\"fopen\");\n      return 1;\n    }\n    bool ok = true;\n    if (fprintf(fmap, \"P6\\n%lu %lu\\n255\\n\",\n                      rgb1[0].xsize(), rgb1[0].ysize()) < 0){\n      perror(\"fprintf\");\n      ok = false;\n    }\n    if (ok && fwrite(rgb.data(), 1, rgb.size(), fmap) != rgb.size()) {\n      perror(\"fwrite\");\n      ok = false;\n    }\n    if (fclose(fmap) != 0) {\n      perror(\"fclose\");\n      ok = false;\n    }\n    if (!ok) return 1;\n  }\n\n  return 0;\n}\n\n}  // namespace\n}  // namespace butteraugli\n\nint main(int argc, char** argv) { return butteraugli::Run(argc, argv); }\n"
  },
  {
    "path": "third_party/butteraugli/jpeg.BUILD",
    "content": "# Description:\n#   The Independent JPEG Group's JPEG runtime library.\n\nlicenses([\"notice\"])  # custom notice-style license, see LICENSE\n\ncc_library(\n    name = \"jpeg\",\n    srcs = [\n        \"cderror.h\",\n        \"cdjpeg.h\",\n        \"jaricom.c\",\n        \"jcapimin.c\",\n        \"jcapistd.c\",\n        \"jcarith.c\",\n        \"jccoefct.c\",\n        \"jccolor.c\",\n        \"jcdctmgr.c\",\n        \"jchuff.c\",\n        \"jcinit.c\",\n        \"jcmainct.c\",\n        \"jcmarker.c\",\n        \"jcmaster.c\",\n        \"jcomapi.c\",\n        \"jconfig.h\",\n        \"jcparam.c\",\n        \"jcprepct.c\",\n        \"jcsample.c\",\n        \"jctrans.c\",\n        \"jdapimin.c\",\n        \"jdapistd.c\",\n        \"jdarith.c\",\n        \"jdatadst.c\",\n        \"jdatasrc.c\",\n        \"jdcoefct.c\",\n        \"jdcolor.c\",\n        \"jdct.h\",\n        \"jddctmgr.c\",\n        \"jdhuff.c\",\n        \"jdinput.c\",\n        \"jdmainct.c\",\n        \"jdmarker.c\",\n        \"jdmaster.c\",\n        \"jdmerge.c\",\n        \"jdpostct.c\",\n        \"jdsample.c\",\n        \"jdtrans.c\",\n        \"jerror.c\",\n        \"jfdctflt.c\",\n        \"jfdctfst.c\",\n        \"jfdctint.c\",\n        \"jidctflt.c\",\n        \"jidctfst.c\",\n        \"jidctint.c\",\n        \"jinclude.h\",\n        \"jmemmgr.c\",\n        \"jmemnobs.c\",\n        \"jmemsys.h\",\n        \"jmorecfg.h\",\n        \"jquant1.c\",\n        \"jquant2.c\",\n        \"jutils.c\",\n        \"jversion.h\",\n        \"transupp.h\",\n    ],\n    hdrs = [\n        \"jerror.h\",\n        \"jpegint.h\",\n        \"jpeglib.h\",\n    ],\n    includes = [\".\"],\n    visibility = [\"//visibility:public\"],\n)\n\ngenrule(\n    name = \"configure\",\n    outs = [\"jconfig.h\"],\n    cmd = \"cat <<EOF >$@\\n\" +\n          \"#define HAVE_PROTOTYPES 1\\n\" +\n          \"#define HAVE_UNSIGNED_CHAR 1\\n\" +\n          \"#define HAVE_UNSIGNED_SHORT 1\\n\" +\n          \"#define HAVE_STDDEF_H 1\\n\" +\n          \"#define HAVE_STDLIB_H 1\\n\" +\n          \"#ifdef WIN32\\n\" +\n          \"#define INLINE __inline\\n\" +\n          \"#else\\n\" +\n          \"#define INLINE __inline__\\n\" +\n          \"#endif\\n\" +\n          \"EOF\\n\",\n)\n"
  },
  {
    "path": "third_party/butteraugli/png.BUILD",
    "content": "# Description:\n#   libpng is the official PNG reference library.\n\nlicenses([\"notice\"])  # BSD/MIT-like license\n\ncc_library(\n    name = \"png\",\n    srcs = [\n        \"png.c\",\n        \"pngerror.c\",\n        \"pngget.c\",\n        \"pngmem.c\",\n        \"pngpread.c\",\n        \"pngread.c\",\n        \"pngrio.c\",\n        \"pngrtran.c\",\n        \"pngrutil.c\",\n        \"pngset.c\",\n        \"pngtrans.c\",\n        \"pngwio.c\",\n        \"pngwrite.c\",\n        \"pngwtran.c\",\n        \"pngwutil.c\",\n    ],\n    hdrs = [\n        \"png.h\",\n        \"pngconf.h\",\n    ],\n    includes = [\".\"],\n    linkopts = [\"-lm\"],\n    visibility = [\"//visibility:public\"],\n    deps = [\"@zlib_archive//:zlib\"],\n)\n"
  },
  {
    "path": "third_party/butteraugli/zlib.BUILD",
    "content": "package(default_visibility = [\"//visibility:public\"])\n\nlicenses([\"notice\"])  # BSD/MIT-like license (for zlib)\n\ncc_library(\n    name = \"zlib\",\n    srcs = [\n        \"adler32.c\",\n        \"compress.c\",\n        \"crc32.c\",\n        \"crc32.h\",\n        \"deflate.c\",\n        \"deflate.h\",\n        \"gzclose.c\",\n        \"gzguts.h\",\n        \"gzlib.c\",\n        \"gzread.c\",\n        \"gzwrite.c\",\n        \"infback.c\",\n        \"inffast.c\",\n        \"inffast.h\",\n        \"inffixed.h\",\n        \"inflate.c\",\n        \"inflate.h\",\n        \"inftrees.c\",\n        \"inftrees.h\",\n        \"trees.c\",\n        \"trees.h\",\n        \"uncompr.c\",\n        \"zconf.h\",\n        \"zutil.c\",\n        \"zutil.h\",\n    ],\n    hdrs = [\"zlib.h\"],\n    includes = [\".\"],\n)\n"
  },
  {
    "path": "tools/guetzli-compare.py",
    "content": "#!/usr/bin/env python\n\nfrom __future__ import print_function\nimport os\nimport glob\nimport sys\nimport subprocess\n\nBA_CMDLINE = './butteraugli {0} {1} {2}'\nGUETZLI_CMDLINE = './guetzli -quality {2} {0} {1}'\nOTHER_CMDLINE = sys.argv[3]\n\ndef run(cmdline):\n  print('running {}'.format(cmdline), file=sys.stderr)\n  return subprocess.check_output(cmdline, shell=True)\n\ndef size(filename):\n  return os.stat(filename).st_size\n\ndef ba_distance(orig, compressed):\n  return float(run(BA_CMDLINE.format(orig, compressed, compressed + \".diffmap.pnm\")))\n\ndef handle_png(png):\n  other_jpeg = png + \".\" + sys.argv[1] + \".other.jpg\"\n  guetzli_jpeg = png + \".\" + sys.argv[1] + \".guetzli.jpg\"\n  run(OTHER_CMDLINE.format(png, other_jpeg))\n  other_distance = ba_distance(png, other_jpeg)\n  left = 84.0\n  right = 110.0\n  while right - left > 0.05:\n    q = (left + right) / 2\n    run(GUETZLI_CMDLINE.format(png, guetzli_jpeg, q))\n    guetzli_distance = ba_distance(png, guetzli_jpeg)\n    if guetzli_distance < other_distance:\n      right = q\n    else:\n      left = q\n  run(GUETZLI_CMDLINE.format(png, guetzli_jpeg, right))\n  guetzli_distance = ba_distance(png, guetzli_jpeg)\n  assert guetzli_distance < other_distance\n  return (size(guetzli_jpeg), size(other_jpeg))\n\npngs = glob.glob(sys.argv[2])\nsizes = (0, 0)\nfor png in pngs:\n  this_size = handle_png(png)\n  print(png, this_size)\n  sizes = (sizes[0] + this_size[0], sizes[1] + this_size[1])\nprint(sizes)\n"
  }
]